blob: 4ca0b8d44be897d513c34fe2ea81a5fb966e422d [file] [log] [blame]
/*
* Copyright 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 ANDROID_AUDIO_FD_TO_STRING_H
#define ANDROID_AUDIO_FD_TO_STRING_H
#include <fcntl.h>
#include <future>
#include <poll.h>
#include <sstream>
#include <unistd.h>
#include <utils/Timers.h>
#include "clock.h"
namespace android {
namespace audio_utils {
/**
* FdToString
*
* Captures string data written to a file descriptor.
* The class will furnish a writable file descriptor by fd().
* The string may be read through getStringAndClose().
*/
class FdToString {
public:
/**
* \param prefix is the prefix string prepended to each new line.
* \param timeoutMs is the total timeout to wait for obtaining data in milliseconds.
*/
explicit FdToString(const std::string &prefix = "- ", int timeoutMs = 200)
: mPrefix(prefix)
, mTimeoutTimeNs(systemTime() + timeoutMs * NANOS_PER_MILLISECOND) {
const int status = pipe2(mPipeFd, O_CLOEXEC);
if (status == 0) {
mOutput = std::async(std::launch::async, reader, mPipeFd[0], mTimeoutTimeNs, mPrefix);
}
// on initialization failure fd() returns -1.
}
~FdToString() {
for (auto &fd : mPipeFd) {
if (fd >= 0) {
close(fd);
fd = -1;
}
}
}
/**
* Returns the write end of the pipe as a file descriptor or -1 if invalid or already closed.
*
* Do not close this fd directly as this class should own the fd. Instead, use
* getStringAndClose() to close the fd and return the string.
*/
int fd() const {
return mPipeFd[1];
}
/**
* Returns the string representation of data written to the fd.
*
* An empty string is returned on failure (or timeout). It is acceptable to call this
* method multiple times to obtain the final string; the fd is closed after the first call.
*/
std::string getStringAndClose() {
if (!mOutput.valid()) return "";
if (mPipeFd[1] >= 0) {
close(mPipeFd[1]);
mPipeFd[1] = -1;
}
const int waitMs = toMillisecondTimeoutDelay(systemTime(), mTimeoutTimeNs);
std::future_status status = mOutput.wait_for(std::chrono::milliseconds(waitMs));
return status == std::future_status::ready ? mOutput.get() : "";
}
private:
static std::string reader(int fd, int64_t timeoutTimeNs, std::string prefix) {
char buf[4096];
int red;
std::stringstream ss;
bool requiresPrefix = true;
while (true) {
struct pollfd pfd = {
.fd = fd,
.events = POLLIN | POLLRDHUP,
};
const int waitMs = toMillisecondTimeoutDelay(systemTime(), timeoutTimeNs);
// ALOGD("waitMs: %d", waitMs);
if (waitMs <= 0) break;
const int retval = poll(&pfd, 1 /* nfds*/, waitMs);
if (retval <= 0 || (pfd.revents & POLLIN) != POLLIN) break; // error or timeout
// data is available
if ((red = read(fd, buf, sizeof(buf))) <= 0) break;
char *delim, *bptr = buf;
while (!prefix.empty() && (delim = (char *)memchr(bptr, '\n', red)) != nullptr) {
if (requiresPrefix) ss << prefix;
const size_t line = delim - bptr + 1;
ss.write(bptr, line);
bptr += line;
red -= line;
requiresPrefix = true;
}
if (red > 0) {
ss << prefix;
ss.write(bptr, red);
requiresPrefix = false;
}
}
return ss.str();
}
const std::string mPrefix;
const int64_t mTimeoutTimeNs;
int mPipeFd[2] = {-1, -1};
std::future<std::string> mOutput;
};
} // namespace audio_utils
} // namespace android
#endif // !ANDROID_AUDIO_FD_TO_STRING_H