Add static table function to return args with defaults based on proto schema.

Bug: 311643446
Test: npm run test:unit:ci
Change-Id: Idc5f19370ef448167db4ca136ed1a55d17236283
diff --git a/Android.bp b/Android.bp
index 2e4aaa2..6d90b84 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13407,6 +13407,7 @@
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc",
     ],
 }
 
diff --git a/BUILD b/BUILD
index f1cb6bb..84c3a3b 100644
--- a/BUILD
+++ b/BUILD
@@ -2651,6 +2651,8 @@
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.h",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
         "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc",
+        "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h",
     ],
 )
 
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
index 7be5b26..a73c7d9 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
@@ -41,6 +41,8 @@
     "experimental_slice_layout.h",
     "flamegraph_construction_algorithms.cc",
     "flamegraph_construction_algorithms.h",
+    "winscope_proto_to_args_with_defaults.cc",
+    "winscope_proto_to_args_with_defaults.h",
     "table_info.cc",
     "table_info.h",
   ]
@@ -62,6 +64,9 @@
     "../../../tables",
     "../../../types",
     "../../../util",
+    "../../../util:descriptors",
+    "../../../util:proto_to_args_parser",
+    "../../../util:winscope_proto_mapping",
     "../../engine",
   ]
   public_deps = [ ":interface" ]
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
index aa882dd..22999ee 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
@@ -80,6 +80,21 @@
     ],
     parent=FLOW_TABLE)
 
+ARGS_WITH_DEFAULTS_TABLE = Table(
+    python_module=__file__,
+    class_name='WinscopeArgsWithDefaultsTable',
+    sql_name='__intrinsic_winscope_proto_to_args_with_defaults',
+    columns=[
+        C("table_name", CppString(), flags=ColumnFlag.HIDDEN),
+        C('base64_proto_id', CppUint32()),
+        C('flat_key', CppString()),
+        C('key', CppString()),
+        C('int_value', CppOptional(CppInt64())),
+        C('string_value', CppOptional(CppString())),
+        C('real_value', CppOptional(CppDouble())),
+        C('value_type', CppString()),
+    ])
+
 DESCENDANT_SLICE_TABLE = Table(
     python_module=__file__,
     class_name="DescendantSliceTable",
@@ -169,6 +184,7 @@
     ANCESTOR_SLICE_TABLE,
     ANCESTOR_STACK_PROFILE_CALLSITE_TABLE,
     CONNECTED_FLOW_TABLE,
+    ARGS_WITH_DEFAULTS_TABLE,
     DESCENDANT_SLICE_BY_STACK_TABLE,
     DESCENDANT_SLICE_TABLE,
     DFS_WEIGHT_BOUNDED_TABLE,
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc
new file mode 100644
index 0000000..ee8e600
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2024 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/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h"
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/base64.h"
+#include "perfetto/ext/base/status_or.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/tables_py.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/proto_to_args_parser.h"
+#include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
+
+namespace perfetto::trace_processor {
+namespace tables {
+WinscopeArgsWithDefaultsTable::~WinscopeArgsWithDefaultsTable() = default;
+}  // namespace tables
+
+namespace {
+using Row = tables::WinscopeArgsWithDefaultsTable::Row;
+
+class Delegate : public util::ProtoToArgsParser::Delegate {
+ public:
+  using Key = util::ProtoToArgsParser::Key;
+  explicit Delegate(StringPool* pool,
+                    const uint32_t base64_proto_id,
+                    tables::WinscopeArgsWithDefaultsTable* table)
+      : pool_(pool), base64_proto_id_(base64_proto_id), table_(table) {}
+
+  void AddInteger(const Key& key, int64_t res) override {
+    Row r;
+    r.int_value = res;
+    SetColumnsAndInsertRow(key, r);
+  }
+  void AddUnsignedInteger(const Key& key, uint64_t res) override {
+    Row r;
+    r.int_value = res;
+    SetColumnsAndInsertRow(key, r);
+  }
+  void AddString(const Key& key, const protozero::ConstChars& res) override {
+    Row r;
+    r.string_value = pool_->InternString(base::StringView((res.ToStdString())));
+    SetColumnsAndInsertRow(key, r);
+  }
+  void AddString(const Key& key, const std::string& res) override {
+    Row r;
+    r.string_value = pool_->InternString(base::StringView(res));
+    SetColumnsAndInsertRow(key, r);
+  }
+  void AddDouble(const Key& key, double res) override {
+    Row r;
+    r.real_value = res;
+    SetColumnsAndInsertRow(key, r);
+  }
+  void AddBoolean(const Key& key, bool res) override {
+    Row r;
+    r.int_value = res;
+    SetColumnsAndInsertRow(key, r);
+  }
+  void AddBytes(const Key& key, const protozero::ConstBytes& res) override {
+    Row r;
+    r.string_value = pool_->InternString(base::StringView((res.ToStdString())));
+    SetColumnsAndInsertRow(key, r);
+  }
+  void AddNull(const Key& key) override {
+    Row r;
+    SetColumnsAndInsertRow(key, r);
+  }
+  void AddPointer(const Key&, const void*) override {
+    PERFETTO_FATAL("Unsupported");
+  }
+  bool AddJson(const Key&, const protozero::ConstChars&) override {
+    PERFETTO_FATAL("Unsupported");
+  }
+  size_t GetArrayEntryIndex(const std::string&) override {
+    PERFETTO_FATAL("Unsupported");
+  }
+  size_t IncrementArrayEntryIndex(const std::string&) override {
+    PERFETTO_FATAL("Unsupported");
+  }
+  PacketSequenceStateGeneration* seq_state() override { return nullptr; }
+
+ private:
+  InternedMessageView* GetInternedMessageView(uint32_t, uint64_t) override {
+    return nullptr;
+  }
+
+  void SetColumnsAndInsertRow(const Key& key, Row& row) {
+    row.key = pool_->InternString(base::StringView(key.key));
+    row.flat_key = pool_->InternString(base::StringView(key.flat_key));
+    row.base64_proto_id = base64_proto_id_;
+    table_->Insert(row);
+  }
+
+  StringPool* pool_;
+  const uint32_t base64_proto_id_;
+  tables::WinscopeArgsWithDefaultsTable* table_;
+};
+
+base::Status InsertRows(
+    const Table& static_table,
+    tables::WinscopeArgsWithDefaultsTable* inflated_args_table,
+    const std::string& proto_name,
+    const std::vector<uint32_t>* allowed_fields,
+    DescriptorPool& descriptor_pool,
+    StringPool* string_pool) {
+  util::ProtoToArgsParser args_parser{descriptor_pool};
+  const auto base64_proto_id_col_idx =
+      static_table.ColumnIdxFromName("base64_proto_id").value();
+  const auto base_64_proto_col_idx =
+      static_table.ColumnIdxFromName("base64_proto").value();
+
+  std::unordered_set<uint32_t> inflated_protos;
+  for (auto it = static_table.IterateRows(); it; ++it) {
+    const auto base64_proto_id =
+        static_cast<uint32_t>(it.Get(base64_proto_id_col_idx).AsLong());
+    if (inflated_protos.count(base64_proto_id) > 0) {
+      continue;
+    }
+    inflated_protos.insert(base64_proto_id);
+    const auto* raw_proto = it.Get(base_64_proto_col_idx).AsString();
+    const auto blob = *base::Base64Decode(raw_proto);
+    const auto cb = protozero::ConstBytes{
+        reinterpret_cast<const uint8_t*>(blob.data()), blob.size()};
+    Delegate delegate(string_pool, base64_proto_id, inflated_args_table);
+    RETURN_IF_ERROR(args_parser.ParseMessage(cb, proto_name, allowed_fields,
+                                             delegate, nullptr, true));
+  }
+  return base::OkStatus();
+}
+}  // namespace
+
+WinscopeProtoToArgsWithDefaults::WinscopeProtoToArgsWithDefaults(
+    StringPool* string_pool,
+    PerfettoSqlEngine* engine,
+    TraceProcessorContext* context)
+    : string_pool_(string_pool), engine_(engine), context_(context) {}
+
+base::StatusOr<std::unique_ptr<Table>>
+WinscopeProtoToArgsWithDefaults::ComputeTable(
+    const std::vector<SqlValue>& arguments) {
+  PERFETTO_CHECK(arguments.size() == 1);
+  if (arguments[0].type != SqlValue::kString) {
+    return base::ErrStatus(
+        "__intrinsic_winscope_proto_to_args_with_defaults takes table name as "
+        "a string.");
+  }
+  std::string table_name = arguments[0].AsString();
+
+  const Table* static_table = engine_->GetStaticTableOrNull(table_name);
+  if (!static_table) {
+    return base::ErrStatus("Failed to find %s table.", table_name.c_str());
+  }
+
+  std::string proto_name;
+  ASSIGN_OR_RETURN(proto_name,
+                   util::winscope_proto_mapping::GetProtoName(table_name));
+
+  auto table =
+      std::make_unique<tables::WinscopeArgsWithDefaultsTable>(string_pool_);
+
+  auto allowed_fields =
+      util::winscope_proto_mapping::GetAllowedFields(table_name);
+  RETURN_IF_ERROR(InsertRows(*static_table, table.get(), proto_name,
+                             allowed_fields ? &allowed_fields.value() : nullptr,
+                             *context_->descriptor_pool_, string_pool_));
+
+  return std::unique_ptr<Table>(std::move(table));
+}
+
+Table::Schema WinscopeProtoToArgsWithDefaults::CreateSchema() {
+  return tables::WinscopeArgsWithDefaultsTable::ComputeStaticSchema();
+}
+
+std::string WinscopeProtoToArgsWithDefaults::TableName() {
+  return tables::WinscopeArgsWithDefaultsTable::Name();
+}
+
+uint32_t WinscopeProtoToArgsWithDefaults::EstimateRowCount() {
+  // 100 inflated args per 100 elements per 100 entries
+  return 1000000;
+}
+}  // namespace perfetto::trace_processor
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h
new file mode 100644
index 0000000..91ab8c8
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_WINSCOPE_PROTO_TO_ARGS_WITH_DEFAULTS_H_
+#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_WINSCOPE_PROTO_TO_ARGS_WITH_DEFAULTS_H_
+
+#include "perfetto/ext/base/status_or.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
+
+namespace perfetto::trace_processor {
+
+class TraceProcessorContext;
+
+class WinscopeProtoToArgsWithDefaults : public StaticTableFunction {
+ public:
+  explicit WinscopeProtoToArgsWithDefaults(StringPool*,
+                                           PerfettoSqlEngine*,
+                                           TraceProcessorContext* context);
+
+  Table::Schema CreateSchema() override;
+  std::string TableName() override;
+  uint32_t EstimateRowCount() override;
+  base::StatusOr<std::unique_ptr<Table>> ComputeTable(
+      const std::vector<SqlValue>& arguments) override;
+
+ private:
+  StringPool* string_pool_ = nullptr;
+  PerfettoSqlEngine* engine_ = nullptr;
+  TraceProcessorContext* context_ = nullptr;
+};
+
+}  // namespace perfetto::trace_processor
+
+#endif  // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_WINSCOPE_PROTO_TO_ARGS_WITH_DEFAULTS_H_
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 296dc99..2459c75 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -115,6 +115,7 @@
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h"
 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h"
 #include "src/trace_processor/perfetto_sql/stdlib/stdlib.h"
 #include "src/trace_processor/sqlite/bindings/sqlite_aggregate_function.h"
 #include "src/trace_processor/sqlite/bindings/sqlite_result.h"
@@ -1061,6 +1062,9 @@
       std::make_unique<ExperimentalFlatSlice>(&context_));
   engine_->RegisterStaticTableFunction(std::make_unique<DfsWeightBounded>(
       context_.storage->mutable_string_pool()));
+  engine_->RegisterStaticTableFunction(
+      std::make_unique<WinscopeProtoToArgsWithDefaults>(
+          context_.storage->mutable_string_pool(), engine_.get(), &context_));
 
   // Value table aggregate functions.
   engine_->RegisterSqliteAggregateFunction<DominatorTree>(
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index bdd04a3..d409320 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -153,7 +153,8 @@
   // kernel version (inside system_info_tracker) to know how to textualise
   // sched_switch.prev_state bitflags.
   context.system_info_tracker = std::move(context_.system_info_tracker);
-  // "proto_to_args_with_defaults" requires proto descriptors.
+  // "__intrinsic_winscope_proto_to_args_with_defaults" requires proto
+  // descriptors.
   context.descriptor_pool_ = std::move(context_.descriptor_pool_);
 
   context_ = std::move(context);
diff --git a/test/trace_processor/diff_tests/parser/android/surfaceflinger_layers.textproto b/test/trace_processor/diff_tests/parser/android/surfaceflinger_layers.textproto
index 7afbb6a..c8da434 100644
--- a/test/trace_processor/diff_tests/parser/android/surfaceflinger_layers.textproto
+++ b/test/trace_processor/diff_tests/parser/android/surfaceflinger_layers.textproto
@@ -48,12 +48,9 @@
         children: 44
         children: 77
         children: 87
-        type: "Layer"
         layer_stack: 0
         z: 0
         crop {
-          left: 0
-          top: 0
           right: -1
           bottom: -1
         }
@@ -393,6 +390,18 @@
       }
       is_virtual: false
     }
+    displays {
+      id: 4619827677550801153
+      name: "Common Panel"
+      size {
+        w: 1080
+        h: 2400
+      }
+      layer_stack_space_rect {
+        right: 1080
+        bottom: 2400
+      }
+    }
     vsync_id: 24767
   }
   trusted_uid: 1000
diff --git a/test/trace_processor/diff_tests/syntax/table_tests.py b/test/trace_processor/diff_tests/syntax/table_tests.py
index a90e71c..fb2cbc2 100644
--- a/test/trace_processor/diff_tests/syntax/table_tests.py
+++ b/test/trace_processor/diff_tests/syntax/table_tests.py
@@ -412,3 +412,159 @@
         "MAX(id)"
         20745
         """))
+
+  def test_winscope_proto_to_args_with_defaults_with_nested_fields(self):
+    return DiffTestBlueprint(
+        trace=Path('../parser/android/surfaceflinger_layers.textproto'),
+        query="""
+        SELECT flat_key, key, int_value, string_value, real_value FROM __intrinsic_winscope_proto_to_args_with_defaults('surfaceflinger_layer') AS sfl
+        ORDER BY sfl.base64_proto_id, key
+        LIMIT 95
+        """,
+        out=Csv("""
+        "flat_key","key","int_value","string_value","real_value"
+        "active_buffer","active_buffer","[NULL]","[NULL]","[NULL]"
+        "app_id","app_id",0,"[NULL]","[NULL]"
+        "background_blur_radius","background_blur_radius",0,"[NULL]","[NULL]"
+        "barrier_layer","barrier_layer","[NULL]","[NULL]","[NULL]"
+        "blur_regions","blur_regions","[NULL]","[NULL]","[NULL]"
+        "bounds.bottom","bounds.bottom","[NULL]","[NULL]",24000.000000
+        "bounds.left","bounds.left","[NULL]","[NULL]",-10800.000000
+        "bounds.right","bounds.right","[NULL]","[NULL]",10800.000000
+        "bounds.top","bounds.top","[NULL]","[NULL]",-24000.000000
+        "buffer_transform","buffer_transform","[NULL]","[NULL]","[NULL]"
+        "children","children[0]",4,"[NULL]","[NULL]"
+        "children","children[1]",35,"[NULL]","[NULL]"
+        "children","children[2]",43,"[NULL]","[NULL]"
+        "children","children[3]",45,"[NULL]","[NULL]"
+        "children","children[4]",44,"[NULL]","[NULL]"
+        "children","children[5]",77,"[NULL]","[NULL]"
+        "children","children[6]",87,"[NULL]","[NULL]"
+        "color.a","color.a","[NULL]","[NULL]",1.000000
+        "color.b","color.b","[NULL]","[NULL]",-1.000000
+        "color.g","color.g","[NULL]","[NULL]",-1.000000
+        "color.r","color.r","[NULL]","[NULL]",-1.000000
+        "color_transform","color_transform","[NULL]","[NULL]","[NULL]"
+        "corner_radius","corner_radius","[NULL]","[NULL]",0.000000
+        "corner_radius_crop","corner_radius_crop","[NULL]","[NULL]","[NULL]"
+        "crop.bottom","crop.bottom",-1,"[NULL]","[NULL]"
+        "crop.left","crop.left",0,"[NULL]","[NULL]"
+        "crop.right","crop.right",-1,"[NULL]","[NULL]"
+        "crop.top","crop.top",0,"[NULL]","[NULL]"
+        "curr_frame","curr_frame",0,"[NULL]","[NULL]"
+        "damage_region","damage_region","[NULL]","[NULL]","[NULL]"
+        "dataspace","dataspace","[NULL]","BT709 sRGB Full range","[NULL]"
+        "destination_frame.bottom","destination_frame.bottom",-1,"[NULL]","[NULL]"
+        "destination_frame.left","destination_frame.left",0,"[NULL]","[NULL]"
+        "destination_frame.right","destination_frame.right",-1,"[NULL]","[NULL]"
+        "destination_frame.top","destination_frame.top",0,"[NULL]","[NULL]"
+        "effective_scaling_mode","effective_scaling_mode",0,"[NULL]","[NULL]"
+        "effective_transform","effective_transform","[NULL]","[NULL]","[NULL]"
+        "final_crop","final_crop","[NULL]","[NULL]","[NULL]"
+        "flags","flags",2,"[NULL]","[NULL]"
+        "hwc_composition_type","hwc_composition_type","[NULL]","HWC_TYPE_UNSPECIFIED","[NULL]"
+        "hwc_crop","hwc_crop","[NULL]","[NULL]","[NULL]"
+        "hwc_frame","hwc_frame","[NULL]","[NULL]","[NULL]"
+        "hwc_transform","hwc_transform",0,"[NULL]","[NULL]"
+        "id","id",3,"[NULL]","[NULL]"
+        "input_window_info","input_window_info","[NULL]","[NULL]","[NULL]"
+        "invalidate","invalidate",1,"[NULL]","[NULL]"
+        "is_opaque","is_opaque",0,"[NULL]","[NULL]"
+        "is_protected","is_protected",0,"[NULL]","[NULL]"
+        "is_relative_of","is_relative_of",0,"[NULL]","[NULL]"
+        "is_trusted_overlay","is_trusted_overlay",0,"[NULL]","[NULL]"
+        "layer_stack","layer_stack",0,"[NULL]","[NULL]"
+        "metadata","metadata","[NULL]","[NULL]","[NULL]"
+        "name","name","[NULL]","Display 0 name=\"Built-in Screen\"#3","[NULL]"
+        "original_id","original_id",0,"[NULL]","[NULL]"
+        "owner_uid","owner_uid",1000,"[NULL]","[NULL]"
+        "parent","parent",0,"[NULL]","[NULL]"
+        "pixel_format","pixel_format","[NULL]","Unknown/None","[NULL]"
+        "position","position","[NULL]","[NULL]","[NULL]"
+        "queued_frames","queued_frames",0,"[NULL]","[NULL]"
+        "refresh_pending","refresh_pending",0,"[NULL]","[NULL]"
+        "relatives","relatives","[NULL]","[NULL]","[NULL]"
+        "requested_color.a","requested_color.a","[NULL]","[NULL]",1.000000
+        "requested_color.b","requested_color.b","[NULL]","[NULL]",-1.000000
+        "requested_color.g","requested_color.g","[NULL]","[NULL]",-1.000000
+        "requested_color.r","requested_color.r","[NULL]","[NULL]",-1.000000
+        "requested_corner_radius","requested_corner_radius","[NULL]","[NULL]",0.000000
+        "requested_position","requested_position","[NULL]","[NULL]","[NULL]"
+        "requested_transform.dsdx","requested_transform.dsdx","[NULL]","[NULL]",0.000000
+        "requested_transform.dsdy","requested_transform.dsdy","[NULL]","[NULL]",0.000000
+        "requested_transform.dtdx","requested_transform.dtdx","[NULL]","[NULL]",0.000000
+        "requested_transform.dtdy","requested_transform.dtdy","[NULL]","[NULL]",0.000000
+        "requested_transform.type","requested_transform.type",0,"[NULL]","[NULL]"
+        "screen_bounds.bottom","screen_bounds.bottom","[NULL]","[NULL]",24000.000000
+        "screen_bounds.left","screen_bounds.left","[NULL]","[NULL]",-10800.000000
+        "screen_bounds.right","screen_bounds.right","[NULL]","[NULL]",10800.000000
+        "screen_bounds.top","screen_bounds.top","[NULL]","[NULL]",-24000.000000
+        "shadow_radius","shadow_radius","[NULL]","[NULL]",0.000000
+        "size","size","[NULL]","[NULL]","[NULL]"
+        "source_bounds.bottom","source_bounds.bottom","[NULL]","[NULL]",24000.000000
+        "source_bounds.left","source_bounds.left","[NULL]","[NULL]",-10800.000000
+        "source_bounds.right","source_bounds.right","[NULL]","[NULL]",10800.000000
+        "source_bounds.top","source_bounds.top","[NULL]","[NULL]",-24000.000000
+        "transform.dsdx","transform.dsdx","[NULL]","[NULL]",0.000000
+        "transform.dsdy","transform.dsdy","[NULL]","[NULL]",0.000000
+        "transform.dtdx","transform.dtdx","[NULL]","[NULL]",0.000000
+        "transform.dtdy","transform.dtdy","[NULL]","[NULL]",0.000000
+        "transform.type","transform.type",0,"[NULL]","[NULL]"
+        "transparent_region","transparent_region","[NULL]","[NULL]","[NULL]"
+        "trusted_overlay","trusted_overlay","[NULL]","UNSET","[NULL]"
+        "type","type","[NULL]","[NULL]","[NULL]"
+        "visible_region","visible_region","[NULL]","[NULL]","[NULL]"
+        "window_type","window_type",0,"[NULL]","[NULL]"
+        "z","z",0,"[NULL]","[NULL]"
+        "z_order_relative_of","z_order_relative_of",0,"[NULL]","[NULL]"
+        "active_buffer","active_buffer","[NULL]","[NULL]","[NULL]"
+        """))
+
+  def test_winscope_proto_to_args_with_defaults_with_repeated_fields(self):
+    return DiffTestBlueprint(
+        trace=Path('../parser/android/surfaceflinger_layers.textproto'),
+        query="""
+        SELECT flat_key, key, int_value, string_value, real_value FROM __intrinsic_winscope_proto_to_args_with_defaults('surfaceflinger_layers_snapshot') AS sfs
+        WHERE key != "hwc_blob"
+        ORDER BY sfs.base64_proto_id DESC, key ASC
+        LIMIT 36
+        """,
+        out=Csv("""
+        "flat_key","key","int_value","string_value","real_value"
+        "displays.dpi_x","displays[0].dpi_x","[NULL]","[NULL]",0.000000
+        "displays.dpi_y","displays[0].dpi_y","[NULL]","[NULL]",0.000000
+        "displays.id","displays[0].id",4619827677550801152,"[NULL]","[NULL]"
+        "displays.is_virtual","displays[0].is_virtual",0,"[NULL]","[NULL]"
+        "displays.layer_stack","displays[0].layer_stack",0,"[NULL]","[NULL]"
+        "displays.layer_stack_space_rect.bottom","displays[0].layer_stack_space_rect.bottom",2400,"[NULL]","[NULL]"
+        "displays.layer_stack_space_rect.left","displays[0].layer_stack_space_rect.left",0,"[NULL]","[NULL]"
+        "displays.layer_stack_space_rect.right","displays[0].layer_stack_space_rect.right",1080,"[NULL]","[NULL]"
+        "displays.layer_stack_space_rect.top","displays[0].layer_stack_space_rect.top",0,"[NULL]","[NULL]"
+        "displays.name","displays[0].name","[NULL]","Common Panel","[NULL]"
+        "displays.size.h","displays[0].size.h",2400,"[NULL]","[NULL]"
+        "displays.size.w","displays[0].size.w",1080,"[NULL]","[NULL]"
+        "displays.transform.dsdx","displays[0].transform.dsdx","[NULL]","[NULL]",0.000000
+        "displays.transform.dsdy","displays[0].transform.dsdy","[NULL]","[NULL]",0.000000
+        "displays.transform.dtdx","displays[0].transform.dtdx","[NULL]","[NULL]",0.000000
+        "displays.transform.dtdy","displays[0].transform.dtdy","[NULL]","[NULL]",0.000000
+        "displays.transform.type","displays[0].transform.type",0,"[NULL]","[NULL]"
+        "displays.dpi_x","displays[1].dpi_x","[NULL]","[NULL]",0.000000
+        "displays.dpi_y","displays[1].dpi_y","[NULL]","[NULL]",0.000000
+        "displays.id","displays[1].id",4619827677550801153,"[NULL]","[NULL]"
+        "displays.is_virtual","displays[1].is_virtual",0,"[NULL]","[NULL]"
+        "displays.layer_stack","displays[1].layer_stack",0,"[NULL]","[NULL]"
+        "displays.layer_stack_space_rect.bottom","displays[1].layer_stack_space_rect.bottom",2400,"[NULL]","[NULL]"
+        "displays.layer_stack_space_rect.left","displays[1].layer_stack_space_rect.left",0,"[NULL]","[NULL]"
+        "displays.layer_stack_space_rect.right","displays[1].layer_stack_space_rect.right",1080,"[NULL]","[NULL]"
+        "displays.layer_stack_space_rect.top","displays[1].layer_stack_space_rect.top",0,"[NULL]","[NULL]"
+        "displays.name","displays[1].name","[NULL]","Common Panel","[NULL]"
+        "displays.size.h","displays[1].size.h",2400,"[NULL]","[NULL]"
+        "displays.size.w","displays[1].size.w",1080,"[NULL]","[NULL]"
+        "displays.transform","displays[1].transform","[NULL]","[NULL]","[NULL]"
+        "elapsed_realtime_nanos","elapsed_realtime_nanos",2749500341063,"[NULL]","[NULL]"
+        "excludes_composition_state","excludes_composition_state",0,"[NULL]","[NULL]"
+        "missed_entries","missed_entries",0,"[NULL]","[NULL]"
+        "vsync_id","vsync_id",24767,"[NULL]","[NULL]"
+        "where","where","[NULL]","bufferLatched","[NULL]"
+        "displays.dpi_x","displays[0].dpi_x","[NULL]","[NULL]",0.000000
+        """))