Merge "ui: Shim access to state.currentSelection" into main
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 139fc7b..5fa2888 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -516,7 +516,7 @@
 
   selectNote(state: StateDraft, args: {id: string}): void {
     if (args.id) {
-      state.currentSelection = {
+      state.legacySelection = {
         kind: 'NOTE',
         id: args.id,
       };
@@ -554,8 +554,8 @@
     args: {color: string; persistent: boolean},
   ): void {
     if (
-      state.currentSelection === null ||
-      state.currentSelection.kind !== 'AREA'
+      state.legacySelection === null ||
+      state.legacySelection.kind !== 'AREA'
     ) {
       return;
     }
@@ -564,15 +564,15 @@
     state.notes[id] = {
       noteType: 'AREA',
       id,
-      areaId: state.currentSelection.areaId,
+      areaId: state.legacySelection.areaId,
       color,
       text: '',
     };
-    state.currentSelection.noteId = id;
+    state.legacySelection.noteId = id;
   },
 
   toggleMarkCurrentArea(state: StateDraft, args: {persistent: boolean}) {
-    const selection = state.currentSelection;
+    const selection = state.legacySelection;
     if (
       selection != null &&
       selection.kind === 'AREA' &&
@@ -621,17 +621,17 @@
     delete state.notes[args.id];
     // For regular notes, we clear the current selection but for an area note
     // we only want to clear the note/marking and leave the area selected.
-    if (state.currentSelection === null) return;
+    if (state.legacySelection === null) return;
     if (
-      state.currentSelection.kind === 'NOTE' &&
-      state.currentSelection.id === args.id
+      state.legacySelection.kind === 'NOTE' &&
+      state.legacySelection.id === args.id
     ) {
-      state.currentSelection = null;
+      state.legacySelection = null;
     } else if (
-      state.currentSelection.kind === 'AREA' &&
-      state.currentSelection.noteId === args.id
+      state.legacySelection.kind === 'AREA' &&
+      state.legacySelection.noteId === args.id
     ) {
-      state.currentSelection.noteId = undefined;
+      state.legacySelection.noteId = undefined;
     }
   },
 
@@ -639,7 +639,7 @@
     state: StateDraft,
     args: {id: number; trackKey: string; scroll?: boolean},
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'SLICE',
       id: args.id,
       trackKey: args.trackKey,
@@ -651,7 +651,7 @@
     state: StateDraft,
     args: {leftTs: time; rightTs: time; id: number; trackKey: string},
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'COUNTER',
       leftTs: args.leftTs,
       rightTs: args.rightTs,
@@ -664,7 +664,7 @@
     state: StateDraft,
     args: {id: number; upid: number; ts: time; type: ProfileType},
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'HEAP_PROFILE',
       id: args.id,
       upid: args.upid,
@@ -690,7 +690,7 @@
       type: ProfileType;
     },
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'PERF_SAMPLES',
       id: args.id,
       upid: args.upid,
@@ -732,7 +732,7 @@
     state: StateDraft,
     args: {id: number; utid: number; ts: time},
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'CPU_PROFILE_SAMPLE',
       id: args.id,
       utid: args.utid,
@@ -768,7 +768,7 @@
     state: StateDraft,
     args: {id: number; trackKey: string; table?: string; scroll?: boolean},
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'CHROME_SLICE',
       id: args.id,
       trackKey: args.trackKey,
@@ -796,7 +796,7 @@
       ...args.detailsPanelConfig.config,
     };
 
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'GENERIC_SLICE',
       id: args.id,
       sqlTableName: args.sqlTableName,
@@ -818,7 +818,7 @@
     state: StateDraft,
     args: {id: number; trackKey: string},
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'THREAD_STATE',
       id: args.id,
       trackKey: args.trackKey,
@@ -829,7 +829,7 @@
     state: StateDraft,
     args: {id: number; trackKey: string; scroll?: boolean},
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'LOG',
       id: args.id,
       trackKey: args.trackKey,
@@ -838,7 +838,7 @@
   },
 
   deselect(state: StateDraft, _: {}): void {
-    state.currentSelection = null;
+    state.legacySelection = null;
   },
 
   updateLogsPagination(state: StateDraft, args: Pagination): void {
@@ -919,7 +919,7 @@
     assertTrue(start <= end);
     const areaId = generateNextId(state);
     state.areas[areaId] = {id: areaId, start, end, tracks};
-    state.currentSelection = {kind: 'AREA', areaId};
+    state.legacySelection = {kind: 'AREA', areaId};
   },
 
   editArea(state: StateDraft, args: {area: Area; areaId: string}): void {
@@ -932,7 +932,7 @@
     state: StateDraft,
     args: {areaId: string; noteId: string},
   ): void {
-    state.currentSelection = {
+    state.legacySelection = {
       kind: 'AREA',
       areaId: args.areaId,
       noteId: args.noteId,
@@ -943,7 +943,7 @@
     state: StateDraft,
     args: {id: string; isTrackGroup: boolean},
   ) {
-    const selection = state.currentSelection;
+    const selection = state.legacySelection;
     if (selection === null || selection.kind !== 'AREA') return;
     const areaId = selection.areaId;
     const index = state.areas[areaId].tracks.indexOf(args.id);
@@ -973,7 +973,7 @@
     // selection to be updated and this leads to bugs for people who do:
     // if (oldSelection !== state.selection) etc.
     // To solve this re-create the selection object here:
-    state.currentSelection = Object.assign({}, state.currentSelection);
+    state.legacySelection = Object.assign({}, state.legacySelection);
   },
 
   setVisibleTraceTime(state: StateDraft, args: VisibleState): void {
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index f3f8a0e..dcae23f 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -139,7 +139,7 @@
     },
 
     status: {msg: '', timestamp: 0},
-    currentSelection: null,
+    legacySelection: null,
     currentFlamegraphState: null,
     traceConversionInProgress: false,
 
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index f7c2562..c994bbb 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -134,7 +134,8 @@
 // 44. Add TabsV2 state.
 // 45. Remove v1 tracks.
 // 46. Remove trackKeyByTrackId.
-export const STATE_VERSION = 46;
+// 47. Selection V2
+export const STATE_VERSION = 47;
 
 export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
 
@@ -573,7 +574,7 @@
   permalink: PermalinkConfig;
   notes: ObjectById<Note | AreaNote>;
   status: Status;
-  currentSelection: Selection | null;
+  legacySelection: Selection | null;
   currentFlamegraphState: FlamegraphState | null;
   logsPagination: Pagination;
   ftracePagination: Pagination;
@@ -997,3 +998,7 @@
   }
   return parentId;
 }
+
+export function getLegacySelection(state: State): Selection | null {
+  return state.legacySelection;
+}
diff --git a/ui/src/controller/aggregation/aggregation_controller.ts b/ui/src/controller/aggregation/aggregation_controller.ts
index 1809a9c..2e8cdb2 100644
--- a/ui/src/controller/aggregation/aggregation_controller.ts
+++ b/ui/src/controller/aggregation/aggregation_controller.ts
@@ -19,7 +19,7 @@
   ColumnDef,
   ThreadStateExtra,
 } from '../../common/aggregation_data';
-import {Area, Sorting} from '../../common/state';
+import {Area, Sorting, getLegacySelection} from '../../common/state';
 import {globals} from '../../frontend/globals';
 import {publishAggregateData} from '../../frontend/publish';
 import {Engine} from '../../trace_processor/engine';
@@ -61,7 +61,7 @@
   }
 
   run() {
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (selection === null || selection.kind !== 'AREA') {
       publishAggregateData({
         data: {
diff --git a/ui/src/controller/area_selection_handler.ts b/ui/src/controller/area_selection_handler.ts
index 3efaca8..0104ca8 100644
--- a/ui/src/controller/area_selection_handler.ts
+++ b/ui/src/controller/area_selection_handler.ts
@@ -12,14 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {Area, AreaById} from '../common/state';
+import {Area, AreaById, getLegacySelection} from '../common/state';
 import {globals} from '../frontend/globals';
 
 export class AreaSelectionHandler {
   private previousArea?: Area;
 
   getAreaChange(): [boolean, AreaById | undefined] {
-    const currentSelection = globals.state.currentSelection;
+    const currentSelection = getLegacySelection(globals.state);
     if (currentSelection === null || currentSelection.kind !== 'AREA') {
       return [false, undefined];
     }
diff --git a/ui/src/controller/area_selection_handler_unittest.ts b/ui/src/controller/area_selection_handler_unittest.ts
index 6b7d312..5cdad1e 100644
--- a/ui/src/controller/area_selection_handler_unittest.ts
+++ b/ui/src/controller/area_selection_handler_unittest.ts
@@ -33,7 +33,7 @@
     id: areaId,
   };
   globals.store.edit((draft) => {
-    draft.currentSelection = {kind: 'AREA', areaId: areaId};
+    draft.legacySelection = {kind: 'AREA', areaId: areaId};
     draft.areas[areaId] = latestArea;
   });
 
@@ -53,7 +53,7 @@
     id: previousAreaId,
   };
   globals.store.edit((draft) => {
-    draft.currentSelection = {
+    draft.legacySelection = {
       kind: 'AREA',
       areaId: previousAreaId,
     };
@@ -64,7 +64,7 @@
 
   const currentAreaId = '1';
   globals.store.edit((draft) => {
-    draft.currentSelection = {
+    draft.legacySelection = {
       kind: 'AREA',
       areaId: currentAreaId,
     };
@@ -77,13 +77,13 @@
 
 test('UndefinedAreaAfterUndefinedArea', () => {
   globals.store.edit((draft) => {
-    draft.currentSelection = {kind: 'AREA', areaId: '0'};
+    draft.legacySelection = {kind: 'AREA', areaId: '0'};
   });
   const areaSelectionHandler = new AreaSelectionHandler();
   areaSelectionHandler.getAreaChange();
 
   globals.store.edit((draft) => {
-    draft.currentSelection = {kind: 'AREA', areaId: '1'};
+    draft.legacySelection = {kind: 'AREA', areaId: '1'};
   });
   const [hasAreaChanged, selectedArea] = areaSelectionHandler.getAreaChange();
 
@@ -100,7 +100,7 @@
     id: previousAreaId,
   };
   globals.store.edit((draft) => {
-    draft.currentSelection = {
+    draft.legacySelection = {
       kind: 'AREA',
       areaId: previousAreaId,
     };
@@ -117,7 +117,7 @@
     id: currentAreaId,
   };
   globals.store.edit((draft) => {
-    draft.currentSelection = {
+    draft.legacySelection = {
       kind: 'AREA',
       areaId: currentAreaId,
     };
@@ -138,7 +138,7 @@
     id: previousAreaId,
   };
   globals.store.edit((draft) => {
-    draft.currentSelection = {
+    draft.legacySelection = {
       kind: 'AREA',
       areaId: previousAreaId,
     };
@@ -155,7 +155,7 @@
     id: currentAreaId,
   };
   globals.store.edit((draft) => {
-    draft.currentSelection = {
+    draft.legacySelection = {
       kind: 'AREA',
       areaId: currentAreaId,
     };
@@ -169,13 +169,13 @@
 
 test('NonAreaSelectionAfterUndefinedArea', () => {
   globals.store.edit((draft) => {
-    draft.currentSelection = {kind: 'AREA', areaId: '0'};
+    draft.legacySelection = {kind: 'AREA', areaId: '0'};
   });
   const areaSelectionHandler = new AreaSelectionHandler();
   areaSelectionHandler.getAreaChange();
 
   globals.store.edit((draft) => {
-    draft.currentSelection = {
+    draft.legacySelection = {
       kind: 'COUNTER',
       leftTs: Time.fromRaw(0n),
       rightTs: Time.fromRaw(0n),
diff --git a/ui/src/controller/cpu_profile_controller.ts b/ui/src/controller/cpu_profile_controller.ts
index 9b7aca2..fd410bf 100644
--- a/ui/src/controller/cpu_profile_controller.ts
+++ b/ui/src/controller/cpu_profile_controller.ts
@@ -12,7 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {CallsiteInfo, CpuProfileSampleSelection} from '../common/state';
+import {
+  CallsiteInfo,
+  CpuProfileSampleSelection,
+  getLegacySelection,
+} from '../common/state';
 import {CpuProfileDetails, globals} from '../frontend/globals';
 import {publishCpuProfileDetails} from '../frontend/publish';
 import {Engine} from '../trace_processor/engine';
@@ -34,7 +38,7 @@
   }
 
   run() {
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (!selection || selection.kind !== 'CPU_PROFILE_SAMPLE') {
       return;
     }
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index 8eaa585..d53142c 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 import {Time} from '../base/time';
-import {Area} from '../common/state';
+import {Area, getLegacySelection} from '../common/state';
 import {featureFlags} from '../core/feature_flags';
 import {Flow, globals} from '../frontend/globals';
 import {publishConnectedFlows, publishSelectedFlows} from '../frontend/publish';
@@ -443,7 +443,7 @@
   }
 
   refreshVisibleFlows() {
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (!selection) {
       this.lastSelectedKind = 'NONE';
       publishConnectedFlows([]);
diff --git a/ui/src/controller/pivot_table_controller.ts b/ui/src/controller/pivot_table_controller.ts
index d01b642..b85f827 100644
--- a/ui/src/controller/pivot_table_controller.ts
+++ b/ui/src/controller/pivot_table_controller.ts
@@ -21,6 +21,7 @@
   PivotTableQueryMetadata,
   PivotTableResult,
   PivotTableState,
+  getLegacySelection,
 } from '../common/state';
 import {featureFlags} from '../core/feature_flags';
 import {globals} from '../frontend/globals';
@@ -298,7 +299,7 @@
     }
 
     const pivotTableState = globals.state.nonSerializableState.pivotTable;
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
 
     if (
       pivotTableState.queryRequested ||
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index ea1db14..d30b458 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -15,7 +15,7 @@
 import {assertTrue} from '../base/logging';
 import {Time, time} from '../base/time';
 import {Args, ArgValue} from '../common/arg_types';
-import {ChromeSliceSelection} from '../common/state';
+import {ChromeSliceSelection, getLegacySelection} from '../common/state';
 import {
   CounterDetails,
   globals,
@@ -68,7 +68,7 @@
   }
 
   run() {
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (!selection || selection.kind === 'AREA') return;
 
     const selectWithId = [
@@ -281,7 +281,7 @@
     }
 
     // Check selection is still the same on completion of query.
-    if (selection === globals.state.currentSelection) {
+    if (selection === getLegacySelection(globals.state)) {
       publishSliceDetails(selected);
     }
   }
@@ -352,7 +352,7 @@
     `;
     const result = await this.args.engine.query(query);
 
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (result.numRows() > 0 && selection) {
       const row = result.firstRow({
         ts: LONG,
@@ -379,7 +379,7 @@
     WHERE sched.id = ${id}`;
     const result = await this.args.engine.query(sqlQuery);
     // Check selection is still the same on completion of query.
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (result.numRows() > 0 && selection) {
       const row = result.firstRow({
         ts: LONG,
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 56507d9..5bdc934 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -32,6 +32,7 @@
   EngineMode,
   PendingDeeplinkState,
   ProfileType,
+  getLegacySelection,
 } from '../common/state';
 import {featureFlags, Flag, PERF_SAMPLE_FLAG} from '../core/feature_flags';
 import {BottomTabList} from '../frontend/bottom_tab';
@@ -675,8 +676,9 @@
     // If the trace was shared via a permalink, it might already have a
     // selection. Emit onSelectionChanged to ensure that the components (like
     // current selection details) react to it.
-    if (globals.state.currentSelection !== null) {
-      onSelectionChanged(globals.state.currentSelection, true);
+    const currentSelection = getLegacySelection(globals.state);
+    if (currentSelection !== null) {
+      onSelectionChanged(currentSelection, true);
     }
 
     globals.dispatch(Actions.maybeExpandOnlyTrackGroup({}));
diff --git a/ui/src/core/selection_manager.ts b/ui/src/core/selection_manager.ts
new file mode 100644
index 0000000..0a5b9be
--- /dev/null
+++ b/ui/src/core/selection_manager.ts
@@ -0,0 +1,13 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
diff --git a/ui/src/frontend/aggregation_panel.ts b/ui/src/frontend/aggregation_panel.ts
index cbd97d9..6f7f2ce 100644
--- a/ui/src/frontend/aggregation_panel.ts
+++ b/ui/src/frontend/aggregation_panel.ts
@@ -15,6 +15,7 @@
 import m from 'mithril';
 
 import {Actions} from '../common/actions';
+import {getLegacySelection} from '../common/state';
 import {
   AggregateData,
   Column,
@@ -39,7 +40,7 @@
   implements m.ClassComponent<AggregationPanelAttrs>
 {
   view({attrs}: m.CVnode<AggregationPanelAttrs>) {
-    if (!globals.state.currentSelection) {
+    if (!getLegacySelection(globals.state)) {
       return m(
         EmptyState,
         {
@@ -156,7 +157,7 @@
   }
 
   showTimeRange() {
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (selection === null || selection.kind !== 'AREA') return undefined;
     const selectedArea = globals.state.areas[selection.areaId];
     const duration = selectedArea.end - selectedArea.start;
diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts
index 0c63e9e..bad01f0 100644
--- a/ui/src/frontend/app.ts
+++ b/ui/src/frontend/app.ts
@@ -22,6 +22,7 @@
 import {undoCommonChatAppReplacements} from '../base/string_utils';
 import {duration, Span, Time, time, TimeSpan} from '../base/time';
 import {Actions} from '../common/actions';
+import {getLegacySelection} from '../common/state';
 import {runQuery} from '../common/queries';
 import {
   DurationPrecision,
@@ -237,7 +238,7 @@
   }
 
   private getFirstUtidOfSelectionOrVisibleWindow(): number {
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (selection && selection.kind === 'AREA') {
       const selectedArea = globals.state.areas[selection.areaId];
       const firstThreadStateTrack = selectedArea.tracks.find((trackId) => {
@@ -562,7 +563,7 @@
       id: 'perfetto.MarkArea',
       name: 'Mark area',
       callback: () => {
-        const selection = globals.state.currentSelection;
+        const selection = getLegacySelection(globals.state);
         if (selection && selection.kind === 'AREA') {
           globals.dispatch(Actions.toggleMarkCurrentArea({persistent: false}));
         } else if (selection) {
@@ -575,7 +576,7 @@
       id: 'perfetto.MarkAreaPersistent',
       name: 'Mark area (persistent)',
       callback: () => {
-        const selection = globals.state.currentSelection;
+        const selection = getLegacySelection(globals.state);
         if (selection && selection.kind === 'AREA') {
           globals.dispatch(Actions.toggleMarkCurrentArea({persistent: true}));
         } else if (selection) {
@@ -614,7 +615,7 @@
       callback: () => {
         let tracksToSelect: string[] = [];
 
-        const selection = globals.state.currentSelection;
+        const selection = getLegacySelection(globals.state);
         if (selection !== null && selection.kind === 'AREA') {
           const area = globals.state.areas[selection.areaId];
           const coversEntireTimeRange =
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index e4203f0..bc49f91 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -25,7 +25,7 @@
 } from '../common/canvas_utils';
 import {colorCompare} from '../core/color';
 import {UNEXPECTED_PINK} from '../core/colorizer';
-import {Selection, SelectionKind} from '../common/state';
+import {Selection, SelectionKind, getLegacySelection} from '../common/state';
 import {featureFlags} from '../core/feature_flags';
 import {raf} from '../core/raf_scheduler';
 import {EngineProxy, Slice, SliceRect, Track} from '../public';
@@ -360,7 +360,7 @@
       vizTime.end.toTime('ceil'),
     );
 
-    let selection = globals.state.currentSelection;
+    let selection = getLegacySelection(globals.state);
     if (!selection || !this.isSelectionHandled(selection)) {
       selection = null;
     }
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index a93418d..22b1601 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -20,7 +20,7 @@
 import {isEmptyData} from '../common/aggregation_data';
 import {LogExists, LogExistsKey} from '../common/logs';
 import {addSelectionChangeObserver} from '../common/selection_observer';
-import {Selection} from '../common/state';
+import {Selection, getLegacySelection} from '../common/state';
 
 import {AggregationPanel} from './aggregation_panel';
 import {ChromeSliceDetailsTab} from './chrome_slice_details_tab';
@@ -140,7 +140,7 @@
       }
     }
 
-    const curSelection = globals.state.currentSelection;
+    const curSelection = getLegacySelection(globals.state);
     if (curSelection) {
       switch (curSelection.kind) {
         case 'NOTE':
diff --git a/ui/src/frontend/flow_events_panel.ts b/ui/src/frontend/flow_events_panel.ts
index ee34b88..02bf691 100644
--- a/ui/src/frontend/flow_events_panel.ts
+++ b/ui/src/frontend/flow_events_panel.ts
@@ -16,6 +16,7 @@
 
 import {Icons} from '../base/semantic_icons';
 import {Actions} from '../common/actions';
+import {getLegacySelection} from '../common/state';
 import {raf} from '../core/raf_scheduler';
 
 import {Flow, globals} from './globals';
@@ -39,7 +40,7 @@
 
 export class FlowEventsPanel implements m.ClassComponent {
   view() {
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (!selection) {
       return m(
         EmptyState,
@@ -145,7 +146,7 @@
 
 export class FlowEventsAreaSelectedPanel implements m.ClassComponent {
   view() {
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (!selection || selection.kind !== 'AREA') {
       return;
     }
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 35cd660..4528881 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -38,6 +38,7 @@
   ProfileType,
   RESOLUTION_DEFAULT,
   State,
+  getLegacySelection,
 } from '../common/state';
 import {TabManager} from '../common/tab_registry';
 import {TimestampFormat, timestampFormat} from '../core/timestamp_format';
@@ -644,12 +645,14 @@
 
     // HACK(stevegolton + altimin): This is a workaround to allow passing the
     // next tab state to the Bottom Tab API
-    if (this.state.currentSelection !== previousState.currentSelection) {
+    const currentSelection = getLegacySelection(this.state);
+    const previousSelection = getLegacySelection(previousState);
+    if (currentSelection !== previousSelection) {
       // TODO(altimin): Currently we are not triggering this when changing
       // the set of selected tracks via toggling per-track checkboxes.
       // Fix that.
       onSelectionChanged(
-        this.state.currentSelection ?? undefined,
+        currentSelection ?? undefined,
         switchToCurrentSelectionTab,
       );
     }
@@ -809,7 +812,7 @@
   }
 
   findTimeRangeOfSelection(): {start: time; end: time} {
-    const selection = this.state.currentSelection;
+    const selection = getLegacySelection(this.state);
     let start = Time.INVALID;
     let end = Time.INVALID;
     if (selection === null) {
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index e73a5d1..60b7c8f 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 import {Actions} from '../common/actions';
-import {Area} from '../common/state';
+import {Area, getLegacySelection} from '../common/state';
 
 import {Flow, globals} from './globals';
 import {focusHorizontalRange, verticalScrollToTrack} from './scroll_helper';
@@ -44,13 +44,11 @@
 
 // Change focus to the next flow event (matching the direction)
 export function focusOtherFlow(direction: Direction) {
-  if (
-    !globals.state.currentSelection ||
-    globals.state.currentSelection.kind !== 'CHROME_SLICE'
-  ) {
+  const currentSelection = getLegacySelection(globals.state);
+  if (!currentSelection || currentSelection.kind !== 'CHROME_SLICE') {
     return;
   }
-  const sliceId = globals.state.currentSelection.id;
+  const sliceId = currentSelection.id;
   if (sliceId === -1) {
     return;
   }
@@ -78,14 +76,12 @@
 
 // Select the slice connected to the flow in focus
 export function moveByFocusedFlow(direction: Direction): void {
-  if (
-    !globals.state.currentSelection ||
-    globals.state.currentSelection.kind !== 'CHROME_SLICE'
-  ) {
+  const currentSelection = getLegacySelection(globals.state);
+  if (!currentSelection || currentSelection.kind !== 'CHROME_SLICE') {
     return;
   }
 
-  const sliceId = globals.state.currentSelection.id;
+  const sliceId = currentSelection.id;
   const flowId =
     direction === 'Backward'
       ? globals.state.focusedFlowIdLeft
@@ -117,21 +113,16 @@
 
 export function lockSliceSpan(persistent = false) {
   const range = globals.findTimeRangeOfSelection();
-  if (
-    range.start !== -1n &&
-    range.end !== -1n &&
-    globals.state.currentSelection !== null
-  ) {
-    const tracks = globals.state.currentSelection.trackKey
-      ? [globals.state.currentSelection.trackKey]
-      : [];
+  const currentSelection = getLegacySelection(globals.state);
+  if (range.start !== -1n && range.end !== -1n && currentSelection !== null) {
+    const tracks = currentSelection.trackKey ? [currentSelection.trackKey] : [];
     const area: Area = {start: range.start, end: range.end, tracks};
     globals.dispatch(Actions.markArea({area, persistent}));
   }
 }
 
 export function findCurrentSelection() {
-  const selection = globals.state.currentSelection;
+  const selection = getLegacySelection(globals.state);
   if (selection === null) return;
 
   const range = globals.findTimeRangeOfSelection();
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index dfc7800..23dcbbc 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -19,7 +19,7 @@
 import {Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {randomColor} from '../core/colorizer';
-import {AreaNote, Note} from '../common/state';
+import {AreaNote, Note, getLegacySelection} from '../common/state';
 import {raf} from '../core/raf_scheduler';
 import {Button} from '../widgets/button';
 
@@ -177,7 +177,7 @@
         this.hoveredX !== null && this.mouseOverNote(this.hoveredX, note);
       if (currentIsHovered) aNoteIsHovered = true;
 
-      const selection = globals.state.currentSelection;
+      const selection = getLegacySelection(globals.state);
       const isSelected =
         selection !== null &&
         ((selection.kind === 'NOTE' && selection.id === note.id) ||
diff --git a/ui/src/frontend/publish.ts b/ui/src/frontend/publish.ts
index ee9f55e..18b5773 100644
--- a/ui/src/frontend/publish.ts
+++ b/ui/src/frontend/publish.ts
@@ -26,6 +26,7 @@
 import {CurrentSearchResults, SearchSummary} from '../common/search_data';
 import {raf} from '../core/raf_scheduler';
 import {HttpRpcState} from '../trace_processor/http_rpc_engine';
+import {getLegacySelection} from '../common/state';
 
 import {
   CounterDetails,
@@ -212,8 +213,9 @@
   // focus. In all other cases the focusedFlowId(Left|Right) will be set to -1.
   globals.dispatch(Actions.setHighlightedFlowLeftId({flowId: -1}));
   globals.dispatch(Actions.setHighlightedFlowRightId({flowId: -1}));
-  if (globals.state.currentSelection?.kind === 'CHROME_SLICE') {
-    const sliceId = globals.state.currentSelection.id;
+  const currentSelection = getLegacySelection(globals.state);
+  if (currentSelection?.kind === 'CHROME_SLICE') {
+    const sliceId = currentSelection.id;
     for (const flow of globals.connectedFlows) {
       if (flow.begin.sliceId === sliceId) {
         globals.dispatch(Actions.setHighlightedFlowRightId({flowId: flow.id}));
diff --git a/ui/src/frontend/slice_track.ts b/ui/src/frontend/slice_track.ts
index 2f9eb40..cde7027 100644
--- a/ui/src/frontend/slice_track.ts
+++ b/ui/src/frontend/slice_track.ts
@@ -20,6 +20,7 @@
 import {TrackData} from '../common/track_data';
 import {TimelineFetcher} from '../common/track_helper';
 import {SliceRect, Track} from '../public';
+import {getLegacySelection} from '../common/state';
 
 import {CROP_INCOMPLETE_SLICE_FLAG} from './base_slice_track';
 import {checkerboardExcept} from './checkerboard';
@@ -153,7 +154,7 @@
         height: SLICE_HEIGHT,
       };
 
-      const currentSelection = globals.state.currentSelection;
+      const currentSelection = getLegacySelection(globals.state);
       const isSelected =
         currentSelection &&
         currentSelection.kind === 'CHROME_SLICE' &&
diff --git a/ui/src/frontend/tab_panel.ts b/ui/src/frontend/tab_panel.ts
index 82414eb..23e8095 100644
--- a/ui/src/frontend/tab_panel.ts
+++ b/ui/src/frontend/tab_panel.ts
@@ -16,6 +16,7 @@
 
 import {Gate} from '../base/mithril_utils';
 import {Actions} from '../common/actions';
+import {getLegacySelection} from '../common/state';
 import {EmptyState} from '../widgets/empty_state';
 
 import {
@@ -62,7 +63,7 @@
 
     if (
       !this.hasBeenDragged &&
-      (tabs.length > 0 || globals.state.currentSelection)
+      (tabs.length > 0 || getLegacySelection(globals.state))
     ) {
       this.detailsHeight = getDefaultDetailsHeight();
     }
@@ -128,7 +129,7 @@
   }
 
   private renderCSTabContent(): {isLoading: boolean; content: m.Children} {
-    const cs = globals.state.currentSelection;
+    const cs = getLegacySelection(globals.state);
     if (!cs) {
       return {
         isLoading: false,
diff --git a/ui/src/frontend/time_selection_panel.ts b/ui/src/frontend/time_selection_panel.ts
index 0da9a23..d77b095 100644
--- a/ui/src/frontend/time_selection_panel.ts
+++ b/ui/src/frontend/time_selection_panel.ts
@@ -32,6 +32,7 @@
 import {PanelSize} from './panel';
 import {Panel} from './panel_container';
 import {renderDuration} from './widgets/duration';
+import {getLegacySelection} from '../common/state';
 
 export interface BBox {
   x: number;
@@ -172,7 +173,7 @@
     }
 
     const localArea = globals.timeline.selectedArea;
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (localArea !== undefined) {
       const start = Time.min(localArea.start, localArea.end);
       const end = Time.max(localArea.start, localArea.end);
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts
index 627ac4e..0f0f401 100644
--- a/ui/src/frontend/track_group_panel.ts
+++ b/ui/src/frontend/track_group_panel.ts
@@ -16,7 +16,7 @@
 
 import {Icons} from '../base/semantic_icons';
 import {Actions} from '../common/actions';
-import {getContainingTrackId} from '../common/state';
+import {getContainingTrackId, getLegacySelection} from '../common/state';
 import {TrackCacheEntry} from '../common/track_cache';
 import {TrackTags} from '../public';
 
@@ -77,7 +77,7 @@
       }
     }
 
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
 
     const trackGroup = globals.state.trackGroups[trackGroupId];
     let checkBox = Icons.BlankCheckbox;
@@ -173,7 +173,7 @@
 
   highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
     const {visibleTimeScale} = globals.timeline;
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (!selection || selection.kind !== 'AREA') return;
     const selectedArea = globals.state.areas[selection.areaId];
     const selectedAreaDuration = selectedArea.end - selectedArea.start;
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index d186fdb..2e15d67 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -36,6 +36,7 @@
 import {Popup} from '../widgets/popup';
 import {canvasClip} from '../common/canvas_utils';
 import {TimeScale} from './time_scale';
+import {getLegacySelection} from '../common/state';
 
 function getTitleSize(title: string): string | undefined {
   const length = title.length;
@@ -62,7 +63,7 @@
 }
 
 function isSelected(id: string) {
-  const selection = globals.state.currentSelection;
+  const selection = getLegacySelection(globals.state);
   if (selection === null || selection.kind !== 'AREA') return false;
   const selectedArea = globals.state.areas[selection.areaId];
   return selectedArea.tracks.includes(id);
@@ -143,6 +144,8 @@
       }
     }
 
+    const currentSelection = getLegacySelection(globals.state);
+
     return m(
       `.track-shell[draggable=true]`,
       {
@@ -183,8 +186,7 @@
           showButton: isPinned(attrs.trackKey),
           fullHeight: true,
         }),
-        globals.state.currentSelection !== null &&
-          globals.state.currentSelection.kind === 'AREA'
+        currentSelection !== null && currentSelection.kind === 'AREA'
           ? m(TrackButton, {
               action: (e: MouseEvent) => {
                 globals.dispatch(
@@ -473,7 +475,7 @@
 
   highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
     const {visibleTimeScale} = globals.timeline;
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     if (!selection || selection.kind !== 'AREA') {
       return;
     }
@@ -574,9 +576,10 @@
   visibleTimeScale: TimeScale,
   size: PanelSize,
 ) {
-  if (globals.state.currentSelection !== null) {
+  const currentSelection = getLegacySelection(globals.state);
+  if (currentSelection !== null) {
     if (
-      globals.state.currentSelection.kind === 'SLICE' &&
+      currentSelection.kind === 'SLICE' &&
       globals.sliceDetails.wakeupTs !== undefined
     ) {
       drawVerticalLineAtTime(
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 0c8a185..ef2686a 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -40,6 +40,7 @@
 import {TrackGroupPanel} from './track_group_panel';
 import {TrackPanel} from './track_panel';
 import {assertExists} from '../base/logging';
+import {getLegacySelection} from '../common/state';
 
 const OVERVIEW_PANEL_FLAG = featureFlags.register({
   id: 'overviewVisible',
@@ -51,7 +52,7 @@
 // Checks if the mousePos is within 3px of the start or end of the
 // current selected time range.
 function onTimeRangeBoundary(mousePos: number): 'START' | 'END' | null {
-  const selection = globals.state.currentSelection;
+  const selection = getLegacySelection(globals.state);
   if (selection !== null && selection.kind === 'AREA') {
     // If frontend selectedArea exists then we are in the process of editing the
     // time range and need to use that value instead.
@@ -152,7 +153,7 @@
         const {visibleTimeScale} = timeline;
         this.keepCurrentSelection = true;
         if (editing) {
-          const selection = globals.state.currentSelection;
+          const selection = getLegacySelection(globals.state);
           if (selection !== null && selection.kind === 'AREA') {
             const area = globals.timeline.selectedArea
               ? globals.timeline.selectedArea
@@ -208,7 +209,7 @@
         // If we are editing we need to pass the current id through to ensure
         // the marked area with that id is also updated.
         if (edit) {
-          const selection = globals.state.currentSelection;
+          const selection = getLegacySelection(globals.state);
           if (selection !== null && selection.kind === 'AREA' && area) {
             globals.dispatch(
               Actions.editArea({area, areaId: selection.areaId}),
diff --git a/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts b/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts
index dac1878..85a2a70 100644
--- a/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts
@@ -29,6 +29,7 @@
   ScrollJankTracks as DecideTracksResult,
 } from './index';
 import {JANK_COLOR} from './jank_colors';
+import {getLegacySelection} from '../../common/state';
 
 export const JANKY_LATENCY_NAME = 'Janky EventLatency';
 
@@ -85,7 +86,7 @@
 
   onUpdatedSlices(slices: EventLatencyTrackTypes['slice'][]) {
     for (const slice of slices) {
-      const currentSelection = globals.state.currentSelection;
+      const currentSelection = getLegacySelection(globals.state);
       const isSelected =
         currentSelection &&
         currentSelection.kind === 'GENERIC_SLICE' &&
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
index fb77174..ba44b9b 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
@@ -31,6 +31,7 @@
 import {JANK_COLOR} from './jank_colors';
 import {ScrollJankV3DetailsPanel} from './scroll_jank_v3_details_panel';
 import {getColorForSlice} from '../../core/colorizer';
+import {getLegacySelection} from '../../common/state';
 
 const UNKNOWN_SLICE_NAME = 'Unknown';
 const JANK_SLICE_NAME = ' Jank';
@@ -100,7 +101,7 @@
 
   onUpdatedSlices(slices: EventLatencyTrackTypes['slice'][]) {
     for (const slice of slices) {
-      const currentSelection = globals.state.currentSelection;
+      const currentSelection = getLegacySelection(globals.state);
       const isSelected =
         currentSelection &&
         currentSelection.kind === 'GENERIC_SLICE' &&
diff --git a/ui/src/tracks/cpu_profile/index.ts b/ui/src/tracks/cpu_profile/index.ts
index fb7c7c5..c3a2b57 100644
--- a/ui/src/tracks/cpu_profile/index.ts
+++ b/ui/src/tracks/cpu_profile/index.ts
@@ -14,6 +14,7 @@
 
 import {searchSegment} from '../../base/binary_search';
 import {duration, Time, time} from '../../base/time';
+import {getLegacySelection} from '../../common/state';
 import {Actions} from '../../common/actions';
 import {colorForSample} from '../../core/colorizer';
 import {TrackData} from '../../common/track_data';
@@ -117,7 +118,7 @@
 
     for (let i = 0; i < data.tsStarts.length; i++) {
       const centerX = Time.fromRaw(data.tsStarts[i]);
-      const selection = globals.state.currentSelection;
+      const selection = getLegacySelection(globals.state);
       const isHovered = this.hoveredTs === centerX;
       const isSelected =
         selection !== null &&
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index 647baed..b4c5fe0 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -20,6 +20,7 @@
 import {Duration, duration, Time, time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {calcCachedBucketSize} from '../../common/cache_utils';
+import {getLegacySelection} from '../../common/state';
 import {
   cropText,
   drawDoubleHeadedArrow,
@@ -392,7 +393,7 @@
       ctx.fillText(subTitle, rectXCenter, MARGIN_TOP + RECT_HEIGHT / 2 + 9);
     }
 
-    const selection = globals.state.currentSelection;
+    const selection = getLegacySelection(globals.state);
     const details = globals.sliceDetails;
     if (selection !== null && selection.kind === 'SLICE') {
       const [startIndex, endIndex] = searchEq(data.ids, selection.id);
diff --git a/ui/src/tracks/perf_samples_profile/index.ts b/ui/src/tracks/perf_samples_profile/index.ts
index 37422d4..6720142 100644
--- a/ui/src/tracks/perf_samples_profile/index.ts
+++ b/ui/src/tracks/perf_samples_profile/index.ts
@@ -15,7 +15,7 @@
 import {searchSegment} from '../../base/binary_search';
 import {duration, Time, time} from '../../base/time';
 import {Actions} from '../../common/actions';
-import {ProfileType} from '../../common/state';
+import {ProfileType, getLegacySelection} from '../../common/state';
 import {TrackData} from '../../common/track_data';
 import {TimelineFetcher} from '../../common/track_helper';
 import {FLAMEGRAPH_HOVERED_COLOR} from '../../frontend/flamegraph';
@@ -114,7 +114,7 @@
 
     for (let i = 0; i < data.tsStarts.length; i++) {
       const centerX = Time.fromRaw(data.tsStarts[i]);
-      const selection = globals.state.currentSelection;
+      const selection = getLegacySelection(globals.state);
       const isHovered = this.hoveredTs === centerX;
       const isSelected =
         selection !== null &&