Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>

<provider
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/dev/pranav/applock/AppLockApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dev.pranav.applock.core.utils.LogUtils
import dev.pranav.applock.data.repository.AppLockRepository
import org.lsposed.hiddenapibypass.HiddenApiBypass
import rikka.sui.Sui
import kotlin.concurrent.thread

class AppLockApplication : Application() {

Expand All @@ -24,6 +25,10 @@ class AppLockApplication : Application() {
initializeComponents()

LogUtils.initialize(this)
// Purge logs older than 3 days on every app start (run in background to avoid ANR)
thread(start = true, name = "LogPurge") {
LogUtils.purgeOldLogs()
}
}

private fun initializeHiddenApiBypass() {
Expand Down
32 changes: 19 additions & 13 deletions app/src/main/java/dev/pranav/applock/core/broadcast/BootReceiver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import dev.pranav.applock.core.utils.LogUtils
import dev.pranav.applock.core.utils.appLockRepository
import dev.pranav.applock.data.repository.BackendImplementation
import dev.pranav.applock.services.AppLockAccessibilityService
Expand All @@ -14,19 +15,24 @@ class BootReceiver : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
val repository = context.appLockRepository()
if (intent.action == Intent.ACTION_PACKAGE_REPLACED) {
repository.setShowDonateLink(true)
}
if (intent.action != Intent.ACTION_BOOT_COMPLETED) {
Log.w(TAG, "Invalid context or intent action")
return
}

try {
val appLockRepository = context.appLockRepository()
startAppropriateServices(context, appLockRepository)
} catch (e: Exception) {
Log.e(TAG, "Error starting services on boot", e)

when (intent.action) {
Intent.ACTION_MY_PACKAGE_REPLACED -> {
Log.d(TAG, "App package replaced, clearing old logs and showing donate link")
repository.setShowDonateLink(true)
// Clear all old logs on app update
LogUtils.clearAllLogs()
}
Intent.ACTION_BOOT_COMPLETED -> {
try {
startAppropriateServices(context, repository)
} catch (e: Exception) {
Log.e(TAG, "Error starting services on boot", e)
}
}
else -> {
Log.w(TAG, "Invalid intent action: ${intent.action}")
}
}
}

Expand Down
69 changes: 69 additions & 0 deletions app/src/main/java/dev/pranav/applock/core/utils/LogUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.util.Log
import androidx.core.content.FileProvider
import java.io.File
import java.time.Instant
import java.time.temporal.ChronoUnit

@SuppressLint("StaticFieldLeak")
object LogUtils {
Expand Down Expand Up @@ -78,4 +79,72 @@ object LogUtils {
return null
}
}

/**
* Clear all security and audit logs.
* Called when the app is updated.
*/
fun clearAllLogs() {
try {
val securityLogFile = File(context.filesDir, SECURITY_LOGS)
if (securityLogFile.exists()) {
securityLogFile.delete()
Log.d(TAG, "Cleared security logs")
}

val appLogFile = File(context.cacheDir, FILE_NAME)
if (appLogFile.exists()) {
appLogFile.delete()
Log.d(TAG, "Cleared app logs")
}
} catch (e: Exception) {
Log.e(TAG, "Error clearing logs", e)
}
}

/**
* Purge log entries older than 3 days from the audit log file.
* This prevents logs from growing indefinitely.
*/
fun purgeOldLogs() {
try {
val securityLogFile = File(context.filesDir, SECURITY_LOGS)
if (!securityLogFile.exists()) {
return
}

val threeDaysAgo = Instant.now().minus(3, ChronoUnit.DAYS)
val lines = securityLogFile.readLines()
val recentLines = mutableListOf<String>()

for (line in lines) {
try {
// Extract timestamp from log line format: "[ISO-8601 timestamp] D [TAG]: [message]"
val timestampStr = line.substringBefore(" ")
val timestamp = Instant.parse(timestampStr)

if (timestamp.isAfter(threeDaysAgo)) {
recentLines.add(line)
}
} catch (e: Exception) {
// If we can't parse the timestamp, keep the line to avoid data loss
recentLines.add(line)
}
}

// Rewrite the file with only recent logs
if (recentLines.size < lines.size) {
if (recentLines.isEmpty()) {
// Delete the file if no recent logs remain
securityLogFile.delete()
Log.d(TAG, "Deleted log file - all entries were older than 3 days")
} else {
securityLogFile.writeText(recentLines.joinToString("\n") + "\n")
Log.d(TAG, "Purged ${lines.size - recentLines.size} old log entries")
}
}
} catch (e: Exception) {
Log.e(TAG, "Error purging old logs", e)
}
}
}