Persist notification big pictures encrypted
This commit is contained in:
@@ -6,6 +6,7 @@ import android.service.notification.NotificationListenerService
|
||||
import android.service.notification.StatusBarNotification
|
||||
import android.util.Log
|
||||
import se.ajpanton.notificationlog.data.EncryptedNotificationLogStore
|
||||
import se.ajpanton.notificationlog.data.EncryptedImageStore
|
||||
import se.ajpanton.notificationlog.model.NotificationAction
|
||||
import se.ajpanton.notificationlog.model.NotificationLogEntry
|
||||
import se.ajpanton.notificationlog.settings.LoggingRuleStore
|
||||
@@ -19,11 +20,13 @@ class NotificationCaptureService : NotificationListenerService() {
|
||||
private lateinit var logStore: EncryptedNotificationLogStore
|
||||
private lateinit var writeExecutor: ExecutorService
|
||||
private lateinit var ruleStore: LoggingRuleStore
|
||||
private lateinit var imageStore: EncryptedImageStore
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
logStore = EncryptedNotificationLogStore(this)
|
||||
ruleStore = LoggingRuleStore(this)
|
||||
imageStore = EncryptedImageStore(this)
|
||||
writeExecutor = Executors.newSingleThreadExecutor { runnable ->
|
||||
Thread(runnable, "notification-log-writer")
|
||||
}
|
||||
@@ -83,6 +86,9 @@ class NotificationCaptureService : NotificationListenerService() {
|
||||
)
|
||||
writeExecutor.execute {
|
||||
try {
|
||||
if (includeContents && snapshot.imageBytes != null && NotificationRuleEvaluator.allows(ruleStore.ruleFor(LoggingType.IMAGE_CONTENT), snapshot.packageName)) {
|
||||
imageStore.save(entry.id, snapshot.imageBytes)
|
||||
}
|
||||
logStore.append(entry)
|
||||
} catch (error: Exception) {
|
||||
Log.e(TAG, "Could not persist notification log entry", error)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package se.ajpanton.notificationlog.capture
|
||||
|
||||
import android.app.Notification
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.service.notification.StatusBarNotification
|
||||
|
||||
@@ -10,6 +11,7 @@ data class NotificationSnapshot(
|
||||
val textContents: String?,
|
||||
val hasImage: Boolean,
|
||||
val isRoutine: Boolean,
|
||||
val imageBytes: ByteArray?,
|
||||
)
|
||||
|
||||
object NotificationContents {
|
||||
@@ -39,6 +41,12 @@ object NotificationContents {
|
||||
} == true,
|
||||
isRoutine = sbn.notification.extras?.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false) == true ||
|
||||
sbn.notification.extras?.containsKey(Notification.EXTRA_PROGRESS) == true,
|
||||
imageBytes = sbn.notification.extras?.getParcelable(Notification.EXTRA_PICTURE, Bitmap::class.java)?.let { bitmap ->
|
||||
java.io.ByteArrayOutputStream().use { output ->
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, output)
|
||||
output.toByteArray()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
private fun CharSequence.addTo(parts: MutableSet<String>) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package se.ajpanton.notificationlog.data
|
||||
|
||||
import android.content.Context
|
||||
import java.io.File
|
||||
|
||||
/** Stores copied notification image bytes independently of the source app's URI lifetime. */
|
||||
class EncryptedImageStore(context: Context) {
|
||||
private val directory = File(context.filesDir, "notification-images").also(File::mkdirs)
|
||||
private val cipher = AesGcmCipher(LogEncryptionKeyProvider().getOrCreate())
|
||||
|
||||
fun save(id: String, bytes: ByteArray) {
|
||||
val payload = cipher.encrypt(bytes)
|
||||
File(directory, "$id.bin").outputStream().use { output ->
|
||||
output.write(payload.initializationVector.size)
|
||||
output.write(payload.initializationVector)
|
||||
output.write(payload.cipherText)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user