Merge "bt-ui: only allow depending on base widgets and public" into main
diff --git a/Android.bp b/Android.bp
index bf201a2..8c2c414 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5052,6 +5052,9 @@
         "protos/perfetto/metrics/android/simpleperf.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
         "protos/perfetto/metrics/android/surfaceflinger.proto",
+        "protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto",
+        "protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto",
+        "protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto",
         "protos/perfetto/metrics/android/task_names.proto",
         "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
         "protos/perfetto/metrics/android/trace_quality.proto",
@@ -5134,6 +5137,9 @@
         "protos/perfetto/metrics/android/simpleperf.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
         "protos/perfetto/metrics/android/surfaceflinger.proto",
+        "protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto",
+        "protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto",
+        "protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto",
         "protos/perfetto/metrics/android/task_names.proto",
         "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
         "protos/perfetto/metrics/android/trace_quality.proto",
@@ -5199,6 +5205,9 @@
         "protos/perfetto/metrics/android/simpleperf.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
         "protos/perfetto/metrics/android/surfaceflinger.proto",
+        "protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto",
+        "protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto",
+        "protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto",
         "protos/perfetto/metrics/android/task_names.proto",
         "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
         "protos/perfetto/metrics/android/trace_quality.proto",
@@ -11502,6 +11511,9 @@
         "src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql",
         "src/trace_processor/metrics/sql/android/startup/system_state.sql",
         "src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql",
+        "src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_metric.sql",
+        "src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_slices.sql",
+        "src/trace_processor/metrics/sql/android/sysui_update_notif_on_ui_mode_changed_metric.sql",
         "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
diff --git a/BUILD b/BUILD
index affa401..300819c 100644
--- a/BUILD
+++ b/BUILD
@@ -1921,6 +1921,9 @@
         "src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql",
         "src/trace_processor/metrics/sql/android/startup/system_state.sql",
         "src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql",
+        "src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_metric.sql",
+        "src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_slices.sql",
+        "src/trace_processor/metrics/sql/android/sysui_update_notif_on_ui_mode_changed_metric.sql",
         "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
     ],
 )
@@ -4122,6 +4125,9 @@
         "protos/perfetto/metrics/android/simpleperf.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
         "protos/perfetto/metrics/android/surfaceflinger.proto",
+        "protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto",
+        "protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto",
+        "protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto",
         "protos/perfetto/metrics/android/task_names.proto",
         "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
         "protos/perfetto/metrics/android/trace_quality.proto",
diff --git a/infra/luci/README.recipes.md b/infra/luci/README.recipes.md
index c818fe4..2c343ab 100644
--- a/infra/luci/README.recipes.md
+++ b/infra/luci/README.recipes.md
@@ -93,11 +93,11 @@
 
 Recipe for building Perfetto.
 
-— **def [BuildForPlatform](/infra/luci/recipes/perfetto.py#130)(api, ctx, platform):**
+— **def [BuildForPlatform](/infra/luci/recipes/perfetto.py#136)(api, ctx, platform):**
 
 — **def [GnArgs](/infra/luci/recipes/perfetto.py#73)(platform):**
 
-— **def [RunSteps](/infra/luci/recipes/perfetto.py#157)(api, repository):**
+— **def [RunSteps](/infra/luci/recipes/perfetto.py#163)(api, repository):**
 
 — **def [UploadArtifact](/infra/luci/recipes/perfetto.py#82)(api, ctx, platform, out_dir, artifact):**
 ### *recipes* / [windows\_sdk:examples/full](/infra/luci/recipe_modules/windows_sdk/examples/full.py)
diff --git a/infra/luci/recipes/perfetto.expected/ci_win.json b/infra/luci/recipes/perfetto.expected/ci_win.json
index 34f2d95..bba862f 100644
--- a/infra/luci/recipes/perfetto.expected/ci_win.json
+++ b/infra/luci/recipes/perfetto.expected/ci_win.json
@@ -375,6 +375,38 @@
   },
   {
     "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]\\resources\\gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]\\gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]\\builder\\perfetto\\out\\windows-amd64\\trace_processor_shell.exe.pdb",
+      "gs://perfetto-luci-artifacts//windows-amd64/trace_processor_shell.exe.pdb"
+    ],
+    "cwd": "[CACHE]\\builder\\perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//windows-amd64/trace_processor_shell.exe.pdb@@@"
+    ]
+  },
+  {
+    "cmd": [
       "cipd.bat",
       "pkg-build",
       "-pkg-def",
@@ -475,7 +507,7 @@
         "hostname": "rdbhost"
       }
     },
-    "name": "Artifact upload.gsutil upload (2)",
+    "name": "Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//windows-amd64/traceconv.exe@@@"
@@ -483,6 +515,38 @@
   },
   {
     "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]\\resources\\gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]\\gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]\\builder\\perfetto\\out\\windows-amd64\\traceconv.exe.pdb",
+      "gs://perfetto-luci-artifacts//windows-amd64/traceconv.exe.pdb"
+    ],
+    "cwd": "[CACHE]\\builder\\perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Artifact upload.gsutil upload (4)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//windows-amd64/traceconv.exe.pdb@@@"
+    ]
+  },
+  {
+    "cmd": [
       "cipd.bat",
       "pkg-build",
       "-pkg-def",
@@ -583,7 +647,7 @@
         "hostname": "rdbhost"
       }
     },
-    "name": "Artifact upload.gsutil upload (3)",
+    "name": "Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//windows-amd64/perfetto.exe@@@"
@@ -591,6 +655,38 @@
   },
   {
     "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]\\resources\\gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]\\gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]\\builder\\perfetto\\out\\windows-amd64\\perfetto.exe.pdb",
+      "gs://perfetto-luci-artifacts//windows-amd64/perfetto.exe.pdb"
+    ],
+    "cwd": "[CACHE]\\builder\\perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Artifact upload.gsutil upload (6)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//windows-amd64/perfetto.exe.pdb@@@"
+    ]
+  },
+  {
+    "cmd": [
       "cipd.bat",
       "pkg-build",
       "-pkg-def",
@@ -691,7 +787,7 @@
         "hostname": "rdbhost"
       }
     },
-    "name": "Artifact upload.gsutil upload (4)",
+    "name": "Artifact upload.gsutil upload (7)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//windows-amd64/traced.exe@@@"
@@ -699,6 +795,38 @@
   },
   {
     "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]\\resources\\gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]\\gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]\\builder\\perfetto\\out\\windows-amd64\\traced.exe.pdb",
+      "gs://perfetto-luci-artifacts//windows-amd64/traced.exe.pdb"
+    ],
+    "cwd": "[CACHE]\\builder\\perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "Artifact upload.gsutil upload (8)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//windows-amd64/traced.exe.pdb@@@"
+    ]
+  },
+  {
+    "cmd": [
       "cipd.bat",
       "pkg-build",
       "-pkg-def",
diff --git a/infra/luci/recipes/perfetto.py b/infra/luci/recipes/perfetto.py
index d95c72d..7e9586b 100644
--- a/infra/luci/recipes/perfetto.py
+++ b/infra/luci/recipes/perfetto.py
@@ -97,6 +97,12 @@
   gcs_target_path = '{}/{}/{}'.format(gcs_upload_dir, platform, artifact_ext)
   api.gsutil.upload(source_path, 'perfetto-luci-artifacts', gcs_target_path)
 
+  # Uploads also the .pdb (debug symbols) to GCS.
+  pdb_path = exe_dir.join(artifact_ext + '.pdb')
+  if api.platform.is_win:
+    api.gsutil.upload(pdb_path, 'perfetto-luci-artifacts',
+                      gcs_target_path + '.pdb')
+
   # Create the CIPD package definition from the artifact path.
   cipd_pkg_name = 'perfetto/{}/{}'.format(artifact['name'], platform)
   pkg_def = api.cipd.PackageDefinition(
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 2504f4c..d7771af 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -23,6 +23,9 @@
     "ad_services_metric.proto",
     "android_blocking_call.proto",
     "android_blocking_calls_cuj_metric.proto",
+    "sysui_slice_performance_statistical_data.proto",
+    "sysui_notif_shade_list_builder_metric.proto",
+    "sysui_update_notif_on_ui_mode_changed_metric.proto",
     "android_boot.proto",
     "android_frame_timeline_metric.proto",
     "android_sysui_notifications_blocking_calls_metric.proto",
diff --git a/protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto b/protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto
new file mode 100644
index 0000000..485be64
--- /dev/null
+++ b/protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto
@@ -0,0 +1,41 @@
+/*
+ * 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;
+import "protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto";
+
+// System UI Notifications ShadeListBuilder.buildList slices.
+// Shows count, average duration, and max duration for each.
+message SysuiNotifShadeListBuilderMetric {
+  optional SysUiSlicePerformanceStatisticalData all_slices_performance = 1;
+  optional SysUiSlicePerformanceStatisticalData slices_with_inflation_performance = 2;
+  optional SysUiSlicePerformanceStatisticalData slices_with_modification_performance = 3;
+
+  // Data row for a single slice
+  message SliceDuration {
+    // Name of the Slice
+    optional string name = 1;
+
+    // Duration in ms
+    optional int64 dur_ms = 2;
+
+    // Duration in ns
+    optional int64 dur_ns = 3;
+  }
+  repeated SliceDuration slice = 4;
+}
\ No newline at end of file
diff --git a/protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto b/protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto
new file mode 100644
index 0000000..5fe588f
--- /dev/null
+++ b/protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+// Statistical performance data row for a set of slices
+message SysUiSlicePerformanceStatisticalData {
+  // Name of the Slice
+  optional string name = 1;
+  // Number of times it happened within the CUJ
+  optional int64 cnt = 2;
+  // Average duration within the CUJ
+  optional int64 avg_dur_ms = 3;
+  // Maximal duration within the CUJ
+  optional int64 max_dur_ms = 4;
+  // Average duration within the CUJ in nanoseconds
+  optional int64 avg_dur_ns = 6;
+  // Maximal duration within the CUJ in nanoseconds
+  optional int64 max_dur_ns = 7;
+}
\ No newline at end of file
diff --git a/protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto b/protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto
new file mode 100644
index 0000000..3128c32
--- /dev/null
+++ b/protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+import "protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto";
+
+// System UI Notifications ShadeListBuilder.buildList slices.
+// Shows count, average duration, and max duration for each.
+message SysuiUpdateNotifOnUiModeChangedMetric {
+  optional SysUiSlicePerformanceStatisticalData all_slices_performance = 1;
+  // Data row for a single slice
+  message SliceDuration {
+    // Name of the Slice
+    optional string name = 1;
+
+    // Duration in ms
+    optional int64 dur_ms = 2;
+
+    // Duration in ns
+    optional int64 dur_ns = 3;
+  }
+  repeated SliceDuration slice = 2;
+}
\ No newline at end of file
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index 4aaba30..5587efa 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -20,6 +20,8 @@
 
 import "protos/perfetto/metrics/android/ad_services_metric.proto";
 import "protos/perfetto/metrics/android/android_boot.proto";
+import "protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto";
+import "protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto";
 import "protos/perfetto/metrics/android/android_frame_timeline_metric.proto";
 import "protos/perfetto/metrics/android/anr_metric.proto";
 import "protos/perfetto/metrics/android/batt_metric.proto";
@@ -111,7 +113,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 59
+// Next id: 61
 message TraceMetrics {
   reserved 4, 10, 13, 14, 16, 19;
 
@@ -272,6 +274,9 @@
   // Metric for AdServices module.
   optional AdServicesMetric ad_services_metric = 58;
 
+  optional SysuiNotifShadeListBuilderMetric sysui_notif_shade_list_builder_metric = 59;
+
+  optional SysuiUpdateNotifOnUiModeChangedMetric sysui_update_notif_on_ui_mode_changed_metric = 60;
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 4021174..abdbb2b 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -2210,6 +2210,69 @@
 
 // End of protos/perfetto/metrics/android/surfaceflinger.proto
 
+// Begin of protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto
+
+// Statistical performance data row for a set of slices
+message SysUiSlicePerformanceStatisticalData {
+  // Name of the Slice
+  optional string name = 1;
+  // Number of times it happened within the CUJ
+  optional int64 cnt = 2;
+  // Average duration within the CUJ
+  optional int64 avg_dur_ms = 3;
+  // Maximal duration within the CUJ
+  optional int64 max_dur_ms = 4;
+  // Average duration within the CUJ in nanoseconds
+  optional int64 avg_dur_ns = 6;
+  // Maximal duration within the CUJ in nanoseconds
+  optional int64 max_dur_ns = 7;
+}
+// End of protos/perfetto/metrics/android/sysui_slice_performance_statistical_data.proto
+
+// Begin of protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto
+// System UI Notifications ShadeListBuilder.buildList slices.
+// Shows count, average duration, and max duration for each.
+message SysuiNotifShadeListBuilderMetric {
+  optional SysUiSlicePerformanceStatisticalData all_slices_performance = 1;
+  optional SysUiSlicePerformanceStatisticalData slices_with_inflation_performance = 2;
+  optional SysUiSlicePerformanceStatisticalData slices_with_modification_performance = 3;
+
+  // Data row for a single slice
+  message SliceDuration {
+    // Name of the Slice
+    optional string name = 1;
+
+    // Duration in ms
+    optional int64 dur_ms = 2;
+
+    // Duration in ns
+    optional int64 dur_ns = 3;
+  }
+  repeated SliceDuration slice = 4;
+}
+// End of protos/perfetto/metrics/android/sysui_notif_shade_list_builder_metric.proto
+
+// Begin of protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto
+
+// System UI Notifications ShadeListBuilder.buildList slices.
+// Shows count, average duration, and max duration for each.
+message SysuiUpdateNotifOnUiModeChangedMetric {
+  optional SysUiSlicePerformanceStatisticalData all_slices_performance = 1;
+  // Data row for a single slice
+  message SliceDuration {
+    // Name of the Slice
+    optional string name = 1;
+
+    // Duration in ms
+    optional int64 dur_ms = 2;
+
+    // Duration in ns
+    optional int64 dur_ns = 3;
+  }
+  repeated SliceDuration slice = 2;
+}
+// End of protos/perfetto/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.proto
+
 // Begin of protos/perfetto/metrics/android/task_names.proto
 
 message AndroidTaskNames {
@@ -2324,7 +2387,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 59
+// Next id: 61
 message TraceMetrics {
   reserved 4, 10, 13, 14, 16, 19;
 
@@ -2485,6 +2548,9 @@
   // Metric for AdServices module.
   optional AdServicesMetric ad_services_metric = 58;
 
+  optional SysuiNotifShadeListBuilderMetric sysui_notif_shade_list_builder_metric = 59;
+
+  optional SysuiUpdateNotifOnUiModeChangedMetric sysui_update_notif_on_ui_mode_changed_metric = 60;
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index 1fb3d89..b4c46bc 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/python/tools/check_imports.py b/python/tools/check_imports.py
index 1bdbf6d..4d04973 100755
--- a/python/tools/check_imports.py
+++ b/python/tools/check_imports.py
@@ -284,6 +284,11 @@
         r'/plugins/.*',
         'widgets should only depend on base',
     ),
+    NoDep(
+        r'/widgets/.*',
+        r'/common/.*',
+        'widgets should only depend on base',
+    ),
 
     # Bigtrace
     NoDep(
diff --git a/src/trace_processor/metrics/sql/android/BUILD.gn b/src/trace_processor/metrics/sql/android/BUILD.gn
index d6c5589..54545cf 100644
--- a/src/trace_processor/metrics/sql/android/BUILD.gn
+++ b/src/trace_processor/metrics/sql/android/BUILD.gn
@@ -24,6 +24,9 @@
     "android_batt.sql",
     "android_binder.sql",
     "android_blocking_calls_cuj_metric.sql",
+    "sysui_notif_shade_list_builder_metric.sql",
+    "sysui_notif_shade_list_builder_slices.sql",
+    "sysui_update_notif_on_ui_mode_changed_metric.sql",
     "android_boot.sql",
     "android_camera.sql",
     "android_camera_unagg.sql",
diff --git a/src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_metric.sql b/src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_metric.sql
new file mode 100644
index 0000000..98889c1
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_metric.sql
@@ -0,0 +1,121 @@
+--
+-- Copyright 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
+--
+--     https://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 PERFETTO MODULE android.slices;
+
+SELECT RUN_METRIC('android/sysui_notif_shade_list_builder_slices.sql');
+
+-- Get statics of all ShadeListBuilder.buildList slices
+DROP TABLE IF EXISTS shade_list_builder_all;
+CREATE PERFETTO TABLE shade_list_builder_all AS
+SELECT
+  s.name name,
+  COUNT(s.name) AS count,
+  cast(avg(dur) as int) average_dur_ns,
+  max(dur) maximum_dur_ns,
+  s.id id
+FROM shade_list_builder_build_list_slices s
+GROUP BY s.name;
+
+-- Id of shade_list_builder slices that has a descendant of inflation
+DROP VIEW IF EXISTS slices_id_with_inflation_descendants;
+CREATE PERFETTO VIEW slices_id_with_inflation_descendants AS
+SELECT DISTINCT id
+  FROM slices_and_descendants
+  WHERE
+    descendant_name = 'HybridGroupManager#inflateHybridView' OR
+    descendant_name = 'NotifChildCont#recreateHeader';
+
+-- Id of shade_list_builder slices that has a descendant of ShadeNode modification
+DROP VIEW IF EXISTS slices_id_with_modification_descendants;
+CREATE PERFETTO VIEW slices_id_with_modification_descendants AS
+SELECT DISTINCT id
+  FROM slices_and_descendants
+  WHERE
+    descendant_name = 'ShadeNode#addChildAt' OR
+    descendant_name = 'ShadeNode#removeChildAt' OR
+    descendant_name = 'ShadeNode#moveChildTo';
+
+DROP TABLE IF EXISTS shade_list_builder_slices_with_inflation;
+CREATE PERFETTO TABLE shade_list_builder_slices_with_inflation AS
+SELECT
+  s.name || "_with_inflation" name,
+  COUNT(s.name) AS count,
+  cast(avg(dur) as int) average_dur_ns,
+  max(dur) maximum_dur_ns
+FROM shade_list_builder_build_list_slices s
+WHERE s.id IN slices_id_with_inflation_descendants
+GROUP BY s.name;
+
+DROP TABLE IF EXISTS shade_list_builder_slices_with_modification;
+CREATE PERFETTO TABLE shade_list_builder_slices_with_modification AS
+SELECT
+  s.name || "_with_node_modification" name,
+  COUNT(s.name) AS count,
+  cast(avg(dur) as int) average_dur_ns,
+  max(dur) maximum_dur_ns
+FROM shade_list_builder_build_list_slices s
+WHERE s.id IN slices_id_with_modification_descendants
+GROUP BY s.name;
+
+
+DROP VIEW IF EXISTS sysui_notif_shade_list_builder_metric_output;
+CREATE PERFETTO VIEW sysui_notif_shade_list_builder_metric_output AS
+SELECT SysuiNotifShadeListBuilderMetric(
+        'all_slices_performance', (
+            SELECT SysUiSlicePerformanceStatisticalData(
+                'name', a.name,
+                'cnt', a.count,
+                'avg_dur_ms', cast (a.average_dur_ns / 1000000 as int),
+                'max_dur_ms', cast (a.maximum_dur_ns / 1000000 as int),
+                'avg_dur_ns', a.average_dur_ns,
+                'max_dur_ns', a.maximum_dur_ns
+            )
+            FROM shade_list_builder_all a
+        ),
+        'slices_with_inflation_performance', (
+            SELECT SysUiSlicePerformanceStatisticalData(
+                'name', a.name,
+                'cnt', a.count,
+                'avg_dur_ms', cast (a.average_dur_ns / 1000000 as int),
+                'max_dur_ms', cast (a.maximum_dur_ns / 1000000 as int),
+                'avg_dur_ns', a.average_dur_ns,
+                'max_dur_ns', a.maximum_dur_ns
+            )
+            FROM shade_list_builder_slices_with_inflation a
+        ),
+        'slices_with_modification_performance', (
+            SELECT SysUiSlicePerformanceStatisticalData(
+                'name', a.name,
+                'cnt', a.count,
+                'avg_dur_ms', cast (a.average_dur_ns / 1000000 as int),
+                'max_dur_ms', cast (a.maximum_dur_ns / 1000000 as int),
+                'avg_dur_ns', a.average_dur_ns,
+                'max_dur_ns', a.maximum_dur_ns
+            )
+            FROM shade_list_builder_slices_with_modification a
+        ),
+        'slice', (
+            SELECT RepeatedField(
+                SysuiNotifShadeListBuilderMetric_SliceDuration(
+                    'name', a.name,
+                    'dur_ms', cast (a.dur / 1000000 as int),
+                    'dur_ns', a.dur
+                )
+            )
+            FROM shade_list_builder_build_list_slices a
+            ORDER BY dur DESC
+        )
+);
\ No newline at end of file
diff --git a/src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_slices.sql b/src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_slices.sql
new file mode 100644
index 0000000..d6fcf7f
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/sysui_notif_shade_list_builder_slices.sql
@@ -0,0 +1,43 @@
+--
+-- Copyright 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
+--
+--     https://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 PERFETTO MODULE android.slices;
+
+-- Table of ShadeListBuilder.buildList slices
+DROP TABLE IF EXISTS shade_list_builder_build_list_slices;
+CREATE PERFETTO TABLE shade_list_builder_build_list_slices AS
+SELECT
+  s.name name,
+  dur,
+  s.id id
+FROM slice s
+  JOIN thread_track ON thread_track.id = s.track_id
+  JOIN thread USING (utid)
+WHERE
+  thread.is_main_thread AND
+  s.dur > 0 AND (
+    s.name GLOB 'ShadeListBuilder.buildList'
+  );
+
+-- Table of ShadeListBuilder.buildList slices with the descendants
+DROP TABLE IF EXISTS slices_and_descendants;
+CREATE PERFETTO TABLE slices_and_descendants AS
+SELECT
+  parent.name name,
+  descendant.name descendant_name,
+  parent.dur dur_ns,
+  parent.id id
+FROM shade_list_builder_build_list_slices parent
+LEFT JOIN descendant_slice(parent.id) AS descendant;
\ No newline at end of file
diff --git a/src/trace_processor/metrics/sql/android/sysui_update_notif_on_ui_mode_changed_metric.sql b/src/trace_processor/metrics/sql/android/sysui_update_notif_on_ui_mode_changed_metric.sql
new file mode 100644
index 0000000..ef89a3e
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/sysui_update_notif_on_ui_mode_changed_metric.sql
@@ -0,0 +1,70 @@
+--
+-- Copyright 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
+--
+--     https://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 PERFETTO MODULE android.slices;
+
+-- Table of updateNotifOnUiModeChanged slices
+DROP TABLE IF EXISTS sysui_update_notif_on_ui_mode_changed_slices;
+CREATE PERFETTO TABLE sysui_update_notif_on_ui_mode_changed_slices AS
+SELECT
+  s.name name,
+  dur,
+  s.id id
+FROM slice s
+  JOIN thread_track ON thread_track.id = s.track_id
+  JOIN thread USING (utid)
+WHERE
+  thread.is_main_thread AND
+  s.dur > 0 AND (
+    s.name GLOB 'updateNotifOnUiModeChanged'
+  );
+
+-- Table of updateNotifOnUiModeChanged slices statistical performance information
+DROP TABLE IF EXISTS sysui_update_notif_on_ui_mode_changed_metric;
+CREATE PERFETTO TABLE sysui_update_notif_on_ui_mode_changed_metric AS
+SELECT
+  s.name name,
+  COUNT(s.name) AS count,
+  cast(avg(dur) as int) average_dur_ns,
+  max(dur) maximum_dur_ns
+FROM sysui_update_notif_on_ui_mode_changed_slices s
+GROUP BY s.name;
+
+DROP VIEW IF EXISTS sysui_update_notif_on_ui_mode_changed_metric_output;
+CREATE PERFETTO VIEW sysui_update_notif_on_ui_mode_changed_metric_output AS
+SELECT SysuiUpdateNotifOnUiModeChangedMetric(
+        'all_slices_performance', (
+            SELECT SysUiSlicePerformanceStatisticalData(
+                'name', a.name,
+                'cnt', a.count,
+                'avg_dur_ms', cast (a.average_dur_ns / 1000000 as int),
+                'max_dur_ms', cast (a.maximum_dur_ns / 1000000 as int),
+                'avg_dur_ns', a.average_dur_ns,
+                'max_dur_ns', a.maximum_dur_ns
+            )
+            FROM sysui_update_notif_on_ui_mode_changed_metric a
+        ),
+        'slice', (
+            SELECT RepeatedField(
+                SysuiUpdateNotifOnUiModeChangedMetric_SliceDuration(
+                    'name', a.name,
+                    'dur_ms', cast (a.dur / 1000000 as int),
+                    'dur_ns', a.dur
+                )
+            )
+            FROM sysui_update_notif_on_ui_mode_changed_slices a
+            ORDER BY dur DESC
+        )
+);
\ No newline at end of file
diff --git a/src/trace_processor/sqlite/sqlite_engine.cc b/src/trace_processor/sqlite/sqlite_engine.cc
index 2a76c53..9513182 100644
--- a/src/trace_processor/sqlite/sqlite_engine.cc
+++ b/src/trace_processor/sqlite/sqlite_engine.cc
@@ -19,9 +19,11 @@
 #include <memory>
 #include <optional>
 #include <utility>
+#include <vector>
 
 #include "perfetto/base/status.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "perfetto/public/compiler.h"
 #include "src/trace_processor/sqlite/db_sqlite_table.h"
 #include "src/trace_processor/sqlite/query_cache.h"
 #include "src/trace_processor/sqlite/scoped_db.h"
@@ -104,7 +106,9 @@
   }
   for (const auto& drop : drop_stmts) {
     int ret = sqlite3_exec(db(), drop.c_str(), nullptr, nullptr, nullptr);
-    PERFETTO_CHECK(ret == SQLITE_OK);
+    if (PERFETTO_UNLIKELY(ret != SQLITE_OK)) {
+      PERFETTO_FATAL("Failed to execute statement: '%s'", drop.c_str());
+    }
   }
 
   // It is important to unregister any functions that have been registered with
@@ -114,7 +118,9 @@
     int ret = sqlite3_create_function_v2(db_.get(), it.key().first.c_str(),
                                          it.key().second, SQLITE_UTF8, nullptr,
                                          nullptr, nullptr, nullptr, nullptr);
-    PERFETTO_CHECK(ret == SQLITE_OK);
+    if (PERFETTO_UNLIKELY(ret != SQLITE_OK)) {
+      PERFETTO_FATAL("Failed to drop function: '%s'", it.key().first.c_str());
+    }
   }
   fn_ctx_.Clear();
 
@@ -127,7 +133,16 @@
   saved_tables_.Clear();
 
   // The above operations should have cleared all the tables.
-  PERFETTO_CHECK(sqlite_tables_.size() == 0);
+  if (PERFETTO_UNLIKELY(sqlite_tables_.size() != 0)) {
+    std::vector<std::string> tables;
+    for (auto it = sqlite_tables_.GetIterator(); it; ++it) {
+      tables.push_back(it.key());
+    }
+    std::string joined = base::Join(tables, ",");
+    PERFETTO_FATAL(
+        "SqliteTable instances still exist: count='%zu', tables='[%s]'",
+        sqlite_tables_.size(), joined.c_str());
+  }
 }
 
 SqliteEngine::PreparedStatement SqliteEngine::PrepareStatement(SqlSource sql) {
diff --git a/test/trace_processor/diff_tests/metrics/android/android_sysui_notif_shade_list_builder_metric.py b/test/trace_processor/diff_tests/metrics/android/android_sysui_notif_shade_list_builder_metric.py
new file mode 100755
index 0000000..f7aeeec
--- /dev/null
+++ b/test/trace_processor/diff_tests/metrics/android/android_sysui_notif_shade_list_builder_metric.py
@@ -0,0 +1,109 @@
+#!/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 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.
+
+from os import sys, path
+import synth_common
+
+# com.android.systemui
+SYSUI_PID = 1000
+
+THIRD_PROCESS_PID = 3000
+
+# duration for each child slice
+child_slice_dur = 10_000_000
+
+# parent slice idle duration
+parent_slice_idle_dur = 10_000_000
+
+# List of interesting slices
+parent_slice_names = [
+    'ShadeListBuilder.buildList',
+    'ShadeListBuilder.buildList',
+    'Should not be in the metric',
+]
+
+# List of inflation-related descendant slices of interesting slices
+inflation_child_slice_names = [
+    'HybridGroupManager#inflateHybridView',
+    'NotifChildCont#recreateHeader',
+]
+
+# List of Shade-node-modification-related descendant slices of interesting slices
+modification_child_slice_names = [
+    'ShadeNode#addChildAt',
+    'ShadeNode#removeChildAt',
+    'ShadeNode#moveChildTo',
+]
+
+
+def add_main_thread_atrace(trace, ts, ts_end, buf, pid):
+    trace.add_atrace_begin(ts=ts, tid=pid, pid=pid, buf=buf)
+    trace.add_atrace_end(ts=ts_end, tid=pid, pid=pid)
+
+
+# Creates a trace that has the interesting slices that we are querying for
+# A ShadeListBuilder.buildList slice that has one of each of the inflation_child_slice_names
+# A ShadeListBuilder.buildList slice that has one of each of the modification_child_slice_names
+def add_slices(trace, pid):
+    slice_ts = 2_000_000
+    slice_ts = add_slice_with_children(trace, pid, slice_ts, 'ShadeListBuilder.buildList', inflation_child_slice_names)
+    add_slice_with_children(trace, pid, slice_ts, 'ShadeListBuilder.buildList', modification_child_slice_names)
+
+# Add a slice with a set of children slices, return the parent slice's end ts
+def add_slice_with_children(trace, pid, current_ts, parent_name, children_list):
+    ts_end = current_ts + parent_slice_idle_dur + len(children_list) * (child_slice_dur + 1)
+    # add the parent slice
+    add_main_thread_atrace(
+        trace,
+        ts=current_ts,
+        ts_end=ts_end,
+        buf=parent_name,
+        pid=pid)
+    current_ts += parent_slice_idle_dur
+    # Add the children
+    for child_name in children_list:
+        ts_child_end = current_ts + child_slice_dur + 1
+        add_main_thread_atrace(
+            trace,
+            ts=current_ts,
+            ts_end=ts_child_end,
+            buf=child_name,
+            pid=pid)
+        current_ts = ts_child_end
+    return ts_end
+
+def add_process(trace, package_name, uid, pid):
+    trace.add_package_list(ts=0, name=package_name, uid=uid, version_code=1)
+    trace.add_process(
+        pid=pid, ppid=0, cmdline=package_name, uid=uid)
+    trace.add_thread(tid=pid, tgid=pid, cmdline="MainThread", name="MainThread")
+
+
+def setup_trace():
+    trace = synth_common.create_trace()
+    trace.add_packet()
+    add_process(trace, package_name="com.android.systemui", uid=10001,
+                pid=SYSUI_PID)
+    trace.add_ftrace_packet(cpu=0)
+    return trace
+
+
+trace = setup_trace()
+
+
+add_slices(trace, pid=SYSUI_PID)
+
+# See test_sysui_notif_shade_list_builder.
+sys.stdout.buffer.write(trace.trace.SerializeToString())
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/metrics/android/sysui_notif_shade_list_builder_metric.out b/test/trace_processor/diff_tests/metrics/android/sysui_notif_shade_list_builder_metric.out
new file mode 100644
index 0000000..d21c788
--- /dev/null
+++ b/test/trace_processor/diff_tests/metrics/android/sysui_notif_shade_list_builder_metric.out
@@ -0,0 +1,36 @@
+sysui_notif_shade_list_builder_metric {
+  all_slices_performance {
+    name: "ShadeListBuilder.buildList"
+    cnt: 2
+    avg_dur_ms: 35
+    max_dur_ms: 40
+    avg_dur_ns: 35000002
+    max_dur_ns: 40000003
+  }
+  slices_with_inflation_performance {
+    name: "ShadeListBuilder.buildList_with_inflation"
+    cnt: 1
+    avg_dur_ms: 30
+    max_dur_ms: 30
+    avg_dur_ns: 30000002
+    max_dur_ns: 30000002
+  }
+  slices_with_modification_performance {
+    name: "ShadeListBuilder.buildList_with_node_modification"
+    cnt: 1
+    avg_dur_ms: 40
+    max_dur_ms: 40
+    avg_dur_ns: 40000003
+    max_dur_ns: 40000003
+  }
+  slice {
+    name: "ShadeListBuilder.buildList"
+    dur_ms: 30
+    dur_ns: 30000002
+  }
+  slice {
+    name: "ShadeListBuilder.buildList"
+    dur_ms: 40
+    dur_ns: 40000003
+  }
+}
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.out b/test/trace_processor/diff_tests/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.out
new file mode 100644
index 0000000..882253e
--- /dev/null
+++ b/test/trace_processor/diff_tests/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.out
@@ -0,0 +1,20 @@
+sysui_update_notif_on_ui_mode_changed_metric {
+  all_slices_performance {
+    name: "updateNotifOnUiModeChanged"
+    cnt: 2
+    avg_dur_ms: 35
+    max_dur_ms: 40
+    avg_dur_ns: 35000002
+    max_dur_ns: 40000003
+  }
+  slice {
+  name: "updateNotifOnUiModeChanged"
+  dur_ms: 30
+  dur_ns: 30000002
+  }
+  slice {
+  name: "updateNotifOnUiModeChanged"
+  dur_ms: 40
+  dur_ns: 40000003
+  }
+}
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.py b/test/trace_processor/diff_tests/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.py
new file mode 100644
index 0000000..5892b09
--- /dev/null
+++ b/test/trace_processor/diff_tests/metrics/android/sysui_update_notif_on_ui_mode_changed_metric.py
@@ -0,0 +1,102 @@
+#!/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 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.
+
+from os import sys, path
+import synth_common
+
+# com.android.systemui
+SYSUI_PID = 1000
+
+THIRD_PROCESS_PID = 3000
+
+# duration for each child slice
+child_slice_dur = 10_000_000
+
+# parent slice idle duration
+parent_slice_idle_dur = 10_000_000
+
+# List of inflation-related descendant slices of interesting slices
+inflation_child_slice_names = [
+    'HybridGroupManager#inflateHybridView',
+    'NotifChildCont#recreateHeader',
+]
+
+# List of Shade-node-modification-related descendant slices of interesting slices
+modification_child_slice_names = [
+    'ShadeNode#addChildAt',
+    'ShadeNode#removeChildAt',
+    'ShadeNode#moveChildTo',
+]
+
+
+def add_main_thread_atrace(trace, ts, ts_end, buf, pid):
+    trace.add_atrace_begin(ts=ts, tid=pid, pid=pid, buf=buf)
+    trace.add_atrace_end(ts=ts_end, tid=pid, pid=pid)
+
+
+# Creates a trace that has the interesting slices that we are querying for
+# A ShadeListBuilder.buildList slice that has one of each of the inflation_child_slice_names
+# A ShadeListBuilder.buildList slice that has one of each of the modification_child_slice_names
+def add_slices(trace, pid):
+    slice_ts = 2_000_000
+    slice_ts = add_slice_with_children(trace, pid, slice_ts, 'updateNotifOnUiModeChanged', inflation_child_slice_names)
+    add_slice_with_children(trace, pid, slice_ts, 'updateNotifOnUiModeChanged', modification_child_slice_names)
+
+# Add a slice with a set of children slices, return the parent slice's end ts
+def add_slice_with_children(trace, pid, current_ts, parent_name, children_list):
+    ts_end = current_ts + parent_slice_idle_dur + len(children_list) * (child_slice_dur + 1)
+    # add the parent slice
+    add_main_thread_atrace(
+        trace,
+        ts=current_ts,
+        ts_end=ts_end,
+        buf=parent_name,
+        pid=pid)
+    current_ts += parent_slice_idle_dur
+    # Add the children
+    for child_name in children_list:
+        ts_child_end = current_ts + child_slice_dur + 1
+        add_main_thread_atrace(
+            trace,
+            ts=current_ts,
+            ts_end=ts_child_end,
+            buf=child_name,
+            pid=pid)
+        current_ts = ts_child_end
+    return ts_end
+
+def add_process(trace, package_name, uid, pid):
+    trace.add_package_list(ts=0, name=package_name, uid=uid, version_code=1)
+    trace.add_process(
+        pid=pid, ppid=0, cmdline=package_name, uid=uid)
+    trace.add_thread(tid=pid, tgid=pid, cmdline="MainThread", name="MainThread")
+
+
+def setup_trace():
+    trace = synth_common.create_trace()
+    trace.add_packet()
+    add_process(trace, package_name="com.android.systemui", uid=10001,
+                pid=SYSUI_PID)
+    trace.add_ftrace_packet(cpu=0)
+    return trace
+
+
+trace = setup_trace()
+
+
+add_slices(trace, pid=SYSUI_PID)
+
+# See test_sysui_update_notif_on_ui_mode_changed.
+sys.stdout.buffer.write(trace.trace.SerializeToString())
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/metrics/android/tests.py b/test/trace_processor/diff_tests/metrics/android/tests.py
index f5ffcf7..e2af7bb 100644
--- a/test/trace_processor/diff_tests/metrics/android/tests.py
+++ b/test/trace_processor/diff_tests/metrics/android/tests.py
@@ -133,6 +133,18 @@
         query=Metric('android_sysui_notifications_blocking_calls_metric'),
         out=Path('android_sysui_notifications_blocking_calls_metric.out'))
 
+  def test_sysui_notif_shade_list_builder(self):
+      return DiffTestBlueprint(
+          trace=Path('android_sysui_notif_shade_list_builder_metric.py'),
+          query=Metric('sysui_notif_shade_list_builder_metric'),
+          out=Path('sysui_notif_shade_list_builder_metric.out'))
+
+  def test_sysui_update_notif_on_ui_mode_changed(self):
+      return DiffTestBlueprint(
+          trace=Path('sysui_update_notif_on_ui_mode_changed_metric.py'),
+          query=Metric('sysui_update_notif_on_ui_mode_changed_metric'),
+          out=Path('sysui_update_notif_on_ui_mode_changed_metric.out'))
+
   def test_monitor_contention_metric(self):
     return DiffTestBlueprint(
         trace=DataPath('android_monitor_contention_trace.atr'),
diff --git a/ui/src/assets/topbar.scss b/ui/src/assets/topbar.scss
index d89ee0b..3b47643 100644
--- a/ui/src/assets/topbar.scss
+++ b/ui/src/assets/topbar.scss
@@ -256,7 +256,6 @@
 }
 
 .pf-omnibox-dropdown {
-  margin: 2px;
   font-family: $pf-font;
 
   .pf-keycap {
diff --git a/ui/src/assets/widgets/popup.scss b/ui/src/assets/widgets/popup.scss
index 0469940..e2884a7 100644
--- a/ui/src/assets/widgets/popup.scss
+++ b/ui/src/assets/widgets/popup.scss
@@ -28,7 +28,8 @@
   .pf-popup-content {
     // Ensures all content is rendered above the arrow
     position: relative;
-    padding: 6px;
+    // Default padding set to some sensible value that works for most content
+    padding: 4px;
   }
 }
 
diff --git a/ui/src/common/errors.ts b/ui/src/base/errors.ts
similarity index 100%
rename from ui/src/common/errors.ts
rename to ui/src/base/errors.ts
diff --git a/ui/src/core/static_initializers.ts b/ui/src/base/static_initializers.ts
similarity index 100%
rename from ui/src/core/static_initializers.ts
rename to ui/src/base/static_initializers.ts
diff --git a/ui/src/bigtrace/index.ts b/ui/src/bigtrace/index.ts
index 98907ab..a05452b 100644
--- a/ui/src/bigtrace/index.ts
+++ b/ui/src/bigtrace/index.ts
@@ -1 +1 @@
-console.log('hello world bitrace');
\ No newline at end of file
+console.log('hello world bitrace');
diff --git a/ui/src/common/flamegraph_util.ts b/ui/src/common/flamegraph_util.ts
index 5087d04..3e6ae8d 100644
--- a/ui/src/common/flamegraph_util.ts
+++ b/ui/src/common/flamegraph_util.ts
@@ -43,19 +43,19 @@
         {
           option:
               FlamegraphStateViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
-          name: 'Unreleased size'
+          name: 'Unreleased size',
         },
         {
           option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
-          name: 'Unreleased count'
+          name: 'Unreleased count',
         },
         {
           option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
-          name: 'Total size'
+          name: 'Total size',
         },
         {
           option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
-          name: 'Total count'
+          name: 'Total count',
         },
       ];
     case ProfileType.NATIVE_HEAP_PROFILE:
@@ -63,41 +63,41 @@
         {
           option:
               FlamegraphStateViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY,
-          name: 'Unreleased malloc size'
+          name: 'Unreleased malloc size',
         },
         {
           option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
-          name: 'Unreleased malloc count'
+          name: 'Unreleased malloc count',
         },
         {
           option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
-          name: 'Total malloc size'
+          name: 'Total malloc size',
         },
         {
           option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
-          name: 'Total malloc count'
+          name: 'Total malloc count',
         },
       ];
     case ProfileType.JAVA_HEAP_SAMPLES:
       return [
         {
           option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
-          name: 'Total allocation size'
+          name: 'Total allocation size',
         },
         {
           option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
-          name: 'Total allocation count'
+          name: 'Total allocation count',
         },
       ];
     case ProfileType.MIXED_HEAP_PROFILE:
       return [
         {
           option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
-          name: 'Total allocation size (malloc + java)'
+          name: 'Total allocation size (malloc + java)',
         },
         {
           option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
-          name: 'Total allocation count (malloc + java)'
+          name: 'Total allocation count (malloc + java)',
         },
       ];
     default:
diff --git a/ui/src/common/recordingV2/recording_error_handling.ts b/ui/src/common/recordingV2/recording_error_handling.ts
index ca03929..6abdfdb 100644
--- a/ui/src/common/recordingV2/recording_error_handling.ts
+++ b/ui/src/common/recordingV2/recording_error_handling.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {getErrorMessage} from '../../base/errors';
 import {
   showAllowUSBDebugging,
   showConnectionLostError,
@@ -22,7 +23,6 @@
   showWebsocketConnectionIssue,
   showWebUSBErrorV2,
 } from '../../frontend/error_dialog';
-import {getErrorMessage} from '../errors';
 
 import {OnMessageCallback} from './recording_interfaces_v2';
 import {
diff --git a/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts b/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts
index 48c1297..8224d95 100644
--- a/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts
+++ b/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {getErrorMessage} from '../../../base/errors';
 import {assertExists} from '../../../base/logging';
-import {getErrorMessage} from '../../errors';
 import {RECORDING_V2_FLAG} from '../../feature_flags';
 import {AdbKeyManager} from '../auth/adb_key_manager';
 import {RecordingError} from '../recording_error_handling';
diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts
index 3ff39f5..d0a2d82 100644
--- a/ui/src/controller/flamegraph_controller.ts
+++ b/ui/src/controller/flamegraph_controller.ts
@@ -25,7 +25,7 @@
   CallsiteInfo,
   FlamegraphState,
   FlamegraphStateViewingOption,
-  ProfileType
+  ProfileType,
 } from '../common/state';
 import {FlamegraphDetails, globals} from '../frontend/globals';
 import {publishFlamegraphDetails} from '../frontend/publish';
diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts
index 500db72..1ce9074 100644
--- a/ui/src/frontend/app.ts
+++ b/ui/src/frontend/app.ts
@@ -408,12 +408,10 @@
     if (msgTTL > 0 || engineIsBusy) {
       setTimeout(() => raf.scheduleFullRedraw(), msgTTL * 1000);
       return m(
-          `.omnibox.message-mode`,
-          m(`input[placeholder=${
-                globals.state.status.msg}][readonly][disabled][ref=omnibox]`,
-            {
-              value: '',
-            }));
+          `.omnibox.message-mode`, m(`input[readonly][disabled][ref=omnibox]`, {
+            value: '',
+            placeholder: globals.state.status.msg,
+          }));
     }
 
     if (this.omniboxMode === OmniboxMode.Command) {
@@ -582,7 +580,7 @@
 
     return m(Omnibox, {
       value: globals.state.omniboxState.omnibox,
-      placeholder: 'Search...',
+      placeholder: 'Search or type \'>\' for commands or \':\' for SQL mode',
       inputRef: App.OMNIBOX_INPUT_REF,
       onInput: (value, prev) => {
         if (prev === '') {
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index 3e24ef5..96d0117 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // Keep this import first.
-import '../core/static_initializers';
+import '../base/static_initializers';
 import '../gen/all_plugins';
 
 import {Draft} from 'immer';
diff --git a/ui/src/frontend/metrics_page.ts b/ui/src/frontend/metrics_page.ts
index 17caaf0..544def3 100644
--- a/ui/src/frontend/metrics_page.ts
+++ b/ui/src/frontend/metrics_page.ts
@@ -29,10 +29,10 @@
 import {STR} from '../trace_processor/query_result';
 import {Select} from '../widgets/select';
 import {Spinner} from '../widgets/spinner';
+import {VegaView} from '../widgets/vega_view';
 
 import {globals} from './globals';
 import {createPage} from './pages';
-import {VegaView} from './widgets/vega_view';
 
 type Format = 'json'|'prototext'|'proto';
 const FORMATS: Format[] = ['json', 'prototext', 'proto'];
diff --git a/ui/src/frontend/viz_page.ts b/ui/src/frontend/viz_page.ts
index 40943a9..e6d019e 100644
--- a/ui/src/frontend/viz_page.ts
+++ b/ui/src/frontend/viz_page.ts
@@ -17,10 +17,10 @@
 import {raf} from '../core/raf_scheduler';
 import {EngineProxy} from '../trace_processor/engine';
 import {Editor} from '../widgets/editor';
+import {VegaView} from '../widgets/vega_view';
 
 import {globals} from './globals';
 import {createPage} from './pages';
-import {VegaView} from './widgets/vega_view';
 
 function getEngine(): EngineProxy|undefined {
   const engineId = globals.getCurrentEngine()?.id;
diff --git a/ui/src/frontend/widgets_page.ts b/ui/src/frontend/widgets_page.ts
index b3ae46f..100f2bc 100644
--- a/ui/src/frontend/widgets_page.ts
+++ b/ui/src/frontend/widgets_page.ts
@@ -42,11 +42,11 @@
 import {TextInput} from '../widgets/text_input';
 import {MultiParagraphText, TextParagraph} from '../widgets/text_paragraph';
 import {LazyTreeNode, Tree, TreeNode} from '../widgets/tree';
+import {VegaView} from '../widgets/vega_view';
 
 import {createPage} from './pages';
 import {PopupMenuButton} from './popup_menu';
 import {TableShowcase} from './tables/table_showcase';
-import {VegaView} from './widgets/vega_view';
 
 const DATA_ENGLISH_LETTER_FREQUENCY = {
   table: [
diff --git a/ui/src/trace_processor/query_result.ts b/ui/src/trace_processor/query_result.ts
index 0e6e69e..c568488 100644
--- a/ui/src/trace_processor/query_result.ts
+++ b/ui/src/trace_processor/query_result.ts
@@ -48,7 +48,7 @@
 // This object is part of the API exposed to tracks / controllers.
 
 // Ensure protobuf is initialized.
-import '../core/static_initializers';
+import '../base/static_initializers';
 
 import protobuf from 'protobufjs/minimal';
 
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_delta_graph.ts b/ui/src/tracks/chrome_scroll_jank/scroll_delta_graph.ts
index 358f391..e82d40a 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_delta_graph.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_delta_graph.ts
@@ -15,9 +15,9 @@
 import m from 'mithril';
 
 import {duration, Time, time} from '../../base/time';
-import {VegaView} from '../../frontend/widgets/vega_view';
 import {EngineProxy} from '../../trace_processor/engine';
 import {LONG, NUM, STR} from '../../trace_processor/query_result';
+import {VegaView} from '../../widgets/vega_view';
 
 const USER_CATEGORY = 'User';
 const APPLIED_CATEGORY = 'Applied';
diff --git a/ui/src/frontend/widgets/vega_view.ts b/ui/src/widgets/vega_view.ts
similarity index 93%
rename from ui/src/frontend/widgets/vega_view.ts
rename to ui/src/widgets/vega_view.ts
index e3c0889..c51e728 100644
--- a/ui/src/frontend/widgets/vega_view.ts
+++ b/ui/src/widgets/vega_view.ts
@@ -16,14 +16,14 @@
 import * as vega from 'vega';
 import * as vegaLite from 'vega-lite';
 
-import {Disposable} from '../../base/disposable';
-import {isString, shallowEquals} from '../../base/object_utils';
-import {SimpleResizeObserver} from '../../base/resize_observer';
-import {getErrorMessage} from '../../common/errors';
-import {EngineProxy} from '../../trace_processor/engine';
-import {QueryError} from '../../trace_processor/query_result';
-import {scheduleFullRedraw} from '../../widgets/raf';
-import {Spinner} from '../../widgets/spinner';
+import {Disposable} from '../base/disposable';
+import {getErrorMessage} from '../base/errors';
+import {isString, shallowEquals} from '../base/object_utils';
+import {SimpleResizeObserver} from '../base/resize_observer';
+import {EngineProxy} from '../trace_processor/engine';
+import {QueryError} from '../trace_processor/query_result';
+import {scheduleFullRedraw} from '../widgets/raf';
+import {Spinner} from '../widgets/spinner';
 
 function isVegaLite(spec: unknown): boolean {
   if (typeof spec === 'object') {