1 /*
2  * Copyright (C) 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 #include <android-base/file.h>
18 #include <android/os/BnDumpstate.h>
19 #include <android/os/BnDumpstateListener.h>
20 #include <binder/IServiceManager.h>
21 #include <binder/ProcessState.h>
22 #include <cutils/properties.h>
23 #include <fcntl.h>
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 #include <libgen.h>
27 #include <ziparchive/zip_archive.h>
28 
29 #include <fstream>
30 #include <regex>
31 
32 #include "dumpstate.h"
33 
34 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
35 
36 namespace android {
37 namespace os {
38 namespace dumpstate {
39 
40 using ::testing::Test;
41 using ::std::literals::chrono_literals::operator""s;
42 using android::base::unique_fd;
43 
44 class DumpstateListener;
45 
46 namespace {
47 
48 struct SectionInfo {
49     std::string name;
50     int32_t size_bytes;
51 };
52 
GetDumpstateService()53 sp<IDumpstate> GetDumpstateService() {
54     return android::interface_cast<IDumpstate>(
55         android::defaultServiceManager()->getService(String16("dumpstate")));
56 }
57 
OpenForWrite(const std::string & filename)58 int OpenForWrite(const std::string& filename) {
59     return TEMP_FAILURE_RETRY(open(filename.c_str(),
60                                    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
61                                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
62 }
63 
GetEntry(const ZipArchiveHandle archive,const std::string_view entry_name,ZipEntry * data)64 void GetEntry(const ZipArchiveHandle archive, const std::string_view entry_name, ZipEntry* data) {
65     int32_t e = FindEntry(archive, entry_name, data);
66     EXPECT_EQ(e, 0) << ErrorCodeString(e) << " entry name: " << entry_name;
67 }
68 
69 // Extracts the main bugreport txt from the given archive and writes into output_fd.
ExtractBugreport(const ZipArchiveHandle * handle,int output_fd)70 void ExtractBugreport(const ZipArchiveHandle* handle, int output_fd) {
71     // Read contents of main_entry.txt which is a single line indicating the name of the zip entry
72     // that contains the main bugreport txt.
73     ZipEntry main_entry;
74     GetEntry(*handle, "main_entry.txt", &main_entry);
75     std::string bugreport_txt_name;
76     bugreport_txt_name.resize(main_entry.uncompressed_length);
77     ExtractToMemory(*handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
78                     main_entry.uncompressed_length);
79 
80     // Read the main bugreport txt and extract to output_fd.
81     ZipEntry entry;
82     GetEntry(*handle, bugreport_txt_name, &entry);
83     ExtractEntryToFile(*handle, &entry, output_fd);
84 }
85 
IsSectionStart(const std::string & line,std::string * section_name)86 bool IsSectionStart(const std::string& line, std::string* section_name) {
87     static const std::regex kSectionStart = std::regex{"DUMP OF SERVICE (.*):"};
88     std::smatch match;
89     if (std::regex_match(line, match, kSectionStart)) {
90         *section_name = match.str(1);
91         return true;
92     }
93     return false;
94 }
95 
IsSectionEnd(const std::string & line)96 bool IsSectionEnd(const std::string& line) {
97     // Not all lines that contain "was the duration of" is a section end, but all section ends do
98     // contain "was the duration of". The disambiguation can be done by the caller.
99     return (line.find("was the duration of") != std::string::npos);
100 }
101 
102 // Extracts the zipped bugreport and identifies the sections.
ParseSections(const std::string & zip_path,std::vector<SectionInfo> * sections)103 void ParseSections(const std::string& zip_path, std::vector<SectionInfo>* sections) {
104     // Open the archive
105     ZipArchiveHandle handle;
106     ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);
107 
108     // Extract the main entry to a temp file
109     TemporaryFile tmp_binary;
110     ASSERT_NE(-1, tmp_binary.fd);
111     ExtractBugreport(&handle, tmp_binary.fd);
112 
113     // Read line by line and identify sections
114     std::ifstream ifs(tmp_binary.path, std::ifstream::in);
115     std::string line;
116     int section_bytes = 0;
117     std::string current_section_name;
118     while (std::getline(ifs, line)) {
119         std::string section_name;
120         if (IsSectionStart(line, &section_name)) {
121             section_bytes = 0;
122             current_section_name = section_name;
123         } else if (IsSectionEnd(line)) {
124             if (!current_section_name.empty()) {
125                 sections->push_back({current_section_name, section_bytes});
126             }
127             current_section_name = "";
128         } else if (!current_section_name.empty()) {
129             section_bytes += line.length();
130         }
131     }
132 
133     CloseArchive(handle);
134 }
135 
136 }  // namespace
137 
138 /**
139  * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
140  * section details generated by dumpstate are added to a vector to be used by Tests later.
141  */
142 class DumpstateListener : public BnDumpstateListener {
143   public:
DumpstateListener(int fd,std::shared_ptr<std::vector<SectionInfo>> sections)144     DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
145         : out_fd_(fd), sections_(sections) {
146     }
147 
DumpstateListener(int fd)148     DumpstateListener(int fd) : out_fd_(fd) {
149     }
150 
onProgress(int32_t progress)151     binder::Status onProgress(int32_t progress) override {
152         dprintf(out_fd_, "\rIn progress %d", progress);
153         return binder::Status::ok();
154     }
155 
onError(int32_t error_code)156     binder::Status onError(int32_t error_code) override {
157         std::lock_guard<std::mutex> lock(lock_);
158         error_code_ = error_code;
159         dprintf(out_fd_, "\rError code %d", error_code);
160         return binder::Status::ok();
161     }
162 
onFinished()163     binder::Status onFinished() override {
164         std::lock_guard<std::mutex> lock(lock_);
165         is_finished_ = true;
166         dprintf(out_fd_, "\rFinished");
167         return binder::Status::ok();
168     }
169 
onScreenshotTaken(bool success)170     binder::Status onScreenshotTaken(bool success) override {
171         std::lock_guard<std::mutex> lock(lock_);
172         dprintf(out_fd_, "\rResult of taking screenshot: %s", success ? "success" : "failure");
173         return binder::Status::ok();
174     }
175 
onUiIntensiveBugreportDumpsFinished(const android::String16 & callingpackage)176     binder::Status onUiIntensiveBugreportDumpsFinished(const android::String16& callingpackage)
177         override {
178         std::lock_guard <std::mutex> lock(lock_);
179         std::string callingpackageUtf8 = std::string(String8(callingpackage).string());
180         dprintf(out_fd_, "\rCalling package of ui intensive bugreport dumps finished: %s",
181                 callingpackageUtf8.c_str());
182         return binder::Status::ok();
183     }
184 
getIsFinished()185     bool getIsFinished() {
186         std::lock_guard<std::mutex> lock(lock_);
187         return is_finished_;
188     }
189 
getErrorCode()190     int getErrorCode() {
191         std::lock_guard<std::mutex> lock(lock_);
192         return error_code_;
193     }
194 
195   private:
196     int out_fd_;
197     int error_code_ = -1;
198     bool is_finished_ = false;
199     std::shared_ptr<std::vector<SectionInfo>> sections_;
200     std::mutex lock_;
201 };
202 
203 /**
204  * Generates bug report and provide access to the bug report file and other info for other tests.
205  * Since bug report generation is slow, the bugreport is only generated once.
206  */
207 class ZippedBugreportGenerationTest : public Test {
208   public:
209     static std::shared_ptr<std::vector<SectionInfo>> sections;
210     static Dumpstate& ds;
211     static std::chrono::milliseconds duration;
SetUpTestCase()212     static void SetUpTestCase() {
213         // clang-format off
214         char* argv[] = {
215             (char*)"dumpstate",
216             (char*)"-d",
217             (char*)"-z",
218             (char*)"-B"
219         };
220         // clang-format on
221         sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
222         ds.listener_ = listener;
223         auto start = std::chrono::steady_clock::now();
224         ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv);
225         auto end = std::chrono::steady_clock::now();
226         duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
227     }
228 
getZipFilePath()229     static const std::string getZipFilePath() {
230         return ds.GetPath(".zip");
231     }
232 };
233 std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections =
234     std::make_shared<std::vector<SectionInfo>>();
235 Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance();
236 std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
237 
TEST_F(ZippedBugreportGenerationTest,IsGeneratedWithoutErrors)238 TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
239     EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
240 }
241 
TEST_F(ZippedBugreportGenerationTest,Is3MBto30MBinSize)242 TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
243     struct stat st;
244     EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
245     EXPECT_GE(st.st_size, 3000000 /* 3MB */);
246     EXPECT_LE(st.st_size, 30000000 /* 30MB */);
247 }
248 
TEST_F(ZippedBugreportGenerationTest,TakesBetween30And150Seconds)249 TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
250     EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
251                              << duration.count() << " s.";
252     EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
253                               << duration.count() << " s.";
254 }
255 
256 /**
257  * Run tests on contents of zipped bug report.
258  */
259 class ZippedBugReportContentsTest : public Test {
260   public:
261     ZipArchiveHandle handle;
SetUp()262     void SetUp() {
263         ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0);
264     }
TearDown()265     void TearDown() {
266         CloseArchive(handle);
267     }
268 
FileExists(const char * filename,uint32_t minsize,uint32_t maxsize)269     void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
270         ZipEntry entry;
271         GetEntry(handle, filename, &entry);
272         EXPECT_GT(entry.uncompressed_length, minsize);
273         EXPECT_LT(entry.uncompressed_length, maxsize);
274     }
275 };
276 
TEST_F(ZippedBugReportContentsTest,ContainsMainEntry)277 TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
278     ZipEntry main_entry;
279     // contains main entry name file
280     GetEntry(handle, "main_entry.txt", &main_entry);
281 
282     std::string bugreport_txt_name;
283     bugreport_txt_name.resize(main_entry.uncompressed_length);
284     ExtractToMemory(handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
285                     main_entry.uncompressed_length);
286 
287     // contains main entry file
288     FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
289 }
290 
TEST_F(ZippedBugReportContentsTest,ContainsVersion)291 TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
292     ZipEntry entry;
293     // contains main entry name file
294     GetEntry(handle, "version.txt", &entry);
295 
296     char* buf = new char[entry.uncompressed_length + 1];
297     ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
298     buf[entry.uncompressed_length] = 0;
299     EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str());
300     delete[] buf;
301 }
302 
TEST_F(ZippedBugReportContentsTest,ContainsBoardSpecificFiles)303 TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
304     FileExists("dumpstate_board.bin", 1000000U, 80000000U);
305     FileExists("dumpstate_board.txt", 100000U, 1000000U);
306 }
307 
TEST_F(ZippedBugReportContentsTest,ContainsProtoFile)308 TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
309     FileExists("proto/activity.proto", 100000U, 1000000U);
310 }
311 
312 // Spot check on some files pulled from the file system
TEST_F(ZippedBugReportContentsTest,ContainsSomeFileSystemFiles)313 TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
314     // FS/proc/*/mountinfo size > 0
315     FileExists("FS/proc/1/mountinfo", 0U, 100000U);
316 
317     // FS/data/misc/profiles/cur/0/*/primary.prof size > 0
318     FileExists("FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", 0U, 100000U);
319 }
320 
321 /**
322  * Runs tests on section data generated by dumpstate and captured by DumpstateListener.
323  */
324 class BugreportSectionTest : public Test {
325   public:
SetUpTestCase()326     static void SetUpTestCase() {
327         ParseSections(ZippedBugreportGenerationTest::getZipFilePath().c_str(),
328                       ZippedBugreportGenerationTest::sections.get());
329     }
330 
numMatches(const std::string & substring)331     int numMatches(const std::string& substring) {
332         int matches = 0;
333         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
334             if (section.name.find(substring) != std::string::npos) {
335                 matches++;
336             }
337         }
338         return matches;
339     }
340 
SectionExists(const std::string & sectionName,int minsize)341     void SectionExists(const std::string& sectionName, int minsize) {
342         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
343             if (sectionName == section.name) {
344                 EXPECT_GE(section.size_bytes, minsize) << " for section:" << sectionName;
345                 return;
346             }
347         }
348         FAIL() << sectionName << " not found.";
349     }
350 };
351 
TEST_F(BugreportSectionTest,Atleast3CriticalDumpsysSectionsGenerated)352 TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
353     int numSections = numMatches("CRITICAL");
354     EXPECT_GE(numSections, 3);
355 }
356 
TEST_F(BugreportSectionTest,Atleast2HighDumpsysSectionsGenerated)357 TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
358     int numSections = numMatches("HIGH");
359     EXPECT_GE(numSections, 2);
360 }
361 
TEST_F(BugreportSectionTest,Atleast50NormalDumpsysSectionsGenerated)362 TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
363     int allSections = ZippedBugreportGenerationTest::sections->size();
364     int criticalSections = numMatches("CRITICAL");
365     int highSections = numMatches("HIGH");
366     int normalSections = allSections - criticalSections - highSections;
367 
368     EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
369                                   << "High:" << highSections << "Normal:" << normalSections << ")";
370 }
371 
372 // Test if some critical sections are being generated.
TEST_F(BugreportSectionTest,CriticalSurfaceFlingerSectionGenerated)373 TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
374     SectionExists("CRITICAL SurfaceFlinger", /* bytes= */ 10000);
375 }
376 
TEST_F(BugreportSectionTest,ActivitySectionsGenerated)377 TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
378     SectionExists("CRITICAL activity", /* bytes= */ 5000);
379     SectionExists("activity", /* bytes= */ 10000);
380 }
381 
TEST_F(BugreportSectionTest,CpuinfoSectionGenerated)382 TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
383     SectionExists("CRITICAL cpuinfo", /* bytes= */ 1000);
384 }
385 
TEST_F(BugreportSectionTest,WindowSectionGenerated)386 TEST_F(BugreportSectionTest, WindowSectionGenerated) {
387     SectionExists("CRITICAL window", /* bytes= */ 20000);
388 }
389 
TEST_F(BugreportSectionTest,ConnectivitySectionsGenerated)390 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
391     SectionExists("HIGH connectivity", /* bytes= */ 3000);
392     SectionExists("connectivity", /* bytes= */ 5000);
393 }
394 
TEST_F(BugreportSectionTest,MeminfoSectionGenerated)395 TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
396     SectionExists("HIGH meminfo", /* bytes= */ 100000);
397 }
398 
TEST_F(BugreportSectionTest,BatteryStatsSectionGenerated)399 TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
400     SectionExists("batterystats", /* bytes= */ 1000);
401 }
402 
TEST_F(BugreportSectionTest,WifiSectionGenerated)403 TEST_F(BugreportSectionTest, WifiSectionGenerated) {
404     SectionExists("wifi", /* bytes= */ 100000);
405 }
406 
407 class DumpstateBinderTest : public Test {
408   protected:
SetUp()409     void SetUp() override {
410         // In case there is a stray service, stop it first.
411         property_set("ctl.stop", "bugreportd");
412         // dry_run results in a faster bugreport.
413         property_set("dumpstate.dry_run", "true");
414         // We need to receive some async calls later. Ensure we have binder threads.
415         ProcessState::self()->startThreadPool();
416     }
417 
TearDown()418     void TearDown() override {
419         property_set("ctl.stop", "bugreportd");
420         property_set("dumpstate.dry_run", "");
421 
422         unlink("/data/local/tmp/tmp.zip");
423         unlink("/data/local/tmp/tmp.png");
424     }
425 
426     // Waits until listener gets the callbacks.
WaitTillExecutionComplete(DumpstateListener * listener)427     void WaitTillExecutionComplete(DumpstateListener* listener) {
428         // Wait till one of finished, error or timeout.
429         static const int kBugreportTimeoutSeconds = 120;
430         int i = 0;
431         while (!listener->getIsFinished() && listener->getErrorCode() == -1 &&
432                i < kBugreportTimeoutSeconds) {
433             sleep(1);
434             i++;
435         }
436     }
437 };
438 
TEST_F(DumpstateBinderTest,Baseline)439 TEST_F(DumpstateBinderTest, Baseline) {
440     // In the beginning dumpstate binder service is not running.
441     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
442     EXPECT_EQ(ds_binder, nullptr);
443 
444     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
445     // and makes it wait.
446     property_set("dumpstate.dry_run", "true");
447     property_set("ctl.start", "bugreportd");
448 
449     // Now we are able to retrieve dumpstate binder service.
450     ds_binder = GetDumpstateService();
451     EXPECT_NE(ds_binder, nullptr);
452 
453     // Prepare arguments
454     unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
455     unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
456 
457     EXPECT_NE(bugreport_fd.get(), -1);
458     EXPECT_NE(screenshot_fd.get(), -1);
459 
460     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
461     android::binder::Status status =
462         ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
463                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener, true);
464     // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
465     // gets expected callbacks.
466     EXPECT_TRUE(status.isOk());
467     WaitTillExecutionComplete(listener.get());
468 
469     // Bugreport generation requires user consent, which we cannot get in a test set up,
470     // so instead of getting is_finished_, we are more likely to get a consent error.
471     EXPECT_TRUE(
472         listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
473         listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
474 
475     // The service should have died on its own, freeing itself up for a new invocation.
476     sleep(2);
477     ds_binder = GetDumpstateService();
478     EXPECT_EQ(ds_binder, nullptr);
479 }
480 
TEST_F(DumpstateBinderTest,ServiceDies_OnInvalidInput)481 TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) {
482     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
483     // and makes it wait.
484     property_set("ctl.start", "bugreportd");
485     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
486     EXPECT_NE(ds_binder, nullptr);
487 
488     // Prepare arguments
489     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
490     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
491 
492     EXPECT_NE(bugreport_fd.get(), -1);
493     EXPECT_NE(screenshot_fd.get(), -1);
494 
495     // Call startBugreport with bad arguments.
496     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
497     android::binder::Status status =
498         ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
499                                   2000,  // invalid bugreport mode
500                                   listener, false);
501     EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
502 
503     // The service should have died, freeing itself up for a new invocation.
504     sleep(2);
505     ds_binder = GetDumpstateService();
506     EXPECT_EQ(ds_binder, nullptr);
507 }
508 
TEST_F(DumpstateBinderTest,SimultaneousBugreportsNotAllowed)509 TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) {
510     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
511     // and makes it wait.
512     property_set("dumpstate.dry_run", "true");
513     property_set("ctl.start", "bugreportd");
514     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
515     EXPECT_NE(ds_binder, nullptr);
516 
517     // Prepare arguments
518     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
519     unique_fd bugreport_fd2(dup(bugreport_fd.get()));
520     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
521     unique_fd screenshot_fd2(dup(screenshot_fd.get()));
522 
523     EXPECT_NE(bugreport_fd.get(), -1);
524     EXPECT_NE(bugreport_fd2.get(), -1);
525     EXPECT_NE(screenshot_fd.get(), -1);
526     EXPECT_NE(screenshot_fd2.get(), -1);
527 
528     sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
529     android::binder::Status status =
530         ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
531                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1, true);
532     EXPECT_TRUE(status.isOk());
533 
534     // try to make another call to startBugreport. This should fail.
535     sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
536     status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
537                                        Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2, true);
538     EXPECT_FALSE(status.isOk());
539     WaitTillExecutionComplete(listener2.get());
540     EXPECT_EQ(listener2->getErrorCode(),
541               IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
542 
543     // Meanwhile the first call works as expected. Service should not die in this case.
544     WaitTillExecutionComplete(listener1.get());
545 
546     // Bugreport generation requires user consent, which we cannot get in a test set up,
547     // so instead of getting is_finished_, we are more likely to get a consent error.
548     EXPECT_TRUE(
549         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
550         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
551 }
552 
553 }  // namespace dumpstate
554 }  // namespace os
555 }  // namespace android
556