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