diff --git a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SentryCrossProcessCursor.kt b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SentryCrossProcessCursor.kt index f5f8424aca3..01da8c476db 100644 --- a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SentryCrossProcessCursor.kt +++ b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SentryCrossProcessCursor.kt @@ -2,20 +2,20 @@ package io.sentry.android.sqlite import android.database.CrossProcessCursor import android.database.CursorWindow +import android.database.CursorWrapper /* * SQLiteCursor executes the query lazily, when one of getCount() and onMove() is called. * Also, by docs, fillWindow() can be used to fill the cursor with data. * So we wrap these methods to create a span. - * SQLiteCursor is never used directly in the code, but only the Cursor interface. - * This means we can use CrossProcessCursor - that extends Cursor - as wrapper, since - * CrossProcessCursor is an interface and we can use Kotlin delegation. + * Ordinary Cursor methods are delegated through CursorWrapper to avoid adding Sentry frames to + * app database exceptions that the wrapper did not instrument. */ internal class SentryCrossProcessCursor( private val delegate: CrossProcessCursor, private val spans: OpenHelperSpans, private val sql: String, -) : CrossProcessCursor by delegate { +) : CursorWrapper(delegate), CrossProcessCursor { // We have to start the span only the first time, regardless of how many times its methods get // called. private var isSpanStarted = false @@ -36,6 +36,8 @@ internal class SentryCrossProcessCursor( return spans.performSql(sql) { delegate.onMove(oldPosition, newPosition) } } + override fun getWindow(): CursorWindow? = delegate.window + override fun fillWindow(position: Int, window: CursorWindow?) { if (isSpanStarted) { return delegate.fillWindow(position, window) diff --git a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentryCrossProcessCursorTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentryCrossProcessCursorTest.kt index 27eff29c9f3..ba77b2398c7 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentryCrossProcessCursorTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentryCrossProcessCursorTest.kt @@ -1,6 +1,7 @@ package io.sentry.android.sqlite import android.database.CrossProcessCursor +import android.database.CursorWrapper import io.sentry.IScopes import io.sentry.ISpan import io.sentry.SentryOptions @@ -52,13 +53,14 @@ class SentryCrossProcessCursorTest { cursor.fillWindow(0, mock()) verify(fixture.mockCursor).fillWindow(eq(0), any()) + } - // Let's verify other methods are delegated, even if not explicitly - cursor.close() - verify(fixture.mockCursor).close() + @Test + fun `ordinary cursor methods are delegated by Android CursorWrapper`() { + val getStringMethod = + SentryCrossProcessCursor::class.java.getMethod("getString", Int::class.javaPrimitiveType!!) - cursor.getString(1) - verify(fixture.mockCursor).getString(eq(1)) + assertEquals(CursorWrapper::class.java, getStringMethod.declaringClass) } @Test