Fix network logging

Keep some files when packaging apk
Improve retreiving security/network logs efficiency
This commit is contained in:
BinTianqi
2024-12-21 12:44:28 +08:00
parent 3fb4fb078f
commit 6d531f8fd5
6 changed files with 62 additions and 83 deletions

View File

@@ -54,15 +54,6 @@ android {
compose = true compose = true
aidl = true aidl = true
} }
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
excludes += "/META-INF/**.version"
excludes += "kotlin/**"
excludes += "**.bin"
excludes += "kotlin-tooling-metadata.json"
}
}
androidResources { androidResources {
generateLocaleConfig = true generateLocaleConfig = true
} }

View File

@@ -229,7 +229,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy(navCtrl) } composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy(navCtrl) }
composable(route = "LockTaskMode") { LockTaskMode(navCtrl) } composable(route = "LockTaskMode") { LockTaskMode(navCtrl) }
composable(route = "CACert") { CACert(navCtrl) } composable(route = "CACert") { CACert(navCtrl) }
composable(route = "SecurityLogs") { SecurityLogging(navCtrl) } composable(route = "SecurityLogging") { SecurityLogging(navCtrl) }
composable(route = "DisableAccountManagement") { DisableAccountManagement(navCtrl) } composable(route = "DisableAccountManagement") { DisableAccountManagement(navCtrl) }
composable(route = "SystemUpdatePolicy") { SystemUpdatePolicy(navCtrl) } composable(route = "SystemUpdatePolicy") { SystemUpdatePolicy(navCtrl) }
composable(route = "InstallSystemUpdate") { InstallSystemUpdate(navCtrl) } composable(route = "InstallSystemUpdate") { InstallSystemUpdate(navCtrl) }

View File

@@ -68,7 +68,8 @@ fun writeClipBoard(context: Context, string: String):Boolean{
lateinit var requestPermission: ActivityResultLauncher<String> lateinit var requestPermission: ActivityResultLauncher<String>
lateinit var exportFile: ActivityResultLauncher<Intent> lateinit var exportFile: ActivityResultLauncher<Intent>
val exportFilePath = MutableStateFlow<String?>(null) var exportFilePath: String? = null
var isExportingSecurityOrNetworkLogs = false
fun registerActivityResult(context: ComponentActivity){ fun registerActivityResult(context: ComponentActivity){
getFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult -> getFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
@@ -87,15 +88,19 @@ fun registerActivityResult(context: ComponentActivity){
exportFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> exportFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val intentData = result.data ?: return@registerForActivityResult val intentData = result.data ?: return@registerForActivityResult
val uriData = intentData.data ?: return@registerForActivityResult val uriData = intentData.data ?: return@registerForActivityResult
val path = exportFilePath.value ?: return@registerForActivityResult val path = exportFilePath ?: return@registerForActivityResult
context.contentResolver.openOutputStream(uriData).use { outStream -> context.contentResolver.openOutputStream(uriData).use { outStream ->
if(outStream != null) { if(outStream != null) {
if(isExportingSecurityOrNetworkLogs) outStream.write("[".encodeToByteArray())
File(path).inputStream().use { inStream -> File(path).inputStream().use { inStream ->
inStream.copyTo(outStream) inStream.copyTo(outStream)
} }
if(isExportingSecurityOrNetworkLogs) outStream.write("]".encodeToByteArray())
Toast.makeText(context.applicationContext, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(context.applicationContext, R.string.success, Toast.LENGTH_SHORT).show()
} }
} }
isExportingSecurityOrNetworkLogs = false
exportFilePath = null
} }
} }

View File

@@ -32,24 +32,16 @@ import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.Dhizuku.binderWrapper import com.rosan.dhizuku.api.Dhizuku.binderWrapper
import com.rosan.dhizuku.api.DhizukuBinderWrapper import com.rosan.dhizuku.api.DhizukuBinderWrapper
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.SerialName import kotlinx.serialization.encodeToString
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.addJsonObject import kotlinx.serialization.json.add
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.put import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import kotlin.io.path.inputStream
import kotlin.io.path.notExists
import kotlin.io.path.outputStream
import kotlin.io.path.writeText
lateinit var createManagedProfile: ActivityResultLauncher<Intent> lateinit var createManagedProfile: ActivityResultLauncher<Intent>
lateinit var addDeviceAdmin: ActivityResultLauncher<Intent> lateinit var addDeviceAdmin: ActivityResultLauncher<Intent>
@@ -331,73 +323,59 @@ fun permissionList(): List<PermissionItem>{
@RequiresApi(26) @RequiresApi(26)
fun handleNetworkLogs(context: Context, batchToken: Long) { fun handleNetworkLogs(context: Context, batchToken: Long) {
val networkEvents = context.getDPM().retrieveNetworkLogs(context.getReceiver(), batchToken) ?: return val networkEvents = context.getDPM().retrieveNetworkLogs(context.getReceiver(), batchToken) ?: return
val file = context.filesDir.toPath().resolve("NetworkLogs.json") val file = context.filesDir.resolve("NetworkLogs.json")
if(file.notExists()) file.writeText("[]") val fileExist = file.exists()
val json = Json { ignoreUnknownKeys = true; explicitNulls = false } val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
var events: MutableList<NetworkEventItem> val buffer = file.bufferedWriter()
file.inputStream().use { networkEvents.forEachIndexed { index, event ->
events = json.decodeFromStream(it) if(fileExist && index == 0) buffer.write(",")
} val item = buildJsonObject {
networkEvents.forEach { event -> if(VERSION.SDK_INT >= 28) put("id", event.id)
try { put("time", event.timestamp)
val dnsEvent = event as DnsEvent put("package", event.packageName)
val addresses = mutableListOf<String?>() if(event is DnsEvent) {
dnsEvent.inetAddresses.forEach { inetAddresses -> put("type", "dns")
addresses += inetAddresses.hostAddress put("host", event.hostname)
} put("count", event.totalResolvedAddressCount)
events += NetworkEventItem( putJsonArray("addresses") {
id = if(VERSION.SDK_INT >= 28) event.id else null, packageName = event.packageName event.inetAddresses.forEach { inetAddresses ->
, timestamp = event.timestamp, type = "dns", hostName = dnsEvent.hostname, add(inetAddresses.hostAddress)
hostAddresses = addresses, totalResolvedAddressCount = dnsEvent.totalResolvedAddressCount
)
} catch(_: Exception) {
val connectEvent = event as ConnectEvent
events += NetworkEventItem(
id = if(VERSION.SDK_INT >= 28) event.id else null, packageName = event.packageName, timestamp = event.timestamp, type = "connect",
hostAddress = connectEvent.inetAddress.hostAddress, port = connectEvent.port
)
} }
} }
file.outputStream().use {
json.encodeToStream(events, it)
} }
if(event is ConnectEvent) {
put("type", "connect")
put("address", event.inetAddress.hostAddress)
put("port", event.port)
}
}
buffer.write(json.encodeToString(item))
if(index < networkEvents.size - 1) buffer.write(",")
}
buffer.close()
} }
@Serializable
data class NetworkEventItem(
val id: Long? = null,
@SerialName("package_name") val packageName: String,
val timestamp: Long,
val type: String,
@SerialName("address") val hostAddress: String? = null,
val port: Int? = null,
@SerialName("host_name") val hostName: String? = null,
@SerialName("count") val totalResolvedAddressCount: Int? = null,
@SerialName("addresses") val hostAddresses: List<String?>? = null
)
@RequiresApi(24) @RequiresApi(24)
fun handleSecurityLogs(context: Context) { fun handleSecurityLogs(context: Context) {
val file = context.filesDir.resolve("SecurityLogs.json") val file = context.filesDir.resolve("SecurityLogs.json")
val json = Json { ignoreUnknownKeys = true; explicitNulls = false } val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
if(!file.exists()) file.writeText("[]")
val securityEvents = context.getDPM().retrieveSecurityLogs(context.getReceiver()) val securityEvents = context.getDPM().retrieveSecurityLogs(context.getReceiver())
securityEvents ?: return securityEvents ?: return
val logArray = json.parseToJsonElement(file.readText()).jsonArray val fileExist = file.exists()
val newLogs = buildJsonArray { val buffer = file.bufferedWriter()
securityEvents.forEach { event -> securityEvents.forEachIndexed { index, event ->
addJsonObject { if(fileExist && index == 0) buffer.write(",")
val item = buildJsonObject {
put("time_nanos", event.timeNanos) put("time_nanos", event.timeNanos)
put("tag", event.tag) put("tag", event.tag)
if(VERSION.SDK_INT >= 28) put("level", event.logLevel) if(VERSION.SDK_INT >= 28) put("level", event.logLevel)
if(VERSION.SDK_INT >= 28) put("id", event.id) if(VERSION.SDK_INT >= 28) put("id", event.id)
parseSecurityEventData(event).let { if(it != null) put("data", it) } parseSecurityEventData(event).let { if(it != null) put("data", it) }
} }
buffer.write(json.encodeToString(item))
if(index < securityEvents.size - 1) buffer.write(",")
} }
} buffer.close()
file.outputStream().use {
json.encodeToStream(logArray + newLogs, it)
}
} }
@RequiresApi(24) @RequiresApi(24)
@@ -409,7 +387,7 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
val payload = event.data as Array<*> val payload = event.data as Array<*>
buildJsonObject { buildJsonObject {
put("name", payload[0] as String) put("name", payload[0] as String)
put("start_time", payload[1] as Long) put("time", payload[1] as Long)
put("uid", payload[2] as Int) put("uid", payload[2] as Int)
put("pid", payload[3] as Int) put("pid", payload[3] as Int)
put("seinfo", payload[4] as String) put("seinfo", payload[4] as String)
@@ -456,7 +434,7 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
put("admin", payload[0] as String) put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int) put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int) put("target_user_id", payload[2] as Int)
put("feature_mask", payload[3] as Int) put("mask", payload[3] as Int)
} }
} }
SecurityLog.TAG_KEYGUARD_DISMISSED -> null SecurityLog.TAG_KEYGUARD_DISMISSED -> null
@@ -521,8 +499,8 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
SecurityLog.TAG_PACKAGE_INSTALLED, SecurityLog.TAG_PACKAGE_UNINSTALLED, SecurityLog.TAG_PACKAGE_UPDATED -> { SecurityLog.TAG_PACKAGE_INSTALLED, SecurityLog.TAG_PACKAGE_UNINSTALLED, SecurityLog.TAG_PACKAGE_UPDATED -> {
val payload = event.data as Array<*> val payload = event.data as Array<*>
buildJsonObject { buildJsonObject {
put("package_name", payload[0] as String) put("name", payload[0] as String)
put("version_code", payload[1] as Long) put("version", payload[1] as Long)
put("user_id", payload[2] as Int) put("user_id", payload[2] as Int)
} }
} }

View File

@@ -96,6 +96,7 @@ import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.exportFile import com.bintianqi.owndroid.exportFile
import com.bintianqi.owndroid.exportFilePath import com.bintianqi.owndroid.exportFilePath
import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs
import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
@@ -585,7 +586,8 @@ fun NetworkLogging(navCtrl: NavHostController) {
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("application/json") intent.setType("application/json")
intent.putExtra(Intent.EXTRA_TITLE, "NetworkLogs.json") intent.putExtra(Intent.EXTRA_TITLE, "NetworkLogs.json")
exportFilePath.value = logFile.path exportFilePath = logFile.path
isExportingSecurityOrNetworkLogs = true
exportFile.launch(intent) exportFile.launch(intent)
}, },
enabled = fileSize > 0, enabled = fileSize > 0,

View File

@@ -117,6 +117,7 @@ import com.bintianqi.owndroid.fileUriFlow
import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.humanReadableDate import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs
import com.bintianqi.owndroid.prepareForNotification import com.bintianqi.owndroid.prepareForNotification
import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.toggle import com.bintianqi.owndroid.toggle
@@ -1033,7 +1034,8 @@ fun SecurityLogging(navCtrl: NavHostController) {
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("application/json") intent.setType("application/json")
intent.putExtra(Intent.EXTRA_TITLE, "SecurityLogs.json") intent.putExtra(Intent.EXTRA_TITLE, "SecurityLogs.json")
exportFilePath.value = logFile.path exportFilePath = logFile.path
isExportingSecurityOrNetworkLogs = true
exportFile.launch(intent) exportFile.launch(intent)
}, },
enabled = fileSize > 0, enabled = fileSize > 0,
@@ -1081,7 +1083,8 @@ fun SecurityLogging(navCtrl: NavHostController) {
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("application/json") intent.setType("application/json")
intent.putExtra(Intent.EXTRA_TITLE, "PreRebootSecurityLogs.json") intent.putExtra(Intent.EXTRA_TITLE, "PreRebootSecurityLogs.json")
exportFilePath.value = preRebootSecurityLogs.path exportFilePath = preRebootSecurityLogs.path
isExportingSecurityOrNetworkLogs = true
exportFile.launch(intent) exportFile.launch(intent)
} }
}, },