Add position and dimentions logging to DesktopModeTaskUpdate logs

Bug: 326231724
Flag: EXEMPT not a major change
Test: Tested locally, added unit tests
Change-Id: I0fdf3c833680f6859c8236cb02288c20459328d4
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
index 9192e6e..fbc11c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -159,13 +159,24 @@
     }
 
     companion object {
+        /**
+         * Describes a task position and dimensions.
+         *
+         * @property instanceId instance id of the task
+         * @property uid uid of the app associated with the task
+         * @property taskHeight height of the task in px
+         * @property taskWidth width of the task in px
+         * @property taskX x-coordinate of the top-left corner
+         * @property taskY y-coordinate of the top-left corner
+         *
+         */
         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,
+            val taskHeight: Int,
+            val taskWidth: Int,
+            val taskX: Int,
+            val taskY: Int,
         )
 
         /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 641952b..42ed988 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -228,6 +228,7 @@
      * Log the appropriate log event based on the new state of TasksInfos and previously cached
      * state and update it
      */
+    // TODO(b/326231724): Trigger logging when task size or position is changed.
     private fun identifyLogEventAndUpdateState(
         transitionInfo: TransitionInfo,
         preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
@@ -282,7 +283,6 @@
         visibleFreeformTaskInfos.putAll(postTransitionVisibleFreeformTasks)
     }
 
-    // TODO(b/326231724) - Add logging around taskInfoChanges Updates
     /** Compare the old and new state of taskInfos and identify and log the changes */
     private fun identifyAndLogTaskUpdates(
         sessionId: Int,
@@ -304,13 +304,17 @@
         }
     }
 
-    // TODO(b/326231724: figure out how to get taskWidth and taskHeight from TaskInfo
     private fun buildTaskUpdateForTask(taskInfo: TaskInfo): TaskUpdate {
-        val taskUpdate = TaskUpdate(taskInfo.taskId, taskInfo.userId)
-        // add task x, y if available
-        taskInfo.positionInParent?.let { taskUpdate.copy(taskX = it.x, taskY = it.y) }
-
-        return taskUpdate
+        val screenBounds = taskInfo.configuration.windowConfiguration.bounds
+        val positionInParent = taskInfo.positionInParent
+        return TaskUpdate(
+            instanceId = taskInfo.taskId,
+            uid = taskInfo.userId,
+            taskHeight = screenBounds.height(),
+            taskWidth = screenBounds.width(),
+            taskX = positionInParent.x,
+            taskY = positionInParent.y,
+        )
     }
 
     /** Get [EnterReason] for this session enter */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index fb03f20..cead21b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -19,6 +19,8 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.content.Context
+import android.graphics.Point
+import android.graphics.Rect
 import android.os.IBinder
 import android.testing.AndroidTestingRunner
 import android.view.SurfaceControl
@@ -36,11 +38,12 @@
 import android.window.TransitionInfo.Change
 import android.window.WindowContainerToken
 import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.ShellExecutor
 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 com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
@@ -53,22 +56,23 @@
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
-import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertNotNull
+import junit.framework.Assert.assertNull
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.verify
 import org.mockito.kotlin.any
 import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.same
+import org.mockito.kotlin.spy
 import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
 
 /**
  * Test class for {@link DesktopModeLoggerTransitionObserver}
@@ -77,20 +81,19 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
-class DesktopModeLoggerTransitionObserverTest {
+class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
 
   @JvmField
   @Rule
   val extendedMockitoRule =
-      ExtendedMockitoRule.Builder(this)
-          .mockStatic(DesktopModeEventLogger::class.java)
-          .mockStatic(DesktopModeStatus::class.java)
-          .build()!!
+    ExtendedMockitoRule.Builder(this)
+            .mockStatic(DesktopModeStatus::class.java)
+            .build()!!
 
-  @Mock lateinit var testExecutor: ShellExecutor
-  @Mock private lateinit var mockShellInit: ShellInit
-  @Mock private lateinit var transitions: Transitions
-  @Mock private lateinit var context: Context
+  private val testExecutor = mock<ShellExecutor>()
+  private val mockShellInit = mock<ShellInit>()
+  private val transitions = mock<Transitions>()
+  private val context = mock<Context>()
 
   private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
   private lateinit var shellInit: ShellInit
@@ -98,9 +101,9 @@
 
   @Before
   fun setup() {
-    doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
-    shellInit = Mockito.spy(ShellInit(testExecutor))
-    desktopModeEventLogger = mock(DesktopModeEventLogger::class.java)
+    whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+    shellInit = spy(ShellInit(testExecutor))
+    desktopModeEventLogger = mock<DesktopModeEventLogger>()
 
     transitionObserver =
         DesktopModeLoggerTransitionObserver(
@@ -121,7 +124,7 @@
 
   @Test
   fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
-    val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
 
     callOnTransitionReady(transitionInfo)
@@ -132,22 +135,17 @@
 
   @Test
   fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
-    val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_FREEFORM_INTENT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     // task change is finalised when drag ends
     val transitionInfo =
         TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
@@ -155,82 +153,57 @@
             .build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
             .addChange(change)
             .build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_MENU_BUTTON, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonAppFromOverview() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
             .addChange(change)
             .build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
             .addChange(change)
             .build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.KEYBOARD_SHORTCUT_ENTER, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitToFront_logTaskAddedAndEnterReasonOverview() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
   }
 
   @Test
@@ -238,35 +211,29 @@
     // previous exit to overview transition
     val previousSessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+    transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
     transitionObserver.setLoggerSessionId(previousSessionId)
-    val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-            .addChange(previousChange)
+            .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
             .build()
 
     callOnTransitionReady(previousTransitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+    verifyTaskRemovedAndExitLogging(
+      previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
 
     // TRANSIT_TO_FRONT
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
   }
 
   @Test
@@ -274,35 +241,29 @@
     // previous exit to overview transition
     val previousSessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+    transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
     transitionObserver.setLoggerSessionId(previousSessionId)
-    val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-            .addChange(previousChange)
+            .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
             .build()
 
     callOnTransitionReady(previousTransitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+    verifyTaskRemovedAndExitLogging(
+      previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
 
     // TRANSIT_CHANGE
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_CHANGE, 0).addChange(change).build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
   }
 
   @Test
@@ -310,35 +271,29 @@
     // previous exit to overview transition
     val previousSessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+    transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
     transitionObserver.setLoggerSessionId(previousSessionId)
-    val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-            .addChange(previousChange)
+            .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
             .build()
 
     callOnTransitionReady(previousTransitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+    verifyTaskRemovedAndExitLogging(
+      previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
 
     // TRANSIT_OPEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
   }
 
   @Test
@@ -349,243 +304,202 @@
     // previous exit to overview transition
     val previousSessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+    transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
     transitionObserver.setLoggerSessionId(previousSessionId)
-    val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
     val previousTransitionInfo =
       TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-              .addChange(previousChange)
+              .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
               .build()
 
     callOnTransitionReady(previousTransitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
-    verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+    verifyTaskRemovedAndExitLogging(
+      previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
 
     // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo =
       TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
               .addChange(change)
               .build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-            .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.UNKNOWN_ENTER, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
 
     callOnTransitionReady(transitionInfo)
-    val sessionId = transitionObserver.getLoggerSessionId()
 
-    assertThat(sessionId).isNotNull()
-    verify(desktopModeEventLogger, times(1))
-        .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-    verifyZeroInteractions(desktopModeEventLogger)
+    verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() {
+  fun transitSleep_logTaskRemovedAndExitReasonScreenOff_sessionIdNull() {
     val sessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.setLoggerSessionId(sessionId)
 
     val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF))
-    verifyZeroInteractions(desktopModeEventLogger)
-    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
     val sessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.setLoggerSessionId(sessionId)
 
     // window mode changing from FREEFORM to FULLSCREEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT))
-    verifyZeroInteractions(desktopModeEventLogger)
-    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
     val sessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.setLoggerSessionId(sessionId)
 
     // window mode changing from FREEFORM to FULLSCREEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
             .addChange(change)
             .build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT))
-    verifyZeroInteractions(desktopModeEventLogger)
-    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+    verifyTaskRemovedAndExitLogging(
+        sessionId, ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
     val sessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.setLoggerSessionId(sessionId)
 
     // window mode changing from FREEFORM to FULLSCREEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT))
-    verifyZeroInteractions(desktopModeEventLogger)
-    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+    verifyTaskRemovedAndExitLogging(
+        sessionId, ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
     val sessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.setLoggerSessionId(sessionId)
 
     // window mode changing from FREEFORM to FULLSCREEN
-    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT))
-    verifyZeroInteractions(desktopModeEventLogger)
-    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
     val sessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.setLoggerSessionId(sessionId)
 
     // recents transition
-    val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
     val transitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
-    verifyZeroInteractions(desktopModeEventLogger)
-    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+    verifyTaskRemovedAndExitLogging(
+        sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
     val sessionId = 1
     // add a freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.setLoggerSessionId(sessionId)
 
     // task closing
-    val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED))
-    verifyZeroInteractions(desktopModeEventLogger)
-    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
     val sessionId = 1
     // add a freeform task to an existing session
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+    transitionObserver.addTaskInfosToCachedMap(taskInfo)
     transitionObserver.setLoggerSessionId(sessionId)
 
     // recents transition sent freeform window to back
-    val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_TO_BACK, taskInfo)
     val transitionInfo1 =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
     callOnTransitionReady(transitionInfo1)
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-    verify(desktopModeEventLogger, times(1))
-        .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
-    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+
+    verifyTaskRemovedAndExitLogging(
+        sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
 
     val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
     callOnTransitionReady(transitionInfo2)
 
-    verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any())
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any())
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
     val sessionId = 1
     // add an existing freeform task
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.setLoggerSessionId(sessionId)
 
     // new freeform task added
-    val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+    val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logTaskAdded(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2)))
     verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
   }
 
@@ -593,42 +507,86 @@
   fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
     val sessionId = 1
     // add two existing freeform tasks
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
     transitionObserver.setLoggerSessionId(sessionId)
 
-    // new freeform task added
-    val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+    // new freeform task closed
+    val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2)))
     verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
   }
 
   /** Simulate calling the onTransitionReady() method */
   private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
-    val transition = mock(IBinder::class.java)
-    val startT = mock(SurfaceControl.Transaction::class.java)
-    val finishT = mock(SurfaceControl.Transaction::class.java)
+    val transition = mock<IBinder>()
+    val startT = mock<SurfaceControl.Transaction>()
+    val finishT = mock<SurfaceControl.Transaction>()
 
     transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
   }
 
-  companion object {
-    fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo {
-      val taskInfo = ActivityManager.RunningTaskInfo()
-      taskInfo.taskId = taskId
-      taskInfo.configuration.windowConfiguration.windowingMode = windowMode
+  private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
+    val sessionId = transitionObserver.getLoggerSessionId()
+    assertNotNull(sessionId)
+    verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate))
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
 
-      return taskInfo
-    }
+  private fun verifyTaskRemovedAndExitLogging(
+      sessionId: Int,
+      exitReason: ExitReason,
+      taskUpdate: TaskUpdate
+  ) {
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), eq(taskUpdate))
+    verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), eq(exitReason))
+    verifyZeroInteractions(desktopModeEventLogger)
+    assertNull(transitionObserver.getLoggerSessionId())
+  }
+
+  private companion object {
+    const val DEFAULT_TASK_ID = 1
+    const val DEFAULT_TASK_UID = 2
+    const val DEFAULT_TASK_HEIGHT = 100
+    const val DEFAULT_TASK_WIDTH = 200
+    const val DEFAULT_TASK_X = 30
+    const val DEFAULT_TASK_Y = 70
+    val DEFAULT_TASK_UPDATE =
+        TaskUpdate(
+            DEFAULT_TASK_ID,
+            DEFAULT_TASK_UID,
+            DEFAULT_TASK_HEIGHT,
+            DEFAULT_TASK_WIDTH,
+            DEFAULT_TASK_X,
+            DEFAULT_TASK_Y,
+          )
+
+    fun createTaskInfo(
+        windowMode: Int,
+        id: Int = DEFAULT_TASK_ID,
+        uid: Int = DEFAULT_TASK_UID,
+        taskHeight: Int = DEFAULT_TASK_HEIGHT,
+        taskWidth: Int = DEFAULT_TASK_WIDTH,
+        taskX: Int = DEFAULT_TASK_X,
+        taskY: Int = DEFAULT_TASK_Y,
+    ) = ActivityManager.RunningTaskInfo().apply {
+          taskId = id
+          userId = uid
+          configuration.windowConfiguration.apply {
+            windowingMode = windowMode
+            positionInParent = Point(taskX, taskY)
+            bounds.set(Rect(taskX, taskY, taskX + taskWidth, taskY + taskHeight))
+        }
+      }
 
     fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
       val change =
-          Change(
-              WindowContainerToken(mock(IWindowContainerToken::class.java)),
-              mock(SurfaceControl::class.java))
+          Change(WindowContainerToken(mock<IWindowContainerToken>()), mock<SurfaceControl>())
       change.mode = mode
       change.taskInfo = taskInfo
       return change