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(const std::string & bugreport_file)163     binder::Status onFinished([[maybe_unused]] const std::string& bugreport_file) 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()176     binder::Status onUiIntensiveBugreportDumpsFinished() override {
177         std::lock_guard <std::mutex> lock(lock_);
178         dprintf(out_fd_, "\rUi intensive bugreport dumps finished");
179         return binder::Status::ok();
180     }
181 
getIsFinished()182     bool getIsFinished() {
183         std::lock_guard<std::mutex> lock(lock_);
184         return is_finished_;
185     }
186 
getErrorCode()187     int getErrorCode() {
188         std::lock_guard<std::mutex> lock(lock_);
189         return error_code_;
190     }
191 
192   private:
193     int out_fd_;
194     int error_code_ = -1;
195     bool is_finished_ = false;
196     std::shared_ptr<std::vector<SectionInfo>> sections_;
197     std::mutex lock_;
198 };
199 
200 /**
201  * Generates bug report and provide access to the bug report file and other info for other tests.
202  * Since bug report generation is slow, the bugreport is only generated once.
203  */
204 class ZippedBugreportGenerationTest : public Test {
205   public:
206     static std::shared_ptr<std::vector<SectionInfo>> sections;
207     static Dumpstate& ds;
208     static std::chrono::milliseconds duration;
GenerateBugreport()209     static void GenerateBugreport() {
210         // clang-format off
211         char* argv[] = {
212             (char*)"dumpstate"
213         };
214         // clang-format on
215         sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
216         ds.listener_ = listener;
217         auto start = std::chrono::steady_clock::now();
218         ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv);
219         auto end = std::chrono::steady_clock::now();
220         duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
221     }
222 
getZipFilePath()223     static const std::string getZipFilePath() {
224         return ds.GetPath(".zip");
225     }
226 };
227 std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections =
228     std::make_shared<std::vector<SectionInfo>>();
229 Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance();
230 std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
231 
TEST_F(ZippedBugreportGenerationTest,IsGeneratedWithoutErrors)232 TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
233     GenerateBugreport();
234     EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
235 }
236 
TEST_F(ZippedBugreportGenerationTest,Is1MBMBinSize)237 TEST_F(ZippedBugreportGenerationTest, Is1MBMBinSize) {
238     struct stat st;
239     EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
240     EXPECT_GE(st.st_size, 1000000 /* 1MB */);
241 }
242 
TEST_F(ZippedBugreportGenerationTest,TakesBetween20And300Seconds)243 TEST_F(ZippedBugreportGenerationTest, TakesBetween20And300Seconds) {
244     EXPECT_GE(duration, 20s) << "Expected completion in more than 20s. Actual time "
245                              << duration.count() << " ms.";
246     EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
247                               << duration.count() << " ms.";
248 }
249 
250 /**
251  * Run tests on contents of zipped bug report.
252  */
253 class ZippedBugReportContentsTest : public Test {
254   public:
255     ZipArchiveHandle handle;
SetUp()256     void SetUp() {
257         ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0);
258     }
TearDown()259     void TearDown() {
260         CloseArchive(handle);
261     }
262 
FileExists(const char * filename,uint32_t minsize,uint32_t maxsize=std::numeric_limits<uint32_t>::max ())263     void FileExists(const char* filename, uint32_t minsize,
264                     uint32_t maxsize = std::numeric_limits<uint32_t>::max()) {
265         ZipEntry entry;
266         GetEntry(handle, filename, &entry);
267         EXPECT_GT(entry.uncompressed_length, minsize);
268         EXPECT_LT(entry.uncompressed_length, maxsize);
269     }
270 };
271 
TEST_F(ZippedBugReportContentsTest,ContainsMainEntry)272 TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
273     ZipEntry main_entry;
274     // contains main entry name file
275     GetEntry(handle, "main_entry.txt", &main_entry);
276 
277     std::string bugreport_txt_name;
278     bugreport_txt_name.resize(main_entry.uncompressed_length);
279     ExtractToMemory(handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
280                     main_entry.uncompressed_length);
281 
282     // contains main entry file
283     FileExists(bugreport_txt_name.c_str(), 1000000U);
284 }
285 
TEST_F(ZippedBugReportContentsTest,ContainsVersion)286 TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
287     ZipEntry entry;
288     // contains main entry name file
289     GetEntry(handle, "version.txt", &entry);
290 
291     char* buf = new char[entry.uncompressed_length + 1];
292     ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
293     buf[entry.uncompressed_length] = 0;
294     EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str());
295     delete[] buf;
296 }
297 
TEST_F(ZippedBugReportContentsTest,ContainsBoardSpecificFiles)298 TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
299     // TODO(b/160109027): cf_x86_phone-userdebug does not dump them.
300     // FileExists("dumpstate_board.bin", 1000000U, 80000000U);
301     // FileExists("dumpstate_board.txt", 100000U, 1000000U);
302 }
303 
TEST_F(ZippedBugReportContentsTest,ContainsProtoFile)304 TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
305     FileExists("proto/activity.proto", 100000U, 1000000U);
306 }
307 
308 // Spot check on some files pulled from the file system
TEST_F(ZippedBugReportContentsTest,ContainsSomeFileSystemFiles)309 TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
310     // FS/proc/*/mountinfo size > 0
311     FileExists("FS/proc/1/mountinfo", 0U, 100000U);
312 
313     // FS/data/misc/profiles/cur/0/*/primary.prof should exist. Also, since dumpstate only adds
314     // profiles to the zip in the non-user build, a build checking is necessary here.
315     if (!PropertiesHelper::IsUserBuild()) {
316         ZipEntry entry;
317         GetEntry(handle, "FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", &entry);
318     }
319 }
320 
321 /**
322  * Runs tests on section data generated by dumpstate and captured by DumpstateListener.
323  */
324 class BugreportSectionTest : public Test {
325   public:
326     ZipArchiveHandle handle;
327 
SetUp()328     void SetUp() {
329         ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0);
330     }
331 
TearDown()332     void TearDown() {
333         CloseArchive(handle);
334     }
335 
SetUpTestCase()336     static void SetUpTestCase() {
337         ParseSections(ZippedBugreportGenerationTest::getZipFilePath().c_str(),
338                       ZippedBugreportGenerationTest::sections.get());
339     }
340 
numMatches(const std::string & substring)341     int numMatches(const std::string& substring) {
342         int matches = 0;
343         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
344             if (section.name.find(substring) != std::string::npos) {
345                 matches++;
346             }
347         }
348         return matches;
349     }
350 
SectionExists(const std::string & sectionName,int minsize)351     void SectionExists(const std::string& sectionName, int minsize) {
352         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
353             if (sectionName == section.name) {
354                 EXPECT_GE(section.size_bytes, minsize) << " for section:" << sectionName;
355                 return;
356             }
357         }
358         FAIL() << sectionName << " not found.";
359     }
360 
361     /**
362      * Whether or not the content of the section is injected by other commands.
363      */
IsContentInjectedByOthers(const std::string & line)364     bool IsContentInjectedByOthers(const std::string& line) {
365         // Command header such as `------ APP ACTIVITIES (/system/bin/dumpsys activity -v) ------`.
366         static const std::regex kCommandHeader = std::regex{"------ .+ \\(.+\\) ------"};
367         std::smatch match;
368         if (std::regex_match(line, match, kCommandHeader)) {
369           return true;
370         }
371         return false;
372     }
373 };
374 
TEST_F(BugreportSectionTest,Atleast3CriticalDumpsysSectionsGenerated)375 TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
376     int numSections = numMatches("CRITICAL");
377     EXPECT_GE(numSections, 3);
378 }
379 
TEST_F(BugreportSectionTest,Atleast2HighDumpsysSectionsGenerated)380 TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
381     int numSections = numMatches("HIGH");
382     EXPECT_GE(numSections, 2);
383 }
384 
TEST_F(BugreportSectionTest,Atleast50NormalDumpsysSectionsGenerated)385 TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
386     int allSections = ZippedBugreportGenerationTest::sections->size();
387     int criticalSections = numMatches("CRITICAL");
388     int highSections = numMatches("HIGH");
389     int normalSections = allSections - criticalSections - highSections;
390 
391     EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
392                                   << "High:" << highSections << "Normal:" << normalSections << ")";
393 }
394 
395 // Test if some critical sections are being generated.
TEST_F(BugreportSectionTest,CriticalSurfaceFlingerSectionGenerated)396 TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
397     SectionExists("CRITICAL SurfaceFlinger", /* bytes= */ 10000);
398 }
399 
TEST_F(BugreportSectionTest,ActivitySectionsGenerated)400 TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
401     SectionExists("CRITICAL activity", /* bytes= */ 5000);
402     SectionExists("activity", /* bytes= */ 10000);
403 }
404 
TEST_F(BugreportSectionTest,CpuinfoSectionGenerated)405 TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
406     SectionExists("CRITICAL cpuinfo", /* bytes= */ 1000);
407 }
408 
TEST_F(BugreportSectionTest,WindowSectionGenerated)409 TEST_F(BugreportSectionTest, WindowSectionGenerated) {
410     SectionExists("CRITICAL window", /* bytes= */ 20000);
411 }
412 
TEST_F(BugreportSectionTest,ConnectivitySectionsGenerated)413 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
414     SectionExists("connectivity", /* bytes= */ 5000);
415 }
416 
TEST_F(BugreportSectionTest,MeminfoSectionGenerated)417 TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
418     SectionExists("HIGH meminfo", /* bytes= */ 100000);
419 }
420 
TEST_F(BugreportSectionTest,BatteryStatsSectionGenerated)421 TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
422     SectionExists("batterystats", /* bytes= */ 1000);
423 }
424 
TEST_F(BugreportSectionTest,DISABLED_WifiSectionGenerated)425 TEST_F(BugreportSectionTest, DISABLED_WifiSectionGenerated) {
426     SectionExists("wifi", /* bytes= */ 100000);
427 }
428 
TEST_F(BugreportSectionTest,NoInjectedContentByOtherCommand)429 TEST_F(BugreportSectionTest, NoInjectedContentByOtherCommand) {
430     // Extract the main entry to a temp file
431     TemporaryFile tmp_binary;
432     ASSERT_NE(-1, tmp_binary.fd);
433     ExtractBugreport(&handle, tmp_binary.fd);
434 
435     // Read line by line and identify sections
436     std::ifstream ifs(tmp_binary.path, std::ifstream::in);
437     std::string line;
438     std::string current_section_name;
439     while (std::getline(ifs, line)) {
440         std::string section_name;
441         if (IsSectionStart(line, &section_name)) {
442             current_section_name = section_name;
443         } else if (IsSectionEnd(line)) {
444             current_section_name = "";
445         } else if (!current_section_name.empty()) {
446             EXPECT_FALSE(IsContentInjectedByOthers(line));
447         }
448     }
449 }
450 
451 class DumpstateBinderTest : public Test {
452   protected:
SetUp()453     void SetUp() override {
454         // In case there is a stray service, stop it first.
455         property_set("ctl.stop", "bugreportd");
456         // dry_run results in a faster bugreport.
457         property_set("dumpstate.dry_run", "true");
458         // We need to receive some async calls later. Ensure we have binder threads.
459         ProcessState::self()->startThreadPool();
460     }
461 
TearDown()462     void TearDown() override {
463         property_set("ctl.stop", "bugreportd");
464         property_set("dumpstate.dry_run", "");
465 
466         unlink("/data/local/tmp/tmp.zip");
467         unlink("/data/local/tmp/tmp.png");
468     }
469 
470     // Waits until listener gets the callbacks.
WaitTillExecutionComplete(DumpstateListener * listener)471     void WaitTillExecutionComplete(DumpstateListener* listener) {
472         // Wait till one of finished, error or timeout.
473         static const int kBugreportTimeoutSeconds = 120;
474         int i = 0;
475         while (!listener->getIsFinished() && listener->getErrorCode() == -1 &&
476                i < kBugreportTimeoutSeconds) {
477             sleep(1);
478             i++;
479         }
480     }
481 };
482 
TEST_F(DumpstateBinderTest,Baseline)483 TEST_F(DumpstateBinderTest, Baseline) {
484     // In the beginning dumpstate binder service is not running.
485     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
486     EXPECT_EQ(ds_binder, nullptr);
487 
488     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
489     // and makes it wait.
490     property_set("dumpstate.dry_run", "true");
491     property_set("ctl.start", "bugreportd");
492 
493     // Now we are able to retrieve dumpstate binder service.
494     ds_binder = GetDumpstateService();
495     EXPECT_NE(ds_binder, nullptr);
496 
497     // Prepare arguments
498     unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
499     unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
500     int flags = 0;
501 
502     EXPECT_NE(bugreport_fd.get(), -1);
503     EXPECT_NE(screenshot_fd.get(), -1);
504 
505     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
506     android::binder::Status status =
507         ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
508                                   std::move(screenshot_fd),
509                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener,
510                                   true, false);
511     // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
512     // gets expected callbacks.
513     EXPECT_TRUE(status.isOk());
514     WaitTillExecutionComplete(listener.get());
515 
516     // Bugreport generation requires user consent, which we cannot get in a test set up,
517     // so instead of getting is_finished_, we are more likely to get a consent error.
518     EXPECT_TRUE(
519         listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
520         listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
521 
522     // The service should have died on its own, freeing itself up for a new invocation.
523     sleep(2);
524     ds_binder = GetDumpstateService();
525     EXPECT_EQ(ds_binder, nullptr);
526 }
527 
TEST_F(DumpstateBinderTest,ServiceDies_OnInvalidInput)528 TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) {
529     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
530     // and makes it wait.
531     property_set("ctl.start", "bugreportd");
532     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
533     EXPECT_NE(ds_binder, nullptr);
534 
535     // Prepare arguments
536     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
537     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
538     int flags = 0;
539 
540     EXPECT_NE(bugreport_fd.get(), -1);
541     EXPECT_NE(screenshot_fd.get(), -1);
542 
543     // Call startBugreport with bad arguments.
544     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
545     android::binder::Status status =
546         ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
547                                   std::move(screenshot_fd), 2000,  // invalid bugreport mode
548                                   flags, listener, false, false);
549     EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
550 
551     // The service should have died, freeing itself up for a new invocation.
552     sleep(2);
553     ds_binder = GetDumpstateService();
554     EXPECT_EQ(ds_binder, nullptr);
555 }
556 
TEST_F(DumpstateBinderTest,SimultaneousBugreportsNotAllowed)557 TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) {
558     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
559     // and makes it wait.
560     property_set("dumpstate.dry_run", "true");
561     property_set("ctl.start", "bugreportd");
562     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
563     EXPECT_NE(ds_binder, nullptr);
564 
565     // Prepare arguments
566     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
567     unique_fd bugreport_fd2(dup(bugreport_fd.get()));
568     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
569     unique_fd screenshot_fd2(dup(screenshot_fd.get()));
570     int flags = 0;
571 
572     EXPECT_NE(bugreport_fd.get(), -1);
573     EXPECT_NE(bugreport_fd2.get(), -1);
574     EXPECT_NE(screenshot_fd.get(), -1);
575     EXPECT_NE(screenshot_fd2.get(), -1);
576 
577     sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
578     android::binder::Status status =
579         ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
580                                   std::move(screenshot_fd),
581                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener1,
582                                   true, false);
583     EXPECT_TRUE(status.isOk());
584 
585     // try to make another call to startBugreport. This should fail.
586     sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
587     status = ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd2),
588                                         std::move(screenshot_fd2),
589                                        Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags,
590                                        listener2, true, false);
591     EXPECT_FALSE(status.isOk());
592     WaitTillExecutionComplete(listener2.get());
593     EXPECT_EQ(listener2->getErrorCode(),
594               IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
595 
596     // Meanwhile the first call works as expected. Service should not die in this case.
597     WaitTillExecutionComplete(listener1.get());
598 
599     // Bugreport generation requires user consent, which we cannot get in a test set up,
600     // so instead of getting is_finished_, we are more likely to get a consent error.
601     EXPECT_TRUE(
602         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
603         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
604 }
605 
606 }  // namespace dumpstate
607 }  // namespace os
608 }  // namespace android
609