Merge "[ui] Migrate actual/expected frames tracks to tracks v2" into main
diff --git a/Android.bp b/Android.bp
index 4ebb8f6..deb19b0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5238,6 +5238,7 @@
         "protos/perfetto/trace/android/initial_display_state.proto",
         "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
+        "protos/perfetto/trace/android/shell_transition.proto",
         "protos/perfetto/trace/android/surfaceflinger_common.proto",
         "protos/perfetto/trace/android/surfaceflinger_layers.proto",
         "protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5267,6 +5268,7 @@
         "external/perfetto/protos/perfetto/trace/android/initial_display_state.gen.cc",
         "external/perfetto/protos/perfetto/trace/android/network_trace.gen.cc",
         "external/perfetto/protos/perfetto/trace/android/packages_list.gen.cc",
+        "external/perfetto/protos/perfetto/trace/android/shell_transition.gen.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.gen.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.gen.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.gen.cc",
@@ -5296,6 +5298,7 @@
         "external/perfetto/protos/perfetto/trace/android/initial_display_state.gen.h",
         "external/perfetto/protos/perfetto/trace/android/network_trace.gen.h",
         "external/perfetto/protos/perfetto/trace/android/packages_list.gen.h",
+        "external/perfetto/protos/perfetto/trace/android/shell_transition.gen.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.gen.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.gen.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.gen.h",
@@ -5320,6 +5323,7 @@
         "protos/perfetto/trace/android/initial_display_state.proto",
         "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
+        "protos/perfetto/trace/android/shell_transition.proto",
         "protos/perfetto/trace/android/surfaceflinger_common.proto",
         "protos/perfetto/trace/android/surfaceflinger_layers.proto",
         "protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5348,6 +5352,7 @@
         "external/perfetto/protos/perfetto/trace/android/initial_display_state.pb.cc",
         "external/perfetto/protos/perfetto/trace/android/network_trace.pb.cc",
         "external/perfetto/protos/perfetto/trace/android/packages_list.pb.cc",
+        "external/perfetto/protos/perfetto/trace/android/shell_transition.pb.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.pb.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.pb.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.pb.cc",
@@ -5376,6 +5381,7 @@
         "external/perfetto/protos/perfetto/trace/android/initial_display_state.pb.h",
         "external/perfetto/protos/perfetto/trace/android/network_trace.pb.h",
         "external/perfetto/protos/perfetto/trace/android/packages_list.pb.h",
+        "external/perfetto/protos/perfetto/trace/android/shell_transition.pb.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.pb.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.pb.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.pb.h",
@@ -5390,6 +5396,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_android_winscope_descriptor",
     srcs: [
+        "protos/perfetto/trace/android/shell_transition.proto",
         "protos/perfetto/trace/android/surfaceflinger_common.proto",
         "protos/perfetto/trace/android/surfaceflinger_layers.proto",
         "protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5418,6 +5425,7 @@
         "protos/perfetto/trace/android/initial_display_state.proto",
         "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
+        "protos/perfetto/trace/android/shell_transition.proto",
         "protos/perfetto/trace/android/surfaceflinger_common.proto",
         "protos/perfetto/trace/android/surfaceflinger_layers.proto",
         "protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5447,6 +5455,7 @@
         "external/perfetto/protos/perfetto/trace/android/initial_display_state.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/android/network_trace.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/android/shell_transition.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.pbzero.cc",
@@ -5476,6 +5485,7 @@
         "external/perfetto/protos/perfetto/trace/android/initial_display_state.pbzero.h",
         "external/perfetto/protos/perfetto/trace/android/network_trace.pbzero.h",
         "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/android/shell_transition.pbzero.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.pbzero.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.pbzero.h",
         "external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.pbzero.h",
@@ -5695,6 +5705,7 @@
         "protos/perfetto/trace/android/initial_display_state.proto",
         "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
+        "protos/perfetto/trace/android/shell_transition.proto",
         "protos/perfetto/trace/android/surfaceflinger_common.proto",
         "protos/perfetto/trace/android/surfaceflinger_layers.proto",
         "protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -11258,6 +11269,8 @@
 filegroup {
     name: "perfetto_src_trace_processor_importers_proto_winscope_full",
     srcs: [
+        "src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc",
+        "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc",
         "src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.cc",
         "src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.cc",
         "src/trace_processor/importers/proto/winscope/winscope_args_parser.cc",
@@ -13109,6 +13122,7 @@
         "protos/perfetto/trace/android/initial_display_state.proto",
         "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
+        "protos/perfetto/trace/android/shell_transition.proto",
         "protos/perfetto/trace/android/surfaceflinger_common.proto",
         "protos/perfetto/trace/android/surfaceflinger_layers.proto",
         "protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -14460,7 +14474,7 @@
         host: {
             static_libs: [
                 "libprotobuf-cpp-full",
-                "libsqlite",
+                "libsqlite_static_noicu",
                 "libz",
                 "sqlite_ext_percentile",
             ],
@@ -14612,7 +14626,7 @@
         ":perfetto_src_traceconv_utils",
     ],
     static_libs: [
-        "libsqlite",
+        "libsqlite_static_noicu",
         "libz",
         "perfetto_src_trace_processor_demangle",
         "sqlite_ext_percentile",
diff --git a/BUILD b/BUILD
index c287080..4392c28 100644
--- a/BUILD
+++ b/BUILD
@@ -1578,6 +1578,10 @@
 perfetto_filegroup(
     name = "src_trace_processor_importers_proto_winscope_full",
     srcs = [
+        "src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc",
+        "src/trace_processor/importers/proto/winscope/shell_transitions_parser.h",
+        "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc",
+        "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h",
         "src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.cc",
         "src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h",
         "src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.cc",
@@ -4261,6 +4265,7 @@
         "protos/perfetto/trace/android/initial_display_state.proto",
         "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
+        "protos/perfetto/trace/android/shell_transition.proto",
         "protos/perfetto/trace/android/surfaceflinger_common.proto",
         "protos/perfetto/trace/android/surfaceflinger_layers.proto",
         "protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -4277,6 +4282,7 @@
 perfetto_proto_library(
     name = "protos_perfetto_trace_android_winscope_deps_protos",
     srcs = [
+        "protos/perfetto/trace/android/shell_transition.proto",
         "protos/perfetto/trace/android/surfaceflinger_common.proto",
         "protos/perfetto/trace/android/surfaceflinger_layers.proto",
         "protos/perfetto/trace/android/surfaceflinger_transactions.proto",
diff --git a/include/perfetto/base/build_config.h b/include/perfetto/base/build_config.h
index cd41e86..a0e6bb9 100644
--- a/include/perfetto/base/build_config.h
+++ b/include/perfetto/base/build_config.h
@@ -133,12 +133,10 @@
 //   http://msdn.microsoft.com/en-us/library/b0084kay.aspx
 //   http://www.agner.org/optimize/calling_conventions.pdf
 //   or with gcc, run: "echo | gcc -E -dM -"
-#if defined(_M_X64) || defined(__x86_64__)
-#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_ARM64() 0
-#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_X86_64() 1
-#elif defined(__aarch64__) || defined(_M_ARM64)
+#if defined(__aarch64__) || defined(_M_ARM64)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_ARM64() 1
-#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_X86_64() 0
+#else
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_ARM64() 0
 #endif
 
 // perfetto_build_flags.h contains the tweakable build flags defined via GN.
diff --git a/protos/perfetto/trace/android/BUILD.gn b/protos/perfetto/trace/android/BUILD.gn
index c5e842f..509c448 100644
--- a/protos/perfetto/trace/android/BUILD.gn
+++ b/protos/perfetto/trace/android/BUILD.gn
@@ -28,6 +28,7 @@
     "initial_display_state.proto",
     "network_trace.proto",
     "packages_list.proto",
+    "shell_transition.proto",
     "surfaceflinger_common.proto",
     "surfaceflinger_layers.proto",
     "surfaceflinger_transactions.proto",
@@ -37,6 +38,7 @@
 perfetto_proto_library("winscope_deps") {
   proto_generators = [ "source_set" ]
   sources = [
+    "shell_transition.proto",
     "surfaceflinger_common.proto",
     "surfaceflinger_layers.proto",
     "surfaceflinger_transactions.proto",
diff --git a/protos/perfetto/trace/android/shell_transition.proto b/protos/perfetto/trace/android/shell_transition.proto
new file mode 100644
index 0000000..9a34d3a
--- /dev/null
+++ b/protos/perfetto/trace/android/shell_transition.proto
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+// ShellTransition messages record information about the shell transitions in
+// the system. This is used to track the animations that are created and execute
+// through the shell transition system.
+message ShellTransition {
+  // The unique identifier of the transition.
+  optional int32 id = 1;
+
+  // The time the transition was created on the WM side
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 create_time_ns = 2;
+  // The time the transition was sent from the WM side to shell
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 send_time_ns = 3;
+  // The time the transition was dispatched by shell to execute
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 dispatch_time_ns = 4;
+  // If the transition merge was accepted by the transition handler, this
+  // contains the time the transition was merged into transition with id
+  // `merge_target`.
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 merge_time_ns = 5;
+  // The time shell proposed the transition should be merged to the transition
+  // handler into transition with id `merge_target`.
+  // (using SystemClock.elapsedRealtimeNanos()).
+  optional int64 merge_request_time_ns = 6;
+  // If the transition was aborted on the shell side, this is the time that
+  // occured.
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 shell_abort_time_ns = 7;
+  // If the transition was aborted on the wm side, this is the time that
+  // occured.
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 wm_abort_time_ns = 8;
+  // The time WM considers the transition to be complete.
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 finish_time_ns = 9;
+
+  // The id of the transaction that WM proposed to use as the starting
+  // transaction. It contains all the layer changes required to setup the
+  // transition and should be executed right at the start of the transition
+  // by the transition handler.
+  optional uint64 start_transaction_id = 10;
+  // The if of the transaction that WM proposed to use as the finish
+  // transaction. It contains all the layer changes required to set the final
+  // state of the transition.
+  optional uint64 finish_transaction_id = 11;
+
+  // The id of the handler that executed the transition. A HandlerMappings
+  // message in the trace will contain the mapping of id to a string
+  // representation of the handler.
+  optional int32 handler = 12;
+  // The transition type of this transition (e.g. TO_FRONT, OPEN, CLOSE).
+  optional int32 type = 13;
+
+  // The list of targets that are part of this transition.
+  repeated Target targets = 14;
+  // The id of the transition we have requested to merge or have merged this
+  // transition into.
+  optional int32 merge_target = 15;
+
+  // The flags set on this transition.
+  optional int32 flags = 16;
+  // The time the starting window was removed. Tracked because this can
+  // happen after the transition finishes, but the app may not yet be visible
+  // until the starting window is removed. So in a sense the transition is not
+  // finished until the starting window is removed. (b/284302118)
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 starting_window_remove_time_ns = 17;
+
+  // Contains the information about the windows targeted in a transition.
+  message Target {
+    // The transition mode of this target (e.g. TO_FRONT, CLOSE...)
+    optional int32 mode = 1;
+    // The layer id of this target.
+    optional int32 layer_id = 2;
+    // The window id of this target.
+    optional int32 window_id = 3;
+    // The flags set on this target.
+    optional int32 flags = 4;
+  }
+}
+
+// Contains mappings from handler ids to string representation of the handlers.
+message ShellHandlerMappings {
+  repeated ShellHandlerMapping mapping = 1;
+}
+
+message ShellHandlerMapping {
+  // The id of the handler used in the ShellTransition message.
+  optional int32 id = 1;
+  // A human readable and meaningful string representation of the handler.
+  optional string name = 2;
+}
diff --git a/protos/perfetto/trace/android/winscope.proto b/protos/perfetto/trace/android/winscope.proto
index 78fbdcc..296e3ee 100644
--- a/protos/perfetto/trace/android/winscope.proto
+++ b/protos/perfetto/trace/android/winscope.proto
@@ -18,6 +18,7 @@
 
 package perfetto.protos;
 
+import "protos/perfetto/trace/android/shell_transition.proto";
 import "protos/perfetto/trace/android/surfaceflinger_layers.proto";
 import "protos/perfetto/trace/android/surfaceflinger_transactions.proto";
 
@@ -26,4 +27,5 @@
 message WinscopeTraceData {
   optional LayersSnapshotProto layers_snapshot = 1;
   optional TransactionTraceEntry transactions = 2;
+  optional ShellTransition shell_transition = 3;
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 2ae554d..2c09723 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -4284,6 +4284,104 @@
 
 // End of protos/perfetto/trace/android/packages_list.proto
 
+// Begin of protos/perfetto/trace/android/shell_transition.proto
+
+// ShellTransition messages record information about the shell transitions in
+// the system. This is used to track the animations that are created and execute
+// through the shell transition system.
+message ShellTransition {
+  // The unique identifier of the transition.
+  optional int32 id = 1;
+
+  // The time the transition was created on the WM side
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 create_time_ns = 2;
+  // The time the transition was sent from the WM side to shell
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 send_time_ns = 3;
+  // The time the transition was dispatched by shell to execute
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 dispatch_time_ns = 4;
+  // If the transition merge was accepted by the transition handler, this
+  // contains the time the transition was merged into transition with id
+  // `merge_target`.
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 merge_time_ns = 5;
+  // The time shell proposed the transition should be merged to the transition
+  // handler into transition with id `merge_target`.
+  // (using SystemClock.elapsedRealtimeNanos()).
+  optional int64 merge_request_time_ns = 6;
+  // If the transition was aborted on the shell side, this is the time that
+  // occured.
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 shell_abort_time_ns = 7;
+  // If the transition was aborted on the wm side, this is the time that
+  // occured.
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 wm_abort_time_ns = 8;
+  // The time WM considers the transition to be complete.
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 finish_time_ns = 9;
+
+  // The id of the transaction that WM proposed to use as the starting
+  // transaction. It contains all the layer changes required to setup the
+  // transition and should be executed right at the start of the transition
+  // by the transition handler.
+  optional uint64 start_transaction_id = 10;
+  // The if of the transaction that WM proposed to use as the finish
+  // transaction. It contains all the layer changes required to set the final
+  // state of the transition.
+  optional uint64 finish_transaction_id = 11;
+
+  // The id of the handler that executed the transition. A HandlerMappings
+  // message in the trace will contain the mapping of id to a string
+  // representation of the handler.
+  optional int32 handler = 12;
+  // The transition type of this transition (e.g. TO_FRONT, OPEN, CLOSE).
+  optional int32 type = 13;
+
+  // The list of targets that are part of this transition.
+  repeated Target targets = 14;
+  // The id of the transition we have requested to merge or have merged this
+  // transition into.
+  optional int32 merge_target = 15;
+
+  // The flags set on this transition.
+  optional int32 flags = 16;
+  // The time the starting window was removed. Tracked because this can
+  // happen after the transition finishes, but the app may not yet be visible
+  // until the starting window is removed. So in a sense the transition is not
+  // finished until the starting window is removed. (b/284302118)
+  // (using SystemClock.elapsedRealtimeNanos())
+  optional int64 starting_window_remove_time_ns = 17;
+
+  // Contains the information about the windows targeted in a transition.
+  message Target {
+    // The transition mode of this target (e.g. TO_FRONT, CLOSE...)
+    optional int32 mode = 1;
+    // The layer id of this target.
+    optional int32 layer_id = 2;
+    // The window id of this target.
+    optional int32 window_id = 3;
+    // The flags set on this target.
+    optional int32 flags = 4;
+  }
+}
+
+// Contains mappings from handler ids to string representation of the handlers.
+message ShellHandlerMappings {
+  repeated ShellHandlerMapping mapping = 1;
+}
+
+message ShellHandlerMapping {
+  // The id of the handler used in the ShellTransition message.
+  optional int32 id = 1;
+  // A human readable and meaningful string representation of the handler.
+  optional string name = 2;
+}
+
+// End of protos/perfetto/trace/android/shell_transition.proto
+
 // Begin of protos/perfetto/trace/android/surfaceflinger_common.proto
 
 message RegionProto {
@@ -13376,6 +13474,8 @@
     // Winscope traces
     LayersSnapshotProto surfaceflinger_layers_snapshot = 93;
     TransactionTraceEntry surfaceflinger_transactions = 94;
+    ShellTransition shell_transition = 96;
+    ShellHandlerMappings shell_handler_mappings = 97;
 
     // Events from the Windows etw infrastructure.
     EtwTraceEventBundle etw_events = 95;
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 9966894..86a90fe 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -29,6 +29,7 @@
 import "protos/perfetto/trace/android/initial_display_state.proto";
 import "protos/perfetto/trace/android/network_trace.proto";
 import "protos/perfetto/trace/android/packages_list.proto";
+import "protos/perfetto/trace/android/shell_transition.proto";
 import "protos/perfetto/trace/android/surfaceflinger_layers.proto";
 import "protos/perfetto/trace/android/surfaceflinger_transactions.proto";
 import "protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto";
@@ -216,6 +217,8 @@
     // Winscope traces
     LayersSnapshotProto surfaceflinger_layers_snapshot = 93;
     TransactionTraceEntry surfaceflinger_transactions = 94;
+    ShellTransition shell_transition = 96;
+    ShellHandlerMappings shell_handler_mappings = 97;
 
     // Events from the Windows etw infrastructure.
     EtwTraceEventBundle etw_events = 95;
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 90cde7d..a68feef 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -1460,7 +1460,7 @@
 
 message StartUp {
   // This enum must be kept up to date with LaunchCauseMetrics.LaunchCause.
-  enum LauchCauseType {
+  enum LaunchCauseType {
     OTHER = 0;
     CUSTOM_TAB = 1;
     TWA = 2;
@@ -1483,7 +1483,8 @@
   }
 
   optional int64 activity_id = 1;
-  optional LauchCauseType launch_cause = 2;
+  // deprecated field 2.
+  optional LaunchCauseType launch_cause = 3;
 }
 
 message WebContentInteraction {
diff --git a/src/trace_processor/importers/common/args_tracker.h b/src/trace_processor/importers/common/args_tracker.h
index 4751fef..2a3a8b5 100644
--- a/src/trace_processor/importers/common/args_tracker.h
+++ b/src/trace_processor/importers/common/args_tracker.h
@@ -139,6 +139,12 @@
         context_->storage->mutable_surfaceflinger_transactions_table(), id);
   }
 
+  BoundInserter AddArgsTo(tables::WindowManagerShellTransitionsTable::Id id) {
+    return AddArgsTo(
+        context_->storage->mutable_window_manager_shell_transitions_table(),
+        id);
+  }
+
   BoundInserter AddArgsTo(MetadataId id) {
     auto* table = context_->storage->mutable_metadata_table();
     uint32_t row = *table->id().IndexOf(id);
diff --git a/src/trace_processor/importers/proto/winscope/BUILD.gn b/src/trace_processor/importers/proto/winscope/BUILD.gn
index 2d48f0c..7b04e85 100644
--- a/src/trace_processor/importers/proto/winscope/BUILD.gn
+++ b/src/trace_processor/importers/proto/winscope/BUILD.gn
@@ -16,30 +16,35 @@
 
 source_set("full") {
   sources = [
+    "shell_transitions_parser.cc",
+    "shell_transitions_parser.h",
+    "shell_transitions_tracker.cc",
+    "shell_transitions_tracker.h",
     "surfaceflinger_layers_parser.cc",
     "surfaceflinger_layers_parser.h",
     "surfaceflinger_transactions_parser.cc",
     "surfaceflinger_transactions_parser.h",
-    "winscope_args_parser.h",
     "winscope_args_parser.cc",
+    "winscope_args_parser.h",
     "winscope_module.cc",
     "winscope_module.h",
   ]
   deps = [
     ":gen_cc_winscope_descriptor",
+    "../:proto_importer_module",
     "../../../../../gn:default_deps",
-    "../../../../../protos/perfetto/trace/android:zero",
     "../../../../../protos/perfetto/trace:zero",
+    "../../../../../protos/perfetto/trace/android:zero",
     "../../../storage",
     "../../../tables",
     "../../../types",
     "../../common",
     "../../common:parser_types",
-    "../:proto_importer_module",
   ]
 }
 
 perfetto_cc_proto_descriptor("gen_cc_winscope_descriptor") {
   descriptor_name = "winscope.descriptor"
-  descriptor_target = "../../../../../protos/perfetto/trace/android:winscope_descriptor"
+  descriptor_target =
+      "../../../../../protos/perfetto/trace/android:winscope_descriptor"
 }
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc
new file mode 100644
index 0000000..68d2a82
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 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/importers/proto/winscope/shell_transitions_parser.h"
+#include "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h"
+
+#include "protos/perfetto/trace/android/shell_transition.pbzero.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
+#include "src/trace_processor/importers/proto/winscope/winscope_args_parser.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+ShellTransitionsParser::ShellTransitionsParser(TraceProcessorContext* context)
+    : context_(context), args_parser_{pool_} {
+  pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
+                                 kWinscopeDescriptor.size());
+}
+
+void ShellTransitionsParser::ParseTransition(protozero::ConstBytes blob) {
+  protos::pbzero::ShellTransition::Decoder transition(blob);
+
+  auto row_id =
+      ShellTransitionsTracker::GetOrCreate(context_)->InternTransition(
+          transition.id());
+
+  auto* window_manager_shell_transitions_table =
+      context_->storage->mutable_window_manager_shell_transitions_table();
+  auto row = window_manager_shell_transitions_table->FindById(row_id).value();
+
+  if (transition.has_dispatch_time_ns()) {
+    row.set_ts(transition.dispatch_time_ns());
+  }
+
+  auto inserter = context_->args_tracker->AddArgsTo(row_id);
+  WinscopeArgsParser writer(inserter, *context_->storage.get());
+  base::Status status = args_parser_.ParseMessage(
+      blob, kShellTransitionsProtoName, nullptr /* parse all fields */, writer);
+
+  if (!status.ok()) {
+    context_->storage->IncrementStats(
+        stats::winscope_shell_transitions_parse_errors);
+  }
+}
+
+void ShellTransitionsParser::ParseHandlerMappings(protozero::ConstBytes blob) {
+  auto* shell_handlers_table =
+      context_->storage
+          ->mutable_window_manager_shell_transition_handlers_table();
+
+  protos::pbzero::ShellHandlerMappings::Decoder handler_mappings(blob);
+  for (auto it = handler_mappings.mapping(); it; ++it) {
+    protos::pbzero::ShellHandlerMapping::Decoder mapping(it.field().as_bytes());
+
+    tables::WindowManagerShellTransitionHandlersTable::Row row;
+    row.handler_id = mapping.id();
+    row.handler_name = context_->storage->InternString(
+        base::StringView(mapping.name().ToStdString()));
+    shell_handlers_table->Insert(row);
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h
new file mode 100644
index 0000000..44b86d1
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_PARSER_H_
+
+#include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/proto_to_args_parser.h"
+
+namespace perfetto {
+
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class ShellTransitionsParser {
+ public:
+  explicit ShellTransitionsParser(TraceProcessorContext*);
+  void ParseTransition(protozero::ConstBytes);
+  void ParseHandlerMappings(protozero::ConstBytes);
+
+ private:
+  static constexpr auto* kShellTransitionsProtoName =
+      ".perfetto.protos.ShellTransition";
+
+  TraceProcessorContext* const context_;
+  DescriptorPool pool_;
+  util::ProtoToArgsParser args_parser_;
+};
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_PARSER_H_
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc
new file mode 100644
index 0000000..6025d91
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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 "shell_transitions_tracker.h"
+#include "perfetto/ext/base/crash_keys.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+ShellTransitionsTracker::ShellTransitionsTracker(TraceProcessorContext* context)
+    : context_(context) {}
+
+ShellTransitionsTracker::~ShellTransitionsTracker() = default;
+
+tables::WindowManagerShellTransitionsTable::Id
+ShellTransitionsTracker::InternTransition(int32_t transition_id) {
+  auto pos = transition_id_to_row_mapping_.find(transition_id);
+  if (pos != transition_id_to_row_mapping_.end()) {
+    return pos->second;
+  }
+
+  auto* window_manager_shell_transitions_table =
+      context_->storage->mutable_window_manager_shell_transitions_table();
+
+  tables::WindowManagerShellTransitionsTable::Row row;
+  row.transition_id = transition_id;
+  auto row_id = window_manager_shell_transitions_table->Insert(row).id;
+
+  transition_id_to_row_mapping_.insert({transition_id, row_id});
+
+  return row_id;
+}
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h
new file mode 100644
index 0000000..07ef736
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 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_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_TRACKER_H_
+
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Tracks information in the transition table.
+class ShellTransitionsTracker : public Destructible {
+ public:
+  explicit ShellTransitionsTracker(TraceProcessorContext*);
+  virtual ~ShellTransitionsTracker() override;
+
+  static ShellTransitionsTracker* GetOrCreate(TraceProcessorContext* context) {
+    if (!context->shell_transitions_tracker) {
+      context->shell_transitions_tracker.reset(
+          new ShellTransitionsTracker(context));
+    }
+    return static_cast<ShellTransitionsTracker*>(
+        context->shell_transitions_tracker.get());
+  }
+
+  tables::WindowManagerShellTransitionsTable::Id InternTransition(
+      int32_t transition_id);
+
+ private:
+  TraceProcessorContext* context_;
+  std::unordered_map<int32_t, tables::WindowManagerShellTransitionsTable::Id>
+      transition_id_to_row_mapping_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/winscope/winscope_module.cc b/src/trace_processor/importers/proto/winscope/winscope_module.cc
index de9e224..7f6c154 100644
--- a/src/trace_processor/importers/proto/winscope/winscope_module.cc
+++ b/src/trace_processor/importers/proto/winscope/winscope_module.cc
@@ -23,11 +23,14 @@
 
 WinscopeModule::WinscopeModule(TraceProcessorContext* context)
     : surfaceflinger_layers_parser_(context),
-      surfaceflinger_transactions_parser_(context) {
+      surfaceflinger_transactions_parser_(context),
+      shell_transitions_parser_(context) {
   RegisterForField(TracePacket::kSurfaceflingerLayersSnapshotFieldNumber,
                    context);
   RegisterForField(TracePacket::kSurfaceflingerTransactionsFieldNumber,
                    context);
+  RegisterForField(TracePacket::kShellTransitionFieldNumber, context);
+  RegisterForField(TracePacket::kShellHandlerMappingsFieldNumber, context);
 }
 
 void WinscopeModule::ParseTracePacketData(const TracePacket::Decoder& decoder,
@@ -43,6 +46,13 @@
       surfaceflinger_transactions_parser_.Parse(
           timestamp, decoder.surfaceflinger_transactions());
       return;
+    case TracePacket::kShellTransitionFieldNumber:
+      shell_transitions_parser_.ParseTransition(decoder.shell_transition());
+      return;
+    case TracePacket::kShellHandlerMappingsFieldNumber:
+      shell_transitions_parser_.ParseHandlerMappings(
+          decoder.shell_handler_mappings());
+      return;
   }
 }
 
diff --git a/src/trace_processor/importers/proto/winscope/winscope_module.h b/src/trace_processor/importers/proto/winscope/winscope_module.h
index d9428d0..fffe42c 100644
--- a/src/trace_processor/importers/proto/winscope/winscope_module.h
+++ b/src/trace_processor/importers/proto/winscope/winscope_module.h
@@ -21,6 +21,7 @@
 #include "perfetto/base/build_config.h"
 #include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/importers/proto/winscope/shell_transitions_parser.h"
 #include "src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h"
 #include "src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.h"
 
@@ -41,6 +42,7 @@
  private:
   SurfaceFlingerLayersParser surfaceflinger_layers_parser_;
   SurfaceFlingerTransactionsParser surfaceflinger_transactions_parser_;
+  ShellTransitionsParser shell_transitions_parser_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index d583632..1e40489 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -262,6 +262,11 @@
                                           kSingle,  kInfo,     kAnalysis,      \
       "SurfaceFlinger transactions packet has unknown fields, which results "  \
       "in some arguments missing. You may need a newer version of trace "      \
+      "processor to parse them."),                                             \
+  F(winscope_shell_transitions_parse_errors,                                   \
+                                          kSingle,  kInfo,     kAnalysis,      \
+      "Shell transition packet has unknown fields, which results "  \
+      "in some arguments missing. You may need a newer version of trace "      \
       "processor to parse them.")
 // clang-format on
 
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index e75233e..79463f0 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -738,6 +738,24 @@
     return &surfaceflinger_transactions_table_;
   }
 
+  const tables::WindowManagerShellTransitionsTable&
+  window_manager_shell_transitions_table() const {
+    return window_manager_shell_transitions_table_;
+  }
+  tables::WindowManagerShellTransitionsTable*
+  mutable_window_manager_shell_transitions_table() {
+    return &window_manager_shell_transitions_table_;
+  }
+
+  const tables::WindowManagerShellTransitionHandlersTable&
+  window_manager_shell_transition_handlers_table() const {
+    return window_manager_shell_transition_handlers_table_;
+  }
+  tables::WindowManagerShellTransitionHandlersTable*
+  mutable_window_manager_shell_transition_handlers_table() {
+    return &window_manager_shell_transition_handlers_table_;
+  }
+
   const tables::ExperimentalProtoPathTable& experimental_proto_path_table()
       const {
     return experimental_proto_path_table_;
@@ -995,6 +1013,10 @@
   tables::SurfaceFlingerLayerTable surfaceflinger_layer_table_{&string_pool_};
   tables::SurfaceFlingerTransactionsTable surfaceflinger_transactions_table_{
       &string_pool_};
+  tables::WindowManagerShellTransitionsTable
+      window_manager_shell_transitions_table_{&string_pool_};
+  tables::WindowManagerShellTransitionHandlersTable
+      window_manager_shell_transition_handlers_table_{&string_pool_};
 
   tables::ExperimentalProtoPathTable experimental_proto_path_table_{
       &string_pool_};
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index 16981fa..989d5e2 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -120,6 +120,10 @@
     default;
 SurfaceFlingerLayerTable::~SurfaceFlingerLayerTable() = default;
 SurfaceFlingerTransactionsTable::~SurfaceFlingerTransactionsTable() = default;
+WindowManagerShellTransitionsTable::~WindowManagerShellTransitionsTable() =
+    default;
+WindowManagerShellTransitionHandlersTable::
+    ~WindowManagerShellTransitionHandlersTable() = default;
 
 }  // namespace tables
 
diff --git a/src/trace_processor/tables/winscope_tables.py b/src/trace_processor/tables/winscope_tables.py
index 22f2080..12e7cef 100644
--- a/src/trace_processor/tables/winscope_tables.py
+++ b/src/trace_processor/tables/winscope_tables.py
@@ -18,6 +18,7 @@
 from python.generators.trace_processor_table.public import CppTableId
 from python.generators.trace_processor_table.public import TableDoc
 from python.generators.trace_processor_table.public import CppUint32
+from python.generators.trace_processor_table.public import CppString
 
 SURFACE_FLINGER_LAYERS_SNAPSHOT_TABLE = Table(
     python_module=__file__,
@@ -60,16 +61,53 @@
         C('arg_set_id', CppUint32()),
     ],
     tabledoc=TableDoc(
-        doc='SurfaceFlinger transactions. Each row contains a set of transactions that SurfaceFlinger committed together.',
+        doc='SurfaceFlinger transactions. Each row contains a set of ' +
+        'transactions that SurfaceFlinger committed together.',
         group='Winscope',
         columns={
             'ts': 'Timestamp of the transactions commit',
             'arg_set_id': 'Extra args parsed from the proto message',
         }))
 
+WINDOW_MANAGER_SHELL_TRANSITIONS_TABLE = Table(
+    python_module=__file__,
+    class_name='WindowManagerShellTransitionsTable',
+    sql_name='window_manager_shell_transitions',
+    columns=[
+        C('ts', CppInt64()),
+        C('transition_id', CppInt64()),
+        C('arg_set_id', CppUint32()),
+    ],
+    tabledoc=TableDoc(
+        doc='Window Manager Shell Transitions',
+        group='Winscope',
+        columns={
+            'ts': 'The timestamp the transition started playing',
+            'transition_id': 'The id of the transition',
+            'arg_set_id': 'Extra args parsed from the proto message',
+        }))
+
+WINDOW_MANAGER_SHELL_TRANSITION_HANDLERS_TABLE = Table(
+    python_module=__file__,
+    class_name='WindowManagerShellTransitionHandlersTable',
+    sql_name='window_manager_shell_transition_handlers',
+    columns=[
+        C('handler_id', CppInt64()),
+        C('handler_name', CppString()),
+    ],
+    tabledoc=TableDoc(
+        doc='Window Manager Shell Transition Handlers',
+        group='Winscope',
+        columns={
+            'handler_id': 'The id of the handler',
+            'handler_name': 'The name of the handler',
+        }))
+
 # Keep this list sorted.
 ALL_TABLES = [
     SURFACE_FLINGER_LAYERS_SNAPSHOT_TABLE,
     SURFACE_FLINGER_LAYER_TABLE,
     SURFACE_FLINGER_TRANSACTIONS_TABLE,
+    WINDOW_MANAGER_SHELL_TRANSITIONS_TABLE,
+    WINDOW_MANAGER_SHELL_TRANSITION_HANDLERS_TABLE,
 ]
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 976357c..db65dae 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -854,6 +854,10 @@
   RegisterStaticTable(storage->surfaceflinger_layer_table());
   RegisterStaticTable(storage->surfaceflinger_transactions_table());
 
+  RegisterStaticTable(storage->window_manager_shell_transitions_table());
+  RegisterStaticTable(
+      storage->window_manager_shell_transition_handlers_table());
+
   RegisterStaticTable(storage->metadata_table());
   RegisterStaticTable(storage->cpu_table());
   RegisterStaticTable(storage->cpu_freq_table());
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index c2644ee..1eb16e4 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -123,6 +123,7 @@
   std::unique_ptr<Destructible> i2c_tracker;             // I2CTracker
   std::unique_ptr<Destructible> perf_data_tracker;       // PerfDataTracker
   std::unique_ptr<Destructible> content_analyzer;
+  std::unique_ptr<Destructible> shell_transitions_tracker;
 
   // These fields are trace readers which will be called by |forwarding_parser|
   // once the format of the trace is discovered. They are placed here as they
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 0aea755..e585f47 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -51,6 +51,7 @@
 from diff_tests.parser.android.tests_games import AndroidGames
 from diff_tests.parser.android.tests_surfaceflinger_layers import SurfaceFlingerLayers
 from diff_tests.parser.android.tests_surfaceflinger_transactions import SurfaceFlingerTransactions
+from diff_tests.parser.android.tests_shell_transitions import ShellTransitions
 from diff_tests.parser.android_fs.tests import AndroidFs
 from diff_tests.parser.atrace.tests import Atrace
 from diff_tests.parser.atrace.tests_error_handling import AtraceErrorHandling
@@ -171,6 +172,8 @@
                             'SurfaceFlingerLayers').fetch(),
       *SurfaceFlingerTransactions(index_path, 'parser/android',
                                   'SurfaceFlingerTransactions').fetch(),
+      *ShellTransitions(index_path, 'parser/android',
+                        'ShellTransitions').fetch(),
       *TrackEvent(index_path, 'parser/track_event', 'TrackEvent').fetch(),
       *TranslatedArgs(index_path, 'parser/translated_args',
                       'TranslatedArgs').fetch(),
diff --git a/test/trace_processor/diff_tests/parser/android/shell_handlers.textproto b/test/trace_processor/diff_tests/parser/android/shell_handlers.textproto
new file mode 100644
index 0000000..9b0182a
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/shell_handlers.textproto
@@ -0,0 +1,21 @@
+packet {
+  trusted_uid: 10225
+  trusted_packet_sequence_id: 12
+  previous_packet_dropped: true
+  trusted_pid: 3981
+  first_packet_on_sequence: true
+  shell_handler_mappings {
+    mapping {
+        id: 1
+        name: "DefaultTransitionHandler"
+    }
+    mapping {
+        id: 2
+        name: "RecentsTransitionHandler"
+    }
+    mapping {
+        id: 3
+        name: "FreeformTaskTransitionHandler"
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/shell_transitions.textproto b/test/trace_processor/diff_tests/parser/android/shell_transitions.textproto
new file mode 100644
index 0000000..b92eb39
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/shell_transitions.textproto
@@ -0,0 +1,167 @@
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  previous_packet_dropped: true
+  trusted_pid: 1305
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 7
+    create_time_ns: 76799049027
+    send_time_ns: 76875395422
+    start_transaction_id: 5604932321952
+    finish_transaction_id: 5604932321954
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1305
+  shell_transition {
+    id: 10
+    create_time_ns: 77854865352
+    send_time_ns: 77894307328
+    start_transaction_id: 5604932322158
+    finish_transaction_id: 5604932322159
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1305
+  shell_transition {
+    id: 11
+    create_time_ns: 82498121051
+    send_time_ns: 82535513345
+    start_transaction_id: 5604932322346
+    finish_transaction_id: 5604932322347
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 3
+  previous_packet_dropped: true
+  trusted_pid: 1305
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 8
+    create_time_ns: 76955664017
+    send_time_ns: 77277756832
+    start_transaction_id: 5604932322028
+    finish_transaction_id: 5604932322029
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 4
+  previous_packet_dropped: true
+  trusted_pid: 1305
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 7
+    starting_window_remove_time_ns: 77706603918
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 5
+  previous_packet_dropped: true
+  trusted_pid: 1305
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 9
+    create_time_ns: 77825423417
+    send_time_ns: 77843436723
+    start_transaction_id: 5604932322137
+    finish_transaction_id: 5604932322138
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 5
+  trusted_pid: 1305
+  shell_transition {
+    id: 9
+    finish_time_ns: 77873935462
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 5
+  trusted_pid: 1305
+  shell_transition {
+    id: 10
+    finish_time_ns: 78621610429
+  }
+}
+packet {
+  trusted_uid: 10241
+  trusted_packet_sequence_id: 6
+  previous_packet_dropped: true
+  trusted_pid: 2528
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 7
+    dispatch_time_ns: 76879063147
+    handler: 2
+  }
+}
+packet {
+  trusted_uid: 10241
+  trusted_packet_sequence_id: 6
+  trusted_pid: 2528
+  shell_transition {
+    id: 8
+    merge_time_ns: 77278725500
+    merge_target: 7
+  }
+}
+packet {
+  trusted_uid: 10241
+  trusted_packet_sequence_id: 6
+  trusted_pid: 2528
+  shell_transition {
+    id: 8
+    dispatch_time_ns: 77320527177
+    handler: 3
+  }
+}
+packet {
+  trusted_uid: 10241
+  trusted_packet_sequence_id: 6
+  trusted_pid: 2528
+  shell_transition {
+    id: 9
+    dispatch_time_ns: 77876414832
+    handler: 3
+  }
+}
+packet {
+  trusted_uid: 10241
+  trusted_packet_sequence_id: 6
+  trusted_pid: 2528
+  shell_transition {
+    id: 10
+    dispatch_time_ns: 77899001013
+    handler: 4
+  }
+}
+packet {
+  trusted_uid: 10241
+  trusted_packet_sequence_id: 6
+  trusted_pid: 2528
+  shell_transition {
+    id: 11
+    dispatch_time_ns: 82536817137
+    handler: 2
+  }
+}
+packet {
+  trusted_uid: 10241
+  trusted_packet_sequence_id: 6
+  trusted_pid: 2528
+  shell_transition {
+    id: 12
+    merge_time_ns: 82697060749
+    merge_target: 11
+  }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/shell_transitions_simple_merge.textproto b/test/trace_processor/diff_tests/parser/android/shell_transitions_simple_merge.textproto
new file mode 100644
index 0000000..6c9cb65
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/shell_transitions_simple_merge.textproto
@@ -0,0 +1,62 @@
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  previous_packet_dropped: true
+  trusted_pid: 1336
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 15
+    create_time_ns: 2187614568227
+    send_time_ns: 2187671767120
+    start_transaction_id: 5738076308937
+    finish_transaction_id: 5738076308938
+    type: 1
+    targets {
+      mode: 1
+      layer_id: 244
+      window_id: 219481253
+      flags: 0
+    }
+    targets {
+      mode: 4
+      layer_id: 47
+      window_id: 54474511
+      flags: 1
+    }
+    flags: 0
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 3
+  previous_packet_dropped: true
+  trusted_pid: 1336
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 15
+    finish_time_ns: 2188195968659
+  }
+}
+packet {
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 5
+  previous_packet_dropped: true
+  trusted_pid: 1336
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 15
+    starting_window_remove_time_ns: 2188652838898
+  }
+}
+packet {
+  trusted_uid: 10225
+  trusted_packet_sequence_id: 12
+  previous_packet_dropped: true
+  trusted_pid: 3981
+  first_packet_on_sequence: true
+  shell_transition {
+    id: 15
+    dispatch_time_ns: 2187673373973
+    handler: 2
+  }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py b/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py
new file mode 100644
index 0000000..a8e0328
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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.
+
+from python.generators.diff_tests.testing import Path
+from python.generators.diff_tests.testing import Csv
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ShellTransitions(TestSuite):
+
+  def test_has_expected_transition_rows(self):
+    return DiffTestBlueprint(
+        trace=Path('shell_transitions.textproto'),
+        query="""
+        SELECT
+          id, transition_id
+        FROM
+          window_manager_shell_transitions;
+        """,
+        out=Csv("""
+        "id","transition_id"
+        0,7
+        1,10
+        2,11
+        3,8
+        4,9
+        5,12
+        """))
+
+  def test_has_expected_transition_args(self):
+    return DiffTestBlueprint(
+        trace=Path('shell_transitions_simple_merge.textproto'),
+        query="""
+        SELECT
+          args.key, args.display_value
+        FROM
+          window_manager_shell_transitions JOIN args ON window_manager_shell_transitions.arg_set_id = args.arg_set_id
+        WHERE window_manager_shell_transitions.transition_id = 15
+        ORDER BY args.key;
+        """,
+        out=Csv("""
+        "key","display_value"
+        "create_time_ns","2187614568227"
+        "dispatch_time_ns","2187673373973"
+        "finish_time_ns","2188195968659"
+        "finish_transaction_id","5738076308938"
+        "flags","0"
+        "handler","2"
+        "id","15"
+        "send_time_ns","2187671767120"
+        "start_transaction_id","5738076308937"
+        "starting_window_remove_time_ns","2188652838898"
+        "targets[0].flags","0"
+        "targets[0].layer_id","244"
+        "targets[0].mode","1"
+        "targets[0].window_id","219481253"
+        "targets[1].flags","1"
+        "targets[1].layer_id","47"
+        "targets[1].mode","4"
+        "targets[1].window_id","54474511"
+        "type","1"
+        """))
+
+  def test_has_shell_handlers(self):
+    return DiffTestBlueprint(
+        trace=Path('shell_handlers.textproto'),
+        query="""
+      SELECT
+        handler_id, handler_name
+      FROM
+        window_manager_shell_transition_handlers;
+      """,
+        out=Csv("""
+      "handler_id","handler_name"
+      1,"DefaultTransitionHandler"
+      2,"RecentsTransitionHandler"
+      3,"FreeformTaskTransitionHandler"
+      """))
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 11f6d16..6d00144 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -335,7 +335,7 @@
 
 def enable_sqlite(module):
   if module.type == 'cc_binary_host':
-    module.static_libs.add('libsqlite')
+    module.static_libs.add('libsqlite_static_noicu')
     module.static_libs.add('sqlite_ext_percentile')
   elif module.host_supported:
     # Copy what the sqlite3 command line tool does.
@@ -344,7 +344,7 @@
     module.android.shared_libs.add('liblog')
     module.android.shared_libs.add('libutils')
     module.android.static_libs.add('sqlite_ext_percentile')
-    module.host.static_libs.add('libsqlite')
+    module.host.static_libs.add('libsqlite_static_noicu')
     module.host.static_libs.add('sqlite_ext_percentile')
   else:
     module.shared_libs.add('libsqlite')
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 91a0c0e..8ee9b76 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -6,7 +6,7 @@
     },
     {
       "name": "canary",
-      "rev": "c69b33b9abcc20fad9ad5f39de883216e4b43130"
+      "rev": "9ca89e30931314dec4af1131d516e07e39d8657d"
     },
     {
       "name": "autopush",
diff --git a/ui/src/controller/aggregation/slice_aggregation_controller.ts b/ui/src/controller/aggregation/slice_aggregation_controller.ts
index 4de7003..7543110 100644
--- a/ui/src/controller/aggregation/slice_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/slice_aggregation_controller.ts
@@ -17,7 +17,9 @@
 import {Area, Sorting} from '../../common/state';
 import {globals} from '../../frontend/globals';
 import {Engine} from '../../trace_processor/engine';
-import {ASYNC_SLICE_TRACK_KIND} from '../../tracks/async_slices';
+import {
+  ASYNC_SLICE_TRACK_KIND,
+} from '../../tracks/async_slices/async_slice_track';
 import {SLICE_TRACK_KIND} from '../../tracks/chrome_slices';
 
 import {AggregationController} from './aggregation_controller';
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 48b68f1..8c6433b 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -39,7 +39,7 @@
   STR_NULL,
 } from '../trace_processor/query_result';
 import {ACTUAL_FRAMES_SLICE_TRACK_KIND} from '../tracks/actual_frames';
-import {ASYNC_SLICE_TRACK_KIND} from '../tracks/async_slices';
+import {ASYNC_SLICE_TRACK_KIND} from '../tracks/async_slices/async_slice_track';
 import {
   ENABLE_SCROLL_JANK_PLUGIN_V2,
   getScrollJankTracks,
@@ -288,14 +288,25 @@
         }
       }
 
-      const track: AddTrackArgs = {
-        uri: `perfetto.AsyncSlices#${rawName}`,
-        trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
-        trackGroup,
-        name,
-      };
+      if (showV1()) {
+        const track: AddTrackArgs = {
+          uri: `perfetto.AsyncSlices#${rawName}`,
+          trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+          trackGroup,
+          name,
+        };
+        this.tracksToAdd.push(track);
+      }
 
-      this.tracksToAdd.push(track);
+      if (showV2()) {
+        const track: AddTrackArgs = {
+          uri: `perfetto.AsyncSlices#${rawName}.v2`,
+          trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+          trackGroup,
+          name,
+        };
+        this.tracksToAdd.push(track);
+      }
     }
   }
 
@@ -1022,12 +1033,24 @@
         processName,
         kind: ASYNC_SLICE_TRACK_KIND,
       });
-      this.tracksToAdd.push({
-        uri: `perfetto.AsyncSlices#process.${pid}${rawTrackIds}`,
-        name,
-        trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
-        trackGroup: uuid,
-      });
+
+      if (showV1()) {
+        this.tracksToAdd.push({
+          uri: `perfetto.AsyncSlices#process.${pid}${rawTrackIds}`,
+          name,
+          trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+          trackGroup: uuid,
+        });
+      }
+
+      if (showV2()) {
+        this.tracksToAdd.push({
+          uri: `perfetto.AsyncSlices#process.${pid}${rawTrackIds}.v2`,
+          name,
+          trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+          trackGroup: uuid,
+        });
+      }
     }
   }
 
diff --git a/ui/src/tracks/async_slices/async_slice_track.ts b/ui/src/tracks/async_slices/async_slice_track.ts
new file mode 100644
index 0000000..5d6e25a
--- /dev/null
+++ b/ui/src/tracks/async_slices/async_slice_track.ts
@@ -0,0 +1,119 @@
+// Copyright (C) 2021 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.
+
+import {BigintMath as BIMath} from '../../base/bigint_math';
+import {duration, time} from '../../base/time';
+import {SliceData, SliceTrackBase} from '../../frontend/slice_track_base';
+import {EngineProxy} from '../../public';
+import {
+  LONG,
+  LONG_NULL,
+  NUM,
+  STR,
+} from '../../trace_processor/query_result';
+
+export const ASYNC_SLICE_TRACK_KIND = 'AsyncSliceTrack';
+
+export class AsyncSliceTrack extends SliceTrackBase {
+  private maxDurNs: duration = 0n;
+
+  constructor(
+      private engine: EngineProxy, maxDepth: number, trackKey: string,
+      private trackIds: number[], namespace?: string) {
+    // TODO is 'slice' right here?
+    super(maxDepth, trackKey, 'slice', namespace);
+  }
+
+  async onBoundsChange(start: time, end: time, resolution: duration):
+      Promise<SliceData> {
+    if (this.maxDurNs === 0n) {
+      const maxDurResult = await this.engine.query(`
+        select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts,
+        dur)) as maxDur from experimental_slice_layout where filter_track_ids
+        = '${this.trackIds.join(',')}'
+      `);
+      this.maxDurNs = maxDurResult.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
+    }
+
+    const queryRes = await this.engine.query(`
+      SELECT
+      (ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
+        ts,
+        max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as
+        dur, layout_depth as depth, ifnull(name, '[null]') as name, id, dur =
+        0 as isInstant, dur = -1 as isIncomplete
+      from experimental_slice_layout
+      where
+        filter_track_ids = '${this.trackIds.join(',')}' and
+        ts >= ${start - this.maxDurNs} and
+        ts <= ${end}
+      group by tsq, layout_depth
+      order by tsq, layout_depth
+    `);
+
+    const numRows = queryRes.numRows();
+    const slices: SliceData = {
+      start,
+      end,
+      resolution,
+      length: numRows,
+      strings: [],
+      sliceIds: new Float64Array(numRows),
+      starts: new BigInt64Array(numRows),
+      ends: new BigInt64Array(numRows),
+      depths: new Uint16Array(numRows),
+      titles: new Uint16Array(numRows),
+      isInstant: new Uint16Array(numRows),
+      isIncomplete: new Uint16Array(numRows),
+    };
+
+    const stringIndexes = new Map<string, number>();
+    function internString(str: string) {
+      let idx = stringIndexes.get(str);
+      if (idx !== undefined) return idx;
+      idx = slices.strings.length;
+      slices.strings.push(str);
+      stringIndexes.set(str, idx);
+      return idx;
+    }
+
+    const it = queryRes.iter({
+      tsq: LONG,
+      ts: LONG,
+      dur: LONG,
+      depth: NUM,
+      name: STR,
+      id: NUM,
+      isInstant: NUM,
+      isIncomplete: NUM,
+    });
+    for (let row = 0; it.valid(); it.next(), row++) {
+      const startQ = it.tsq;
+      const start = it.ts;
+      const dur = it.dur;
+      const end = start + dur;
+      const minEnd = startQ + resolution;
+      const endQ = BIMath.max(BIMath.quant(end, resolution), minEnd);
+
+      slices.starts[row] = startQ;
+      slices.ends[row] = endQ;
+      slices.depths[row] = it.depth;
+      slices.titles[row] = internString(it.name);
+      slices.sliceIds[row] = it.id;
+      slices.isInstant[row] = it.isInstant;
+      slices.isIncomplete[row] = it.isIncomplete;
+    }
+    return slices;
+  }
+}
diff --git a/ui/src/tracks/async_slices/async_slice_track_v2.ts b/ui/src/tracks/async_slices/async_slice_track_v2.ts
new file mode 100644
index 0000000..4e87c9b
--- /dev/null
+++ b/ui/src/tracks/async_slices/async_slice_track_v2.ts
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 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.
+
+import {NamedSliceTrack} from '../../frontend/named_slice_track';
+import {NewTrackArgs} from '../../frontend/track';
+import {Slice} from '../../public';
+
+export class AsyncSliceTrackV2 extends NamedSliceTrack {
+  constructor(
+      args: NewTrackArgs, maxDepth: number, private trackIds: number[]) {
+    super(args);
+    this.sliceLayout.maxDepth = maxDepth + 1;
+  }
+
+  getSqlSource(): string {
+    return `
+    select
+      ts,
+      dur,
+      layout_depth as depth,
+      ifnull(name, '[null]') as name,
+      id,
+      thread_dur as threadDur
+    from experimental_slice_layout
+    where filter_track_ids = '${this.trackIds.join(',')}'
+    `;
+  }
+
+  onUpdatedSlices(slices: Slice[]) {
+    for (const slice of slices) {
+      slice.isHighlighted = (slice === this.hoveredSlice);
+    }
+  }
+}
diff --git a/ui/src/tracks/async_slices/index.ts b/ui/src/tracks/async_slices/index.ts
index 97d43fa..eb3ad88 100644
--- a/ui/src/tracks/async_slices/index.ts
+++ b/ui/src/tracks/async_slices/index.ts
@@ -12,11 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {BigintMath as BIMath} from '../../base/bigint_math';
-import {duration, time} from '../../base/time';
-import {SliceData, SliceTrackBase} from '../../frontend/slice_track_base';
 import {
-  EngineProxy,
   Plugin,
   PluginContext,
   PluginContextTrace,
@@ -24,109 +20,17 @@
 } from '../../public';
 import {getTrackName} from '../../public/utils';
 import {
-  LONG,
-  LONG_NULL,
   NUM,
   NUM_NULL,
   STR,
   STR_NULL,
 } from '../../trace_processor/query_result';
 
+import {AsyncSliceTrack} from './async_slice_track';
+import {AsyncSliceTrackV2} from './async_slice_track_v2';
+
 export const ASYNC_SLICE_TRACK_KIND = 'AsyncSliceTrack';
 
-class AsyncSliceTrack extends SliceTrackBase {
-  private maxDurNs: duration = 0n;
-
-  constructor(
-      private engine: EngineProxy, maxDepth: number, trackKey: string,
-      private trackIds: number[], namespace?: string) {
-    // TODO is 'slice' right here?
-    super(maxDepth, trackKey, 'slice', namespace);
-  }
-
-  async onBoundsChange(start: time, end: time, resolution: duration):
-      Promise<SliceData> {
-    if (this.maxDurNs === 0n) {
-      const maxDurResult = await this.engine.query(`
-        select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts,
-        dur)) as maxDur from experimental_slice_layout where filter_track_ids
-        = '${this.trackIds.join(',')}'
-      `);
-      this.maxDurNs = maxDurResult.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
-    }
-
-    const queryRes = await this.engine.query(`
-      SELECT
-      (ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
-        ts,
-        max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as
-        dur, layout_depth as depth, ifnull(name, '[null]') as name, id, dur =
-        0 as isInstant, dur = -1 as isIncomplete
-      from experimental_slice_layout
-      where
-        filter_track_ids = '${this.trackIds.join(',')}' and
-        ts >= ${start - this.maxDurNs} and
-        ts <= ${end}
-      group by tsq, layout_depth
-      order by tsq, layout_depth
-    `);
-
-    const numRows = queryRes.numRows();
-    const slices: SliceData = {
-      start,
-      end,
-      resolution,
-      length: numRows,
-      strings: [],
-      sliceIds: new Float64Array(numRows),
-      starts: new BigInt64Array(numRows),
-      ends: new BigInt64Array(numRows),
-      depths: new Uint16Array(numRows),
-      titles: new Uint16Array(numRows),
-      isInstant: new Uint16Array(numRows),
-      isIncomplete: new Uint16Array(numRows),
-    };
-
-    const stringIndexes = new Map<string, number>();
-    function internString(str: string) {
-      let idx = stringIndexes.get(str);
-      if (idx !== undefined) return idx;
-      idx = slices.strings.length;
-      slices.strings.push(str);
-      stringIndexes.set(str, idx);
-      return idx;
-    }
-
-    const it = queryRes.iter({
-      tsq: LONG,
-      ts: LONG,
-      dur: LONG,
-      depth: NUM,
-      name: STR,
-      id: NUM,
-      isInstant: NUM,
-      isIncomplete: NUM,
-    });
-    for (let row = 0; it.valid(); it.next(), row++) {
-      const startQ = it.tsq;
-      const start = it.ts;
-      const dur = it.dur;
-      const end = start + dur;
-      const minEnd = startQ + resolution;
-      const endQ = BIMath.max(BIMath.quant(end, resolution), minEnd);
-
-      slices.starts[row] = startQ;
-      slices.ends[row] = endQ;
-      slices.depths[row] = it.depth;
-      slices.titles[row] = internString(it.name);
-      slices.sliceIds[row] = it.id;
-      slices.isInstant[row] = it.isInstant;
-      slices.isIncomplete[row] = it.isIncomplete;
-    }
-    return slices;
-  }
-}
-
 class AsyncSlicePlugin implements Plugin {
   onActivate(_ctx: PluginContext) {}
 
@@ -220,6 +124,20 @@
           );
         },
       });
+
+      ctx.registerStaticTrack({
+        uri: `perfetto.AsyncSlices#${rawName}.v2`,
+        displayName,
+        trackIds,
+        kind: ASYNC_SLICE_TRACK_KIND,
+        track: ({trackKey}) => {
+          return new AsyncSliceTrackV2(
+              {engine, trackKey},
+              maxDepth,
+              trackIds,
+          );
+        },
+      });
     }
   }
 
@@ -288,6 +206,20 @@
           );
         },
       });
+
+      ctx.registerStaticTrack({
+        uri: `perfetto.AsyncSlices#process.${pid}${rawTrackIds}.v2`,
+        displayName,
+        trackIds,
+        kind: ASYNC_SLICE_TRACK_KIND,
+        track: ({trackKey}) => {
+          return new AsyncSliceTrackV2(
+              {engine: ctx.engine, trackKey},
+              maxDepth,
+              trackIds,
+          );
+        },
+      });
     }
   }
 }
diff --git a/ui/src/tracks/thread_state/thread_state_track_v2.ts b/ui/src/tracks/thread_state/thread_state_track_v2.ts
deleted file mode 100644
index c61ca5b..0000000
--- a/ui/src/tracks/thread_state/thread_state_track_v2.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (C) 2021 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.
-
-import {Actions} from '../../common/actions';
-import {colorForState} from '../../common/colorizer';
-import {Selection} from '../../common/state';
-import {translateState} from '../../common/thread_state';
-import {
-  BASE_ROW,
-  BaseSliceTrack,
-  BaseSliceTrackTypes,
-  OnSliceClickArgs,
-} from '../../frontend/base_slice_track';
-import {globals} from '../../frontend/globals';
-import {
-  SLICE_LAYOUT_FLAT_DEFAULTS,
-  SliceLayout,
-} from '../../frontend/slice_layout';
-import {NewTrackArgs} from '../../frontend/track';
-import {NUM_NULL, STR} from '../../trace_processor/query_result';
-
-export const THREAD_STATE_ROW = {
-  ...BASE_ROW,
-  state: STR,
-  ioWait: NUM_NULL,
-};
-
-export type ThreadStateRow = typeof THREAD_STATE_ROW;
-
-export interface ThreadStateTrackTypes extends BaseSliceTrackTypes {
-  row: ThreadStateRow;
-}
-
-export class ThreadStateTrack extends BaseSliceTrack<ThreadStateTrackTypes> {
-  protected sliceLayout: SliceLayout = {...SLICE_LAYOUT_FLAT_DEFAULTS};
-
-  constructor(args: NewTrackArgs, private utid: number) {
-    super(args);
-  }
-
-  // This is used by the base class to call iter().
-  getRowSpec(): ThreadStateTrackTypes['row'] {
-    return THREAD_STATE_ROW;
-  }
-
-  getSqlSource(): string {
-    // Do not display states 'x' and 'S' (dead & sleeping).
-    const sql = `
-      select
-        id,
-        ts,
-        dur,
-        cpu,
-        state,
-        io_wait as ioWait,
-        0 as depth
-      from thread_state
-      where
-        utid = ${this.utid} and
-        state != 'x' and
-        state != 'S'
-    `;
-    return sql;
-  }
-
-  rowToSlice(row: ThreadStateTrackTypes['row']):
-      ThreadStateTrackTypes['slice'] {
-    const baseSlice = super.rowToSlice(row);
-    const ioWait = row.ioWait === null ? undefined : !!row.ioWait;
-    const title = translateState(row.state, ioWait);
-    const color = colorForState(title);
-    return {...baseSlice, title, colorScheme: color};
-  }
-
-  onUpdatedSlices(slices: ThreadStateTrackTypes['slice'][]) {
-    for (const slice of slices) {
-      slice.isHighlighted = (slice === this.hoveredSlice);
-    }
-  }
-
-  onSliceClick(args: OnSliceClickArgs<ThreadStateTrackTypes['slice']>) {
-    globals.makeSelection(Actions.selectThreadState({
-      id: args.slice.id,
-      trackKey: this.trackKey,
-    }));
-  }
-
-  protected isSelectionHandled(selection: Selection): boolean {
-    return selection.kind === 'THREAD_STATE';
-  }
-}
diff --git a/ui/src/tracks/thread_state/thread_state_v2.ts b/ui/src/tracks/thread_state/thread_state_v2.ts
index 54dec7d..c35ed5c 100644
--- a/ui/src/tracks/thread_state/thread_state_v2.ts
+++ b/ui/src/tracks/thread_state/thread_state_v2.ts
@@ -35,19 +35,13 @@
   state: STR,
   ioWait: NUM_NULL,
 };
+
 export type ThreadStateRow = typeof THREAD_STATE_ROW;
 
-
-export interface ThreadStateTrackConfig {
-  utid: number;
-}
-
 export interface ThreadStateTrackTypes extends BaseSliceTrackTypes {
   row: ThreadStateRow;
 }
 
-export const THREAD_STATE_TRACK_V2_KIND = 'ThreadStateTrackV2';
-
 export class ThreadStateTrack extends BaseSliceTrack<ThreadStateTrackTypes> {
   protected sliceLayout: SliceLayout = {...SLICE_LAYOUT_FLAT_DEFAULTS};
 
@@ -62,11 +56,15 @@
 
   getSqlSource(): string {
     // Do not display states 'x' and 'S' (dead & sleeping).
+    // Note: Thread state tracks V1 basically ignores incomplete slices, faking
+    // their duration as 1 instead. Let's just do this here as well for now to
+    // achieve feature parity with tracks V1 and tackle the issue of overlapping
+    // incomplete slices later.
     return `
       select
         id,
         ts,
-        dur,
+        max(dur, 1) as dur,
         cpu,
         state,
         io_wait as ioWait,