/*
* 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.
*/
#define LOG_TAG "NBLog"
//#define LOG_NDEBUG 0
#include <memory>
#include <stddef.h>
#include <stdint.h>
#include <audio_utils/fifo.h>
#include <media/nblog/Entry.h>
#include <media/nblog/Events.h>
#include <utils/Log.h>
namespace android {
namespace NBLog {
int Entry::copyEntryDataAt(size_t offset) const
{
// FIXME This is too slow
if (offset == 0) {
return mEvent;
} else if (offset == 1) {
return mLength;
} else if (offset < (size_t) (mLength + 2)) {
return (int) ((char *) mData)[offset - 2];
} else if (offset == (size_t) (mLength + 2)) {
return mLength;
} else {
return 0; // FIXME is this an error?
}
}
EntryIterator::EntryIterator() // Dummy initialization.
: mPtr(nullptr)
{
}
EntryIterator::EntryIterator(const uint8_t *entry)
: mPtr(entry)
{
}
EntryIterator::EntryIterator(const EntryIterator &other)
: mPtr(other.mPtr)
{
}
const entry& EntryIterator::operator*() const
{
return *(entry*) mPtr;
}
const entry* EntryIterator::operator->() const
{
return (entry*) mPtr;
}
EntryIterator& EntryIterator::operator++()
{
mPtr += mPtr[offsetof(entry, length)] + Entry::kOverhead;
return *this;
}
EntryIterator& EntryIterator::operator--()
{
mPtr -= mPtr[Entry::kPreviousLengthOffset] + Entry::kOverhead;
return *this;
}
EntryIterator EntryIterator::next() const
{
EntryIterator aux(*this);
return ++aux;
}
EntryIterator EntryIterator::prev() const
{
EntryIterator aux(*this);
return --aux;
}
bool EntryIterator::operator!=(const EntryIterator &other) const
{
return mPtr != other.mPtr;
}
int EntryIterator::operator-(const EntryIterator &other) const
{
return mPtr - other.mPtr;
}
bool EntryIterator::hasConsistentLength() const
{
return mPtr[offsetof(entry, length)] == mPtr[mPtr[offsetof(entry, length)] +
Entry::kOverhead + Entry::kPreviousLengthOffset];
}
void EntryIterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const
{
size_t length = mPtr[offsetof(entry, length)] + Entry::kOverhead;
dst->write(mPtr, length);
}
void EntryIterator::copyData(uint8_t *dst) const
{
memcpy((void*) dst, mPtr + offsetof(entry, data), mPtr[offsetof(entry, length)]);
}
// ---------------------------------------------------------------------------
std::unique_ptr<AbstractEntry> AbstractEntry::buildEntry(const uint8_t *ptr)
{
if (ptr == nullptr) {
return nullptr;
}
const uint8_t type = EntryIterator(ptr)->type;
switch (type) {
case EVENT_FMT_START:
return std::make_unique<FormatEntry>(FormatEntry(ptr));
case EVENT_AUDIO_STATE:
case EVENT_HISTOGRAM_ENTRY_TS:
return std::make_unique<HistogramEntry>(HistogramEntry(ptr));
default:
ALOGW("Tried to create AbstractEntry of type %d", type);
return nullptr;
}
}
EntryIterator FormatEntry::begin() const
{
return EntryIterator(mEntry);
}
const char *FormatEntry::formatString() const
{
return (const char*) mEntry + offsetof(entry, data);
}
size_t FormatEntry::formatStringLength() const
{
return mEntry[offsetof(entry, length)];
}
EntryIterator FormatEntry::args() const
{
auto it = begin();
++it; // skip start fmt
++it; // skip timestamp
++it; // skip hash
// Skip author if present
if (it->type == EVENT_FMT_AUTHOR) {
++it;
}
return it;
}
int64_t FormatEntry::timestamp() const
{
auto it = begin();
++it; // skip start fmt
return it.payload<int64_t>();
}
log_hash_t FormatEntry::hash() const
{
auto it = begin();
++it; // skip start fmt
++it; // skip timestamp
// unaligned 64-bit read not supported
log_hash_t hash;
memcpy(&hash, it->data, sizeof(hash));
return hash;
}
int FormatEntry::author() const
{
auto it = begin();
++it; // skip start fmt
++it; // skip timestamp
++it; // skip hash
// if there is an author entry, return it, return -1 otherwise
return it->type == EVENT_FMT_AUTHOR ? it.payload<int>() : -1;
}
EntryIterator FormatEntry::copyWithAuthor(
std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const
{
auto it = begin();
it.copyTo(dst); // copy fmt start entry
(++it).copyTo(dst); // copy timestamp
(++it).copyTo(dst); // copy hash
// insert author entry
size_t authorEntrySize = Entry::kOverhead + sizeof(author);
uint8_t authorEntry[authorEntrySize];
authorEntry[offsetof(entry, type)] = EVENT_FMT_AUTHOR;
authorEntry[offsetof(entry, length)] =
authorEntry[authorEntrySize + Entry::kPreviousLengthOffset] =
sizeof(author);
*(int*) (&authorEntry[offsetof(entry, data)]) = author;
dst->write(authorEntry, authorEntrySize);
// copy rest of entries
while ((++it)->type != EVENT_FMT_END) {
it.copyTo(dst);
}
it.copyTo(dst);
++it;
return it;
}
int64_t HistogramEntry::timestamp() const
{
return EntryIterator(mEntry).payload<HistTsEntry>().ts;
}
log_hash_t HistogramEntry::hash() const
{
return EntryIterator(mEntry).payload<HistTsEntry>().hash;
}
int HistogramEntry::author() const
{
EntryIterator it(mEntry);
return it->length == sizeof(HistTsEntryWithAuthor)
? it.payload<HistTsEntryWithAuthor>().author : -1;
}
EntryIterator HistogramEntry::copyWithAuthor(
std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const
{
// Current histogram entry has {type, length, struct HistTsEntry, length}.
// We now want {type, length, struct HistTsEntryWithAuthor, length}
uint8_t buffer[Entry::kOverhead + sizeof(HistTsEntryWithAuthor)];
// Copy content until the point we want to add the author
memcpy(buffer, mEntry, sizeof(entry) + sizeof(HistTsEntry));
// Copy the author
*(int*) (buffer + sizeof(entry) + sizeof(HistTsEntry)) = author;
// Update lengths
buffer[offsetof(entry, length)] = sizeof(HistTsEntryWithAuthor);
buffer[offsetof(entry, data) + sizeof(HistTsEntryWithAuthor) + offsetof(ending, length)]
= sizeof(HistTsEntryWithAuthor);
// Write new buffer into FIFO
dst->write(buffer, sizeof(buffer));
return EntryIterator(mEntry).next();
}
} // namespace NBLog
} // namespace android