Merge "Update display metrics with initial density if it is not forced" into main
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index ff737a4..49e2358 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -664,9 +664,14 @@
     
     int mStatusIcon;
 
+    /** Latest reported value of back disposition mode. */
     @BackDispositionMode
     int mBackDisposition;
 
+    /** Latest reported value of IME window visibility state. */
+    @ImeWindowVisibility
+    private int mImeWindowVisibility;
+
     private Object mLock = new Object();
     @GuardedBy("mLock")
     private boolean mNotifyUserActionSent;
@@ -1047,7 +1052,7 @@
                 ImeTracker.forLogging().onFailed(statsToken,
                         ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
             }
-            setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+            setImeWindowVisibility(computeImeWindowVis());
 
             final boolean isVisible = isInputViewShown();
             final boolean visibilityChanged = isVisible != wasVisible;
@@ -1357,9 +1362,22 @@
         mImeSurfaceRemoverRunnable = null;
     }
 
-    private void setImeWindowStatus(@ImeWindowVisibility int visibilityFlags,
+    /**
+     * Sets the IME window visibility state.
+     *
+     * @param vis the IME window visibility state to be set.
+     */
+    private void setImeWindowVisibility(@ImeWindowVisibility int vis) {
+        if (vis == mImeWindowVisibility) {
+            return;
+        }
+        mImeWindowVisibility = vis;
+        setImeWindowStatus(mImeWindowVisibility, mBackDisposition);
+    }
+
+    private void setImeWindowStatus(@ImeWindowVisibility int vis,
             @BackDispositionMode int backDisposition) {
-        mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition);
+        mPrivOps.setImeWindowStatusAsync(vis, backDisposition);
     }
 
     /** Set region of the keyboard to be avoided from back gesture */
@@ -1986,7 +2004,7 @@
             }
             // If user uses hard keyboard, IME button should always be shown.
             boolean showing = onEvaluateInputViewShown();
-            setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+            setImeWindowVisibility(IME_ACTIVE | (showing ? IME_VISIBLE : 0));
         }
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
@@ -2053,7 +2071,7 @@
             return;
         }
         mBackDisposition = disposition;
-        setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+        setImeWindowStatus(mImeWindowVisibility, mBackDisposition);
     }
 
     /**
@@ -3132,14 +3150,8 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
         mDecorViewWasVisible = mDecorViewVisible;
         mInShowWindow = true;
-        final int previousImeWindowStatus =
-                (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
-                        ? (!mWindowVisible ? -1 : IME_VISIBLE) : 0);
         startViews(prepareWindow(showInput));
-        final int nextImeWindowStatus = mapToImeWindowStatus();
-        if (previousImeWindowStatus != nextImeWindowStatus) {
-            setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
-        }
+        setImeWindowVisibility(computeImeWindowVis());
 
         mNavigationBarController.onWindowShown();
         // compute visibility
@@ -3317,7 +3329,7 @@
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW);
         ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
                 null /* icProto */);
-        setImeWindowStatus(0 /* visibilityFlags */, mBackDisposition);
+        setImeWindowVisibility(0 /* vis */);
         if (android.view.inputmethod.Flags.refactorInsetsController()) {
             // The ImeInsetsSourceProvider need the statsToken when dispatching the control. We
             // send the token here, so that another request in the provider can be cancelled.
@@ -4492,10 +4504,10 @@
         };
     }
 
+    /** Computes the IME window visibility state. */
     @ImeWindowVisibility
-    private int mapToImeWindowStatus() {
-        return IME_ACTIVE
-                | (isInputViewShown() ? IME_VISIBLE : 0);
+    private int computeImeWindowVis() {
+        return IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
     }
 
     /**
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 4264358..1fd933f 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -99,11 +99,8 @@
     public static final String NULL_STRING = "null";
     private final AtomicInteger mTracingInstances = new AtomicInteger();
 
-    private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
-            this::onTracingInstanceStart,
-            this::onTracingFlush,
-            this::onTracingInstanceStop
-    );
+    @NonNull
+    private final ProtoLogDataSource mDataSource;
     @Nullable
     private final ProtoLogViewerConfigReader mViewerConfigReader;
     @Nullable
@@ -156,8 +153,11 @@
             @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
             @Nullable ProtoLogViewerConfigReader viewerConfigReader,
             @NonNull Runnable cacheUpdater,
-            @NonNull IProtoLogGroup[] groups) {
-        this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater, groups);
+            @NonNull IProtoLogGroup[] groups,
+            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+            @NonNull ProtoLogConfigurationService configurationService) {
+        this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater,
+                groups, dataSourceBuilder, configurationService);
     }
 
     private PerfettoProtoLogImpl(
@@ -166,11 +166,31 @@
             @Nullable ProtoLogViewerConfigReader viewerConfigReader,
             @NonNull Runnable cacheUpdater,
             @NonNull IProtoLogGroup[] groups) {
+        this(viewerConfigFilePath, viewerConfigInputStreamProvider, viewerConfigReader,
+                cacheUpdater, groups,
+                ProtoLogDataSource::new,
+                IProtoLogConfigurationService.Stub
+                        .asInterface(ServiceManager.getService(PROTOLOG_CONFIGURATION_SERVICE))
+        );
+    }
+
+    private PerfettoProtoLogImpl(
+            @Nullable String viewerConfigFilePath,
+            @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
+            @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+            @NonNull Runnable cacheUpdater,
+            @NonNull IProtoLogGroup[] groups,
+            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+            @Nullable IProtoLogConfigurationService configurationService) {
         if (viewerConfigFilePath != null && viewerConfigInputStreamProvider != null) {
             throw new RuntimeException("Only one of viewerConfigFilePath and "
                     + "viewerConfigInputStreamProvider can be set");
         }
 
+        mDataSource = dataSourceBuilder.build(
+                this::onTracingInstanceStart,
+                this::onTracingFlush,
+                this::onTracingInstanceStop);
         Producer.init(InitArguments.DEFAULTS);
         DataSourceParams params =
                 new DataSourceParams.Builder()
@@ -186,9 +206,7 @@
         registerGroupsLocally(groups);
 
         if (android.tracing.Flags.clientSideProtoLogging()) {
-            mProtoLogConfigurationService =
-                    IProtoLogConfigurationService.Stub.asInterface(ServiceManager.getService(
-                            PROTOLOG_CONFIGURATION_SERVICE));
+            mProtoLogConfigurationService = configurationService;
             Objects.requireNonNull(mProtoLogConfigurationService,
                     "ServiceManager returned a null ProtoLog Configuration Service");
 
@@ -733,6 +751,10 @@
             incrementalState.protologMessageInterningSet.add(messageHash);
 
             final ProtoOutputStream os = ctx.newTracePacket();
+
+            // Dependent on the ProtoLog viewer config packet that contains the group information.
+            os.write(SEQUENCE_FLAGS, SEQ_NEEDS_INCREMENTAL_STATE);
+
             final long protologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
             final long messageConfigToken = os.start(MESSAGES);
 
diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
index eeac139..d54a80b 100644
--- a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
+++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
@@ -75,11 +75,7 @@
 public final class ProtoLogConfigurationService extends IProtoLogConfigurationService.Stub {
     private static final String LOG_TAG = "ProtoLogConfigurationService";
 
-    private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
-            this::onTracingInstanceStart,
-            this::onTracingInstanceFlush,
-            this::onTracingInstanceStop
-    );
+    private final ProtoLogDataSource mDataSource;
 
     /**
      * Keeps track of how many of each viewer config file is currently registered.
@@ -116,11 +112,28 @@
     private final ViewerConfigFileTracer mViewerConfigFileTracer;
 
     public ProtoLogConfigurationService() {
-        this(ProtoLogConfigurationService::dumpTransitionTraceConfig);
+        this(ProtoLogDataSource::new, ProtoLogConfigurationService::dumpTransitionTraceConfig);
+    }
+
+    @VisibleForTesting
+    public ProtoLogConfigurationService(@NonNull ProtoLogDataSourceBuilder dataSourceBuilder) {
+        this(dataSourceBuilder, ProtoLogConfigurationService::dumpTransitionTraceConfig);
     }
 
     @VisibleForTesting
     public ProtoLogConfigurationService(@NonNull ViewerConfigFileTracer tracer) {
+        this(ProtoLogDataSource::new, tracer);
+    }
+
+    private ProtoLogConfigurationService(
+            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+            @NonNull ViewerConfigFileTracer tracer) {
+        mDataSource = dataSourceBuilder.build(
+            this::onTracingInstanceStart,
+            this::onTracingInstanceFlush,
+            this::onTracingInstanceStop
+        );
+
         // Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be
         // receive the lifecycle callbacks of the datasource and write the viewer configs if and
         // when required to the datasource.
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java b/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java
new file mode 100644
index 0000000..84436b9
--- /dev/null
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.protolog;
+
+public interface ProtoLogDataSourceBuilder {
+    /**
+     * Builder method for the DataSource the PerfettoProtoLogImpl is going to us.
+     * @param onStart The onStart callback that should be used by the created datasource.
+     * @param onFlush The onFlush callback that should be used by the created datasource.
+     * @param onStop The onStop callback that should be used by the created datasource.
+     * @return A new DataSource that uses the provided callbacks.
+     */
+    ProtoLogDataSource build(ProtoLogDataSource.Instance.TracingInstanceStartCallback onStart,
+            Runnable onFlush, ProtoLogDataSource.Instance.TracingInstanceStopCallback onStop);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 0ffb763..616ab07 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -104,6 +104,22 @@
     /**
      * @param context to access resources from
      * @param cachedDevice to get class from
+     * @return pair containing the drawable and the description of the type of the device. The type
+     *     could either derived from metadata or CoD.
+     */
+    public static Pair<Drawable, String> getDerivedBtClassDrawableWithDescription(
+            Context context, CachedBluetoothDevice cachedDevice) {
+        return BluetoothUtils.isAdvancedUntetheredDevice(cachedDevice.getDevice())
+                ? new Pair<>(
+                        getBluetoothDrawable(
+                                context, com.android.internal.R.drawable.ic_bt_headphones_a2dp),
+                        context.getString(R.string.bluetooth_talkback_headphone))
+                : BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice);
+    }
+
+    /**
+     * @param context to access resources from
+     * @param cachedDevice to get class from
      * @return pair containing the drawable and the description of the Bluetooth class of the
      *     device.
      */
@@ -731,9 +747,7 @@
         int broadcastId = broadcast.getLatestBroadcastId();
         return !sourceList.isEmpty()
                 && broadcastId != UNKNOWN_VALUE_PLACEHOLDER
-                && sourceList.stream()
-                        .anyMatch(
-                                source -> isSourceMatched(source, broadcastId));
+                && sourceList.stream().anyMatch(source -> isSourceMatched(source, broadcastId));
     }
 
     /** Checks the connectivity status based on the provided broadcast receive state. */
@@ -1030,8 +1044,7 @@
                                         cachedDevice.getAddress());
                         break;
                     case BluetoothProfile.LE_AUDIO:
-                        if (audioDeviceCategory
-                                == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) {
+                        if (audioDeviceCategory == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) {
                             saDevice =
                                     new AudioDeviceAttributes(
                                             AudioDeviceAttributes.ROLE_OUTPUT,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index a0e764a..8eedb35 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -46,6 +46,7 @@
 import android.provider.Settings;
 import android.util.Pair;
 
+import com.android.internal.R;
 import com.android.settingslib.widget.AdaptiveIcon;
 
 import com.google.common.collect.ImmutableList;
@@ -118,6 +119,34 @@
     }
 
     @Test
+    public void
+            getDerivedBtClassDrawableWithDescription_isAdvancedUntetheredDevice_returnHeadset() {
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn(BOOL_METADATA.getBytes());
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        Pair<Drawable, String> pair =
+                BluetoothUtils.getDerivedBtClassDrawableWithDescription(
+                        mContext, mCachedBluetoothDevice);
+
+        verify(mContext).getDrawable(R.drawable.ic_bt_headphones_a2dp);
+    }
+
+    @Test
+    public void
+            getDerivedBtClassDrawableWithDescription_notAdvancedUntetheredDevice_returnPhone() {
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn("false".getBytes());
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass())
+                .thenReturn(BluetoothClass.Device.Major.PHONE);
+        Pair<Drawable, String> pair =
+                BluetoothUtils.getDerivedBtClassDrawableWithDescription(
+                        mContext, mCachedBluetoothDevice);
+
+        verify(mContext).getDrawable(R.drawable.ic_phone);
+    }
+
+    @Test
     public void getBtClassDrawableWithDescription_typePhone_returnPhoneDrawable() {
         when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass())
                 .thenReturn(BluetoothClass.Device.Major.PHONE);
@@ -681,8 +710,8 @@
         when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
 
         assertThat(
-                BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
-                        mBluetoothDevice, mLocalBluetoothManager))
+                        BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
+                                mBluetoothDevice, mLocalBluetoothManager))
                 .isTrue();
     }
 
@@ -694,12 +723,11 @@
         when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
 
         assertThat(
-                BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
-                        mBluetoothDevice, mLocalBluetoothManager))
+                        BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
+                                mBluetoothDevice, mLocalBluetoothManager))
                 .isFalse();
     }
 
-
     @Test
     public void isAvailableHearingDevice_isConnectedHearingAid_returnTure() {
         when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true);
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 5780c08..0d05f4e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -221,19 +221,59 @@
     private suspend fun PointerInputScope.pointerTracker() {
         val currentContext = currentCoroutineContext()
         awaitPointerEventScope {
+            var velocityPointerId: PointerId? = null
             // Intercepts pointer inputs and exposes [PointersInfo], via
             // [requireAncestorPointersInfoOwner], to our descendants.
             while (currentContext.isActive) {
                 // During the Initial pass, we receive the event after our ancestors.
-                val pointers = awaitPointerEvent(PointerEventPass.Initial).changes
-                pointersDown = pointers.countDown()
-                if (pointersDown == 0) {
-                    // There are no more pointers down
-                    startedPosition = null
-                } else if (startedPosition == null) {
-                    startedPosition = pointers.first().position
-                    if (enabled()) {
-                        onFirstPointerDown()
+                val changes = awaitPointerEvent(PointerEventPass.Initial).changes
+                pointersDown = changes.countDown()
+
+                when {
+                    // There are no more pointers down.
+                    pointersDown == 0 -> {
+                        startedPosition = null
+
+                        // This is the last pointer up
+                        velocityTracker.addPointerInputChange(changes.single())
+                    }
+
+                    // The first pointer down, startedPosition was not set.
+                    startedPosition == null -> {
+                        val firstPointerDown = changes.single()
+                        velocityPointerId = firstPointerDown.id
+                        velocityTracker.resetTracking()
+                        velocityTracker.addPointerInputChange(firstPointerDown)
+                        startedPosition = firstPointerDown.position
+                        if (enabled()) {
+                            onFirstPointerDown()
+                        }
+                    }
+
+                    // Changes with at least one pointer
+                    else -> {
+                        val pointerChange = changes.first()
+
+                        // Assuming that the list of changes doesn't have two changes with the same
+                        // id (PointerId), we can check:
+                        // - If the first change has `id` equals to `velocityPointerId` (this should
+                        //   always be true unless the pointer has been removed).
+                        // - If it does, we've found our change event (assuming there aren't any
+                        //   others changes with the same id in this PointerEvent - not checked).
+                        // - If it doesn't, we can check that the change with that id isn't in first
+                        //   place (which should never happen - this will crash).
+                        check(
+                            pointerChange.id == velocityPointerId ||
+                                !changes.fastAny { it.id == velocityPointerId }
+                        ) {
+                            "$velocityPointerId is present, but not the first: $changes"
+                        }
+
+                        // If the previous pointer has been removed, we use the first available
+                        // change to keep tracking the velocity.
+                        velocityPointerId = pointerChange.id
+
+                        velocityTracker.addPointerInputChange(pointerChange)
                     }
                 }
             }
@@ -253,11 +293,9 @@
                         orientation = orientation,
                         startDragImmediately = startDragImmediately,
                         onDragStart = { startedPosition, overSlop, pointersDown ->
-                            velocityTracker.resetTracking()
                             onDragStarted(startedPosition, overSlop, pointersDown)
                         },
-                        onDrag = { controller, change, amount ->
-                            velocityTracker.addPointerInputChange(change)
+                        onDrag = { controller, amount ->
                             dispatchScrollEvents(
                                 availableOnPreScroll = amount,
                                 onScroll = { controller.onDrag(it) },
@@ -403,7 +441,7 @@
         startDragImmediately: (startedPosition: Offset) -> Boolean,
         onDragStart:
             (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
-        onDrag: (controller: DragController, change: PointerInputChange, dragAmount: Float) -> Unit,
+        onDrag: (controller: DragController, dragAmount: Float) -> Unit,
         onDragEnd: (controller: DragController) -> Unit,
         onDragCancel: (controller: DragController) -> Unit,
         swipeDetector: SwipeDetector,
@@ -482,14 +520,14 @@
 
             val successful: Boolean
             try {
-                onDrag(controller, drag, overSlop)
+                onDrag(controller, overSlop)
 
                 successful =
                     drag(
                         initialPointerId = drag.id,
                         hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
                         onDrag = {
-                            onDrag(controller, it, it.positionChange().toFloat())
+                            onDrag(controller, it.positionChange().toFloat())
                             it.consume()
                         },
                         onIgnoredEvent = {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index bd21a69..8ae3a11 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -46,8 +46,21 @@
         }
     }
 
+    /** The current overlays. */
+    fun currentOverlays(): Flow<Set<OverlayKey>> {
+        return when (this) {
+            is Idle -> flowOf(currentOverlays)
+            is Transition -> currentOverlays
+        }
+    }
+
     /** No transition/animation is currently running. */
-    data class Idle(val currentScene: SceneKey) : ObservableTransitionState
+    data class Idle
+    @JvmOverloads
+    constructor(
+        val currentScene: SceneKey,
+        val currentOverlays: Set<OverlayKey> = emptySet(),
+    ) : ObservableTransitionState
 
     /** There is a transition animating between two scenes. */
     sealed class Transition(
@@ -94,7 +107,7 @@
             val fromScene: SceneKey,
             val toScene: SceneKey,
             val currentScene: Flow<SceneKey>,
-            currentOverlays: Flow<Set<OverlayKey>>,
+            currentOverlays: Set<OverlayKey>,
             progress: Flow<Float>,
             isInitiatedByUserInput: Boolean,
             isUserInputOngoing: Flow<Boolean>,
@@ -104,7 +117,7 @@
             Transition(
                 fromScene,
                 toScene,
-                currentOverlays,
+                flowOf(currentOverlays),
                 progress,
                 isInitiatedByUserInput,
                 isUserInputOngoing,
@@ -169,7 +182,7 @@
                 isUserInputOngoing: Flow<Boolean>,
                 previewProgress: Flow<Float> = flowOf(0f),
                 isInPreviewStage: Flow<Boolean> = flowOf(false),
-                currentOverlays: Flow<Set<OverlayKey>> = flowOf(emptySet()),
+                currentOverlays: Set<OverlayKey> = emptySet(),
             ): ChangeScene {
                 return ChangeScene(
                     fromScene,
@@ -205,13 +218,17 @@
 fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTransitionState> {
     return snapshotFlow {
             when (val state = transitionState) {
-                is TransitionState.Idle -> ObservableTransitionState.Idle(state.currentScene)
+                is TransitionState.Idle ->
+                    ObservableTransitionState.Idle(
+                        state.currentScene,
+                        state.currentOverlays,
+                    )
                 is TransitionState.Transition.ChangeScene -> {
                     ObservableTransitionState.Transition.ChangeScene(
                         fromScene = state.fromScene,
                         toScene = state.toScene,
                         currentScene = snapshotFlow { state.currentScene },
-                        currentOverlays = flowOf(state.currentOverlays),
+                        currentOverlays = state.currentOverlays,
                         progress = snapshotFlow { state.progress },
                         isInitiatedByUserInput = state.isInitiatedByUserInput,
                         isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index d742592..bf192e7 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -38,12 +38,15 @@
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.TouchInjectionScope
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import kotlin.properties.Delegates
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.isActive
 import org.junit.Rule
@@ -719,4 +722,88 @@
         assertThat(availableOnPreFling).isEqualTo(consumedOnDragStop)
         assertThat(availableOnPostFling).isEqualTo(0f)
     }
+
+    @Test
+    fun multiPointerOnStopVelocity() {
+        val size = 200f
+        val middle = Offset(size / 2f, size / 2f)
+
+        var stopped = false
+        var lastVelocity = -1f
+        var touchSlop = 0f
+        var density: Density by Delegates.notNull()
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            density = LocalDensity.current
+            Box(
+                Modifier.size(with(density) { Size(size, size).toDpSize() })
+                    .nestedScrollDispatcher()
+                    .multiPointerDraggable(
+                        orientation = Orientation.Vertical,
+                        enabled = { true },
+                        startDragImmediately = { false },
+                        onDragStarted = { _, _, _ ->
+                            SimpleDragController(
+                                onDrag = { /* do nothing */ },
+                                onStop = {
+                                    stopped = true
+                                    lastVelocity = it
+                                },
+                            )
+                        },
+                        dispatcher = defaultDispatcher,
+                    )
+            )
+        }
+
+        var eventMillis: Long by Delegates.notNull()
+        rule.onRoot().performTouchInput { eventMillis = eventPeriodMillis }
+
+        fun swipeGesture(block: TouchInjectionScope.() -> Unit) {
+            stopped = false
+            rule.onRoot().performTouchInput {
+                down(middle)
+                block()
+                up()
+            }
+            assertThat(stopped).isEqualTo(true)
+        }
+
+        val shortDistance = touchSlop / 2f
+        swipeGesture {
+            moveBy(delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+            moveBy(delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+        }
+        assertThat(lastVelocity).isGreaterThan(0f)
+        assertThat(lastVelocity).isWithin(1f).of((shortDistance / eventMillis) * 1000f)
+
+        val longDistance = touchSlop * 4f
+        swipeGesture {
+            moveBy(delta = Offset(0f, longDistance), delayMillis = eventMillis)
+            moveBy(delta = Offset(0f, longDistance), delayMillis = eventMillis)
+        }
+        assertThat(lastVelocity).isGreaterThan(0f)
+        assertThat(lastVelocity).isWithin(1f).of((longDistance / eventMillis) * 1000f)
+
+        rule.onRoot().performTouchInput {
+            down(pointerId = 0, position = middle)
+            down(pointerId = 1, position = middle)
+            moveBy(pointerId = 0, delta = Offset(0f, longDistance), delayMillis = eventMillis)
+            moveBy(pointerId = 0, delta = Offset(0f, longDistance), delayMillis = eventMillis)
+            // The velocity should be:
+            // (longDistance / eventMillis) pixels/ms
+
+            // 1 pointer left, the second one
+            up(pointerId = 0)
+
+            // After a few events the velocity should be:
+            // (shortDistance / eventMillis) pixels/ms
+            repeat(10) {
+                moveBy(pointerId = 1, delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+            }
+            up(pointerId = 1)
+        }
+        assertThat(lastVelocity).isGreaterThan(0f)
+        assertThat(lastVelocity).isWithin(1f).of((shortDistance / eventMillis) * 1000f)
+    }
 }
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index ee75b31..541aebe 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -66,7 +66,7 @@
             <FrameLayout
                 android:id="@+id/status_bar_start_side_content"
                 android:layout_width="wrap_content"
-                android:layout_height="match_parent"
+                android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical|start"
                 android:clipChildren="false">
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 456265b..bef552c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -78,7 +78,27 @@
 
     private lateinit var battery: BatteryMeterView
     private lateinit var clock: Clock
-    private lateinit var statusContainer: View
+    private lateinit var startSideContainer: View
+    private lateinit var endSideContainer: View
+
+    private val iconsOnTouchListener =
+        object : View.OnTouchListener {
+            override fun onTouch(v: View, event: MotionEvent): Boolean {
+                // We want to handle only mouse events here to avoid stealing finger touches
+                // from status bar which expands shade when swiped down on. See b/326097469.
+                // We're using onTouchListener instead of onClickListener as the later will lead
+                // to isClickable being set to true and hence ALL touches always being
+                // intercepted. See [View.OnTouchEvent]
+                if (event.source == InputDevice.SOURCE_MOUSE) {
+                    if (event.action == MotionEvent.ACTION_UP) {
+                        v.performClick()
+                        shadeController.animateExpandShade()
+                    }
+                    return true
+                }
+                return false
+            }
+        }
 
     private val configurationListener =
         object : ConfigurationController.ConfigurationListener {
@@ -88,34 +108,10 @@
         }
 
     override fun onViewAttached() {
-        statusContainer = mView.requireViewById(R.id.system_icons)
         clock = mView.requireViewById(R.id.clock)
         battery = mView.requireViewById(R.id.battery)
-
         addDarkReceivers()
-
-        statusContainer.setOnHoverListener(
-            statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)
-        )
-        statusContainer.setOnTouchListener(
-            object : View.OnTouchListener {
-                override fun onTouch(v: View, event: MotionEvent): Boolean {
-                    // We want to handle only mouse events here to avoid stealing finger touches
-                    // from status bar which expands shade when swiped down on. See b/326097469.
-                    // We're using onTouchListener instead of onClickListener as the later will lead
-                    // to isClickable being set to true and hence ALL touches always being
-                    // intercepted. See [View.OnTouchEvent]
-                    if (event.source == InputDevice.SOURCE_MOUSE) {
-                        if (event.action == MotionEvent.ACTION_UP) {
-                            v.performClick()
-                            shadeController.animateExpandShade()
-                        }
-                        return true
-                    }
-                    return false
-                }
-            }
-        )
+        addCursorSupportToIconContainers()
 
         progressProvider?.setReadyToHandleTransition(true)
         configurationController.addCallback(configurationListener)
@@ -146,10 +142,25 @@
         }
     }
 
+    private fun addCursorSupportToIconContainers() {
+        endSideContainer = mView.requireViewById(R.id.system_icons)
+        endSideContainer.setOnHoverListener(
+            statusOverlayHoverListenerFactory.createDarkAwareListener(endSideContainer)
+        )
+        endSideContainer.setOnTouchListener(iconsOnTouchListener)
+
+        startSideContainer = mView.requireViewById(R.id.status_bar_start_side_content)
+        startSideContainer.setOnHoverListener(
+            statusOverlayHoverListenerFactory.createDarkAwareListener(startSideContainer)
+        )
+        startSideContainer.setOnTouchListener(iconsOnTouchListener)
+    }
+
     @VisibleForTesting
     public override fun onViewDetached() {
         removeDarkReceivers()
-        statusContainer.setOnHoverListener(null)
+        startSideContainer.setOnHoverListener(null)
+        endSideContainer.setOnHoverListener(null)
         progressProvider?.setReadyToHandleTransition(false)
         moveFromCenterAnimationController?.onViewDetached()
         configurationController.removeCallback(configurationListener)
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index dd4239c..556fae3 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -201,6 +201,15 @@
                     "include-filter": "com.android.server.wm.BackgroundActivityStart*"
                 }
             ]
+        },
+        {
+            "name": "CtsOsTestCases",
+            "file_patterns": ["StorageManagerService\\.java"],
+            "options": [
+                {
+                    "include-filter": "android.os.storage.cts.StorageManagerTest"
+                }
+            ]
         }
    ]
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 79e09d7..7442277 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1732,41 +1732,47 @@
                 // In the future, we can remove this logic for every notification here and add a
                 // callback so listeners know when their PhoneStateListener's subId becomes invalid,
                 // but for now we use the simplest fix.
-                if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
+                if (validatePhoneId(phoneId)) {
                     mServiceState[phoneId] = state;
 
-                    for (Record r : mRecords) {
-                        if (VDBG) {
-                            log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
-                                    + " phoneId=" + phoneId + " state=" + state);
-                        }
-                        if (r.matchTelephonyCallbackEvent(
-                                TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
-                                && idMatch(r, subId, phoneId)) {
+                    if (SubscriptionManager.isValidSubscriptionId(subId)) {
 
-                            try {
-                                ServiceState stateToSend;
-                                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
-                                    stateToSend = new ServiceState(state);
-                                } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
-                                    stateToSend = state.createLocationInfoSanitizedCopy(false);
-                                } else {
-                                    stateToSend = state.createLocationInfoSanitizedCopy(true);
+                        for (Record r : mRecords) {
+                            if (VDBG) {
+                                log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
+                                        + " phoneId=" + phoneId + " state=" + state);
+                            }
+                            if (r.matchTelephonyCallbackEvent(
+                                    TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
+                                    && idMatch(r, subId, phoneId)) {
+
+                                try {
+                                    ServiceState stateToSend;
+                                    if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                        stateToSend = new ServiceState(state);
+                                    } else if(checkCoarseLocationAccess(
+                                            r, Build.VERSION_CODES.Q)) {
+                                        stateToSend = state.createLocationInfoSanitizedCopy(false);
+                                    } else {
+                                        stateToSend = state.createLocationInfoSanitizedCopy(true);
+                                    }
+                                    if (DBG) {
+                                        log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
+                                                + " subId=" + subId + " phoneId=" + phoneId
+                                                + " state=" + stateToSend);
+                                    }
+                                    r.callback.onServiceStateChanged(stateToSend);
+                                } catch (RemoteException ex) {
+                                    mRemoveList.add(r.binder);
                                 }
-                                if (DBG) {
-                                    log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
-                                            + " subId=" + subId + " phoneId=" + phoneId
-                                            + " state=" + stateToSend);
-                                }
-                                r.callback.onServiceStateChanged(stateToSend);
-                            } catch (RemoteException ex) {
-                                mRemoveList.add(r.binder);
                             }
                         }
                     }
+                    else {
+                        log("notifyServiceStateForSubscriber: INVALID subId=" +subId);
+                    }
                 } else {
-                    log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
-                            + " or subId=" + subId);
+                    log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
                 }
                 handleRemoveListLocked();
             }
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index 6db5f82..27cc923 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -75,6 +75,7 @@
 @Presubmit
 @RunWith(JUnit4.class)
 public class PerfettoProtoLogImplTest {
+    private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog";
     private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation()
             .getTargetContext().getFilesDir();
 
@@ -91,6 +92,7 @@
             new TraceConfig(false, true, false)
     );
 
+    private ProtoLogConfigurationService mProtoLogConfigurationService;
     private PerfettoProtoLogImpl mProtoLog;
     private Protolog.ProtoLogViewerConfig.Builder mViewerConfigBuilder;
     private File mFile;
@@ -162,9 +164,15 @@
 
         mCacheUpdater = () -> {};
         mReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
+
+        final ProtoLogDataSourceBuilder dataSourceBuilder =
+                (onStart, onFlush, onStop) -> new ProtoLogDataSource(
+                        onStart, onFlush, onStop, TEST_PROTOLOG_DATASOURCE_NAME);
+        mProtoLogConfigurationService =
+                new ProtoLogConfigurationService(dataSourceBuilder);
         mProtoLog = new PerfettoProtoLogImpl(
-                viewerConfigInputStreamProvider, mReader,
-                () -> mCacheUpdater.run(), TestProtoLogGroup.values());
+                viewerConfigInputStreamProvider, mReader, () -> mCacheUpdater.run(),
+                TestProtoLogGroup.values(), dataSourceBuilder, mProtoLogConfigurationService);
     }
 
     @After
@@ -183,8 +191,9 @@
 
     @Test
     public void isEnabled_returnsTrueAfterStart() {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             assertTrue(mProtoLog.isProtoEnabled());
@@ -195,8 +204,9 @@
 
     @Test
     public void isEnabled_returnsFalseAfterStop() {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             assertTrue(mProtoLog.isProtoEnabled());
@@ -209,8 +219,9 @@
 
     @Test
     public void defaultMode() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             // Shouldn't be logging anything except WTF unless explicitly requested in the group
@@ -238,11 +249,13 @@
 
     @Test
     public void respectsOverrideConfigs_defaultMode() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(
+                        true,
                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
-                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)))
-                        .build();
+                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)),
+                        TEST_PROTOLOG_DATASOURCE_NAME
+                ).build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -273,10 +286,12 @@
     @Test
     public void respectsOverrideConfigs_allEnabledMode() throws IOException {
         PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+                PerfettoTraceMonitor.newBuilder().enableProtoLog(
+                        true,
                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
-                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)))
-                        .build();
+                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)),
+                        TEST_PROTOLOG_DATASOURCE_NAME
+                    ).build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -304,9 +319,9 @@
 
     @Test
     public void respectsAllEnabledMode() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true, List.of())
-                        .build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -405,8 +420,9 @@
                 ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
                 "My test message :: %s, %d, %o, %x, %f, %e, %g, %b");
 
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         long before;
         long after;
         try {
@@ -437,8 +453,9 @@
 
     @Test
     public void log_noProcessing() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         long before;
         long after;
         try {
@@ -469,8 +486,9 @@
 
     @Test
     public  void supportsLocationInformation() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true).build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -504,8 +522,9 @@
         final long messageHash = addMessageToConfig(
                 ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
                 "My test message :: %s, %d, %f, %b");
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         long before;
         long after;
         try {
@@ -526,8 +545,9 @@
 
     @Test
     public void log_protoDisabled() throws Exception {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -544,12 +564,14 @@
 
     @Test
     public void stackTraceTrimmed() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(
+                        true,
                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
                                 TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
-                                true)))
-                        .build();
+                                true)),
+                        TEST_PROTOLOG_DATASOURCE_NAME
+                ).build();
         try {
             traceMonitor.start();
 
@@ -579,18 +601,18 @@
         final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0);
         mCacheUpdater = cacheUpdateCallCount::incrementAndGet;
 
-        PerfettoTraceMonitor traceMonitor1 =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
-                                List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
-                                        TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
-                                        false)))
-                        .build();
+        PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true,
+                        List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
+                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
+                                false)), TEST_PROTOLOG_DATASOURCE_NAME
+                ).build();
 
         PerfettoTraceMonitor traceMonitor2 =
                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
-                                        false)))
+                                        false)), TEST_PROTOLOG_DATASOURCE_NAME)
                         .build();
 
         Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(0);
@@ -635,14 +657,14 @@
                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
-                                        false)))
+                                        false)), TEST_PROTOLOG_DATASOURCE_NAME)
                         .build();
 
         PerfettoTraceMonitor traceMonitor2 =
                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
-                                        false)))
+                                        false)), TEST_PROTOLOG_DATASOURCE_NAME)
                         .build();
 
         try {
@@ -712,9 +734,9 @@
 
     @Test
     public void supportsNullString() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
-                        .build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
 
         try {
             traceMonitor.start();
@@ -735,9 +757,9 @@
 
     @Test
     public void supportNullParams() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
-                        .build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
 
         try {
             traceMonitor.start();
@@ -758,13 +780,13 @@
 
     @Test
     public void handlesConcurrentTracingSessions() throws IOException {
-        PerfettoTraceMonitor traceMonitor1 =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
-                        .build();
+        PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
 
-        PerfettoTraceMonitor traceMonitor2 =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
-                        .build();
+        PerfettoTraceMonitor traceMonitor2 = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
 
         final ResultWriter writer2 = new ResultWriter()
                 .forScenario(new ScenarioBuilder()
@@ -800,8 +822,9 @@
 
     @Test
     public void usesDefaultLogFromLevel() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(LogLevel.WARN).build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(LogLevel.WARN, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,