From 6d531f8fd5fac908071fc381f979c8228bfa4c58 Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Sat, 21 Dec 2024 12:44:28 +0800 Subject: [PATCH] Fix network logging Keep some files when packaging apk Improve retreiving security/network logs efficiency --- app/build.gradle.kts | 9 -- .../com/bintianqi/owndroid/MainActivity.kt | 2 +- .../main/java/com/bintianqi/owndroid/Utils.kt | 9 +- .../java/com/bintianqi/owndroid/dpm/DPM.kt | 114 +++++++----------- .../com/bintianqi/owndroid/dpm/Network.kt | 4 +- .../java/com/bintianqi/owndroid/dpm/System.kt | 7 +- 6 files changed, 62 insertions(+), 83 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9efb9d8..41a93de 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -54,15 +54,6 @@ android { compose = 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 { generateLocaleConfig = true } diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 80a1cda..ff60f00 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -229,7 +229,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) { composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy(navCtrl) } composable(route = "LockTaskMode") { LockTaskMode(navCtrl) } composable(route = "CACert") { CACert(navCtrl) } - composable(route = "SecurityLogs") { SecurityLogging(navCtrl) } + composable(route = "SecurityLogging") { SecurityLogging(navCtrl) } composable(route = "DisableAccountManagement") { DisableAccountManagement(navCtrl) } composable(route = "SystemUpdatePolicy") { SystemUpdatePolicy(navCtrl) } composable(route = "InstallSystemUpdate") { InstallSystemUpdate(navCtrl) } diff --git a/app/src/main/java/com/bintianqi/owndroid/Utils.kt b/app/src/main/java/com/bintianqi/owndroid/Utils.kt index 6179a8b..bf3df3c 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Utils.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Utils.kt @@ -68,7 +68,8 @@ fun writeClipBoard(context: Context, string: String):Boolean{ lateinit var requestPermission: ActivityResultLauncher lateinit var exportFile: ActivityResultLauncher -val exportFilePath = MutableStateFlow(null) +var exportFilePath: String? = null +var isExportingSecurityOrNetworkLogs = false fun registerActivityResult(context: ComponentActivity){ getFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult -> @@ -87,15 +88,19 @@ fun registerActivityResult(context: ComponentActivity){ exportFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> val intentData = result.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 -> if(outStream != null) { + if(isExportingSecurityOrNetworkLogs) outStream.write("[".encodeToByteArray()) File(path).inputStream().use { inStream -> inStream.copyTo(outStream) } + if(isExportingSecurityOrNetworkLogs) outStream.write("]".encodeToByteArray()) Toast.makeText(context.applicationContext, R.string.success, Toast.LENGTH_SHORT).show() } } + isExportingSecurityOrNetworkLogs = false + exportFilePath = null } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt index 1529bd9..8640055 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt @@ -32,24 +32,16 @@ import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.Dhizuku.binderWrapper import com.rosan.dhizuku.api.DhizukuBinderWrapper import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.addJsonObject -import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.add 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.putJsonArray import java.io.IOException 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 lateinit var addDeviceAdmin: ActivityResultLauncher @@ -331,73 +323,59 @@ fun permissionList(): List{ @RequiresApi(26) fun handleNetworkLogs(context: Context, batchToken: Long) { val networkEvents = context.getDPM().retrieveNetworkLogs(context.getReceiver(), batchToken) ?: return - val file = context.filesDir.toPath().resolve("NetworkLogs.json") - if(file.notExists()) file.writeText("[]") + val file = context.filesDir.resolve("NetworkLogs.json") + val fileExist = file.exists() val json = Json { ignoreUnknownKeys = true; explicitNulls = false } - var events: MutableList - file.inputStream().use { - events = json.decodeFromStream(it) - } - networkEvents.forEach { event -> - try { - val dnsEvent = event as DnsEvent - val addresses = mutableListOf() - dnsEvent.inetAddresses.forEach { inetAddresses -> - addresses += inetAddresses.hostAddress + val buffer = file.bufferedWriter() + networkEvents.forEachIndexed { index, event -> + if(fileExist && index == 0) buffer.write(",") + val item = buildJsonObject { + if(VERSION.SDK_INT >= 28) put("id", event.id) + put("time", event.timestamp) + put("package", event.packageName) + if(event is DnsEvent) { + put("type", "dns") + put("host", event.hostname) + put("count", event.totalResolvedAddressCount) + putJsonArray("addresses") { + event.inetAddresses.forEach { inetAddresses -> + add(inetAddresses.hostAddress) + } + } + } + if(event is ConnectEvent) { + put("type", "connect") + put("address", event.inetAddress.hostAddress) + put("port", event.port) } - events += NetworkEventItem( - id = if(VERSION.SDK_INT >= 28) event.id else null, packageName = event.packageName - , timestamp = event.timestamp, type = "dns", hostName = dnsEvent.hostname, - 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 - ) } + buffer.write(json.encodeToString(item)) + if(index < networkEvents.size - 1) buffer.write(",") } - file.outputStream().use { - json.encodeToStream(events, it) - } + 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? = null -) - @RequiresApi(24) fun handleSecurityLogs(context: Context) { val file = context.filesDir.resolve("SecurityLogs.json") val json = Json { ignoreUnknownKeys = true; explicitNulls = false } - if(!file.exists()) file.writeText("[]") val securityEvents = context.getDPM().retrieveSecurityLogs(context.getReceiver()) securityEvents ?: return - val logArray = json.parseToJsonElement(file.readText()).jsonArray - val newLogs = buildJsonArray { - securityEvents.forEach { event -> - addJsonObject { - put("time_nanos", event.timeNanos) - put("tag", event.tag) - if(VERSION.SDK_INT >= 28) put("level", event.logLevel) - if(VERSION.SDK_INT >= 28) put("id", event.id) - parseSecurityEventData(event).let { if(it != null) put("data", it) } - } + val fileExist = file.exists() + val buffer = file.bufferedWriter() + securityEvents.forEachIndexed { index, event -> + if(fileExist && index == 0) buffer.write(",") + val item = buildJsonObject { + put("time_nanos", event.timeNanos) + put("tag", event.tag) + if(VERSION.SDK_INT >= 28) put("level", event.logLevel) + if(VERSION.SDK_INT >= 28) put("id", event.id) + parseSecurityEventData(event).let { if(it != null) put("data", it) } } + buffer.write(json.encodeToString(item)) + if(index < securityEvents.size - 1) buffer.write(",") } - file.outputStream().use { - json.encodeToStream(logArray + newLogs, it) - } + buffer.close() } @RequiresApi(24) @@ -409,7 +387,7 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? { val payload = event.data as Array<*> buildJsonObject { 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("pid", payload[3] as Int) put("seinfo", payload[4] as String) @@ -456,7 +434,7 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? { put("admin", payload[0] as String) put("admin_user_id", payload[1] 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 @@ -521,8 +499,8 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? { SecurityLog.TAG_PACKAGE_INSTALLED, SecurityLog.TAG_PACKAGE_UNINSTALLED, SecurityLog.TAG_PACKAGE_UPDATED -> { val payload = event.data as Array<*> buildJsonObject { - put("package_name", payload[0] as String) - put("version_code", payload[1] as Long) + put("name", payload[0] as String) + put("version", payload[1] as Long) put("user_id", payload[2] as Int) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt index f7cdd3e..b7c94f0 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -96,6 +96,7 @@ import com.bintianqi.owndroid.R import com.bintianqi.owndroid.exportFile import com.bintianqi.owndroid.exportFilePath import com.bintianqi.owndroid.formatFileSize +import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.FunctionItem @@ -585,7 +586,8 @@ fun NetworkLogging(navCtrl: NavHostController) { intent.addCategory(Intent.CATEGORY_OPENABLE) intent.setType("application/json") intent.putExtra(Intent.EXTRA_TITLE, "NetworkLogs.json") - exportFilePath.value = logFile.path + exportFilePath = logFile.path + isExportingSecurityOrNetworkLogs = true exportFile.launch(intent) }, enabled = fileSize > 0, diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt index 54e82ab..ab9dbf0 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt @@ -117,6 +117,7 @@ import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.humanReadableDate +import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs import com.bintianqi.owndroid.prepareForNotification import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.toggle @@ -1033,7 +1034,8 @@ fun SecurityLogging(navCtrl: NavHostController) { intent.addCategory(Intent.CATEGORY_OPENABLE) intent.setType("application/json") intent.putExtra(Intent.EXTRA_TITLE, "SecurityLogs.json") - exportFilePath.value = logFile.path + exportFilePath = logFile.path + isExportingSecurityOrNetworkLogs = true exportFile.launch(intent) }, enabled = fileSize > 0, @@ -1081,7 +1083,8 @@ fun SecurityLogging(navCtrl: NavHostController) { intent.addCategory(Intent.CATEGORY_OPENABLE) intent.setType("application/json") intent.putExtra(Intent.EXTRA_TITLE, "PreRebootSecurityLogs.json") - exportFilePath.value = preRebootSecurityLogs.path + exportFilePath = preRebootSecurityLogs.path + isExportingSecurityOrNetworkLogs = true exportFile.launch(intent) } },