1 /* 2 * Copyright 2018 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 #ifndef ANDROID_AUDIO_FD_TO_STRING_H 18 #define ANDROID_AUDIO_FD_TO_STRING_H 19 20 #include <fcntl.h> 21 #include <future> 22 #include <poll.h> 23 #include <sstream> 24 #include <unistd.h> 25 #include <utils/Timers.h> 26 27 #include "clock.h" 28 29 namespace android { 30 namespace audio_utils { 31 32 /** 33 * FdToString 34 * 35 * Captures string data written to a file descriptor. 36 * The class will furnish a writable file descriptor by fd(). 37 * The string may be read through getStringAndClose(). 38 */ 39 40 class FdToString { 41 public: 42 /** 43 * \param prefix is the prefix string prepended to each new line. 44 * \param timeout is the total timeout to wait for obtaining data. 45 */ 46 explicit FdToString(const std::string &prefix = "- ", int timeoutMs = 200) mPrefix(prefix)47 : mPrefix(prefix) 48 , mTimeoutTimeNs(systemTime() + timeoutMs * NANOS_PER_MILLISECOND) { 49 const int status = pipe2(mPipeFd, O_CLOEXEC); 50 if (status == 0) { 51 mOutput = std::async(std::launch::async, reader, mPipeFd[0], mTimeoutTimeNs, mPrefix); 52 } 53 // on initialization failure fd() returns -1. 54 } 55 ~FdToString()56 ~FdToString() { 57 for (auto &fd : mPipeFd) { 58 if (fd >= 0) { 59 close(fd); 60 fd = -1; 61 } 62 } 63 } 64 65 /** 66 * Returns the write end of the pipe as a file descriptor or -1 if invalid or already closed. 67 * 68 * Do not close this fd directly as this class should own the fd. Instead, use 69 * getStringAndClose() to close the fd and return the string. 70 */ fd()71 int fd() const { 72 return mPipeFd[1]; 73 } 74 75 /** 76 * Returns the string representation of data written to the fd. 77 * 78 * An empty string is returned on failure (or timeout). It is acceptable to call this 79 * method multiple times to obtain the final string; the fd is closed after the first call. 80 */ getStringAndClose()81 std::string getStringAndClose() { 82 if (!mOutput.valid()) return ""; 83 if (mPipeFd[1] >= 0) { 84 close(mPipeFd[1]); 85 mPipeFd[1] = -1; 86 } 87 const int waitMs = toMillisecondTimeoutDelay(systemTime(), mTimeoutTimeNs); 88 std::future_status status = mOutput.wait_for(std::chrono::milliseconds(waitMs)); 89 return status == std::future_status::ready ? mOutput.get() : ""; 90 } 91 92 private: reader(int fd,int64_t timeoutTimeNs,std::string prefix)93 static std::string reader(int fd, int64_t timeoutTimeNs, std::string prefix) { 94 char buf[4096]; 95 int red; 96 std::stringstream ss; 97 bool requiresPrefix = true; 98 99 while (true) { 100 struct pollfd pfd = { 101 .fd = fd, 102 .events = POLLIN | POLLRDHUP, 103 }; 104 const int waitMs = toMillisecondTimeoutDelay(systemTime(), timeoutTimeNs); 105 // ALOGD("waitMs: %d", waitMs); 106 if (waitMs <= 0) break; 107 const int retval = poll(&pfd, 1 /* nfds*/, waitMs); 108 if (retval <= 0 || (pfd.revents & POLLIN) != POLLIN) break; // error or timeout 109 // data is available 110 if ((red = read(fd, buf, sizeof(buf))) <= 0) break; 111 char *delim, *bptr = buf; 112 while (!prefix.empty() && (delim = (char *)memchr(bptr, '\n', red)) != nullptr) { 113 if (requiresPrefix) ss << prefix; 114 const size_t line = delim - bptr + 1; 115 ss.write(bptr, line); 116 bptr += line; 117 red -= line; 118 requiresPrefix = true; 119 } 120 if (red > 0) { 121 ss << prefix; 122 ss.write(bptr, red); 123 requiresPrefix = false; 124 } 125 } 126 return ss.str(); 127 } 128 129 const std::string mPrefix; 130 const int64_t mTimeoutTimeNs; 131 int mPipeFd[2] = {-1, -1}; 132 std::future<std::string> mOutput; 133 }; 134 135 } // namespace audio_utils 136 } // namespace android 137 138 #endif // !ANDROID_AUDIO_FD_TO_STRING_H 139