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

Commit 64995e62 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fixing FakeMotionEvent" into main

parents 8b2393d4 aa8903ed
Loading
Loading
Loading
Loading
+14 −28
Original line number Diff line number Diff line
@@ -21,10 +21,9 @@ import android.view.InputDevice.SOURCE_MOUSE
import android.view.MotionEvent
import android.view.MotionEvent.CLASSIFICATION_NONE
import android.view.MotionEvent.TOOL_TYPE_FINGER
import java.lang.reflect.Method
import org.mockito.kotlin.doNothing
import org.mockito.AdditionalAnswers
import org.mockito.Mockito.mock
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever

fun motionEvent(
@@ -40,31 +39,18 @@ fun motionEvent(
    val event =
        MotionEvent.obtain(/* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0)
    event.source = source
    val spy =
        spy<MotionEvent>(event) {
            on { getToolType(0) } doReturn toolType
            on { getPointerCount() } doReturn pointerCount
            axisValues.forEach { (key, value) -> on { getAxisValue(key) } doReturn value }
            on { getClassification() } doReturn classification
        }
    ensureFinalizeIsNotCalledTwice(spy)
    return spy
}

private fun ensureFinalizeIsNotCalledTwice(spy: MotionEvent) {
    // Spy in mockito will create copy of the spied object, copying all its field etc. Here it means
    // we create copy of MotionEvent and its mNativePtr, so we have two separate objects of type
    // MotionEvents with the same mNativePtr. That breaks because MotionEvent has custom finalize()
    // method which goes to native code and tries to delete the reference from mNativePtr. It works
    // first time but second time reference is already deleted and it breaks. That's why we have to
    // avoid calling finalize twice
    doNothing().whenever(spy).finalizeUsingReflection()
}

private fun MotionEvent.finalizeUsingReflection() {
    val finalizeMethod: Method = MotionEvent::class.java.getDeclaredMethod("finalize")
    finalizeMethod.isAccessible = true
    finalizeMethod.invoke(this)
    // we need to use mock with delegation instead of spy because:
    // 1. Spy will try to deallocate the same memory again when finalize() is called as it keep the
    // same memory pointer to native MotionEvent
    // 2. Even after workaround for issue above there still remains problem with destructor of
    // native event trying to free the same chunk of native memory. I'm not sure why it happens but
    // mock seems to fix the issue and because it delegates all calls seems safer overall
    val delegate = mock(MotionEvent::class.java, AdditionalAnswers.delegatesTo<MotionEvent>(event))
    doReturn(toolType).whenever(delegate).getToolType(0)
    doReturn(pointerCount).whenever(delegate).pointerCount
    doReturn(classification).whenever(delegate).classification
    axisValues.forEach { (key, value) -> doReturn(value).whenever(delegate).getAxisValue(key) }
    return delegate
}

fun touchpadEvent(