1// Copyright (C) 2017 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14#define DEBUG false
15#include "Log.h"
16
17#include "Section.h"
18
19#include <android-base/file.h>
20#include <android-base/test_utils.h>
21#include <android/os/IncidentReportArgs.h>
22#include <android/util/protobuf.h>
23#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
24#include <gmock/gmock.h>
25#include <gtest/gtest.h>
26#include <string.h>
27
28using namespace android;
29using namespace android::base;
30using namespace android::binder;
31using namespace android::os;
32using namespace android::os::incidentd;
33using namespace android::util;
34using ::testing::StrEq;
35using ::testing::Test;
36using ::testing::internal::CaptureStdout;
37using ::testing::internal::GetCapturedStdout;
38
39const int TIMEOUT_PARSER = -1;
40const int NOOP_PARSER = 0;
41const int REVERSE_PARSER = 1;
42
43const int QUICK_TIMEOUT_MS = 100;
44
45const std::string VARINT_FIELD_1 = "\x08\x96\x01";  // 150
46const std::string STRING_FIELD_2 = "\x12\vandroidwins";
47const std::string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff";  // -1
48
49// NOTICE: this test requires /system/bin/incident_helper is installed.
50class SectionTest : public Test {
51public:
52    virtual void SetUp() override { ASSERT_NE(tf.fd, -1); }
53
54    void printDebugString(std::string s) {
55        fprintf(stderr, "size: %zu\n", s.length());
56        for (size_t i = 0; i < s.length(); i++) {
57            char c = s[i];
58            fprintf(stderr, "\\x%x", c);
59        }
60        fprintf(stderr, "\n");
61    }
62
63protected:
64    TemporaryFile tf;
65    ReportRequestSet requests;
66
67    const std::string kTestPath = GetExecutableDirectory();
68    const std::string kTestDataPath = kTestPath + "/testdata/";
69};
70
71class SimpleListener : public IIncidentReportStatusListener {
72public:
73    SimpleListener(){};
74    virtual ~SimpleListener(){};
75
76    virtual Status onReportStarted() { return Status::ok(); };
77    virtual Status onReportSectionStatus(int /*section*/, int /*status*/) { return Status::ok(); };
78    virtual Status onReportFinished() { return Status::ok(); };
79    virtual Status onReportFailed() { return Status::ok(); };
80
81protected:
82    virtual IBinder* onAsBinder() override { return nullptr; };
83};
84
85TEST_F(SectionTest, HeaderSection) {
86    HeaderSection hs;
87
88    IncidentReportArgs args1, args2;
89    args1.addSection(1);
90    args1.addSection(2);
91    args2.setAll(true);
92
93    IncidentHeaderProto head1, head2;
94    head1.set_reason("axe");
95    head2.set_reason("pup");
96
97    args1.addHeader(head1);
98    args1.addHeader(head2);
99    args2.addHeader(head2);
100
101    requests.add(new ReportRequest(args1, new SimpleListener(), -1));
102    requests.add(new ReportRequest(args2, new SimpleListener(), tf.fd));
103    requests.setMainFd(STDOUT_FILENO);
104
105    std::string content;
106    CaptureStdout();
107    ASSERT_EQ(NO_ERROR, hs.Execute(&requests));
108    EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5"
109                                           "\x12\x3"
110                                           "axe\n\x05\x12\x03pup"));
111
112    EXPECT_TRUE(ReadFileToString(tf.path, &content));
113    EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
114}
115
116TEST_F(SectionTest, MetadataSection) {
117    MetadataSection ms;
118    const std::string testFile = kTestDataPath + "metadata.txt";
119    std::string expect;
120    ASSERT_TRUE(ReadFileToString(testFile, &expect));
121
122    requests.setMainFd(STDOUT_FILENO);
123    requests.setMainDest(android::os::DEST_LOCAL);
124    requests.sectionStats(1)->set_success(true);
125
126    CaptureStdout();
127    ASSERT_EQ(NO_ERROR, ms.Execute(&requests));
128    // Notice message_lite.h ParseFromString doesn't work so we just match the bytes directly.
129    EXPECT_THAT(GetCapturedStdout(), StrEq(expect));
130}
131
132TEST_F(SectionTest, FileSection) {
133    FileSection fs(REVERSE_PARSER, tf.path);
134
135    ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path));
136
137    requests.setMainFd(STDOUT_FILENO);
138
139    CaptureStdout();
140    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
141    // The input string is reversed in incident helper
142    // The length is 11, in 128Varint it is "0000 1011" -> \v
143    EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\vatadtsetmai"));
144}
145
146TEST_F(SectionTest, FileSectionNotExist) {
147    FileSection fs1(NOOP_PARSER, "notexist", false, QUICK_TIMEOUT_MS);
148    ASSERT_EQ(NAME_NOT_FOUND, fs1.Execute(&requests));
149
150    FileSection fs2(NOOP_PARSER, "notexist", true, QUICK_TIMEOUT_MS);
151    ASSERT_EQ(NO_ERROR, fs2.Execute(&requests));
152}
153
154TEST_F(SectionTest, FileSectionTimeout) {
155    FileSection fs(TIMEOUT_PARSER, tf.path, false, QUICK_TIMEOUT_MS);
156    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
157    ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out());
158}
159
160TEST_F(SectionTest, GZipSection) {
161    const std::string testFile = kTestDataPath + "kmsg.txt";
162    const std::string testGzFile = testFile + ".gz";
163    GZipSection gs(NOOP_PARSER, "/tmp/nonexist", testFile.c_str(), NULL);
164
165    requests.setMainFd(tf.fd);
166    requests.setMainDest(android::os::DEST_LOCAL);
167
168    ASSERT_EQ(NO_ERROR, gs.Execute(&requests));
169    std::string expected, gzFile, actual;
170    ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile));
171    ASSERT_TRUE(ReadFileToString(tf.path, &actual));
172    // generates the expected protobuf result.
173    size_t fileLen = testFile.size();
174    size_t totalLen = 1 + get_varint_size(fileLen) + fileLen + 3 + gzFile.size();
175    uint8_t header[20];
176    header[0] = '\x2';  // header 0 << 3 + 2
177    uint8_t* ptr = write_raw_varint(header + 1, totalLen);
178    *ptr = '\n';  // header 1 << 3 + 2
179    ptr = write_raw_varint(++ptr, fileLen);
180    expected.assign((const char*)header, ptr - header);
181    expected += testFile + "\x12\x9F\x6" + gzFile;
182    EXPECT_THAT(actual, StrEq(expected));
183}
184
185TEST_F(SectionTest, GZipSectionNoFileFound) {
186    GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL);
187    requests.setMainFd(STDOUT_FILENO);
188    ASSERT_EQ(NO_ERROR, gs.Execute(&requests));
189}
190
191TEST_F(SectionTest, CommandSectionConstructor) {
192    CommandSection cs1(1, "echo", "\"this is a test\"", "ooo", NULL);
193    CommandSection cs2(2, "single_command", NULL);
194    CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL);
195    CommandSection cs4(2, 43214, "single_command", NULL);
196
197    EXPECT_THAT(cs1.name.string(), StrEq("cmd echo \"this is a test\" ooo"));
198    EXPECT_THAT(cs2.name.string(), StrEq("cmd single_command"));
199    EXPECT_EQ(3123, cs3.timeoutMs);
200    EXPECT_EQ(43214, cs4.timeoutMs);
201    EXPECT_THAT(cs3.name.string(), StrEq("cmd echo \"this is a test\" ooo"));
202    EXPECT_THAT(cs4.name.string(), StrEq("cmd single_command"));
203}
204
205TEST_F(SectionTest, CommandSectionEcho) {
206    CommandSection cs(REVERSE_PARSER, "/system/bin/echo", "about", NULL);
207    requests.setMainFd(STDOUT_FILENO);
208    CaptureStdout();
209    ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
210    EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\x06\ntuoba"));
211}
212
213TEST_F(SectionTest, CommandSectionCommandTimeout) {
214    CommandSection cs(NOOP_PARSER, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL);
215    ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
216    ASSERT_TRUE(requests.sectionStats(NOOP_PARSER)->timed_out());
217}
218
219TEST_F(SectionTest, CommandSectionIncidentHelperTimeout) {
220    CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL);
221    requests.setMainFd(STDOUT_FILENO);
222    ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
223    ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out());
224}
225
226TEST_F(SectionTest, CommandSectionBadCommand) {
227    CommandSection cs(NOOP_PARSER, "echoo", "about", NULL);
228    ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests));
229}
230
231TEST_F(SectionTest, CommandSectionBadCommandAndTimeout) {
232    CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL);
233    // timeout will return first
234    ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
235    ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out());
236}
237
238TEST_F(SectionTest, LogSectionBinary) {
239    LogSection ls(1, LOG_ID_EVENTS);
240    requests.setMainFd(STDOUT_FILENO);
241    CaptureStdout();
242    ASSERT_EQ(NO_ERROR, ls.Execute(&requests));
243    std::string results = GetCapturedStdout();
244    EXPECT_FALSE(results.empty());
245}
246
247TEST_F(SectionTest, LogSectionSystem) {
248    LogSection ls(1, LOG_ID_SYSTEM);
249    requests.setMainFd(STDOUT_FILENO);
250    CaptureStdout();
251    ASSERT_EQ(NO_ERROR, ls.Execute(&requests));
252    std::string results = GetCapturedStdout();
253    EXPECT_FALSE(results.empty());
254}
255
256TEST_F(SectionTest, TestFilterPiiTaggedFields) {
257    FileSection fs(NOOP_PARSER, tf.path);
258
259    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
260
261    requests.setMainFd(STDOUT_FILENO);
262
263    CaptureStdout();
264    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
265    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
266}
267
268TEST_F(SectionTest, TestBadFdRequest) {
269    FileSection fs(NOOP_PARSER, tf.path);
270    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
271
272    IncidentReportArgs args;
273    args.setAll(true);
274    args.setDest(0);
275    sp<ReportRequest> badFdRequest = new ReportRequest(args, new SimpleListener(), 1234567);
276    requests.add(badFdRequest);
277    requests.setMainFd(STDOUT_FILENO);
278
279    CaptureStdout();
280    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
281    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
282    EXPECT_EQ(badFdRequest->err, -EBADF);
283}
284
285TEST_F(SectionTest, TestBadRequests) {
286    FileSection fs(NOOP_PARSER, tf.path);
287    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
288
289    IncidentReportArgs args;
290    args.setAll(true);
291    args.setDest(0);
292    requests.add(new ReportRequest(args, new SimpleListener(), -1));
293    EXPECT_EQ(fs.Execute(&requests), -EBADF);
294}
295
296TEST_F(SectionTest, TestMultipleRequests) {
297    TemporaryFile output1, output2, output3;
298    FileSection fs(NOOP_PARSER, tf.path);
299
300    ASSERT_TRUE(output1.fd != -1);
301    ASSERT_TRUE(output2.fd != -1);
302    ASSERT_TRUE(output3.fd != -1);
303    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
304
305    IncidentReportArgs args1, args2, args3;
306    args1.setAll(true);
307    args1.setDest(android::os::DEST_LOCAL);
308    args2.setAll(true);
309    args2.setDest(android::os::DEST_EXPLICIT);
310    sp<SimpleListener> l = new SimpleListener();
311    requests.add(new ReportRequest(args1, l, output1.fd));
312    requests.add(new ReportRequest(args2, l, output2.fd));
313    requests.add(new ReportRequest(args3, l, output3.fd));
314    requests.setMainFd(STDOUT_FILENO);
315
316    CaptureStdout();
317    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
318    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
319
320    std::string content, expect;
321    expect = VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3;
322    char c = (char)expect.size();
323    EXPECT_TRUE(ReadFileToString(output1.path, &content));
324    EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
325
326    expect = STRING_FIELD_2 + FIX64_FIELD_3;
327    c = (char)expect.size();
328    EXPECT_TRUE(ReadFileToString(output2.path, &content));
329    EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
330
331    // because args3 doesn't set section, so it should receive nothing
332    EXPECT_TRUE(ReadFileToString(output3.path, &content));
333    EXPECT_THAT(content, StrEq(""));
334}
335
336TEST_F(SectionTest, TestMultipleRequestsBySpec) {
337    TemporaryFile output1, output2, output3;
338    FileSection fs(NOOP_PARSER, tf.path);
339
340    ASSERT_TRUE(output1.fd != -1);
341    ASSERT_TRUE(output2.fd != -1);
342    ASSERT_TRUE(output3.fd != -1);
343
344    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
345
346    IncidentReportArgs args1, args2, args3;
347    args1.setAll(true);
348    args1.setDest(android::os::DEST_EXPLICIT);
349    args2.setAll(true);
350    args2.setDest(android::os::DEST_EXPLICIT);
351    args3.setAll(true);
352    sp<SimpleListener> l = new SimpleListener();
353    requests.add(new ReportRequest(args1, l, output1.fd));
354    requests.add(new ReportRequest(args2, l, output2.fd));
355    requests.add(new ReportRequest(args3, l, output3.fd));
356    requests.setMainFd(STDOUT_FILENO);
357
358    CaptureStdout();
359    ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
360    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2));
361
362    std::string content, expect;
363    expect = STRING_FIELD_2 + FIX64_FIELD_3;
364    char c = (char)expect.size();
365
366    // output1 and output2 are the same
367    EXPECT_TRUE(ReadFileToString(output1.path, &content));
368    EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
369    EXPECT_TRUE(ReadFileToString(output2.path, &content));
370    EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
371
372    // output3 has only auto field
373    c = (char)STRING_FIELD_2.size();
374    EXPECT_TRUE(ReadFileToString(output3.path, &content));
375    EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2));
376}
377