/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/traced/probes/android_log/android_log_data_source.h"

#include "perfetto/tracing/core/data_source_config.h"
#include "src/base/test/test_task_runner.h"
#include "src/tracing/core/trace_writer_for_testing.h"
#include "test/gtest_and_gmock.h"

#include "protos/perfetto/common/android_log_constants.gen.h"
#include "protos/perfetto/config/android/android_log_config.gen.h"
#include "protos/perfetto/trace/android/android_log.gen.h"

using ::perfetto::protos::gen::AndroidLogConfig;
using ::perfetto::protos::gen::AndroidLogId;
using ::testing::Return;

namespace perfetto {
namespace {

class TestAndroidLogDataSource : public AndroidLogDataSource {
 public:
  TestAndroidLogDataSource(const DataSourceConfig& config,
                           base::TaskRunner* task_runner,
                           TracingSessionID id,
                           std::unique_ptr<TraceWriter> writer)
      : AndroidLogDataSource(config, task_runner, id, std::move(writer)) {}

  MOCK_METHOD(std::string, ReadEventLogDefinitions, (), (override));
  MOCK_METHOD(base::UnixSocketRaw, ConnectLogdrSocket, (), (override));
};

class AndroidLogDataSourceTest : public ::testing::Test {
 protected:
  AndroidLogDataSourceTest() {}

  void CreateInstance(const DataSourceConfig& cfg) {
    auto writer =
        std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
    writer_raw_ = writer.get();
    data_source_.reset(
        new TestAndroidLogDataSource(cfg, &task_runner_, 0, std::move(writer)));
  }

  void StartAndSimulateLogd(
      const std::vector<std::vector<uint8_t>>& fake_events,
      const char* expected_cmd = "stream tail=1 lids=0,2,3,4,7") {
    base::UnixSocketRaw send_sock;
    base::UnixSocketRaw recv_sock;
    // In theory this should be a kSeqPacket. We use kDgram here so that the
    // test can run also on MacOS (which doesn't support SOCK_SEQPACKET).
    std::tie(send_sock, recv_sock) = base::UnixSocketRaw::CreatePairPosix(
        base::SockFamily::kUnix, base::SockType::kDgram);
    ASSERT_TRUE(send_sock);
    ASSERT_TRUE(recv_sock);

    EXPECT_CALL(*data_source_, ConnectLogdrSocket()).WillOnce([&recv_sock] {
      return std::move(recv_sock);
    });

    data_source_->Start();

    char cmd[64]{};
    EXPECT_GT(send_sock.Receive(cmd, sizeof(cmd) - 1), 0);
    EXPECT_STREQ(expected_cmd, cmd);

    // Send back log messages emulating Android's logdr socket.
    for (const auto& buf : fake_events)
      send_sock.Send(buf.data(), buf.size());

    auto on_flush = task_runner_.CreateCheckpoint("on_flush");
    data_source_->Flush(1, on_flush);
    task_runner_.RunUntilCheckpoint("on_flush");
  }

  base::TestTaskRunner task_runner_;
  std::unique_ptr<TestAndroidLogDataSource> data_source_;
  TraceWriterForTesting* writer_raw_;

  const std::vector<std::vector<uint8_t>> kValidTextEvents{
      // 12-29 23:13:59.679  7546  8991 I ActivityManager:
      // Killing 11660:com.google.android.videos/u0a168 (adj 985): empty #17
      {0x55, 0x00, 0x1c, 0x00, 0x7a, 0x1d, 0x00, 0x00, 0x1f, 0x23, 0x00, 0x00,
       0xb7, 0xff, 0x27, 0x5c, 0xe6, 0x58, 0x7b, 0x28, 0x03, 0x00, 0x00, 0x00,
       0xe8, 0x03, 0x00, 0x00, 0x04, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74,
       0x79, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x00, 0x4b, 0x69, 0x6c,
       0x6c, 0x69, 0x6e, 0x67, 0x20, 0x31, 0x31, 0x36, 0x36, 0x30, 0x3a, 0x63,
       0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x6e,
       0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x73,
       0x2f, 0x75, 0x30, 0x61, 0x31, 0x36, 0x38, 0x20, 0x28, 0x61, 0x64, 0x6a,
       0x20, 0x39, 0x38, 0x35, 0x29, 0x3a, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79,
       0x20, 0x23, 0x31, 0x37, 0x00},

      // 12-29 23:13:59.683  7546  7570 W libprocessgroup:
      // kill(-11660, 9) failed: No such process
      {0x39, 0x00, 0x1c, 0x00, 0x7a, 0x1d, 0x00, 0x00, 0x92, 0x1d, 0x00,
       0x00, 0xb7, 0xff, 0x27, 0x5c, 0x12, 0xf3, 0xbd, 0x28, 0x00, 0x00,
       0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x6c, 0x69, 0x62, 0x70,
       0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x67, 0x72, 0x6f, 0x75, 0x70,
       0x00, 0x6b, 0x69, 0x6c, 0x6c, 0x28, 0x2d, 0x31, 0x31, 0x36, 0x36,
       0x30, 0x2c, 0x20, 0x39, 0x29, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65,
       0x64, 0x3a, 0x20, 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20,
       0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x00},

      // 12-29 23:13:59.719  7415  7415 I Zygote:
      // Process 11660 exited due to signal (9)
      {0x2f, 0x00, 0x1c, 0x00, 0xf7, 0x1c, 0x00, 0x00, 0xf7, 0x1c, 0x00,
       0x00, 0xb7, 0xff, 0x27, 0x5c, 0x7c, 0x11, 0xe2, 0x2a, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5a, 0x79, 0x67, 0x6f,
       0x74, 0x65, 0x00, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x20,
       0x31, 0x31, 0x36, 0x36, 0x30, 0x20, 0x65, 0x78, 0x69, 0x74, 0x65,
       0x64, 0x20, 0x64, 0x75, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69,
       0x67, 0x6e, 0x61, 0x6c, 0x20, 0x28, 0x39, 0x29, 0x00},
  };

  const std::vector<std::vector<uint8_t>> kValidBinaryEvents{
      // 12-30 10:22:08.914 29981 30962 I am_kill :
      // [0,31730,android.process.acore,985,empty #17]
      {0x3d, 0x00, 0x1c, 0x00, 0x1d, 0x75, 0x00, 0x00, 0xf2, 0x78, 0x00, 0x00,
       0x50, 0x9c, 0x28, 0x5c, 0xdb, 0x77, 0x7e, 0x36, 0x02, 0x00, 0x00, 0x00,
       0xe8, 0x03, 0x00, 0x00, 0x47, 0x75, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0xf2, 0x7b, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00,
       0x00, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x70, 0x72, 0x6f,
       0x63, 0x65, 0x73, 0x73, 0x2e, 0x61, 0x63, 0x6f, 0x72, 0x65, 0x00, 0xd9,
       0x03, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x00, 0x65, 0x6d, 0x70, 0x74,
       0x79, 0x20, 0x23, 0x31, 0x37},

      // 12-30 10:22:08.946 29981 30962 I am_uid_stopped: 10018
      {0x09, 0x00, 0x1c, 0x00, 0x1d, 0x75, 0x00, 0x00, 0xf2, 0x78,
       0x00, 0x00, 0x50, 0x9c, 0x28, 0x5c, 0x24, 0x5a, 0x66, 0x38,
       0x02, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x65, 0x75,
       0x00, 0x00, 0x00, 0x22, 0x27, 0x00, 0x00},

      // 12-30 10:22:08.960 29981 29998 I am_pss  :
      // [1417,10098,com.google.android.connectivitymonitor,4831232,3723264,0,56053760,0,9,39]
      {0x72, 0x00, 0x1c, 0x00, 0x1d, 0x75, 0x00, 0x00, 0x2e, 0x75, 0x00, 0x00,
       0x50, 0x9c, 0x28, 0x5c, 0xf4, 0xd7, 0x44, 0x39, 0x02, 0x00, 0x00, 0x00,
       0xe8, 0x03, 0x00, 0x00, 0x5f, 0x75, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x89,
       0x05, 0x00, 0x00, 0x00, 0x72, 0x27, 0x00, 0x00, 0x02, 0x26, 0x00, 0x00,
       0x00, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
       0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x63, 0x6f, 0x6e, 0x6e,
       0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x6d, 0x6f, 0x6e, 0x69,
       0x74, 0x6f, 0x72, 0x01, 0x00, 0xb8, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x01, 0x00, 0xd0, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x50, 0x57, 0x03, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
       0x00, 0x01, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
};

TEST_F(AndroidLogDataSourceTest, ParseEventLogDefinitions) {
  CreateInstance(DataSourceConfig());
  static const char kContents[] = R"(
42 answer (to life the universe etc|3)
314 pi
1003 auditd (avc|3)
1004 chatty (dropped|3)
1005 tag_def (tag|1),(name|3),(format|3)
2718 e
2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
invalid_line (
9999 invalid_line2 (
1937006964 stats_log (atom_id|1|5),(data|4)
)";
  EXPECT_CALL(*data_source_, ReadEventLogDefinitions())
      .WillOnce(Return(kContents));
  data_source_->ParseEventLogDefinitions();

  auto* fmt = data_source_->GetEventFormat(42);
  ASSERT_NE(fmt, nullptr);
  ASSERT_EQ(fmt->name, "answer");
  ASSERT_EQ(fmt->fields.size(), 1u);
  ASSERT_EQ(fmt->fields[0], "to life the universe etc");

  fmt = data_source_->GetEventFormat(314);
  ASSERT_NE(fmt, nullptr);
  ASSERT_EQ(fmt->name, "pi");
  ASSERT_EQ(fmt->fields.size(), 0u);

  fmt = data_source_->GetEventFormat(1005);
  ASSERT_NE(fmt, nullptr);
  ASSERT_EQ(fmt->name, "tag_def");
  ASSERT_EQ(fmt->fields.size(), 3u);
  ASSERT_EQ(fmt->fields[0], "tag");
  ASSERT_EQ(fmt->fields[1], "name");
  ASSERT_EQ(fmt->fields[2], "format");

  fmt = data_source_->GetEventFormat(1937006964);
  ASSERT_NE(fmt, nullptr);
  ASSERT_EQ(fmt->name, "stats_log");
  ASSERT_EQ(fmt->fields.size(), 2u);
  ASSERT_EQ(fmt->fields[0], "atom_id");
  ASSERT_EQ(fmt->fields[1], "data");
}

TEST_F(AndroidLogDataSourceTest, TextEvents) {
  DataSourceConfig cfg;
  CreateInstance(cfg);
  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(""));
  StartAndSimulateLogd(kValidTextEvents);

  // Read back the data that would have been written into the trace. One packet
  // with the events, one with stats.
  auto packets = writer_raw_->GetAllTracePackets();
  ASSERT_TRUE(packets.size() == 2);
  auto event_packet = packets[0];
  auto stats_packet = packets[1];
  EXPECT_TRUE(stats_packet.android_log().has_stats());

  EXPECT_EQ(event_packet.android_log().events_size(), 3);
  const auto& decoded = event_packet.android_log().events();

  EXPECT_EQ(decoded[0].log_id(), protos::gen::AndroidLogId::LID_SYSTEM);
  EXPECT_EQ(decoded[0].pid(), 7546);
  EXPECT_EQ(decoded[0].tid(), 8991);
  EXPECT_EQ(decoded[0].uid(), 1000);
  EXPECT_EQ(decoded[0].prio(), protos::gen::AndroidLogPriority::PRIO_INFO);
  EXPECT_EQ(decoded[0].timestamp(), 1546125239679172326ULL);
  EXPECT_EQ(decoded[0].tag(), "ActivityManager");
  EXPECT_EQ(
      decoded[0].message(),
      "Killing 11660:com.google.android.videos/u0a168 (adj 985): empty #17");

  EXPECT_EQ(decoded[1].log_id(), protos::gen::AndroidLogId::LID_DEFAULT);
  EXPECT_EQ(decoded[1].pid(), 7546);
  EXPECT_EQ(decoded[1].tid(), 7570);
  EXPECT_EQ(decoded[1].uid(), 1000);
  EXPECT_EQ(decoded[1].prio(), protos::gen::AndroidLogPriority::PRIO_WARN);
  EXPECT_EQ(decoded[1].timestamp(), 1546125239683537170ULL);
  EXPECT_EQ(decoded[1].tag(), "libprocessgroup");
  EXPECT_EQ(decoded[1].message(), "kill(-11660, 9) failed: No such process");

  EXPECT_EQ(decoded[2].log_id(), protos::gen::AndroidLogId::LID_DEFAULT);
  EXPECT_EQ(decoded[2].pid(), 7415);
  EXPECT_EQ(decoded[2].tid(), 7415);
  EXPECT_EQ(decoded[2].uid(), 0);
  EXPECT_EQ(decoded[2].prio(), protos::gen::AndroidLogPriority::PRIO_INFO);
  EXPECT_EQ(decoded[2].timestamp(), 1546125239719458684ULL);
  EXPECT_EQ(decoded[2].tag(), "Zygote");
  EXPECT_EQ(decoded[2].message(), "Process 11660 exited due to signal (9)");
}

TEST_F(AndroidLogDataSourceTest, TextEventsWithTagFiltering) {
  DataSourceConfig cfg;
  AndroidLogConfig acfg;
  acfg.add_filter_tags("Zygote");
  acfg.add_filter_tags("ActivityManager");
  acfg.add_filter_tags("Unmatched");
  acfg.add_filter_tags("libprocessgroupZZ");
  cfg.set_android_log_config_raw(acfg.SerializeAsString());

  CreateInstance(cfg);
  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(""));
  StartAndSimulateLogd(kValidTextEvents);

  auto packets = writer_raw_->GetAllTracePackets();
  ASSERT_TRUE(packets.size() == 2);
  auto event_packet = packets[0];
  auto stats_packet = packets[1];
  EXPECT_TRUE(stats_packet.android_log().has_stats());

  EXPECT_EQ(event_packet.android_log().events_size(), 2);
  const auto& decoded = event_packet.android_log().events();
  EXPECT_EQ(decoded[0].tag(), "ActivityManager");
  EXPECT_EQ(decoded[1].tag(), "Zygote");
}

TEST_F(AndroidLogDataSourceTest, TextEventsWithPrioFiltering) {
  DataSourceConfig cfg;
  AndroidLogConfig acfg;
  acfg.set_min_prio(protos::gen::AndroidLogPriority::PRIO_WARN);
  cfg.set_android_log_config_raw(acfg.SerializeAsString());

  CreateInstance(cfg);
  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(""));
  StartAndSimulateLogd(kValidTextEvents);

  auto packets = writer_raw_->GetAllTracePackets();
  ASSERT_TRUE(packets.size() == 2);
  auto event_packet = packets[0];
  auto stats_packet = packets[1];
  EXPECT_TRUE(stats_packet.android_log().has_stats());

  EXPECT_EQ(event_packet.android_log().events_size(), 1);
  const auto& decoded = event_packet.android_log().events();
  EXPECT_EQ(decoded[0].tag(), "libprocessgroup");
}

TEST_F(AndroidLogDataSourceTest, TextEventsWithPreserveLogBuffer) {
  DataSourceConfig cfg;
  AndroidLogConfig acfg;
  acfg.set_preserve_log_buffer(true);
  cfg.set_android_log_config_raw(acfg.SerializeAsString());

  CreateInstance(cfg);
  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(""));
  // When preserve_log_buffer is true, the command should not include "tail=1".
  StartAndSimulateLogd(kValidTextEvents, "stream lids=0,2,3,4,7");

  auto packets = writer_raw_->GetAllTracePackets();
  ASSERT_TRUE(packets.size() == 2);
  auto event_packet = packets[0];
  auto stats_packet = packets[1];
  EXPECT_TRUE(stats_packet.android_log().has_stats());

  // Verify events are still parsed correctly.
  EXPECT_EQ(event_packet.android_log().events_size(), 3);
}

TEST_F(AndroidLogDataSourceTest, BinaryEvents) {
  DataSourceConfig cfg;
  CreateInstance(cfg);
  static const char kDefs[] = R"(
30023 am_kill (User|1|5),(PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
30053 am_uid_stopped (UID|1|5)
30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(Pss|2|2),(Uss|2|2),(SwapPss|2|2),(Rss|2|2),(StatType|1|5),(ProcState|1|5),(TimeToCollect|2|2)
)";
  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(kDefs));
  StartAndSimulateLogd(kValidBinaryEvents);

  // Read back the data that would have been written into the trace. One packet
  // with the events, one with stats.
  auto packets = writer_raw_->GetAllTracePackets();
  ASSERT_TRUE(packets.size() == 2);
  auto event_packet = packets[0];
  auto stats_packet = packets[1];
  EXPECT_TRUE(stats_packet.android_log().has_stats());

  EXPECT_EQ(event_packet.android_log().events_size(), 3);
  const auto& decoded = event_packet.android_log().events();

  EXPECT_EQ(decoded[0].log_id(), protos::gen::AndroidLogId::LID_EVENTS);
  EXPECT_EQ(decoded[0].pid(), 29981);
  EXPECT_EQ(decoded[0].tid(), 30962);
  EXPECT_EQ(decoded[0].uid(), 1000);
  EXPECT_EQ(decoded[0].timestamp(), 1546165328914257883ULL);
  EXPECT_EQ(decoded[0].tag(), "am_kill");
  ASSERT_EQ(decoded[0].args_size(), 5);
  EXPECT_EQ(decoded[0].args()[0].name(), "User");
  EXPECT_EQ(decoded[0].args()[0].int_value(), 0);
  EXPECT_EQ(decoded[0].args()[1].name(), "PID");
  EXPECT_EQ(decoded[0].args()[1].int_value(), 31730);
  EXPECT_EQ(decoded[0].args()[2].name(), "Process Name");
  EXPECT_EQ(decoded[0].args()[2].string_value(), "android.process.acore");
  EXPECT_EQ(decoded[0].args()[3].name(), "OomAdj");
  EXPECT_EQ(decoded[0].args()[3].int_value(), 985);
  EXPECT_EQ(decoded[0].args()[4].name(), "Reason");
  EXPECT_EQ(decoded[0].args()[4].string_value(), "empty #17");

  EXPECT_EQ(decoded[1].log_id(), protos::gen::AndroidLogId::LID_EVENTS);
  EXPECT_EQ(decoded[1].pid(), 29981);
  EXPECT_EQ(decoded[1].tid(), 30962);
  EXPECT_EQ(decoded[1].uid(), 1000);
  EXPECT_EQ(decoded[1].timestamp(), 1546165328946231844ULL);
  EXPECT_EQ(decoded[1].tag(), "am_uid_stopped");
  ASSERT_EQ(decoded[1].args_size(), 1);
  EXPECT_EQ(decoded[1].args()[0].name(), "UID");
  EXPECT_EQ(decoded[1].args()[0].int_value(), 10018);

  EXPECT_EQ(decoded[2].log_id(), protos::gen::AndroidLogId::LID_EVENTS);
  EXPECT_EQ(decoded[2].pid(), 29981);
  EXPECT_EQ(decoded[2].tid(), 29998);
  EXPECT_EQ(decoded[2].uid(), 1000);
  EXPECT_EQ(decoded[2].timestamp(), 1546165328960813044ULL);
  EXPECT_EQ(decoded[2].tag(), "am_pss");
  ASSERT_EQ(decoded[2].args_size(), 10);
  EXPECT_EQ(decoded[2].args()[0].name(), "Pid");
  EXPECT_EQ(decoded[2].args()[0].int_value(), 1417);
  EXPECT_EQ(decoded[2].args()[1].name(), "UID");
  EXPECT_EQ(decoded[2].args()[1].int_value(), 10098);
  EXPECT_EQ(decoded[2].args()[2].name(), "Process Name");
  EXPECT_EQ(decoded[2].args()[2].string_value(),
            "com.google.android.connectivitymonitor");
  EXPECT_EQ(decoded[2].args()[3].name(), "Pss");
  EXPECT_EQ(decoded[2].args()[3].int_value(), 4831232);
  EXPECT_EQ(decoded[2].args()[4].name(), "Uss");
  EXPECT_EQ(decoded[2].args()[4].int_value(), 3723264);
  EXPECT_EQ(decoded[2].args()[5].name(), "SwapPss");
  EXPECT_EQ(decoded[2].args()[5].int_value(), 0);
  EXPECT_EQ(decoded[2].args()[6].name(), "Rss");
  EXPECT_EQ(decoded[2].args()[6].int_value(), 56053760);
  EXPECT_EQ(decoded[2].args()[7].name(), "StatType");
  EXPECT_EQ(decoded[2].args()[7].int_value(), 0);
  EXPECT_EQ(decoded[2].args()[8].name(), "ProcState");
  EXPECT_EQ(decoded[2].args()[8].int_value(), 9);
  EXPECT_EQ(decoded[2].args()[9].name(), "TimeToCollect");
  EXPECT_EQ(decoded[2].args()[9].int_value(), 39);
}

TEST_F(AndroidLogDataSourceTest, BinaryEventsWithTagFiltering) {
  DataSourceConfig cfg;
  AndroidLogConfig acfg;
  acfg.add_filter_tags("not matched");
  acfg.add_filter_tags("am_uid_stopped");
  cfg.set_android_log_config_raw(acfg.SerializeAsString());
  CreateInstance(cfg);
  static const char kDefs[] = R"(
30023 am_kill (User|1|5),(PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
30053 am_uid_stopped (UID|1|5)
30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(Pss|2|2),(Uss|2|2),(SwapPss|2|2),(Rss|2|2),(StatType|1|5),(ProcState|1|5),(TimeToCollect|2|2)
)";
  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(kDefs));
  StartAndSimulateLogd(kValidBinaryEvents);

  // Read back the data that would have been written into the trace. One packet
  // with the events, one with stats.
  auto packets = writer_raw_->GetAllTracePackets();
  ASSERT_TRUE(packets.size() == 2);
  auto event_packet = packets[0];
  auto stats_packet = packets[1];
  EXPECT_TRUE(stats_packet.android_log().has_stats());

  EXPECT_EQ(event_packet.android_log().events_size(), 1);
  const auto& decoded = event_packet.android_log().events();
  EXPECT_EQ(decoded[0].timestamp(), 1546165328946231844ULL);
  EXPECT_EQ(decoded[0].tag(), "am_uid_stopped");
}

}  // namespace
}  // namespace perfetto
