Loading motiontoollib/Android.bp +2 −2 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ java_library { name: "motion_tool_proto", srcs: ["src/com/android/app/motiontool/proto/*.proto"], proto: { type: "nano", type: "lite", local_include_dirs:[ "src/com/android/app/motiontool/proto" ], Loading @@ -29,7 +29,7 @@ java_library { ], }, static_libs: [ "libprotobuf-java-nano", "libprotobuf-java-lite", "view_capture_proto", ], java_version: "1.8", Loading motiontoollib/build.gradle +15 −9 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ android { dependencies { implementation "androidx.core:core:1.9.0" implementation PROTOBUF_DEPENDENCY implementation "com.google.protobuf:protobuf-lite:${protobuf_version}" api project(":ViewCaptureLib") androidTestImplementation project(':SharedTestLib') androidTestImplementation 'androidx.test.ext:junit:1.1.3' Loading @@ -49,14 +49,20 @@ protobuf { // Configure the protoc executable protoc { artifact = "com.google.protobuf:protoc:${protobuf_version}${PROTO_ARCH_SUFFIX}" } plugins { javalite { // The codegen for lite comes as a separate artifact artifact = "com.google.protobuf:protoc-gen-javalite:${protobuf_version}${PROTO_ARCH_SUFFIX}" } } generateProtoTasks { all().each { task -> task.builtins { remove java javanano { option "enum_style=c" } } task.plugins { javalite { } } } } Loading motiontoollib/src/com/android/app/motiontool/DdmHandleMotionTool.kt +48 −62 Original line number Diff line number Diff line Loading @@ -17,9 +17,7 @@ package com.android.app.motiontool import android.ddm.DdmHandle import com.android.app.motiontool.nano.* import com.google.protobuf.nano.InvalidProtocolBufferNanoException import com.google.protobuf.nano.MessageNano import com.google.protobuf.InvalidProtocolBufferException import org.apache.harmony.dalvik.ddmc.Chunk import org.apache.harmony.dalvik.ddmc.ChunkHandler import org.apache.harmony.dalvik.ddmc.DdmServer Loading Loading @@ -67,19 +65,18 @@ class DdmHandleMotionTool private constructor( val protoRequest = try { MotionToolsRequest.parseFrom(requestDataBuffer.array()) } catch (e: InvalidProtocolBufferNanoException) { val parseErrorResponse = ErrorResponse().apply { code = ErrorResponse.INVALID_REQUEST message = "Invalid request format (Protobuf parse exception)" } val wrappedResponse = MotionToolsResponse().apply { error = parseErrorResponse } val responseData = MessageNano.toByteArray(wrappedResponse) } catch (e: InvalidProtocolBufferException) { val responseData: ByteArray = MotionToolsResponse.newBuilder() .setError(ErrorResponse.newBuilder() .setCode(ErrorResponse.Code.INVALID_REQUEST) .setMessage("Invalid request format (Protobuf parse exception)")) .build() .toByteArray() return Chunk(CHUNK_MOTO, responseData, 0, responseData.size) } val response = when (protoRequest.typeCase) { when (protoRequest.typeCase.number) { MotionToolsRequest.HANDSHAKE_FIELD_NUMBER -> handleHandshakeRequest(protoRequest.handshake) MotionToolsRequest.BEGIN_TRACE_FIELD_NUMBER -> Loading @@ -89,61 +86,50 @@ class DdmHandleMotionTool private constructor( MotionToolsRequest.END_TRACE_FIELD_NUMBER -> handleEndTraceRequest(protoRequest.endTrace) else -> MotionToolsResponse().apply { error = ErrorResponse().apply { code = ErrorResponse.INVALID_REQUEST message = "Unknown request type" } } MotionToolsResponse.newBuilder().setError(ErrorResponse.newBuilder() .setCode(ErrorResponse.Code.INVALID_REQUEST) .setMessage("Unknown request type")).build() } val responseData = MessageNano.toByteArray(response) val responseData = response.toByteArray() return Chunk(CHUNK_MOTO, responseData, 0, responseData.size) } private fun handleBeginTraceRequest(beginTraceRequest: BeginTraceRequest) = MotionToolsResponse().apply { private fun handleBeginTraceRequest(beginTraceRequest: BeginTraceRequest): MotionToolsResponse = MotionToolsResponse.newBuilder().apply { tryCatchingMotionToolManagerExceptions { beginTrace = BeginTraceResponse().apply { traceId = motionToolManager.beginTrace(beginTraceRequest.window.rootWindow) } } setBeginTrace(BeginTraceResponse.newBuilder().setTraceId( motionToolManager.beginTrace(beginTraceRequest.window.rootWindow))) } }.build() private fun handlePollTraceRequest(pollTraceRequest: PollTraceRequest) = MotionToolsResponse().apply { private fun handlePollTraceRequest(pollTraceRequest: PollTraceRequest): MotionToolsResponse = MotionToolsResponse.newBuilder().apply { tryCatchingMotionToolManagerExceptions { pollTrace = PollTraceResponse().apply { exportedData = motionToolManager.pollTrace(pollTraceRequest.traceId) } } setPollTrace(PollTraceResponse.newBuilder() .setExportedData(motionToolManager.pollTrace(pollTraceRequest.traceId))) } }.build() private fun handleEndTraceRequest(endTraceRequest: EndTraceRequest) = MotionToolsResponse().apply { private fun handleEndTraceRequest(endTraceRequest: EndTraceRequest): MotionToolsResponse = MotionToolsResponse.newBuilder().apply { tryCatchingMotionToolManagerExceptions { endTrace = EndTraceResponse().apply { exportedData = motionToolManager.endTrace(endTraceRequest.traceId) } } setEndTrace(EndTraceResponse.newBuilder() .setExportedData(motionToolManager.endTrace(endTraceRequest.traceId))) } }.build() private fun handleHandshakeRequest(handshakeRequest: HandshakeRequest) = MotionToolsResponse().apply { handshake = HandshakeResponse().apply { serverVersion = SERVER_VERSION status = if (motionToolManager.hasWindow(handshakeRequest.window)) { HandshakeResponse.OK } else { HandshakeResponse.WINDOW_NOT_FOUND } } private fun handleHandshakeRequest(handshakeRequest: HandshakeRequest): MotionToolsResponse { val status = if (motionToolManager.hasWindow(handshakeRequest.window)) HandshakeResponse.Status.OK else HandshakeResponse.Status.WINDOW_NOT_FOUND return MotionToolsResponse.newBuilder() .setHandshake(HandshakeResponse.newBuilder() .setServerVersion(SERVER_VERSION) .setStatus(status)) .build() } /** Loading @@ -151,25 +137,25 @@ class DdmHandleMotionTool private constructor( * exception being caught, the error response field of the [MotionToolsResponse] is being set * with the according [ErrorResponse]. */ private fun MotionToolsResponse.tryCatchingMotionToolManagerExceptions(block: () -> Unit) { private fun MotionToolsResponse.Builder.tryCatchingMotionToolManagerExceptions(block: () -> Unit) { try { block() } catch (e: UnknownTraceIdException) { error = createUnknownTraceIdResponse(e.traceId) setError(createUnknownTraceIdResponse(e.traceId)) } catch (e: WindowNotFoundException) { error = createWindowNotFoundResponse(e.windowId) setError(createWindowNotFoundResponse(e.windowId)) } } private fun createUnknownTraceIdResponse(traceId: Int) = ErrorResponse().apply { this.code = ErrorResponse.UNKNOWN_TRACE_ID ErrorResponse.newBuilder().apply { this.code = ErrorResponse.Code.UNKNOWN_TRACE_ID this.message = "No running Trace found with traceId $traceId" } private fun createWindowNotFoundResponse(windowId: String) = ErrorResponse().apply { this.code = ErrorResponse.WINDOW_NOT_FOUND ErrorResponse.newBuilder().apply { this.code = ErrorResponse.Code.WINDOW_NOT_FOUND this.message = "No window found with windowId $windowId" } Loading motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt +13 −10 Original line number Diff line number Diff line Loading @@ -22,9 +22,8 @@ import android.view.Choreographer import android.view.View import android.view.WindowManagerGlobal import androidx.annotation.VisibleForTesting import com.android.app.motiontool.nano.WindowIdentifier import com.android.app.viewcapture.ViewCapture import com.android.app.viewcapture.data.nano.ExportedData import com.android.app.viewcapture.data.ExportedData /** * Singleton to manage motion tracing sessions. Loading Loading @@ -120,15 +119,19 @@ class MotionToolManager private constructor(private val windowManagerGlobal: Win val rootView = getRootView(traceMetadata.windowId) ?: throw WindowNotFoundException(traceMetadata.windowId) return viewCapture val exportedData = viewCapture .getDumpTask(rootView) ?.orElse(null) ?.get() ?.apply { frameData = frameData?.filter { it.timestamp > traceMetadata.lastPolledTime } ?.toTypedArray() } ?: ExportedData() ?.get() ?: return ExportedData.newBuilder().build() val filteredFrameData = exportedData.frameDataList ?.filter { it.timestamp > traceMetadata.lastPolledTime } return exportedData.toBuilder() .clearFrameData() .addAllFrameData(filteredFrameData) .build() } private fun getRootView(windowId: String): View? { Loading @@ -146,7 +149,7 @@ private data class TraceMetadata( var stopTrace: () -> Unit ) { fun updateLastPolledTime(exportedData: ExportedData?) { exportedData?.frameData?.maxOfOrNull { it.timestamp }?.let { maxFrameTimestamp -> exportedData?.frameDataList?.maxOfOrNull { it.timestamp }?.let { maxFrameTimestamp -> lastPolledTime = maxFrameTimestamp } } Loading motiontoollib/tests/com/android/app/motiontool/DdmHandleMotionToolTest.kt +31 −47 Original line number Diff line number Diff line Loading @@ -25,17 +25,7 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.app.motiontool.DdmHandleMotionTool.Companion.CHUNK_MOTO import com.android.app.motiontool.nano.BeginTraceRequest import com.android.app.motiontool.nano.EndTraceRequest import com.android.app.motiontool.nano.ErrorResponse import com.android.app.motiontool.nano.HandshakeRequest import com.android.app.motiontool.nano.HandshakeResponse import com.android.app.motiontool.nano.MotionToolsRequest import com.android.app.motiontool.nano.MotionToolsResponse import com.android.app.motiontool.nano.PollTraceRequest import com.android.app.motiontool.nano.WindowIdentifier import com.android.app.motiontool.util.TestActivity import com.google.protobuf.nano.MessageNano import junit.framework.Assert import junit.framework.Assert.assertEquals import org.apache.harmony.dalvik.ddmc.Chunk Loading Loading @@ -74,45 +64,45 @@ class DdmHandleMotionToolTest { @Test fun testHandshakeErrorWithInvalidWindowId() { val handshakeResponse = performHandshakeRequest("InvalidWindowId") assertEquals(HandshakeResponse.WINDOW_NOT_FOUND, handshakeResponse.handshake.status) assertEquals(HandshakeResponse.Status.WINDOW_NOT_FOUND, handshakeResponse.handshake.status) } @Test fun testHandshakeOkWithValidWindowId() { val handshakeResponse = performHandshakeRequest(getActivityViewRootId()) assertEquals(HandshakeResponse.OK, handshakeResponse.handshake.status) assertEquals(HandshakeResponse.Status.OK, handshakeResponse.handshake.status) } @Test fun testBeginFailsWithInvalidWindowId() { val errorResponse = performBeginTraceRequest("InvalidWindowId") assertEquals(ErrorResponse.WINDOW_NOT_FOUND, errorResponse.error.code) assertEquals(ErrorResponse.Code.WINDOW_NOT_FOUND, errorResponse.error.code) } @Test fun testEndTraceFailsWithoutPrecedingBeginTrace() { val errorResponse = performEndTraceRequest(0) assertEquals(ErrorResponse.UNKNOWN_TRACE_ID, errorResponse.error.code) assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, errorResponse.error.code) } @Test fun testPollTraceFailsWithoutPrecedingBeginTrace() { val errorResponse = performPollTraceRequest(0) assertEquals(ErrorResponse.UNKNOWN_TRACE_ID, errorResponse.error.code) assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, errorResponse.error.code) } @Test fun testEndTraceFailsWithInvalidTraceId() { val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) val endTraceResponse = performEndTraceRequest(beginTraceResponse.beginTrace.traceId + 1) assertEquals(ErrorResponse.UNKNOWN_TRACE_ID, endTraceResponse.error.code) assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, endTraceResponse.error.code) } @Test fun testPollTraceFailsWithInvalidTraceId() { val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) val endTraceResponse = performPollTraceRequest(beginTraceResponse.beginTrace.traceId + 1) assertEquals(ErrorResponse.UNKNOWN_TRACE_ID, endTraceResponse.error.code) assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, endTraceResponse.error.code) } @Test Loading @@ -121,7 +111,7 @@ class DdmHandleMotionToolTest { val requestChunk = Chunk(CHUNK_MOTO, requestBytes, 0, requestBytes.size) val responseChunk = ddmHandleMotionTool.handleChunk(requestChunk) val response = MotionToolsResponse.parseFrom(wrapChunk(responseChunk).array()).error assertEquals(ErrorResponse.INVALID_REQUEST, response.code) assertEquals(ErrorResponse.Code.INVALID_REQUEST, response.code) } @Test Loading @@ -129,7 +119,7 @@ class DdmHandleMotionToolTest { activityScenarioRule.scenario.onActivity { val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) val endTraceResponse = performEndTraceRequest(beginTraceResponse.beginTrace.traceId) Assert.assertTrue(endTraceResponse.endTrace.exportedData.frameData.isEmpty()) Assert.assertTrue(endTraceResponse.endTrace.exportedData.frameDataList.isEmpty()) } } Loading @@ -143,58 +133,52 @@ class DdmHandleMotionToolTest { activity.findViewById<View>(android.R.id.content).viewTreeObserver.dispatchOnDraw() val pollTraceResponse = performPollTraceRequest(traceId) assertEquals(1, pollTraceResponse.pollTrace.exportedData.frameData.size) assertEquals(1, pollTraceResponse.pollTrace.exportedData.frameDataList.size) // Verify that frameData is only included once and is not returned again val endTraceResponse = performEndTraceRequest(traceId) assertEquals(0, endTraceResponse.endTrace.exportedData.frameData.size) assertEquals(0, endTraceResponse.endTrace.exportedData.frameDataList.size) } } } private fun performPollTraceRequest(requestTraceId: Int): MotionToolsResponse { val pollTraceRequest = MotionToolsRequest().apply { pollTrace = PollTraceRequest().apply { traceId = requestTraceId } } val pollTraceRequest = MotionToolsRequest.newBuilder() .setPollTrace(PollTraceRequest.newBuilder() .setTraceId(requestTraceId)) .build() return performRequest(pollTraceRequest) } private fun performEndTraceRequest(requestTraceId: Int): MotionToolsResponse { val endTraceRequest = MotionToolsRequest().apply { endTrace = EndTraceRequest().apply { traceId = requestTraceId } } val endTraceRequest = MotionToolsRequest.newBuilder() .setEndTrace(EndTraceRequest.newBuilder() .setTraceId(requestTraceId)) .build() return performRequest(endTraceRequest) } private fun performBeginTraceRequest(windowId: String): MotionToolsResponse { val beginTraceRequest = MotionToolsRequest().apply { beginTrace = BeginTraceRequest().apply { window = WindowIdentifier().apply { rootWindow = windowId } } } val beginTraceRequest = MotionToolsRequest.newBuilder() .setBeginTrace(BeginTraceRequest.newBuilder() .setWindow(WindowIdentifier.newBuilder() .setRootWindow(windowId))) .build() return performRequest(beginTraceRequest) } private fun performHandshakeRequest(windowId: String): MotionToolsResponse { val handshakeRequest = MotionToolsRequest().apply { handshake = HandshakeRequest().apply { window = WindowIdentifier().apply { rootWindow = windowId } clientVersion = CLIENT_VERSION } } val handshakeRequest = MotionToolsRequest.newBuilder() .setHandshake(HandshakeRequest.newBuilder() .setWindow(WindowIdentifier.newBuilder() .setRootWindow(windowId)) .setClientVersion(CLIENT_VERSION)) .build() return performRequest(handshakeRequest) } private fun performRequest(motionToolsRequest: MotionToolsRequest): MotionToolsResponse { val requestBytes = MessageNano.toByteArray(motionToolsRequest) val requestBytes = motionToolsRequest.toByteArray() val requestChunk = Chunk(CHUNK_MOTO, requestBytes, 0, requestBytes.size) val responseChunk = ddmHandleMotionTool.handleChunk(requestChunk) return MotionToolsResponse.parseFrom(wrapChunk(responseChunk).array()) Loading Loading
motiontoollib/Android.bp +2 −2 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ java_library { name: "motion_tool_proto", srcs: ["src/com/android/app/motiontool/proto/*.proto"], proto: { type: "nano", type: "lite", local_include_dirs:[ "src/com/android/app/motiontool/proto" ], Loading @@ -29,7 +29,7 @@ java_library { ], }, static_libs: [ "libprotobuf-java-nano", "libprotobuf-java-lite", "view_capture_proto", ], java_version: "1.8", Loading
motiontoollib/build.gradle +15 −9 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ android { dependencies { implementation "androidx.core:core:1.9.0" implementation PROTOBUF_DEPENDENCY implementation "com.google.protobuf:protobuf-lite:${protobuf_version}" api project(":ViewCaptureLib") androidTestImplementation project(':SharedTestLib') androidTestImplementation 'androidx.test.ext:junit:1.1.3' Loading @@ -49,14 +49,20 @@ protobuf { // Configure the protoc executable protoc { artifact = "com.google.protobuf:protoc:${protobuf_version}${PROTO_ARCH_SUFFIX}" } plugins { javalite { // The codegen for lite comes as a separate artifact artifact = "com.google.protobuf:protoc-gen-javalite:${protobuf_version}${PROTO_ARCH_SUFFIX}" } } generateProtoTasks { all().each { task -> task.builtins { remove java javanano { option "enum_style=c" } } task.plugins { javalite { } } } } Loading
motiontoollib/src/com/android/app/motiontool/DdmHandleMotionTool.kt +48 −62 Original line number Diff line number Diff line Loading @@ -17,9 +17,7 @@ package com.android.app.motiontool import android.ddm.DdmHandle import com.android.app.motiontool.nano.* import com.google.protobuf.nano.InvalidProtocolBufferNanoException import com.google.protobuf.nano.MessageNano import com.google.protobuf.InvalidProtocolBufferException import org.apache.harmony.dalvik.ddmc.Chunk import org.apache.harmony.dalvik.ddmc.ChunkHandler import org.apache.harmony.dalvik.ddmc.DdmServer Loading Loading @@ -67,19 +65,18 @@ class DdmHandleMotionTool private constructor( val protoRequest = try { MotionToolsRequest.parseFrom(requestDataBuffer.array()) } catch (e: InvalidProtocolBufferNanoException) { val parseErrorResponse = ErrorResponse().apply { code = ErrorResponse.INVALID_REQUEST message = "Invalid request format (Protobuf parse exception)" } val wrappedResponse = MotionToolsResponse().apply { error = parseErrorResponse } val responseData = MessageNano.toByteArray(wrappedResponse) } catch (e: InvalidProtocolBufferException) { val responseData: ByteArray = MotionToolsResponse.newBuilder() .setError(ErrorResponse.newBuilder() .setCode(ErrorResponse.Code.INVALID_REQUEST) .setMessage("Invalid request format (Protobuf parse exception)")) .build() .toByteArray() return Chunk(CHUNK_MOTO, responseData, 0, responseData.size) } val response = when (protoRequest.typeCase) { when (protoRequest.typeCase.number) { MotionToolsRequest.HANDSHAKE_FIELD_NUMBER -> handleHandshakeRequest(protoRequest.handshake) MotionToolsRequest.BEGIN_TRACE_FIELD_NUMBER -> Loading @@ -89,61 +86,50 @@ class DdmHandleMotionTool private constructor( MotionToolsRequest.END_TRACE_FIELD_NUMBER -> handleEndTraceRequest(protoRequest.endTrace) else -> MotionToolsResponse().apply { error = ErrorResponse().apply { code = ErrorResponse.INVALID_REQUEST message = "Unknown request type" } } MotionToolsResponse.newBuilder().setError(ErrorResponse.newBuilder() .setCode(ErrorResponse.Code.INVALID_REQUEST) .setMessage("Unknown request type")).build() } val responseData = MessageNano.toByteArray(response) val responseData = response.toByteArray() return Chunk(CHUNK_MOTO, responseData, 0, responseData.size) } private fun handleBeginTraceRequest(beginTraceRequest: BeginTraceRequest) = MotionToolsResponse().apply { private fun handleBeginTraceRequest(beginTraceRequest: BeginTraceRequest): MotionToolsResponse = MotionToolsResponse.newBuilder().apply { tryCatchingMotionToolManagerExceptions { beginTrace = BeginTraceResponse().apply { traceId = motionToolManager.beginTrace(beginTraceRequest.window.rootWindow) } } setBeginTrace(BeginTraceResponse.newBuilder().setTraceId( motionToolManager.beginTrace(beginTraceRequest.window.rootWindow))) } }.build() private fun handlePollTraceRequest(pollTraceRequest: PollTraceRequest) = MotionToolsResponse().apply { private fun handlePollTraceRequest(pollTraceRequest: PollTraceRequest): MotionToolsResponse = MotionToolsResponse.newBuilder().apply { tryCatchingMotionToolManagerExceptions { pollTrace = PollTraceResponse().apply { exportedData = motionToolManager.pollTrace(pollTraceRequest.traceId) } } setPollTrace(PollTraceResponse.newBuilder() .setExportedData(motionToolManager.pollTrace(pollTraceRequest.traceId))) } }.build() private fun handleEndTraceRequest(endTraceRequest: EndTraceRequest) = MotionToolsResponse().apply { private fun handleEndTraceRequest(endTraceRequest: EndTraceRequest): MotionToolsResponse = MotionToolsResponse.newBuilder().apply { tryCatchingMotionToolManagerExceptions { endTrace = EndTraceResponse().apply { exportedData = motionToolManager.endTrace(endTraceRequest.traceId) } } setEndTrace(EndTraceResponse.newBuilder() .setExportedData(motionToolManager.endTrace(endTraceRequest.traceId))) } }.build() private fun handleHandshakeRequest(handshakeRequest: HandshakeRequest) = MotionToolsResponse().apply { handshake = HandshakeResponse().apply { serverVersion = SERVER_VERSION status = if (motionToolManager.hasWindow(handshakeRequest.window)) { HandshakeResponse.OK } else { HandshakeResponse.WINDOW_NOT_FOUND } } private fun handleHandshakeRequest(handshakeRequest: HandshakeRequest): MotionToolsResponse { val status = if (motionToolManager.hasWindow(handshakeRequest.window)) HandshakeResponse.Status.OK else HandshakeResponse.Status.WINDOW_NOT_FOUND return MotionToolsResponse.newBuilder() .setHandshake(HandshakeResponse.newBuilder() .setServerVersion(SERVER_VERSION) .setStatus(status)) .build() } /** Loading @@ -151,25 +137,25 @@ class DdmHandleMotionTool private constructor( * exception being caught, the error response field of the [MotionToolsResponse] is being set * with the according [ErrorResponse]. */ private fun MotionToolsResponse.tryCatchingMotionToolManagerExceptions(block: () -> Unit) { private fun MotionToolsResponse.Builder.tryCatchingMotionToolManagerExceptions(block: () -> Unit) { try { block() } catch (e: UnknownTraceIdException) { error = createUnknownTraceIdResponse(e.traceId) setError(createUnknownTraceIdResponse(e.traceId)) } catch (e: WindowNotFoundException) { error = createWindowNotFoundResponse(e.windowId) setError(createWindowNotFoundResponse(e.windowId)) } } private fun createUnknownTraceIdResponse(traceId: Int) = ErrorResponse().apply { this.code = ErrorResponse.UNKNOWN_TRACE_ID ErrorResponse.newBuilder().apply { this.code = ErrorResponse.Code.UNKNOWN_TRACE_ID this.message = "No running Trace found with traceId $traceId" } private fun createWindowNotFoundResponse(windowId: String) = ErrorResponse().apply { this.code = ErrorResponse.WINDOW_NOT_FOUND ErrorResponse.newBuilder().apply { this.code = ErrorResponse.Code.WINDOW_NOT_FOUND this.message = "No window found with windowId $windowId" } Loading
motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt +13 −10 Original line number Diff line number Diff line Loading @@ -22,9 +22,8 @@ import android.view.Choreographer import android.view.View import android.view.WindowManagerGlobal import androidx.annotation.VisibleForTesting import com.android.app.motiontool.nano.WindowIdentifier import com.android.app.viewcapture.ViewCapture import com.android.app.viewcapture.data.nano.ExportedData import com.android.app.viewcapture.data.ExportedData /** * Singleton to manage motion tracing sessions. Loading Loading @@ -120,15 +119,19 @@ class MotionToolManager private constructor(private val windowManagerGlobal: Win val rootView = getRootView(traceMetadata.windowId) ?: throw WindowNotFoundException(traceMetadata.windowId) return viewCapture val exportedData = viewCapture .getDumpTask(rootView) ?.orElse(null) ?.get() ?.apply { frameData = frameData?.filter { it.timestamp > traceMetadata.lastPolledTime } ?.toTypedArray() } ?: ExportedData() ?.get() ?: return ExportedData.newBuilder().build() val filteredFrameData = exportedData.frameDataList ?.filter { it.timestamp > traceMetadata.lastPolledTime } return exportedData.toBuilder() .clearFrameData() .addAllFrameData(filteredFrameData) .build() } private fun getRootView(windowId: String): View? { Loading @@ -146,7 +149,7 @@ private data class TraceMetadata( var stopTrace: () -> Unit ) { fun updateLastPolledTime(exportedData: ExportedData?) { exportedData?.frameData?.maxOfOrNull { it.timestamp }?.let { maxFrameTimestamp -> exportedData?.frameDataList?.maxOfOrNull { it.timestamp }?.let { maxFrameTimestamp -> lastPolledTime = maxFrameTimestamp } } Loading
motiontoollib/tests/com/android/app/motiontool/DdmHandleMotionToolTest.kt +31 −47 Original line number Diff line number Diff line Loading @@ -25,17 +25,7 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.app.motiontool.DdmHandleMotionTool.Companion.CHUNK_MOTO import com.android.app.motiontool.nano.BeginTraceRequest import com.android.app.motiontool.nano.EndTraceRequest import com.android.app.motiontool.nano.ErrorResponse import com.android.app.motiontool.nano.HandshakeRequest import com.android.app.motiontool.nano.HandshakeResponse import com.android.app.motiontool.nano.MotionToolsRequest import com.android.app.motiontool.nano.MotionToolsResponse import com.android.app.motiontool.nano.PollTraceRequest import com.android.app.motiontool.nano.WindowIdentifier import com.android.app.motiontool.util.TestActivity import com.google.protobuf.nano.MessageNano import junit.framework.Assert import junit.framework.Assert.assertEquals import org.apache.harmony.dalvik.ddmc.Chunk Loading Loading @@ -74,45 +64,45 @@ class DdmHandleMotionToolTest { @Test fun testHandshakeErrorWithInvalidWindowId() { val handshakeResponse = performHandshakeRequest("InvalidWindowId") assertEquals(HandshakeResponse.WINDOW_NOT_FOUND, handshakeResponse.handshake.status) assertEquals(HandshakeResponse.Status.WINDOW_NOT_FOUND, handshakeResponse.handshake.status) } @Test fun testHandshakeOkWithValidWindowId() { val handshakeResponse = performHandshakeRequest(getActivityViewRootId()) assertEquals(HandshakeResponse.OK, handshakeResponse.handshake.status) assertEquals(HandshakeResponse.Status.OK, handshakeResponse.handshake.status) } @Test fun testBeginFailsWithInvalidWindowId() { val errorResponse = performBeginTraceRequest("InvalidWindowId") assertEquals(ErrorResponse.WINDOW_NOT_FOUND, errorResponse.error.code) assertEquals(ErrorResponse.Code.WINDOW_NOT_FOUND, errorResponse.error.code) } @Test fun testEndTraceFailsWithoutPrecedingBeginTrace() { val errorResponse = performEndTraceRequest(0) assertEquals(ErrorResponse.UNKNOWN_TRACE_ID, errorResponse.error.code) assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, errorResponse.error.code) } @Test fun testPollTraceFailsWithoutPrecedingBeginTrace() { val errorResponse = performPollTraceRequest(0) assertEquals(ErrorResponse.UNKNOWN_TRACE_ID, errorResponse.error.code) assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, errorResponse.error.code) } @Test fun testEndTraceFailsWithInvalidTraceId() { val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) val endTraceResponse = performEndTraceRequest(beginTraceResponse.beginTrace.traceId + 1) assertEquals(ErrorResponse.UNKNOWN_TRACE_ID, endTraceResponse.error.code) assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, endTraceResponse.error.code) } @Test fun testPollTraceFailsWithInvalidTraceId() { val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) val endTraceResponse = performPollTraceRequest(beginTraceResponse.beginTrace.traceId + 1) assertEquals(ErrorResponse.UNKNOWN_TRACE_ID, endTraceResponse.error.code) assertEquals(ErrorResponse.Code.UNKNOWN_TRACE_ID, endTraceResponse.error.code) } @Test Loading @@ -121,7 +111,7 @@ class DdmHandleMotionToolTest { val requestChunk = Chunk(CHUNK_MOTO, requestBytes, 0, requestBytes.size) val responseChunk = ddmHandleMotionTool.handleChunk(requestChunk) val response = MotionToolsResponse.parseFrom(wrapChunk(responseChunk).array()).error assertEquals(ErrorResponse.INVALID_REQUEST, response.code) assertEquals(ErrorResponse.Code.INVALID_REQUEST, response.code) } @Test Loading @@ -129,7 +119,7 @@ class DdmHandleMotionToolTest { activityScenarioRule.scenario.onActivity { val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId()) val endTraceResponse = performEndTraceRequest(beginTraceResponse.beginTrace.traceId) Assert.assertTrue(endTraceResponse.endTrace.exportedData.frameData.isEmpty()) Assert.assertTrue(endTraceResponse.endTrace.exportedData.frameDataList.isEmpty()) } } Loading @@ -143,58 +133,52 @@ class DdmHandleMotionToolTest { activity.findViewById<View>(android.R.id.content).viewTreeObserver.dispatchOnDraw() val pollTraceResponse = performPollTraceRequest(traceId) assertEquals(1, pollTraceResponse.pollTrace.exportedData.frameData.size) assertEquals(1, pollTraceResponse.pollTrace.exportedData.frameDataList.size) // Verify that frameData is only included once and is not returned again val endTraceResponse = performEndTraceRequest(traceId) assertEquals(0, endTraceResponse.endTrace.exportedData.frameData.size) assertEquals(0, endTraceResponse.endTrace.exportedData.frameDataList.size) } } } private fun performPollTraceRequest(requestTraceId: Int): MotionToolsResponse { val pollTraceRequest = MotionToolsRequest().apply { pollTrace = PollTraceRequest().apply { traceId = requestTraceId } } val pollTraceRequest = MotionToolsRequest.newBuilder() .setPollTrace(PollTraceRequest.newBuilder() .setTraceId(requestTraceId)) .build() return performRequest(pollTraceRequest) } private fun performEndTraceRequest(requestTraceId: Int): MotionToolsResponse { val endTraceRequest = MotionToolsRequest().apply { endTrace = EndTraceRequest().apply { traceId = requestTraceId } } val endTraceRequest = MotionToolsRequest.newBuilder() .setEndTrace(EndTraceRequest.newBuilder() .setTraceId(requestTraceId)) .build() return performRequest(endTraceRequest) } private fun performBeginTraceRequest(windowId: String): MotionToolsResponse { val beginTraceRequest = MotionToolsRequest().apply { beginTrace = BeginTraceRequest().apply { window = WindowIdentifier().apply { rootWindow = windowId } } } val beginTraceRequest = MotionToolsRequest.newBuilder() .setBeginTrace(BeginTraceRequest.newBuilder() .setWindow(WindowIdentifier.newBuilder() .setRootWindow(windowId))) .build() return performRequest(beginTraceRequest) } private fun performHandshakeRequest(windowId: String): MotionToolsResponse { val handshakeRequest = MotionToolsRequest().apply { handshake = HandshakeRequest().apply { window = WindowIdentifier().apply { rootWindow = windowId } clientVersion = CLIENT_VERSION } } val handshakeRequest = MotionToolsRequest.newBuilder() .setHandshake(HandshakeRequest.newBuilder() .setWindow(WindowIdentifier.newBuilder() .setRootWindow(windowId)) .setClientVersion(CLIENT_VERSION)) .build() return performRequest(handshakeRequest) } private fun performRequest(motionToolsRequest: MotionToolsRequest): MotionToolsResponse { val requestBytes = MessageNano.toByteArray(motionToolsRequest) val requestBytes = motionToolsRequest.toByteArray() val requestChunk = Chunk(CHUNK_MOTO, requestBytes, 0, requestBytes.size) val responseChunk = ddmHandleMotionTool.handleChunk(requestChunk) return MotionToolsResponse.parseFrom(wrapChunk(responseChunk).array()) Loading