move ringbuffer interactions to wifi hal

Shallow copy of ringbuffer is stored inside the hal.
The maximum ringbuffer size is configured by the
framework.

overrides IBase::debug to:
1. write ringbuffers in memory to flash (wifi tombstones)
2. write wifi tombstones to lshal debug fd in archived format

Bug: 70171481
Test: compile, run on device
Test: run "lshal debug android.hardware.wifi@1.2::IWifi >>
archive.cpio" and compared archived files with originals using diff command
Change-Id: I33c30e37f245cfa36e005ba8cdc46e91f6de9237
diff --git a/wifi/1.2/default/Android.mk b/wifi/1.2/default/Android.mk
index 95414bc..8d0262b 100644
--- a/wifi/1.2/default/Android.mk
+++ b/wifi/1.2/default/Android.mk
@@ -30,6 +30,7 @@
 LOCAL_SRC_FILES := \
     hidl_struct_util.cpp \
     hidl_sync_util.cpp \
+    ringbuffer.cpp \
     wifi.cpp \
     wifi_ap_iface.cpp \
     wifi_chip.cpp \
@@ -97,6 +98,7 @@
     tests/mock_wifi_feature_flags.cpp \
     tests/mock_wifi_legacy_hal.cpp \
     tests/mock_wifi_mode_controller.cpp \
+    tests/ringbuffer_unit_tests.cpp \
     tests/wifi_chip_unit_tests.cpp
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
diff --git a/wifi/1.2/default/ringbuffer.cpp b/wifi/1.2/default/ringbuffer.cpp
new file mode 100644
index 0000000..5511f2f
--- /dev/null
+++ b/wifi/1.2/default/ringbuffer.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ringbuffer.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+Ringbuffer::Ringbuffer(size_t maxSize) : size_(0), maxSize_(maxSize) {}
+
+void Ringbuffer::append(const std::vector<uint8_t>& input) {
+    if (input.size() == 0) {
+        return;
+    }
+    data_.push_back(input);
+    size_ += input.size() * sizeof(input[0]);
+    while (size_ > maxSize_) {
+        size_ -= data_.front().size() * sizeof(data_.front()[0]);
+        data_.pop_front();
+    }
+}
+
+const std::list<std::vector<uint8_t>>& Ringbuffer::getData() const {
+    return data_;
+}
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/ringbuffer.h b/wifi/1.2/default/ringbuffer.h
new file mode 100644
index 0000000..4808e40
--- /dev/null
+++ b/wifi/1.2/default/ringbuffer.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RINGBUFFER_H_
+#define RINGBUFFER_H_
+
+#include <list>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+/**
+ * Ringbuffer object used to store debug data.
+ */
+class Ringbuffer {
+   public:
+    explicit Ringbuffer(size_t maxSize);
+
+    // Appends the data buffer and deletes from the front until buffer is
+    // within |maxSize_|.
+    void append(const std::vector<uint8_t>& input);
+    const std::list<std::vector<uint8_t>>& getData() const;
+
+   private:
+    std::list<std::vector<uint8_t>> data_;
+    size_t size_;
+    size_t maxSize_;
+};
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+
+#endif  // RINGBUFFER_H_
diff --git a/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp b/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp
new file mode 100644
index 0000000..1b332f9
--- /dev/null
+++ b/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include "ringbuffer.h"
+
+using testing::Return;
+using testing::Test;
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+class RingbufferTest : public Test {
+   public:
+    const uint32_t maxBufferSize_ = 10;
+    Ringbuffer buffer_{maxBufferSize_};
+};
+
+TEST_F(RingbufferTest, CreateEmptyBuffer) {
+    ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, CanUseFullBufferCapacity) {
+    const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+    const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+    buffer_.append(input);
+    buffer_.append(input2);
+    ASSERT_EQ(2u, buffer_.getData().size());
+    EXPECT_EQ(input, buffer_.getData().front());
+    EXPECT_EQ(input2, buffer_.getData().back());
+}
+
+TEST_F(RingbufferTest, OldDataIsRemovedOnOverflow) {
+    const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+    const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+    const std::vector<uint8_t> input3 = {'G'};
+    buffer_.append(input);
+    buffer_.append(input2);
+    buffer_.append(input3);
+    ASSERT_EQ(2u, buffer_.getData().size());
+    EXPECT_EQ(input2, buffer_.getData().front());
+    EXPECT_EQ(input3, buffer_.getData().back());
+}
+
+TEST_F(RingbufferTest, MultipleOldDataIsRemovedOnOverflow) {
+    const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+    const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+    const std::vector<uint8_t> input3(maxBufferSize_, '2');
+    buffer_.append(input);
+    buffer_.append(input2);
+    buffer_.append(input3);
+    ASSERT_EQ(1u, buffer_.getData().size());
+    EXPECT_EQ(input3, buffer_.getData().front());
+}
+
+TEST_F(RingbufferTest, AppendingEmptyBufferDoesNotAddGarbage) {
+    const std::vector<uint8_t> input = {};
+    buffer_.append(input);
+    ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, OversizedAppendIsDropped) {
+    const std::vector<uint8_t> input(maxBufferSize_ + 1, '0');
+    buffer_.append(input);
+    ASSERT_TRUE(buffer_.getData().empty());
+}
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/wifi.cpp b/wifi/1.2/default/wifi.cpp
index 06f5058..d6106b4 100644
--- a/wifi/1.2/default/wifi.cpp
+++ b/wifi/1.2/default/wifi.cpp
@@ -77,6 +77,12 @@
                            &Wifi::getChipInternal, hidl_status_cb, chip_id);
 }
 
+Return<void> Wifi::debug(const hidl_handle& handle,
+                         const hidl_vec<hidl_string>&) {
+    LOG(INFO) << "-----------Debug is called----------------";
+    return chip_->debug(handle, {});
+}
+
 WifiStatus Wifi::registerEventCallbackInternal(
     const sp<IWifiEventCallback>& event_callback) {
     if (!event_cb_handler_.addCallback(event_callback)) {
diff --git a/wifi/1.2/default/wifi.h b/wifi/1.2/default/wifi.h
index 440c3c7..86919b1 100644
--- a/wifi/1.2/default/wifi.h
+++ b/wifi/1.2/default/wifi.h
@@ -56,6 +56,8 @@
     Return<void> stop(stop_cb hidl_status_cb) override;
     Return<void> getChipIds(getChipIds_cb hidl_status_cb) override;
     Return<void> getChip(ChipId chip_id, getChip_cb hidl_status_cb) override;
+    Return<void> debug(const hidl_handle& handle,
+                       const hidl_vec<hidl_string>& options) override;
 
    private:
     enum class RunState { STOPPED, STARTED, STOPPING };
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index adba054..8d39458 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <cutils/properties.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
 
 #include "hidl_return_util.h"
 #include "hidl_struct_util.h"
@@ -23,6 +28,7 @@
 #include "wifi_status_util.h"
 
 namespace {
+using android::base::unique_fd;
 using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 using android::hardware::wifi::V1_0::ChipModeId;
@@ -39,6 +45,11 @@
 // Mode ID for V2
 constexpr ChipModeId kV2ChipModeId = 2;
 
+constexpr char kCpioMagic[] = "070701";
+constexpr size_t kMaxBufferSizeBytes = 1024 * 1024;
+constexpr uint32_t kMaxRingBufferFileAgeSeconds = 60 * 60;
+constexpr char kTombstoneFolderPath[] = "/data/vendor/tombstones/wifi/";
+
 template <typename Iface>
 void invalidateAndClear(std::vector<sp<Iface>>& ifaces, sp<Iface> iface) {
     iface->invalidate();
@@ -93,6 +104,165 @@
     return buffer.data();
 }
 
+// delete files older than a predefined time in the wifi tombstone dir
+bool removeOldFilesInternal() {
+    time_t now = time(0);
+    const time_t delete_files_before = now - kMaxRingBufferFileAgeSeconds;
+    DIR* dir_dump = opendir(kTombstoneFolderPath);
+    if (!dir_dump) {
+        LOG(ERROR) << "Failed to open directory: " << strerror(errno);
+        return false;
+    }
+    unique_fd dir_auto_closer(dirfd(dir_dump));
+    struct dirent* dp;
+    bool success = true;
+    while ((dp = readdir(dir_dump))) {
+        if (dp->d_type != DT_REG) {
+            continue;
+        }
+        std::string cur_file_name(dp->d_name);
+        struct stat cur_file_stat;
+        std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+        if (stat(cur_file_path.c_str(), &cur_file_stat) != -1) {
+            if (cur_file_stat.st_mtime < delete_files_before) {
+                if (unlink(cur_file_path.c_str()) != 0) {
+                    LOG(ERROR) << "Error deleting file " << strerror(errno);
+                    success = false;
+                }
+            }
+        } else {
+            LOG(ERROR) << "Failed to get file stat for " << cur_file_path
+                       << ": " << strerror(errno);
+            success = false;
+        }
+    }
+    return success;
+}
+
+// Archives all files in |input_dir| and writes result into |out_fd|
+// Logic obtained from //external/toybox/toys/posix/cpio.c "Output cpio archive"
+// portion
+size_t cpioFilesInDir(int out_fd, const char* input_dir) {
+    struct dirent* dp;
+    size_t n_error = 0;
+    char read_buf[32 * 1024];
+    DIR* dir_dump = opendir(input_dir);
+    if (!dir_dump) {
+        LOG(ERROR) << "Failed to open directory: " << strerror(errno);
+        n_error++;
+        return n_error;
+    }
+    unique_fd dir_auto_closer(dirfd(dir_dump));
+    while ((dp = readdir(dir_dump))) {
+        if (dp->d_type != DT_REG) {
+            continue;
+        }
+        std::string cur_file_name(dp->d_name);
+        const size_t file_name_len =
+            cur_file_name.size() + 1;  // string.size() does not include the
+                                       // null terminator. The cpio FreeBSD file
+                                       // header expects the null character to
+                                       // be included in the length.
+        struct stat st;
+        ssize_t llen;
+        const std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+        if (stat(cur_file_path.c_str(), &st) != -1) {
+            const int fd_read = open(cur_file_path.c_str(), O_RDONLY);
+            unique_fd file_auto_closer(fd_read);
+            if (fd_read == -1) {
+                LOG(ERROR) << "Failed to read file " << cur_file_path << " "
+                           << strerror(errno);
+                n_error++;
+                continue;
+            }
+            llen = sprintf(
+                read_buf,
+                "%s%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
+                kCpioMagic, static_cast<int>(st.st_ino), st.st_mode, st.st_uid,
+                st.st_gid, static_cast<int>(st.st_nlink),
+                static_cast<int>(st.st_mtime), static_cast<int>(st.st_size),
+                major(st.st_dev), minor(st.st_dev), major(st.st_rdev),
+                minor(st.st_rdev), static_cast<uint32_t>(file_name_len), 0);
+            if (write(out_fd, read_buf, llen) == -1) {
+                LOG(ERROR) << "Error writing cpio header to file "
+                           << cur_file_path << " " << strerror(errno);
+                n_error++;
+                return n_error;
+            }
+            if (write(out_fd, cur_file_name.c_str(), file_name_len) == -1) {
+                LOG(ERROR) << "Error writing filename to file " << cur_file_path
+                           << " " << strerror(errno);
+                n_error++;
+                return n_error;
+            }
+
+            // NUL Pad header up to 4 multiple bytes.
+            llen = (llen + file_name_len) % 4;
+            if (llen != 0) {
+                const uint32_t zero = 0;
+                if (write(out_fd, &zero, 4 - llen) == -1) {
+                    LOG(ERROR) << "Error padding 0s to file " << cur_file_path
+                               << " " << strerror(errno);
+                    n_error++;
+                    return n_error;
+                }
+            }
+
+            // writing content of file
+            llen = st.st_size;
+            while (llen > 0) {
+                ssize_t bytes_read = read(fd_read, read_buf, sizeof(read_buf));
+                if (bytes_read == -1) {
+                    LOG(ERROR) << "Error reading file " << cur_file_path << " "
+                               << strerror(errno);
+                    n_error++;
+                    return n_error;
+                }
+                llen -= bytes_read;
+                if (write(out_fd, read_buf, bytes_read) == -1) {
+                    LOG(ERROR) << "Error writing data to file " << cur_file_path
+                               << " " << strerror(errno);
+                    n_error++;
+                    return n_error;
+                }
+                if (bytes_read ==
+                    0) {  // this should never happen, but just in case
+                          // to unstuck from while loop
+                    LOG(ERROR) << "Unexpected file size for " << cur_file_path
+                               << " " << strerror(errno);
+                    n_error++;
+                    break;
+                }
+            }
+            llen = st.st_size % 4;
+            if (llen != 0) {
+                const uint32_t zero = 0;
+                write(out_fd, &zero, 4 - llen);
+            }
+        } else {
+            LOG(ERROR) << "Failed to get file stat for " << cur_file_path
+                       << ": " << strerror(errno);
+            n_error++;
+        }
+    }
+    memset(read_buf, 0, sizeof(read_buf));
+    if (write(out_fd, read_buf,
+              sprintf(read_buf, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0) +
+                  4) == -1) {
+        LOG(ERROR) << "Error writing trailing bytes " << strerror(errno);
+        n_error++;
+    }
+    return n_error;
+}
+
+// Helper function to create a non-const char*.
+std::vector<char> makeCharVec(const std::string& str) {
+    std::vector<char> vec(str.size() + 1);
+    vec.assign(str.begin(), str.end());
+    vec.push_back('\0');
+    return vec;
+}
+
 }  // namespace
 
 namespace android {
@@ -350,6 +520,24 @@
                            hidl_status_cb);
 }
 
+Return<void> WifiChip::debug(const hidl_handle& handle,
+                             const hidl_vec<hidl_string>&) {
+    if (handle != nullptr && handle->numFds >= 1) {
+        int fd = handle->data[0];
+        if (!writeRingbufferFilesInternal()) {
+            LOG(ERROR) << "Error writing files to flash";
+        }
+        uint32_t n_error = cpioFilesInDir(fd, kTombstoneFolderPath);
+        if (n_error != 0) {
+            LOG(ERROR) << n_error << " errors occured in cpio function";
+        }
+        fsync(fd);
+    } else {
+        LOG(ERROR) << "File handle error";
+    }
+    return Void();
+}
+
 void WifiChip::invalidateAndRemoveAllIfaces() {
     invalidateAndClearAll(ap_ifaces_);
     invalidateAndClearAll(nan_ifaces_);
@@ -726,6 +914,8 @@
                 std::underlying_type<WifiDebugRingBufferVerboseLevel>::type>(
                 verbose_level),
             max_interval_in_sec, min_data_size_in_bytes);
+    ringbuffer_map_.insert(std::pair<std::string, Ringbuffer>(
+        ring_name, Ringbuffer(kMaxBufferSizeBytes)));
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
@@ -737,6 +927,7 @@
     }
     legacy_hal::wifi_error legacy_status =
         legacy_hal_.lock()->getRingBufferData(getWlan0IfaceName(), ring_name);
+
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
@@ -848,7 +1039,7 @@
 
     android::wp<WifiChip> weak_ptr_this(this);
     const auto& on_ring_buffer_data_callback =
-        [weak_ptr_this](const std::string& /* name */,
+        [weak_ptr_this](const std::string& name,
                         const std::vector<uint8_t>& data,
                         const legacy_hal::wifi_ring_buffer_status& status) {
             const auto shared_ptr_this = weak_ptr_this.promote();
@@ -862,13 +1053,13 @@
                 LOG(ERROR) << "Error converting ring buffer status";
                 return;
             }
-            for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
-                if (!callback->onDebugRingBufferDataAvailable(hidl_status, data)
-                         .isOk()) {
-                    LOG(ERROR)
-                        << "Failed to invoke onDebugRingBufferDataAvailable"
-                        << " callback on: " << toString(callback);
-                }
+            const auto& target = shared_ptr_this->ringbuffer_map_.find(name);
+            if (target != shared_ptr_this->ringbuffer_map_.end()) {
+                Ringbuffer& cur_buffer = target->second;
+                cur_buffer.append(data);
+            } else {
+                LOG(ERROR) << "Ringname " << name << " not found";
+                return;
             }
         };
     legacy_hal::wifi_error legacy_status =
@@ -1079,6 +1270,35 @@
     return {};
 }
 
+bool WifiChip::writeRingbufferFilesInternal() {
+    if (!removeOldFilesInternal()) {
+        LOG(ERROR) << "Error occurred while deleting old tombstone files";
+        return false;
+    }
+    // write ringbuffers to file
+    for (const auto& item : ringbuffer_map_) {
+        const Ringbuffer& cur_buffer = item.second;
+        if (cur_buffer.getData().empty()) {
+            continue;
+        }
+        const std::string file_path_raw =
+            kTombstoneFolderPath + item.first + "XXXXXXXXXX";
+        const int dump_fd = mkstemp(makeCharVec(file_path_raw).data());
+        if (dump_fd == -1) {
+            LOG(ERROR) << "create file failed: " << strerror(errno);
+            return false;
+        }
+        unique_fd file_auto_closer(dump_fd);
+        for (const auto& cur_block : cur_buffer.getData()) {
+            if (write(dump_fd, cur_block.data(),
+                      sizeof(cur_block[0]) * cur_block.size()) == -1) {
+                LOG(ERROR) << "Error writing to file " << strerror(errno);
+            }
+        }
+    }
+    return true;
+}
+
 }  // namespace implementation
 }  // namespace V1_2
 }  // namespace wifi
diff --git a/wifi/1.2/default/wifi_chip.h b/wifi/1.2/default/wifi_chip.h
index b5dcc8c..4ffa8da 100644
--- a/wifi/1.2/default/wifi_chip.h
+++ b/wifi/1.2/default/wifi_chip.h
@@ -17,12 +17,14 @@
 #ifndef WIFI_CHIP_H_
 #define WIFI_CHIP_H_
 
+#include <list>
 #include <map>
 
 #include <android-base/macros.h>
 #include <android/hardware/wifi/1.2/IWifiChip.h>
 
 #include "hidl_callback_util.h"
+#include "ringbuffer.h"
 #include "wifi_ap_iface.h"
 #include "wifi_feature_flags.h"
 #include "wifi_legacy_hal.h"
@@ -134,6 +136,8 @@
         selectTxPowerScenario_cb hidl_status_cb) override;
     Return<void> resetTxPowerScenario(
         resetTxPowerScenario_cb hidl_status_cb) override;
+    Return<void> debug(const hidl_handle& handle,
+                       const hidl_vec<hidl_string>& options) override;
 
    private:
     void invalidateAndRemoveAllIfaces();
@@ -204,6 +208,7 @@
     bool canCurrentModeSupportIfaceOfType(IfaceType type);
     bool isValidModeId(ChipModeId mode_id);
     std::string allocateApOrStaIfaceName();
+    bool writeRingbufferFilesInternal();
 
     ChipId chip_id_;
     std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
@@ -214,6 +219,7 @@
     std::vector<sp<WifiP2pIface>> p2p_ifaces_;
     std::vector<sp<WifiStaIface>> sta_ifaces_;
     std::vector<sp<WifiRttController>> rtt_controllers_;
+    std::map<std::string, Ringbuffer> ringbuffer_map_;
     bool is_valid_;
     // Members pertaining to chip configuration.
     uint32_t current_mode_id_;