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

Commit 3461cc34 authored by Liran Binyamin's avatar Liran Binyamin
Browse files

Log bubble entry points

Fixes: 438484301
Flag: NONE no behavior change -- just logging
Test: atest BubbleControllerTest
Change-Id: If9508e8fa81eb8dd3cd9c7fee6a0f24bfb740a31
parent cb6e6783
Loading
Loading
Loading
Loading
+95 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.bubbles

import android.app.ActivityManager
import android.app.Notification
import android.app.TaskInfo
import android.content.ComponentName
import android.content.Context
@@ -34,6 +35,8 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.SetFlagsRule
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.view.IWindowManager
import android.view.InsetsSource
import android.view.InsetsState
@@ -62,6 +65,9 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener
import com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_CREATED_FROM_NOTIF
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_CREATED_FROM_NOTIF_BUBBLE_BUTTON
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_CREATED_FROM_ALL_APPS_ICON_MENU
import com.android.wm.shell.bubbles.logging.BubbleSessionTracker
import com.android.wm.shell.bubbles.logging.BubbleSessionTrackerImpl
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
@@ -80,6 +86,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.shared.TransactionPool
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.shared.bubbles.DeviceConfig
import com.android.wm.shell.shared.bubbles.logging.EntryPoint
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -907,6 +914,94 @@ class BubbleControllerTest(flags: FlagsParameterization) {
            .that(overflowBubble.isInflated).isFalse()
    }

    @Test
    fun bubbleCreatedFromNotification_shouldLogEntryPoint() {
        bubbleController.asBubbles().onEntryAdded(createBubbleEntry(pkgName = "package.name"))
        mainExecutor.flushAll()

        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
        val log = uiEventLoggerFake.logs.first()
        assertThat(log.packageName).isEqualTo("package.name")
        assertThat(log.eventId).isEqualTo(BUBBLE_CREATED_FROM_NOTIF.id)
    }

    @Test
    fun bubbleCreatedFromNotificationButton_shouldLogEntryPoint() {
        bubbleController.asBubbles().onEntryUpdated(
            createBubbleEntry(pkgName = "package.name"),
            /* shouldBubbleUp= */ true,
            /* fromSystem= */ true
        )
        mainExecutor.flushAll()

        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
        val log = uiEventLoggerFake.logs.first()
        assertThat(log.packageName).isEqualTo("package.name")
        assertThat(log.eventId).isEqualTo(BUBBLE_CREATED_FROM_NOTIF_BUBBLE_BUTTON.id)
    }

    @Test
    fun bubbleNotificationUpdated_shouldNotLogEntryPoint() {
        val bubble = createBubble("bubble-key")
        getInstrumentation().runOnMainSync {
            bubbleController.inflateAndAdd(
                bubble,
                /* suppressFlyout= */ true,
                /* showInShade= */ true
            )
        }
        bubbleController.asBubbles().onEntryUpdated(
            createBubbleEntry(bubbleKey = "bubble-key", pkgName = "package.name"),
            /* shouldBubbleUp= */ true,
            /* fromSystem= */ true
        )
        mainExecutor.flushAll()

        assertThat(uiEventLoggerFake.logs).isEmpty()
    }

    @EnableFlags(FLAG_ENABLE_CREATE_ANY_BUBBLE)
    @Test
    fun expandStackAndSelectBubble_shouldLogEntryPoint() {
        val intent = Intent().apply {
            setPackage("package.name")
        }
        getInstrumentation().runOnMainSync {
            bubbleController.expandStackAndSelectBubble(
                intent,
                UserHandle.of(0),
                EntryPoint.ALL_APPS_ICON_MENU,
                /* bubbleBarLocation= */ null
            )
        }

        assertThat(uiEventLoggerFake.logs).isNotEmpty()
        val log = uiEventLoggerFake.logs.first()
        assertThat(log.packageName).isEqualTo("package.name")
        assertThat(log.eventId).isEqualTo(BUBBLE_CREATED_FROM_ALL_APPS_ICON_MENU.id)
    }

    private fun createBubbleEntry(bubbleKey: String = "key", pkgName: String): BubbleEntry {
        val notif =
            Notification.Builder(context)
                .setBubbleMetadata(Notification.BubbleMetadata.Builder("shortcutId").build())
                .setFlag(Notification.FLAG_BUBBLE, true)
                .build()
        val sbn = mock<StatusBarNotification>().stub {
            on { key } doReturn bubbleKey
            on { packageName } doReturn pkgName
            on { notification } doReturn notif
        }
        return BubbleEntry(
            sbn,
            mock<NotificationListenerService.Ranking>(),
            /* isDismissable= */ false,
            /* shouldSuppressNotificationDot= */ true,
            /* shouldSuppressNotificationList= */ true,
            /* shouldSuppressPeek= */ true
        )
    }

    private fun createBubble(key: String, taskId: Int = 0): Bubble {
        val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
        val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
+2 −0
Original line number Diff line number Diff line
@@ -28,4 +28,6 @@ enum class EntryPoint : Parcelable {
    HOTSEAT_ICON_MENU,
    TASKBAR_ICON_DRAG,
    ALL_APPS_ICON_DRAG,
    NOTIFICATION,
    NOTIFICATION_BUBBLE_BUTTON,
}
+16 −5
Original line number Diff line number Diff line
@@ -1726,7 +1726,7 @@ public class BubbleController implements ConfigurationChangeListener,
                bubbleBarLocation == null
                        ? null
                        : new UpdateLocationRequest(bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
        expandStackAndSelectAppBubble(b, updateLocationRequest);
        expandStackAndSelectAppBubble(b, entryPoint, updateLocationRequest);
    }

    /**
@@ -1747,7 +1747,7 @@ public class BubbleController implements ConfigurationChangeListener,
                bubbleBarLocation == null
                        ? null
                        : new UpdateLocationRequest(bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
        expandStackAndSelectAppBubble(b, updateLocationRequest);
        expandStackAndSelectAppBubble(b, entryPoint, updateLocationRequest);
    }

    /**
@@ -1766,16 +1766,20 @@ public class BubbleController implements ConfigurationChangeListener,
                bubbleBarLocation == null
                        ? null
                        : new UpdateLocationRequest(bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
        expandStackAndSelectAppBubble(b, updateLocationRequest);
        expandStackAndSelectAppBubble(b, entryPoint, updateLocationRequest);
    }

    void expandStackAndSelectAppBubble(Bubble b) {
        expandStackAndSelectAppBubble(b, /* updateLocationRequest= */ null);
        expandStackAndSelectAppBubble(b, /* entryPoint= */ null, /* updateLocationRequest= */ null);
    }

    void expandStackAndSelectAppBubble(Bubble b,
    private void expandStackAndSelectAppBubble(Bubble b,
            @Nullable EntryPoint entryPoint,
            @Nullable UpdateLocationRequest updateLocationRequest) {
        if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
        if (entryPoint != null) {
            mLogger.logEntryPoint(isShowingAsBubbleBar(), entryPoint, b.getPackageName());
        }
        BubbleBarLocation location =
                isShowingAsBubbleBar() && updateLocationRequest != null
                        ? updateLocationRequest.getLocation()
@@ -2367,6 +2371,8 @@ public class BubbleController implements ConfigurationChangeListener,

    private void onEntryAdded(BubbleEntry entry) {
        if (canLaunchInTaskView(mContext, entry)) {
            mLogger.logEntryPoint(isShowingAsBubbleBar(), EntryPoint.NOTIFICATION,
                    entry.getStatusBarNotification().getPackageName());
            updateBubble(entry);
        }
    }
@@ -2382,6 +2388,11 @@ public class BubbleController implements ConfigurationChangeListener,
            // It was previously a bubble but no longer a bubble -- lets remove it
            removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
        } else if (shouldBubble && entry.isBubble()) {
            if (!mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
                // only log the entry point if this is a newly promoted bubble
                mLogger.logEntryPoint(isShowingAsBubbleBar(), EntryPoint.NOTIFICATION_BUBBLE_BUTTON,
                        entry.getStatusBarNotification().getPackageName());
            }
            updateBubble(entry);
        }
    }
+12 −0
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.bubbles.logging.BubbleLoggerExt;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.shared.bubbles.logging.EntryPoint;

import javax.inject.Inject;

@@ -268,6 +270,16 @@ public class BubbleLogger {
        mUiEventLogger.logWithInstanceId(e, /* uid= */ 0, packageName, sessionId);
    }

    /** Logs a UiEvent for the bubble entry point. */
    public void logEntryPoint(boolean isBubbleBar, EntryPoint entryPoint, String packageName) {
        UiEventLogger.UiEventEnum e = isBubbleBar
                ? BubbleLoggerExt.toBubbleBarUiEvent(entryPoint)
                : BubbleLoggerExt.toFloatingBubblesUiEvent(entryPoint);
        if (e != null) {
            mUiEventLogger.log(e, /* uid= */ 0, packageName);
        }
    }

    /**
     * Log when a bubble is removed from overflow in stack view
     *
+67 −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.
 */

@file:JvmName("BubbleLoggerExt")

package com.android.wm.shell.bubbles.logging

import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_CREATED_FROM_ALL_APPS_ICON_DRAG
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_CREATED_FROM_ALL_APPS_ICON_MENU
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_CREATED_FROM_HOTSEAT_ICON_MENU
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_CREATED_FROM_LAUNCHER_ICON_MENU
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_CREATED_FROM_NOTIF
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_CREATED_FROM_NOTIF_BUBBLE_BUTTON
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_CREATED_FROM_TASKBAR_ICON_MENU
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_BAR_CREATED_FROM_TASKBAR_ICON_DRAG
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_CREATED_FROM_ALL_APPS_ICON_MENU
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_CREATED_FROM_HOTSEAT_ICON_MENU
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_CREATED_FROM_LAUNCHER_ICON_MENU
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_CREATED_FROM_NOTIF
import com.android.wm.shell.bubbles.BubbleLogger.Event.BUBBLE_CREATED_FROM_NOTIF_BUBBLE_BUTTON
import com.android.wm.shell.shared.bubbles.logging.EntryPoint
import com.android.wm.shell.shared.bubbles.logging.EntryPoint.ALL_APPS_ICON_DRAG
import com.android.wm.shell.shared.bubbles.logging.EntryPoint.ALL_APPS_ICON_MENU
import com.android.wm.shell.shared.bubbles.logging.EntryPoint.HOTSEAT_ICON_MENU
import com.android.wm.shell.shared.bubbles.logging.EntryPoint.LAUNCHER_ICON_MENU
import com.android.wm.shell.shared.bubbles.logging.EntryPoint.NOTIFICATION
import com.android.wm.shell.shared.bubbles.logging.EntryPoint.NOTIFICATION_BUBBLE_BUTTON
import com.android.wm.shell.shared.bubbles.logging.EntryPoint.TASKBAR_ICON_DRAG
import com.android.wm.shell.shared.bubbles.logging.EntryPoint.TASKBAR_ICON_MENU

/** Converts the [EntryPoint] to a bubble bar UiEvent. */
fun EntryPoint.toBubbleBarUiEvent() = when (this) {
    TASKBAR_ICON_MENU -> BUBBLE_BAR_CREATED_FROM_TASKBAR_ICON_MENU
    LAUNCHER_ICON_MENU -> BUBBLE_BAR_CREATED_FROM_LAUNCHER_ICON_MENU
    ALL_APPS_ICON_MENU -> BUBBLE_BAR_CREATED_FROM_ALL_APPS_ICON_MENU
    HOTSEAT_ICON_MENU -> BUBBLE_BAR_CREATED_FROM_HOTSEAT_ICON_MENU
    TASKBAR_ICON_DRAG -> BUBBLE_BAR_CREATED_FROM_TASKBAR_ICON_DRAG
    ALL_APPS_ICON_DRAG -> BUBBLE_BAR_CREATED_FROM_ALL_APPS_ICON_DRAG
    NOTIFICATION -> BUBBLE_BAR_CREATED_FROM_NOTIF
    NOTIFICATION_BUBBLE_BUTTON -> BUBBLE_BAR_CREATED_FROM_NOTIF_BUBBLE_BUTTON
}

/** Converts the [EntryPoint] to a floating bubbles UiEvent. */
fun EntryPoint.toFloatingBubblesUiEvent() = when (this) {
    LAUNCHER_ICON_MENU -> BUBBLE_CREATED_FROM_LAUNCHER_ICON_MENU
    ALL_APPS_ICON_MENU -> BUBBLE_CREATED_FROM_ALL_APPS_ICON_MENU
    HOTSEAT_ICON_MENU -> BUBBLE_CREATED_FROM_HOTSEAT_ICON_MENU
    NOTIFICATION -> BUBBLE_CREATED_FROM_NOTIF
    NOTIFICATION_BUBBLE_BUTTON -> BUBBLE_CREATED_FROM_NOTIF_BUBBLE_BUTTON
    // the events below are only applicable to bubble bar
    ALL_APPS_ICON_DRAG,
    TASKBAR_ICON_DRAG,
    TASKBAR_ICON_MENU -> null
}