Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5dc9a81c authored by Liran Binyamin's avatar Liran Binyamin Committed by Android (Google) Code Review
Browse files

Merge changes I3339d02b,Ie5f34d29,I8713e810 into main

* changes:
  Log bubble session events
  Add bubble session events
  Create BubbleSessionTrackerImpl
parents 3e271bc4 3e69cf98
Loading
Loading
Loading
Loading
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.wm.shell.bubbles.logging

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.InstanceIdSequence
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_SESSION_ENDED
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_SESSION_STARTED
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_SESSION_ENDED
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_SESSION_STARTED
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

/** Unit tests for [BubbleSessionTrackerImpl]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleSessionTrackerImplTest {

    private val instanceIdSequence = FakeInstanceIdSequence()
    private val uiEventLoggerFake = UiEventLoggerFake()
    private val bubbleLogger = BubbleLogger(uiEventLoggerFake)
    private val bubbleSessionTracker = BubbleSessionTrackerImpl(instanceIdSequence, bubbleLogger)

    @Before
    fun setUp() {
        ProtoLog.REQUIRE_PROTOLOGTOOL = false
        ProtoLog.init()
    }

    @Test
    fun startSession_logsNewSessionId() {
        bubbleSessionTracker.startBubbleBar()
        bubbleSessionTracker.stopBubbleBar()
        bubbleSessionTracker.startBubbleBar()

        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(3)
        val firstSessionStart = uiEventLoggerFake.logs.first()
        val secondSessionStart = uiEventLoggerFake.logs.last()
        assertThat(firstSessionStart.eventId).isEqualTo(BUBBLE_BAR_SESSION_STARTED.id)
        assertThat(secondSessionStart.eventId).isEqualTo(BUBBLE_BAR_SESSION_STARTED.id)
        assertThat(firstSessionStart.instanceId).isNotEqualTo(secondSessionStart.instanceId)
    }

    @Test
    fun endSession_logsSameSessionId() {
        bubbleSessionTracker.startBubbleBar()
        bubbleSessionTracker.stopBubbleBar()

        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2)
        val sessionStart = uiEventLoggerFake.logs.first()
        val sessionEnd = uiEventLoggerFake.logs.last()
        assertThat(sessionStart.eventId).isEqualTo(BUBBLE_BAR_SESSION_STARTED.id)
        assertThat(sessionEnd.eventId).isEqualTo(BUBBLE_BAR_SESSION_ENDED.id)
        assertThat(sessionStart.instanceId).isEqualTo(sessionEnd.instanceId)
    }

    @Test
    fun logCorrectEventId() {
        bubbleSessionTracker.startBubbleBar()
        bubbleSessionTracker.stopBubbleBar()
        bubbleSessionTracker.startFloating()
        bubbleSessionTracker.stopFloating()

        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(4)
        assertThat(uiEventLoggerFake.logs.map { it.eventId })
            .containsExactly(
                BUBBLE_BAR_SESSION_STARTED.id,
                BUBBLE_BAR_SESSION_ENDED.id,
                BUBBLE_SESSION_STARTED.id,
                BUBBLE_SESSION_ENDED.id
            )
            .inOrder()
    }

    @Test
    fun stopSession_noActiveSession_shouldNotLog() {
        bubbleSessionTracker.stopBubbleBar()

        assertThat(uiEventLoggerFake.logs).isEmpty()
    }

    class FakeInstanceIdSequence : InstanceIdSequence(/* instanceIdMax= */ 10) {

        var id = -1
            private set

        override fun newInstanceId(): InstanceId {
            id = if (id == -1 || id == 10) 1 else id + 1
            return InstanceId.fakeInstanceId(id)
        }
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.bubbles;

import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
@@ -68,6 +69,12 @@ public class BubbleLogger {
        @UiEvent(doc = "Restore bubble to overflow after phone reboot.")
        BUBBLE_OVERFLOW_RECOVER(691),

        @UiEvent(doc = "Bubble session started.")
        BUBBLE_SESSION_STARTED(2422),

        @UiEvent(doc = "Bubble session ended.")
        BUBBLE_SESSION_ENDED(2423),

        // endregion

        // region bubble bar events
@@ -162,6 +169,12 @@ public class BubbleLogger {
        @UiEvent(doc = "bubble bar moved to the right edge of the screen by dragging a task")
        BUBBLE_BAR_MOVED_RIGHT_DRAG_TASK(2147),

        @UiEvent(doc = "Bubble Bar session started.")
        BUBBLE_BAR_SESSION_STARTED(2424),

        @UiEvent(doc = "Bubble Bar session ended.")
        BUBBLE_BAR_SESSION_ENDED(2425),

        // endregion
        ;

@@ -196,6 +209,11 @@ public class BubbleLogger {
        mUiEventLogger.logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
    }

    /** Log a UiEvent for the specified {@code sessionId}. */
    public void logWithSessionId(UiEventLogger.UiEventEnum e, InstanceId sessionId) {
        mUiEventLogger.log(e, sessionId);
    }

    /**
     * Log when a bubble is removed from overflow in stack view
     *
+37 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.wm.shell.bubbles.logging

/**
 * Keeps track of the current Bubble session.
 *
 * Bubble sessions start when the stack expands and end when the stack collapses.
 */
interface BubbleSessionTracker {

    /** Starts tracking a new bubble bar session. */
    fun startBubbleBar()

    /** Stops tracking the current bubble bar session. */
    fun stopBubbleBar()

    /** Starts tracking a new floating bubble session. */
    fun startFloating()

    /** Stops tracking the current floating bubble session. */
    fun stopFloating()
}
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.wm.shell.bubbles.logging

import com.android.internal.logging.InstanceId
import com.android.internal.logging.InstanceIdSequence
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubbleLogger.Event
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_SESSION_ENDED
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_SESSION_STARTED
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_SESSION_ENDED
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_SESSION_STARTED
import com.android.wm.shell.dagger.Bubbles
import com.android.wm.shell.dagger.WMSingleton
import com.android.wm.shell.protolog.ShellProtoLogGroup
import javax.inject.Inject

/**
 * Keeps track of the current bubble session and logs when sessions start and end.
 *
 * Sessions are identified using an [InstanceId].
 */
@WMSingleton
class BubbleSessionTrackerImpl
@Inject
constructor(
    @param:Bubbles private val instanceIdSequence: InstanceIdSequence,
    private val logger: BubbleLogger
) : BubbleSessionTracker {

    private var currentSession: InstanceId? = null

    override fun startBubbleBar() {
        start(BUBBLE_BAR_SESSION_STARTED)
    }

    override fun startFloating() {
        start(BUBBLE_SESSION_STARTED)
    }

    private fun start(event: Event) {
        if (currentSession != null) {
            ProtoLog.d(
                ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY,
                "BubbleSessionTracker: starting to track a new session. " +
                    "previous session still active"
            )
        }
        val sessionId = instanceIdSequence.newInstanceId()
        logger.logWithSessionId(event, sessionId)
        currentSession = sessionId
    }

    override fun stopBubbleBar() {
        stop(BUBBLE_BAR_SESSION_ENDED)
    }

    override fun stopFloating() {
        stop(BUBBLE_SESSION_ENDED)
    }

    fun stop(event: Event) {
        val sessionId = currentSession
        if (sessionId == null) {
            ProtoLog.d(
                ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY,
                "BubbleSessionTracker: session tracking stopped but current session is null"
            )
            return
        }
        logger.logWithSessionId(event, sessionId)
        currentSession = null
    }
}