Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4bc2fd9b authored by Dieter Hsu's avatar Dieter Hsu Committed by Automerger Merge Worker
Browse files

Merge changes from topic "br-stream-zip" am: c9e0e9ef

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1467063

Change-Id: Ia67d9babc06760b4cb72ed1d0f056c6214390483
parents 340d9f18 c9e0e9ef
Loading
Loading
Loading
Loading
+96 −144
Original line number Diff line number Diff line
@@ -719,6 +719,9 @@ static unsigned long logcat_timeout(const std::vector<std::string>& buffers) {
    return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
}

// Opens a socket and returns its file descriptor.
static int open_socket(const char* service);

Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) {
}

@@ -2286,20 +2289,18 @@ void Dumpstate::DumpstateBoard(int out_fd) {

static void ShowUsage() {
    fprintf(stderr,
            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] "
            "[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-p] "
            "[-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
            "  -h: display this help message\n"
            "  -b: play sound file instead of vibrate, at beginning of job\n"
            "  -e: play sound file instead of vibrate, at end of job\n"
            "  -o: write to custom directory (only in limited mode)\n"
            "  -d: append date to filename\n"
            "  -p: capture screenshot to filename.png\n"
            "  -z: generate zipped file\n"
            "  -s: write output to control socket (for init)\n"
            "  -S: write file location to control socket (for init; requires -z)\n"
            "  -s: write zipped file to control socket (for init)\n"
            "  -S: write file location to control socket (for init)\n"
            "  -q: disable vibrate\n"
            "  -P: send broadcast when started and do progress updates\n"
            "  -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n"
            "  -R: take bugreport in remote mode (shouldn't be used with -P)\n"
            "  -w: start binder service and make it wait for a call to startBugreport\n"
            "  -L: output limited information that is safe for submission in feedback reports\n"
            "  -v: prints the dumpstate header and exit\n");
@@ -2398,21 +2399,17 @@ static void MaybeResolveSymlink(std::string* path) {

/*
 * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter
 * if we are writing zip files and adds the version file.
 * and adds the version file. Return false if zip_file could not be open to write.
 */
static void PrepareToWriteToFile() {
static bool PrepareToWriteToFile() {
    MaybeResolveSymlink(&ds.bugreport_internal_dir_);

    std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
    std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE");
    ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str());
    if (ds.options_->do_add_date) {
    char date[80];
    strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
    ds.name_ = date;
    } else {
        ds.name_ = "undated";
    }

    if (ds.options_->telephony_only) {
        ds.base_name_ += "-telephony";
@@ -2439,18 +2436,17 @@ static void PrepareToWriteToFile() {
        destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(),
        ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());

    if (ds.options_->do_zip_file) {
    ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
    MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
    create_parent_dirs(ds.path_.c_str());
    ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
    if (ds.zip_file == nullptr) {
        MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
        } else {
            ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
        return false;
    }
    ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
    ds.AddTextZipEntry("version.txt", ds.version_);
    }
    return true;
}

/*
@@ -2458,14 +2454,9 @@ static void PrepareToWriteToFile() {
 * printing zipped file status, etc.
 */
static void FinalizeFile() {
    bool do_text_file = true;
    if (ds.options_->do_zip_file) {
        if (!ds.FinishZipFile()) {
    bool do_text_file = !ds.FinishZipFile();
    if (do_text_file) {
        MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
            do_text_file = true;
        } else {
            do_text_file = false;
        }
    }

    std::string final_path = ds.path_;
@@ -2474,7 +2465,9 @@ static void FinalizeFile() {
        android::os::CopyFileToFile(ds.path_, final_path);
    }

    if (ds.options_->use_control_socket) {
    if (ds.options_->stream_to_socket) {
        android::os::CopyFileToFd(ds.path_, ds.control_socket_fd_);
    } else if (ds.options_->progress_updates_to_socket) {
        if (do_text_file) {
            dprintf(ds.control_socket_fd_,
                    "FAIL:could not create zip file, check %s "
@@ -2530,7 +2523,6 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt
            break;
        case Dumpstate::BugreportMode::BUGREPORT_WEAR:
            options->do_progress_updates = true;
            options->do_zip_file = true;
            options->do_screenshot = is_screenshot_requested;
            options->dumpstate_hal_mode = DumpstateMode::WEAR;
            break;
@@ -2543,7 +2535,6 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt
            break;
        case Dumpstate::BugreportMode::BUGREPORT_WIFI:
            options->wifi_only = true;
            options->do_zip_file = true;
            options->do_screenshot = false;
            options->dumpstate_hal_mode = DumpstateMode::WIFI;
            break;
@@ -2554,11 +2545,11 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt

static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
    MYLOGI(
        "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
        "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d "
        "is_remote_mode: %d show_header_only: %d telephony_only: %d "
        "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
        "limited_only: %d args: %s\n",
        options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
        options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket,
        options.do_screenshot, options.is_remote_mode, options.show_header_only,
        options.telephony_only, options.wifi_only,
        options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
@@ -2569,11 +2560,6 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
                                        const android::base::unique_fd& bugreport_fd_in,
                                        const android::base::unique_fd& screenshot_fd_in,
                                        bool is_screenshot_requested) {
    // In the new API world, date is always added; output is always a zip file.
    // TODO(111441001): remove these options once they are obsolete.
    do_add_date = true;
    do_zip_file = true;

    // Duplicate the fds because the passed in fds don't outlive the binder transaction.
    bugreport_fd.reset(dup(bugreport_fd_in.get()));
    screenshot_fd.reset(dup(screenshot_fd_in.get()));
@@ -2587,18 +2573,20 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[])
    while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) {
        switch (c) {
            // clang-format off
            case 'd': do_add_date = true;            break;
            case 'z': do_zip_file = true;            break;
            case 'o': out_dir = optarg;              break;
            case 's': use_socket = true;             break;
            case 'S': use_control_socket = true;     break;
            case 's': stream_to_socket = true;       break;
            case 'S': progress_updates_to_socket = true;    break;
            case 'v': show_header_only = true;       break;
            case 'q': do_vibrate = false;            break;
            case 'p': do_screenshot = true;          break;
            case 'P': do_progress_updates = true;    break;
            case 'R': is_remote_mode = true;         break;
            case 'L': limited_only = true;           break;
            case 'V':                                break;  // compatibility no-op
            case 'V':
            case 'd':
            case 'z':
                // compatibility no-op
                break;
            case 'w':
                // This was already processed
                break;
@@ -2627,19 +2615,15 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[])
}

bool Dumpstate::DumpOptions::ValidateOptions() const {
    if (bugreport_fd.get() != -1 && !do_zip_file) {
    if (bugreport_fd.get() != -1 && stream_to_socket) {
        return false;
    }

    if ((do_zip_file || do_add_date || do_progress_updates) && !OutputToFile()) {
    if ((progress_updates_to_socket || do_progress_updates) && stream_to_socket) {
        return false;
    }

    if (use_control_socket && !do_zip_file) {
        return false;
    }

    if (is_remote_mode && (do_progress_updates || !do_zip_file || !do_add_date)) {
    if (is_remote_mode && (do_progress_updates || stream_to_socket)) {
        return false;
    }
    return true;
@@ -2714,11 +2698,9 @@ void Dumpstate::Cancel() {
 * The temporary bugreport is then populated via printfs, dumping contents of files and
 * output of commands to stdout.
 *
 * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final
 * text file.
 * A bunch of other files and dumps are added to the zip archive.
 *
 * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also
 * gets added to the archive.
 * The temporary bugreport file and the log file also get added to the archive.
 *
 * Bugreports are first generated in a local directory and later copied to the caller's fd
 * or directory if supplied.
@@ -2766,14 +2748,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
    MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n",
            calling_uid, calling_package.c_str());

    // Redirect output if needed
    bool is_redirecting = options_->OutputToFile();

    // TODO: temporarily set progress until it's part of the Dumpstate constructor
    std::string stats_path =
        is_redirecting
            ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str())
            : "";
        android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str());
    progress_.reset(new Progress(stats_path));

    if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
@@ -2796,24 +2773,23 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,

    // If we are going to use a socket, do it as early as possible
    // to avoid timeouts from bugreport.
    if (options_->use_socket) {
        if (!redirect_to_socket(stdout, "dumpstate")) {
            return ERROR;
        }
    }

    if (options_->use_control_socket) {
    if (options_->stream_to_socket || options_->progress_updates_to_socket) {
        MYLOGD("Opening control socket\n");
        control_socket_fd_ = open_socket("dumpstate");
        control_socket_fd_ = open_socket_fn_("dumpstate");
        if (control_socket_fd_ == -1) {
            return ERROR;
        }
        if (options_->progress_updates_to_socket) {
            options_->do_progress_updates = 1;
        }
    }

    if (is_redirecting) {
        PrepareToWriteToFile();
    if (!PrepareToWriteToFile()) {
        return ERROR;
    }

    // Interactive, wear & telephony modes are default to true.
    // and may enable from cli option or when using control socket
    if (options_->do_progress_updates) {
        // clang-format off
        std::vector<std::string> am_args = {
@@ -2822,11 +2798,10 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
        // clang-format on
        // Send STARTED broadcast for apps that listen to bugreport generation events
        SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
            if (options_->use_control_socket) {
        if (options_->progress_updates_to_socket) {
            dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str());
        }
    }
    }

    /* read /proc/cmdline before dropping root */
    FILE *cmdline = fopen("/proc/cmdline", "re");
@@ -2839,7 +2814,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
        Vibrate(150);
    }

    if (options_->do_zip_file && zip_file != nullptr) {
    if (zip_file != nullptr) {
        if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) {
            MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(),
                    strerror(errno));
@@ -2848,7 +2823,6 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,

    int dup_stdout_fd;
    int dup_stderr_fd;
    if (is_redirecting) {
    // Redirect stderr to log_path_ for debugging.
    TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
    if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) {
@@ -2873,7 +2847,6 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
        MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
                tmp_path_.c_str(), strerror(errno));
    }
    }

    // Don't buffer stdout
    setvbuf(stdout, nullptr, _IONBF, 0);
@@ -2926,14 +2899,10 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
    }

    /* close output if needed */
    if (is_redirecting) {
    TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
    }

    // Zip the (now complete) .tmp file within the internal directory.
    if (options_->OutputToFile()) {
    FinalizeFile();
    }

    // Share the final file with the caller if the user has consented or Shell is the caller.
    Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
@@ -2976,11 +2945,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
    progress_->Save();
    MYLOGI("done (id %d)\n", id_);

    if (is_redirecting) {
    TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));
    }

    if (options_->use_control_socket && control_socket_fd_ != -1) {
    if (control_socket_fd_ != -1) {
        MYLOGD("Closing control socket\n");
        close(control_socket_fd_);
    }
@@ -3051,10 +3018,7 @@ void Dumpstate::CleanupTmpFiles() {
}

void Dumpstate::EnableParallelRunIfNeeded() {
    // The thread pool needs to create temporary files to receive dump results.
    // That's why we only enable it when the bugreport client chooses to output
    // to a file.
    if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) {
    if (!PropertiesHelper::IsParallelRun()) {
        return;
    }
    dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
@@ -3199,7 +3163,8 @@ Dumpstate::Dumpstate(const std::string& version)
      options_(new Dumpstate::DumpOptions()),
      last_reported_percent_progress_(0),
      version_(version),
      now_(time(nullptr)) {
      now_(time(nullptr)),
      open_socket_fn_(open_socket) {
}

Dumpstate& Dumpstate::GetInstance() {
@@ -3833,7 +3798,7 @@ void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::stri
    RunCommand(title, dumpsys, options, false, out_fd);
}

int open_socket(const char *service) {
static int open_socket(const char* service) {
    int s = android_get_control_socket(service);
    if (s < 0) {
        MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
@@ -3868,19 +3833,6 @@ int open_socket(const char *service) {
    return fd;
}

/* redirect output to a service control socket */
bool redirect_to_socket(FILE* redirect, const char* service) {
    int fd = open_socket(service);
    if (fd == -1) {
        return false;
    }
    fflush(redirect);
    // TODO: handle dup2 failure
    TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect)));
    close(fd);
    return true;
}

// TODO: should call is_valid_output_file and/or be merged into it.
void create_parent_dirs(const char *path) {
    char *chp = const_cast<char *> (path);
+12 −25
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ namespace dumpstate {

class DumpstateTest;
class ProgressTest;
class ZippedBugReportStreamTest;

}  // namespace dumpstate
}  // namespace os
@@ -197,6 +198,7 @@ struct DumpData {
 */
class Dumpstate {
    friend class android::os::dumpstate::DumpstateTest;
    friend class android::os::dumpstate::ZippedBugReportStreamTest;

  public:
    enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
@@ -388,12 +390,11 @@ class Dumpstate {
     * Structure to hold options that determine the behavior of dumpstate.
     */
    struct DumpOptions {
        bool do_add_date = false;
        bool do_zip_file = false;
        bool do_vibrate = true;
        // Writes bugreport content to a socket; only flatfile format is supported.
        bool use_socket = false;
        bool use_control_socket = false;
        // Writes bugreport zipped file to a socket.
        bool stream_to_socket = false;
        // Writes generation progress updates to a socket.
        bool progress_updates_to_socket = false;
        bool do_screenshot = false;
        bool is_screenshot_copied = false;
        bool is_remote_mode = false;
@@ -434,13 +435,6 @@ class Dumpstate {
        /* Returns true if the options set so far are consistent. */
        bool ValidateOptions() const;

        /* Returns if options specified require writing bugreport to a file */
        bool OutputToFile() const {
            // If we are not writing to socket, we will write to a file. If bugreport_fd is
            // specified, it is preferred. If not bugreport is written to /bugreports.
            return !use_socket;
        }

        /* Returns if options specified require writing to custom file location */
        bool OutputToCustomFile() {
            // Custom location is only honored in limited mode.
@@ -466,7 +460,8 @@ class Dumpstate {

    std::unique_ptr<Progress> progress_;

    // When set, defines a socket file-descriptor use to report progress to bugreportz.
    // When set, defines a socket file-descriptor use to report progress to bugreportz
    // or to stream the zipped file to.
    int control_socket_fd_ = -1;

    // Bugreport format version;
@@ -478,8 +473,8 @@ class Dumpstate {
    // `bugreport-BUILD_ID`.
    std::string base_name_;

    // Name is the suffix part of the bugreport files - it's typically the date (when invoked with
    // `-d`), but it could be changed by the user..
    // Name is the suffix part of the bugreport files - it's typically the date,
    // but it could be changed by the user..
    std::string name_;

    std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY;
@@ -568,6 +563,8 @@ class Dumpstate {
    // called by Shell.
    RunStatus CopyBugreportIfUserConsented(int32_t calling_uid);

    std::function<int(const char *)> open_socket_fn_;

    // Used by GetInstance() only.
    explicit Dumpstate(const std::string& version = VERSION_CURRENT);

@@ -601,16 +598,6 @@ int dump_file_from_fd(const char *title, const char *path, int fd);
int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
               int (*dump_from_fd)(const char* title, const char* path, int fd));

/** opens a socket and returns its file descriptor */
int open_socket(const char *service);

/*
 * Redirects 'redirect' to a service control socket.
 *
 * Returns true if redirect succeeds.
 */
bool redirect_to_socket(FILE* redirect, const char* service);

/*
 * Redirects 'redirect' to a file indicated by 'path', truncating it.
 *
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ service dumpstate /system/bin/dumpstate -s

# dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
# it is finished.
service dumpstatez /system/bin/dumpstate -S -d -z
service dumpstatez /system/bin/dumpstate -S
    socket dumpstate stream 0660 shell log
    class main
    disabled
+1 −3
Original line number Diff line number Diff line
@@ -212,9 +212,7 @@ class ZippedBugreportGenerationTest : public Test {
    static void GenerateBugreport() {
        // clang-format off
        char* argv[] = {
            (char*)"dumpstate",
            (char*)"-d",
            (char*)"-z"
            (char*)"dumpstate"
        };
        // clang-format on
        sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
+103 −89

File changed.

Preview size limit exceeded, changes collapsed.