blob: 8a3eb0825f0a4d10917d18be3078401c62c722e2 [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)
33 : br_(br), dest_file_(dest_file), show_progress_(show_progress), status_(-1), line_() {
Felipe Leme0d4f0502016-07-26 12:14:39 -070034 }
35
36 void OnStdout(const char* buffer, int length) {
Felipe Leme6f5080f2016-07-26 12:23:40 -070037 for (int i = 0; i < length; i++) {
38 char c = buffer[i];
39 if (c == '\n') {
40 ProcessLine(line_);
41 line_.clear();
42 } else {
43 line_.append(1, c);
44 }
45 }
Felipe Leme0d4f0502016-07-26 12:14:39 -070046 }
47
48 void OnStderr(const char* buffer, int length) {
49 OnStream(nullptr, stderr, buffer, length);
50 }
Felipe Leme0d4f0502016-07-26 12:14:39 -070051 int Done(int unused_) {
Felipe Leme6f5080f2016-07-26 12:23:40 -070052 // Process remaining line, if any...
53 ProcessLine(line_);
54 // ..then return.
55 return status_;
Felipe Leme0d4f0502016-07-26 12:14:39 -070056 }
57
58 private:
Felipe Leme6f5080f2016-07-26 12:23:40 -070059 void ProcessLine(const std::string& line) {
60 if (line.empty()) return;
61
62 if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
63 if (show_progress_) {
64 // Make sure pull message doesn't conflict with generation message.
65 br_->UpdateProgress(dest_file_, 100, 100, true);
66 }
67
68 const char* zip_file = &line[strlen(BUGZ_OK_PREFIX)];
69 std::vector<const char*> srcs{zip_file};
70 status_ = br_->DoSyncPull(srcs, dest_file_.c_str(), true, dest_file_.c_str()) ? 0 : 1;
71 if (status_ != 0) {
72 fprintf(stderr, "Could not copy file '%s' to '%s'\n", zip_file, dest_file_.c_str());
73 }
74 } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
75 const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
76 fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
77 status_ = -1;
78 } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
79 // progress_line should have the following format:
80 //
81 // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
82 //
83 size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
84 size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
85 int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
86 int total = std::stoi(line.substr(idx2 + 1));
87 br_->UpdateProgress(dest_file_, progress, total);
88 } else {
89 fprintf(stderr,
90 "WARNING: unexpected line (%s) returned by bugreportz, "
91 "device probably does not support zipped bugreports.\n"
92 "Try 'adb bugreport' instead.",
93 line.c_str());
94 }
95 }
96
Felipe Leme0d4f0502016-07-26 12:14:39 -070097 Bugreport* br_;
98 const std::string dest_file_;
Felipe Leme6f5080f2016-07-26 12:23:40 -070099 bool show_progress_;
100 int status_;
101
102 // Temporary buffer containing the characters read since the last newline
103 // (\n).
104 std::string line_;
Felipe Leme0d4f0502016-07-26 12:14:39 -0700105
106 DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
107};
108
Felipe Leme6f5080f2016-07-26 12:23:40 -0700109// Implemented in commandline.cpp
110int usage();
111
Felipe Leme698e0652016-07-19 17:07:22 -0700112int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
113 if (argc == 1) return SendShellCommand(transport_type, serial, "bugreport", false);
114 if (argc != 2) return usage();
115
116 // Zipped bugreport option - will call 'bugreportz', which prints the location
117 // of the generated
118 // file, then pull it to the destination file provided by the user.
119 std::string dest_file = argv[1];
120 if (!android::base::EndsWith(argv[1], ".zip")) {
121 // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
122 dest_file += ".zip";
123 }
Felipe Leme6f5080f2016-07-26 12:23:40 -0700124
125 // Gets bugreportz version.
126 std::string bugz_stderr;
127 DefaultStandardStreamsCallback version_callback(nullptr, &bugz_stderr);
128 int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
129
130 if (status != 0) {
131 fprintf(stderr,
132 "Failed to get bugreportz version: 'bugreport -v' returned '%s' "
133 "(code %d)."
134 "\nIf the device does not support it, try running 'adb bugreport' "
135 "to get a "
136 "flat-file bugreport.",
137 bugz_stderr.c_str(), status);
138 return status;
139 }
140 std::string bugz_version = android::base::Trim(bugz_stderr);
141
142 bool show_progress = true;
143 std::string bugz_command = "bugreportz -p";
144 if (bugz_version == "1.0") {
145 // 1.0 does not support progress notifications, so print a disclaimer
146 // message instead.
147 fprintf(stderr,
148 "Bugreport is in progress and it could take minutes to complete.\n"
149 "Please be patient and do not cancel or disconnect your device "
150 "until it completes."
151 "\n");
152 show_progress = false;
153 bugz_command = "bugreportz";
154 }
155 BugreportStandardStreamsCallback bugz_callback(dest_file, show_progress, this);
156 return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
157}
158
159void Bugreport::UpdateProgress(const std::string& file_name, int progress, int total,
160 bool keep_info_line) {
161 int progress_percentage = (progress * 100 / total);
162 line_printer_.Print(android::base::StringPrintf("[%3d%%] generating %s", progress_percentage,
163 file_name.c_str()),
164 LinePrinter::INFO);
165 if (keep_info_line) {
166 line_printer_.KeepInfoLine();
167 }
Felipe Leme698e0652016-07-19 17:07:22 -0700168}
169
170int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
171 const std::string& command, bool disable_shell_protocol,
Felipe Leme0d4f0502016-07-26 12:14:39 -0700172 StandardStreamsCallbackInterface* callback) {
173 return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
Felipe Leme698e0652016-07-19 17:07:22 -0700174}
175
176bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
177 const char* name) {
178 return do_sync_pull(srcs, dst, copy_attrs, name);
179}