Skip to content

Commit

Permalink
feat: Add FileReader/FileWriter instrumentation (#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
romtsn committed Dec 23, 2021
1 parent 7f1e3dd commit e61eb20
Show file tree
Hide file tree
Showing 17 changed files with 360 additions and 31 deletions.
1 change: 0 additions & 1 deletion examples/android-room/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,5 @@ sentry {

tracingInstrumentation {
forceInstrumentDependencies.set(true)
debug.set(true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ class SampleApp : Application() {
tracks.forEachIndexed { index, track ->
// add lyrics for every 2nd track
if (index % 2 == 0) {
val dir = File("$filesDir${File.separatorChar}lyrics")
dir.mkdirs()

val file = File(dir, "${track.id}.txt")
val file = File(filesDir, "${track.id}.txt")
file.writeText(DEFAULT_LYRICS)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import io.sentry.Sentry
import io.sentry.SpanStatus
import io.sentry.android.roomsample.R
import io.sentry.android.roomsample.data.Track
import io.sentry.android.roomsample.util.Filesystem
import java.io.File

@SuppressLint("SetTextI18n")
class LyricsActivity : ComponentActivity() {
private lateinit var file: File
private lateinit var lyricsInput: EditText
private lateinit var filesystem: Filesystem
private lateinit var track: Track

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -29,16 +31,14 @@ class LyricsActivity : ComponentActivity() {
lyricsInput = findViewById(R.id.lyrics)
val toolbar = findViewById<Toolbar>(R.id.toolbar)

val track: Track = intent.getSerializableExtra(TRACK_EXTRA_KEY) as Track
track = intent.getSerializableExtra(TRACK_EXTRA_KEY) as Track
filesystem = intent.getSerializableExtra(FILESYSTEM_EXTRA_KEY) as Filesystem
toolbar.title = "Lyrics for ${track.name}"

val dir = File("$filesDir${File.separatorChar}lyrics")
dir.mkdirs()

file = File(dir, "${track.id}.txt")
if (file.exists()) {
lyricsInput.setText(file.readText())
}
lyricsInput.setText(filesystem.read(this, "${track.id}.txt"))
transaction.finish(SpanStatus.OK)
}

Expand All @@ -48,15 +48,13 @@ class LyricsActivity : ComponentActivity() {
"ui.action.lyrics_finish",
true
)
if (!file.exists()) {
file.createNewFile()
}
file.writeText(lyricsInput.text.toString())
filesystem.write(this, "${track.id}.txt", lyricsInput.text.toString())
transaction.finish(SpanStatus.OK)
super.onBackPressed()
}

companion object {
const val TRACK_EXTRA_KEY = "LyricsActivity.Track"
const val FILESYSTEM_EXTRA_KEY = "LyricsActivity.Filesystem"
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package io.sentry.android.roomsample.ui.list

import android.app.AlertDialog
import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.recyclerview.widget.RecyclerView
import io.sentry.Sentry
import io.sentry.SpanStatus
Expand All @@ -11,6 +13,7 @@ import io.sentry.android.roomsample.SampleApp
import io.sentry.android.roomsample.data.Track
import io.sentry.android.roomsample.ui.EditActivity
import io.sentry.android.roomsample.ui.LyricsActivity
import io.sentry.android.roomsample.util.Filesystem
import kotlinx.coroutines.runBlocking

class TrackAdapter : RecyclerView.Adapter<TrackAdapter.ViewHolder>() {
Expand Down Expand Up @@ -62,19 +65,40 @@ class TrackAdapter : RecyclerView.Adapter<TrackAdapter.ViewHolder>() {
holder.row.infoButton.setOnClickListener {
val context = holder.row.context
val track = data[holder.bindingAdapterPosition]
context.startActivity(
Intent(
context,
LyricsActivity::class.java
).putExtra(LyricsActivity.TRACK_EXTRA_KEY, track)
)

/* ktlint-disable experimental:argument-list-wrapping */
AlertDialog.Builder(context)
.setTitle("Choose File API")
.setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
.setAdapter(
ArrayAdapter(
context,
android.R.layout.select_dialog_item,
listOf(
"FileIOStream",
"FileReader/FileWriter",
"Context.openFileInput/Output"
)
)
) { dialog, which ->
context.startActivity(
Intent(
context,
LyricsActivity::class.java
).putExtra(LyricsActivity.TRACK_EXTRA_KEY, track)
.putExtra(LyricsActivity.FILESYSTEM_EXTRA_KEY, Filesystem.from(which))
)
dialog.dismiss()
}.show()
/* ktlint-enable experimental:argument-list-wrapping */
}
}

override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder)
holder.row.deleteButton.setOnClickListener(null)
holder.row.editButton.setOnClickListener(null)
holder.row.infoButton.setOnClickListener(null)
}

inner class ViewHolder(val row: TrackRow) : RecyclerView.ViewHolder(row)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.sentry.android.roomsample.util

import android.content.Context as AndroidContext
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.Serializable

sealed class Filesystem : Serializable {

abstract fun read(context: AndroidContext, filename: String): String

abstract fun write(context: AndroidContext, filename: String, text: String)

companion object {
fun from(which: Int) = when (which) {
0 -> IOStream
1 -> ReaderWriter
2 -> Context
else -> error("Unknown File API")
}
}

object ReaderWriter : Filesystem() {

override fun read(context: AndroidContext, filename: String): String {
val file = File(context.filesDir, filename)
return if (file.exists()) FileReader(file).use { it.readText() } else ""
}

override fun write(context: AndroidContext, filename: String, text: String) {
val file = File(context.filesDir, filename)
if (!file.exists()) {
file.createNewFile()
}
FileWriter(file).use { it.write(text) }
}
}

object IOStream : Filesystem() {
override fun read(context: AndroidContext, filename: String): String {
val file = File(context.filesDir, filename)
return if (file.exists()) file.readText() else ""
}

override fun write(context: AndroidContext, filename: String, text: String) {
val file = File(context.filesDir, filename)
if (!file.exists()) {
file.createNewFile()
}
file.writeText(text)
}
}

object Context : Filesystem() {
override fun read(context: AndroidContext, filename: String): String {
val fis = context.openFileInput(filename)
return fis.bufferedReader().readText()
}

override fun write(context: AndroidContext, filename: String, text: String) {
val fos = context.openFileOutput(filename, AndroidContext.MODE_PRIVATE)
fos.bufferedWriter().write(text)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@file:Suppress("UnstableApiUsage")

package io.sentry.android.gradle.instrumentation

import com.android.build.api.instrumentation.ClassContext
import java.util.LinkedList
import org.objectweb.asm.ClassVisitor

class ChainedInstrumentable(
private val instrumentables: List<ClassInstrumentable> = emptyList()
) : ClassInstrumentable {

override fun getVisitor(
instrumentableContext: ClassContext,
apiVersion: Int,
originalVisitor: ClassVisitor,
parameters: SpanAddingClassVisitorFactory.SpanAddingParameters
): ClassVisitor {
// build a chain of visitors in order they are provdied
val queue = LinkedList(instrumentables)
var prevVisitor = originalVisitor
var visitor: ClassVisitor? = null
while (queue.isNotEmpty()) {
val instrumentable = queue.poll()

visitor = if (instrumentable.isInstrumentable(instrumentableContext)) {
instrumentable.getVisitor(
instrumentableContext,
apiVersion,
prevVisitor,
parameters
)
} else {
prevVisitor
}
prevVisitor = visitor
}
return visitor ?: originalVisitor
}

override fun isInstrumentable(data: ClassContext): Boolean =
instrumentables.any { it.isInstrumentable(data) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.sentry.android.gradle.SentryPlugin
import io.sentry.android.gradle.instrumentation.androidx.room.AndroidXRoomDao
import io.sentry.android.gradle.instrumentation.androidx.sqlite.database.AndroidXSQLiteDatabase
import io.sentry.android.gradle.instrumentation.androidx.sqlite.statement.AndroidXSQLiteStatement
import io.sentry.android.gradle.instrumentation.remap.RemappingInstrumentable
import io.sentry.android.gradle.instrumentation.wrap.WrappingInstrumentable
import io.sentry.android.gradle.util.warn
import java.io.File
Expand Down Expand Up @@ -44,7 +45,9 @@ abstract class SpanAddingClassVisitorFactory :
AndroidXSQLiteDatabase(),
AndroidXSQLiteStatement(),
AndroidXRoomDao(),
WrappingInstrumentable()
ChainedInstrumentable(
listOf(WrappingInstrumentable(), RemappingInstrumentable())
)
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@file:Suppress("UnstableApiUsage")

package io.sentry.android.gradle.instrumentation.remap

import com.android.build.api.instrumentation.ClassContext
import io.sentry.android.gradle.instrumentation.ClassInstrumentable
import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory
import io.sentry.android.gradle.instrumentation.util.isSentryClass
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.commons.ClassRemapper
import org.objectweb.asm.commons.SimpleRemapper

class RemappingInstrumentable : ClassInstrumentable {
companion object {
private val mapping = mapOf(
"java/io/FileReader" to "io/sentry/instrumentation/file/SentryFileReader",
"java/io/FileWriter" to "io/sentry/instrumentation/file/SentryFileWriter"
)
}

override fun getVisitor(
instrumentableContext: ClassContext,
apiVersion: Int,
originalVisitor: ClassVisitor,
parameters: SpanAddingClassVisitorFactory.SpanAddingParameters
): ClassVisitor =
ClassRemapper(originalVisitor, SimpleRemapper(mapping))

override fun isInstrumentable(data: ClassContext): Boolean = !data.isSentryClass()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@file:Suppress("UnstableApiUsage")

package io.sentry.android.gradle.instrumentation.util

import com.android.build.api.instrumentation.ClassContext

fun ClassContext.isSentryClass(): Boolean =
when {
currentClassData.className.startsWith("io.sentry") &&
!currentClassData.className.startsWith("io.sentry.android.roomsample") &&
!currentClassData.className.startsWith("io.sentry.samples") &&
!currentClassData.className.startsWith("io.sentry.mobile") -> true
else -> false
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.sentry.android.gradle.instrumentation.CommonClassVisitor
import io.sentry.android.gradle.instrumentation.MethodContext
import io.sentry.android.gradle.instrumentation.MethodInstrumentable
import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory
import io.sentry.android.gradle.instrumentation.util.isSentryClass
import io.sentry.android.gradle.instrumentation.wrap.visitor.WrappingVisitor
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
Expand All @@ -32,13 +33,7 @@ class WrappingInstrumentable : ClassInstrumentable {
)
}

override fun isInstrumentable(data: ClassContext): Boolean {
return when {
data.currentClassData.className.startsWith("io.sentry") &&
!data.currentClassData.className.startsWith("io.sentry.android.roomsample") -> false
else -> true
}
}
override fun isInstrumentable(data: ClassContext): Boolean = !data.isSentryClass()
}

class Wrap(private val classContext: ClassData) : MethodInstrumentable {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package androidx.sqlite.db

interface SupportSQLiteOpenHelper {
abstract class Callback
}

0 comments on commit e61eb20

Please sign in to comment.