diff options
3 files changed, 391 insertions, 1 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt new file mode 100644 index 000000000000..95d47146e834 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -0,0 +1,218 @@ +/* + * 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.wm.shell.desktopmode + +import com.android.internal.util.FrameworkStatsLog +import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.util.KtProtoLog + +/** + * Event logger for logging desktop mode session events + */ +class DesktopModeEventLogger { + /** + * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for + * entering desktop mode + */ + fun logSessionEnter(sessionId: Int, enterReason: EnterReason) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging session enter, session: %s reason: %s", + sessionId, enterReason.name + ) + FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID, + /* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER, + /* enterReason */ enterReason.reason, + /* exitReason */ 0, + /* session_id */ sessionId) + } + + /** + * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for + * exiting desktop mode + */ + fun logSessionExit(sessionId: Int, exitReason: ExitReason) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging session exit, session: %s reason: %s", + sessionId, exitReason.name + ) + FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID, + /* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT, + /* enterReason */ 0, + /* exitReason */ exitReason.reason, + /* session_id */ sessionId) + } + + /** + * Logs that the task with update [taskUpdate] was added in the desktop mode session having + * session id [sessionId] + */ + fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task added, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + /** + * Logs that the task with update [taskUpdate] was removed in the desktop mode session having + * session id [sessionId] + */ + fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task remove, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + /** + * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session + * having session id [sessionId] + */ + fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task info changed, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + companion object { + data class TaskUpdate( + val instanceId: Int, + val uid: Int, + val taskHeight: Int = Int.MIN_VALUE, + val taskWidth: Int = Int.MIN_VALUE, + val taskX: Int = Int.MIN_VALUE, + val taskY: Int = Int.MIN_VALUE, + ) + + /** + * Enum EnterReason mapped to the EnterReason definition in + * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto + */ + enum class EnterReason(val reason: Int) { + UNKNOWN_ENTER( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER + ), + OVERVIEW( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__OVERVIEW + ), + APP_HANDLE_DRAG( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_DRAG + ), + APP_HANDLE_MENU_BUTTON( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_MENU_BUTTON + ), + APP_FREEFORM_INTENT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FREEFORM_INTENT + ), + KEYBOARD_SHORTCUT_ENTER( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER + ), + SCREEN_ON( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON + ); + } + + /** + * Enum ExitReason mapped to the ExitReason definition in + * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto + */ + enum class ExitReason(val reason: Int) { + UNKNOWN_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT + ), + DRAG_TO_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT + ), + APP_HANDLE_MENU_BUTTON_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__APP_HANDLE_MENU_BUTTON_EXIT + ), + KEYBOARD_SHORTCUT_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT + ), + RETURN_HOME_OR_OVERVIEW( + FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME + ), + TASK_FINISHED( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED + ), + SCREEN_OFF( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF + ) + } + + private const val DESKTOP_MODE_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED + private const val DESKTOP_MODE_TASK_UPDATE_ATOM_ID = + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index 8c4711603904..32c070305e05 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -24,7 +24,10 @@ package { android_test { name: "WMShellUnitTests", - + defaults: [ + // For ExtendedMockito dependencies. + "modules-utils-testable-device-config-defaults", + ], srcs: [ "**/*.java", "**/*.kt", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt new file mode 100644 index 000000000000..4548fcb06c55 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -0,0 +1,169 @@ +/* + * 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.wm.shell.desktopmode + +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.internal.util.FrameworkStatsLog +import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.kotlin.eq + +/** + * Tests for [DesktopModeEventLogger]. + */ +class DesktopModeEventLoggerTest { + + private val desktopModeEventLogger = DesktopModeEventLogger() + + @JvmField + @Rule + val extendedMockitoRule = ExtendedMockitoRule.Builder(this) + .mockStatic(FrameworkStatsLog::class.java).build()!! + + @Test + fun logSessionEnter_enterReason() = runBlocking { + desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER) + + ExtendedMockito.verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), + /* enter_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER), + /* exit_reason */ + eq(0), + /* sessionId */ + eq(SESSION_ID) + ) + } + } + + @Test + fun logSessionExit_exitReason() = runBlocking { + desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT) + + ExtendedMockito.verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT), + /* enter_reason */ + eq(0), + /* exit_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT), + /* sessionId */ + eq(SESSION_ID) + ) + } + } + + @Test + fun logTaskAdded_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + @Test + fun logTaskRemoved_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + @Test + fun logTaskInfoChanged_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + companion object { + private const val SESSION_ID = 1 + private const val TASK_ID = 1 + private const val TASK_UID = 1 + private const val TASK_X = 0 + private const val TASK_Y = 0 + private const val TASK_HEIGHT = 100 + private const val TASK_WIDTH = 100 + + private val TASK_UPDATE = TaskUpdate( + TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y + ) + } +}
\ No newline at end of file |