blob: 64f01bd8ea1819c9a6207304198a55784cf9c88e [file] [log] [blame]
Felipe Leme698e0652016-07-19 17:07:22 -07001/*
2 * Copyright (C) 2016 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#include <string>
18
19#include <android-base/strings.h>
20
21#include "bugreport.h"
Felipe Leme698e0652016-07-19 17:07:22 -070022#include "file_sync_service.h"
23
24static constexpr char BUGZ_OK_PREFIX[] = "OK:";
25static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
Felipe Leme6f5080f2016-07-26 12:23:40 -070026static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
27static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
Felipe Leme698e0652016-07-19 17:07:22 -070028
Felipe Leme0d4f0502016-07-26 12:14:39 -070029// Custom callback used to handle the output of zipped bugreports.
30class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
31 public:
Felipe Leme6f5080f2016-07-26 12:23:40 -070032 BugreportStandardStreamsCallback(const std::string& dest_file, bool show_progress, Bugreport* br)
Felipe Lemedaf46282016-07-27 19:23:09 -070033 : br_(br),
34 dest_file_(dest_file),
35 line_message_(android::base::StringPrintf("generating %s", dest_file_.c_str())),
36 show_progress_(show_progress),
37 status_(-1),
38 line_() {
Felipe Leme0d4f0502016-07-26 12:14:39 -070039 }
40
41 void OnStdout(const char* buffer, int length) {
Felipe Leme6f5080f2016-07-26 12:23:40 -070042 for (int i = 0; i < length; i++) {
43 char c = buffer[i];
44 if (c == '\n') {
45 ProcessLine(line_);
46 line_.clear();
47 } else {
48 line_.append(1, c);
49 }
50 }
Felipe Leme0d4f0502016-07-26 12:14:39 -070051 }
52
53 void OnStderr(const char* buffer, int length) {
54 OnStream(nullptr, stderr, buffer, length);
55 }
Felipe Leme0d4f0502016-07-26 12:14:39 -070056 int Done(int unused_) {
Felipe Leme6f5080f2016-07-26 12:23:40 -070057 // Process remaining line, if any...
58 ProcessLine(line_);
59 // ..then return.
60 return status_;
Felipe Leme0d4f0502016-07-26 12:14:39 -070061 }
62
63 private:
Felipe Leme6f5080f2016-07-26 12:23:40 -070064 void ProcessLine(const std::string& line) {
65 if (line.empty()) return;
66
67 if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
68 if (show_progress_) {
69 // Make sure pull message doesn't conflict with generation message.
Felipe Lemedaf46282016-07-27 19:23:09 -070070 br_->UpdateProgress(line_message_, 100, 100);
Felipe Leme6f5080f2016-07-26 12:23:40 -070071 }
72
73 const char* zip_file = &line[strlen(BUGZ_OK_PREFIX)];
74 std::vector<const char*> srcs{zip_file};
Felipe Lemedaf46282016-07-27 19:23:09 -070075 status_ = br_->DoSyncPull(srcs, dest_file_.c_str(), true, line_message_.c_str()) ? 0 : 1;
Felipe Leme6f5080f2016-07-26 12:23:40 -070076 if (status_ != 0) {
77 fprintf(stderr, "Could not copy file '%s' to '%s'\n", zip_file, dest_file_.c_str());
78 }
79 } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
80 const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
81 fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
82 status_ = -1;
83 } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
84 // progress_line should have the following format:
85 //
86 // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
87 //
88 size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
89 size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
90 int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
91 int total = std::stoi(line.substr(idx2 + 1));
92 br_->UpdateProgress(dest_file_, progress, total);
93 } else {
94 fprintf(stderr,
95 "WARNING: unexpected line (%s) returned by bugreportz, "
96 "device probably does not support zipped bugreports.\n"
97 "Try 'adb bugreport' instead.",
98 line.c_str());
99 }
100 }
101
Felipe Leme0d4f0502016-07-26 12:14:39 -0700102 Bugreport* br_;
103 const std::string dest_file_;
Felipe Lemedaf46282016-07-27 19:23:09 -0700104 const std::string line_message_;
Felipe Leme6f5080f2016-07-26 12:23:40 -0700105 bool show_progress_;
106 int status_;
107
108 // Temporary buffer containing the characters read since the last newline
109 // (\n).
110 std::string line_;
Felipe Leme0d4f0502016-07-26 12:14:39 -0700111
112 DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
113};
114
Felipe Leme6f5080f2016-07-26 12:23:40 -0700115// Implemented in commandline.cpp
116int usage();
117
Felipe Leme698e0652016-07-19 17:07:22 -0700118int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
119 if (argc == 1) return SendShellCommand(transport_type, serial, "bugreport", false);
120 if (argc != 2) return usage();
121
122 // Zipped bugreport option - will call 'bugreportz', which prints the location
123 // of the generated
124 // file, then pull it to the destination file provided by the user.
125 std::string dest_file = argv[1];
126 if (!android::base::EndsWith(argv[1], ".zip")) {
127 // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
128 dest_file += ".zip";
129 }
Felipe Leme6f5080f2016-07-26 12:23:40 -0700130
131 // Gets bugreportz version.
132 std::string bugz_stderr;
133 DefaultStandardStreamsCallback version_callback(nullptr, &bugz_stderr);
134 int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
135
136 if (status != 0) {
137 fprintf(stderr,
138 "Failed to get bugreportz version: 'bugreport -v' returned '%s' "
139 "(code %d)."
140 "\nIf the device does not support it, try running 'adb bugreport' "
141 "to get a "
142 "flat-file bugreport.",
143 bugz_stderr.c_str(), status);
144 return status;
145 }
146 std::string bugz_version = android::base::Trim(bugz_stderr);
147
148 bool show_progress = true;
149 std::string bugz_command = "bugreportz -p";
150 if (bugz_version == "1.0") {
151 // 1.0 does not support progress notifications, so print a disclaimer
152 // message instead.
153 fprintf(stderr,
154 "Bugreport is in progress and it could take minutes to complete.\n"
155 "Please be patient and do not cancel or disconnect your device "
156 "until it completes."
157 "\n");
158 show_progress = false;
159 bugz_command = "bugreportz";
160 }
161 BugreportStandardStreamsCallback bugz_callback(dest_file, show_progress, this);
162 return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
163}
164
Felipe Lemedaf46282016-07-27 19:23:09 -0700165void Bugreport::UpdateProgress(const std::string& message, int progress, int total) {
Felipe Leme6f5080f2016-07-26 12:23:40 -0700166 int progress_percentage = (progress * 100 / total);
Felipe Lemedaf46282016-07-27 19:23:09 -0700167 line_printer_.Print(
168 android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
169 LinePrinter::INFO);
Felipe Leme698e0652016-07-19 17:07:22 -0700170}
171
172int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
173 const std::string& command, bool disable_shell_protocol,
Felipe Leme0d4f0502016-07-26 12:14:39 -0700174 StandardStreamsCallbackInterface* callback) {
175 return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
Felipe Leme698e0652016-07-19 17:07:22 -0700176}
177
178bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
179 const char* name) {
180 return do_sync_pull(srcs, dst, copy_attrs, name);
181}