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.service.notification.StatusBarNotification
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import se.ajpanton.notificationlog.data.EncryptedNotificationLogStore
|
import se.ajpanton.notificationlog.data.EncryptedNotificationLogStore
|
||||||
|
import se.ajpanton.notificationlog.data.EncryptedImageStore
|
||||||
import se.ajpanton.notificationlog.model.NotificationAction
|
import se.ajpanton.notificationlog.model.NotificationAction
|
||||||
import se.ajpanton.notificationlog.model.NotificationLogEntry
|
import se.ajpanton.notificationlog.model.NotificationLogEntry
|
||||||
import se.ajpanton.notificationlog.settings.LoggingRuleStore
|
import se.ajpanton.notificationlog.settings.LoggingRuleStore
|
||||||
@@ -19,11 +20,13 @@ class NotificationCaptureService : NotificationListenerService() {
|
|||||||
private lateinit var logStore: EncryptedNotificationLogStore
|
private lateinit var logStore: EncryptedNotificationLogStore
|
||||||
private lateinit var writeExecutor: ExecutorService
|
private lateinit var writeExecutor: ExecutorService
|
||||||
private lateinit var ruleStore: LoggingRuleStore
|
private lateinit var ruleStore: LoggingRuleStore
|
||||||
|
private lateinit var imageStore: EncryptedImageStore
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
logStore = EncryptedNotificationLogStore(this)
|
logStore = EncryptedNotificationLogStore(this)
|
||||||
ruleStore = LoggingRuleStore(this)
|
ruleStore = LoggingRuleStore(this)
|
||||||
|
imageStore = EncryptedImageStore(this)
|
||||||
writeExecutor = Executors.newSingleThreadExecutor { runnable ->
|
writeExecutor = Executors.newSingleThreadExecutor { runnable ->
|
||||||
Thread(runnable, "notification-log-writer")
|
Thread(runnable, "notification-log-writer")
|
||||||
}
|
}
|
||||||
@@ -83,6 +86,9 @@ class NotificationCaptureService : NotificationListenerService() {
|
|||||||
)
|
)
|
||||||
writeExecutor.execute {
|
writeExecutor.execute {
|
||||||
try {
|
try {
|
||||||
|
if (includeContents && snapshot.imageBytes != null && NotificationRuleEvaluator.allows(ruleStore.ruleFor(LoggingType.IMAGE_CONTENT), snapshot.packageName)) {
|
||||||
|
imageStore.save(entry.id, snapshot.imageBytes)
|
||||||
|
}
|
||||||
logStore.append(entry)
|
logStore.append(entry)
|
||||||
} catch (error: Exception) {
|
} catch (error: Exception) {
|
||||||
Log.e(TAG, "Could not persist notification log entry", error)
|
Log.e(TAG, "Could not persist notification log entry", error)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package se.ajpanton.notificationlog.capture
|
package se.ajpanton.notificationlog.capture
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.service.notification.StatusBarNotification
|
import android.service.notification.StatusBarNotification
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ data class NotificationSnapshot(
|
|||||||
val textContents: String?,
|
val textContents: String?,
|
||||||
val hasImage: Boolean,
|
val hasImage: Boolean,
|
||||||
val isRoutine: Boolean,
|
val isRoutine: Boolean,
|
||||||
|
val imageBytes: ByteArray?,
|
||||||
)
|
)
|
||||||
|
|
||||||
object NotificationContents {
|
object NotificationContents {
|
||||||
@@ -39,6 +41,12 @@ object NotificationContents {
|
|||||||
} == true,
|
} == true,
|
||||||
isRoutine = sbn.notification.extras?.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false) == true ||
|
isRoutine = sbn.notification.extras?.getBoolean(Notification.EXTRA_SHOW_CHRONOMETER, false) == true ||
|
||||||
sbn.notification.extras?.containsKey(Notification.EXTRA_PROGRESS) == 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>) {
|
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