mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
Refactor security logging
Bump version number
This commit is contained in:
@@ -14,21 +14,28 @@ import android.content.pm.PackageInstaller
|
||||
import android.os.Build.VERSION
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.bintianqi.owndroid.MyApplication
|
||||
import com.bintianqi.owndroid.MyNotificationChannel
|
||||
import com.bintianqi.owndroid.NotificationType
|
||||
import com.bintianqi.owndroid.NotificationUtils
|
||||
import com.bintianqi.owndroid.Privilege
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.SP
|
||||
import com.bintianqi.owndroid.ShortcutUtils
|
||||
import com.rosan.dhizuku.api.Dhizuku
|
||||
import com.rosan.dhizuku.api.DhizukuBinderWrapper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.add
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonArray
|
||||
import java.io.OutputStream
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager? {
|
||||
@@ -166,230 +173,336 @@ fun handleNetworkLogs(context: Context, batchToken: Long) {
|
||||
buffer.close()
|
||||
}
|
||||
|
||||
@RequiresApi(24)
|
||||
fun processSecurityLogs(securityEvents: List<SecurityLog.SecurityEvent>, outputStream: OutputStream) {
|
||||
val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
|
||||
val buffer = outputStream.bufferedWriter()
|
||||
securityEvents.forEachIndexed { index, event ->
|
||||
val item = buildJsonObject {
|
||||
put("time", event.timeNanos / 1000)
|
||||
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(",")
|
||||
}
|
||||
buffer.close()
|
||||
@Serializable
|
||||
class SecurityEvent(
|
||||
val id: Long?, val tag: Int, val level: Int?, val time: Long, val data: JsonObject?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class SecurityEventWithData(
|
||||
val id: Long?, val tag: Int, val level: Int?, val time: Long, val data: SecurityEventData?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
sealed class SecurityEventData {
|
||||
@Serializable
|
||||
class AdbShellCmd(val command: String): SecurityEventData()
|
||||
@Serializable
|
||||
class AppProcessStart(
|
||||
val name: String,
|
||||
val time: Long,
|
||||
val uid: Int,
|
||||
val pid: Int,
|
||||
val seinfo: String,
|
||||
val hash: String
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class BackupServiceToggled(
|
||||
val admin: String,
|
||||
val user: Int,
|
||||
val state: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class BluetoothConnection(
|
||||
val mac: String,
|
||||
val successful: Int,
|
||||
@SerialName("failure_reason") val failureReason: String
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class BluetoothDisconnection(
|
||||
val mac: String,
|
||||
val reason: String
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class CameraPolicySet(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
val disabled: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class CaInstalledRemoved(
|
||||
val result: Int,
|
||||
val subject: String,
|
||||
val user: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class CertValidationFailure(val reason: String): SecurityEventData()
|
||||
@Serializable
|
||||
class CryptoSelfTestCompleted(val result: Int): SecurityEventData()
|
||||
@Serializable
|
||||
class KeyguardDisabledFeaturesSet(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
val mask: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class KeyguardDismissAuthAttempt(
|
||||
val result: Int,
|
||||
val strength: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class KeyGeneratedImportDestruction(
|
||||
val result: Int,
|
||||
val alias: String,
|
||||
val uid: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class KeyIntegrityViolation(
|
||||
val alias: String,
|
||||
val uid: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class MaxPasswordAttemptsSet(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
val value: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class MaxScreenLockTimeoutSet(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
val timeout: Long
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class MediaMountUnmount(
|
||||
@SerialName("mount_point") val mountPoint: String,
|
||||
val label: String
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class OsStartup(
|
||||
@SerialName("verified_boot_state") val verifiedBootState: String,
|
||||
@SerialName("dm_verity_mode") val dmVerityMode: String
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class PackageInstalledUninstalledUpdated(
|
||||
val name: String,
|
||||
val version: Long,
|
||||
val user: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class PasswordChanged(
|
||||
val complexity: Int,
|
||||
val user: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class PasswordComplexityRequired(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
val complexity: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class PasswordComplexitySet(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
val length: Int,
|
||||
val quality: Int,
|
||||
val letters: Int,
|
||||
@SerialName("non_letters") val nonLetters: Int,
|
||||
val digits: Int,
|
||||
val uppercase: Int,
|
||||
val lowercase: Int,
|
||||
val symbols: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class PasswordExpirationSet(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
val expiration: Long
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class PasswordHistoryLengthSet(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
val length: Int
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class RemoteLock(
|
||||
val admin: String,
|
||||
@SerialName("admin_user") val adminUser: Int,
|
||||
@SerialName("target_user") val targetUser: Int,
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class SyncRecvSendFile(val path: String): SecurityEventData()
|
||||
@Serializable
|
||||
class UserRestrictionAddedRemoved(
|
||||
val admin: String,
|
||||
val user: Int,
|
||||
val restriction: String
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class WifiConnection(
|
||||
val bssid: String,
|
||||
val type: String,
|
||||
@SerialName("failure_reason") val failureReason: String
|
||||
): SecurityEventData()
|
||||
@Serializable
|
||||
class WifiDisconnection(
|
||||
val bssid: String,
|
||||
val reason: String
|
||||
): SecurityEventData()
|
||||
}
|
||||
|
||||
@RequiresApi(24)
|
||||
fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
|
||||
return when(event.tag) {
|
||||
SecurityLog.TAG_ADB_SHELL_CMD -> JsonPrimitive(event.data as String)
|
||||
fun transformSecurityEventData(tag: Int, payload: Any): SecurityEventData? {
|
||||
return when(tag) {
|
||||
SecurityLog.TAG_ADB_SHELL_CMD -> SecurityEventData.AdbShellCmd(payload as String)
|
||||
SecurityLog.TAG_ADB_SHELL_INTERACTIVE -> null
|
||||
SecurityLog.TAG_APP_PROCESS_START -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("name", payload[0] as String)
|
||||
put("time", payload[1] as Long)
|
||||
put("uid", payload[2] as Int)
|
||||
put("pid", payload[3] as Int)
|
||||
put("seinfo", payload[4] as String)
|
||||
put("apk_hash", payload[5] as String)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.AppProcessStart(
|
||||
data[0] as String, data[1] as Long, data[2] as Int, data[3] as Int,
|
||||
data[4] as String, data[5] as String
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_BACKUP_SERVICE_TOGGLED -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("state", payload[2] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.BackupServiceToggled(data[0] as String, data[1] as Int, data[2] as Int)
|
||||
}
|
||||
SecurityLog.TAG_BLUETOOTH_CONNECTION -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("mac", payload[0] as String)
|
||||
put("successful", payload[1] as Int)
|
||||
(payload[2] as String).let { if(it != "") put("failure_reason", it) }
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.BluetoothConnection(data[0] as String, data[1] as Int, data[2] as String)
|
||||
}
|
||||
SecurityLog.TAG_BLUETOOTH_DISCONNECTION -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("mac", payload[0] as String)
|
||||
(payload[1] as String).let { if(it != "") put("reason", it) }
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.BluetoothDisconnection(data[0] as String, data[1] as String)
|
||||
}
|
||||
SecurityLog.TAG_CAMERA_POLICY_SET -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("target_user_id", payload[2] as Int)
|
||||
put("disabled", payload[3] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.CameraPolicySet(
|
||||
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_CERT_AUTHORITY_INSTALLED, SecurityLog.TAG_CERT_AUTHORITY_REMOVED -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("result", payload[0] as Int)
|
||||
put("subject", payload[1] as String)
|
||||
if(VERSION.SDK_INT >= 30) put("user", payload[2] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.CaInstalledRemoved(data[0] as Int, data[1] as String, data[2] as Int)
|
||||
}
|
||||
SecurityLog.TAG_CERT_VALIDATION_FAILURE -> JsonPrimitive(event.data as String)
|
||||
SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED -> JsonPrimitive(event.data as Int)
|
||||
SecurityLog.TAG_CERT_VALIDATION_FAILURE ->
|
||||
SecurityEventData.CertValidationFailure(payload as String)
|
||||
SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED ->
|
||||
SecurityEventData.CryptoSelfTestCompleted(payload as Int)
|
||||
SecurityLog.TAG_KEYGUARD_DISABLED_FEATURES_SET -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("target_user_id", payload[2] as Int)
|
||||
put("mask", payload[3] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.KeyguardDisabledFeaturesSet(
|
||||
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_KEYGUARD_DISMISSED -> null
|
||||
SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("result", payload[0] as Int)
|
||||
put("strength", payload[1] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.KeyguardDismissAuthAttempt(data[0] as Int, data[1] as Int)
|
||||
}
|
||||
SecurityLog.TAG_KEYGUARD_SECURED -> null
|
||||
SecurityLog.TAG_KEY_DESTRUCTION, SecurityLog.TAG_KEY_GENERATED, SecurityLog.TAG_KEY_IMPORT -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("result", payload[0] as Int)
|
||||
put("alias", payload[1] as String)
|
||||
put("uid", payload[2] as Int)
|
||||
}
|
||||
}
|
||||
SecurityLog.TAG_KEY_INTEGRITY_VIOLATION -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("alias", payload[0] as String)
|
||||
put("uid", payload[1] as Int)
|
||||
}
|
||||
SecurityLog.TAG_KEY_GENERATED, SecurityLog.TAG_KEY_IMPORT, SecurityLog.TAG_KEY_DESTRUCTION -> {
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.KeyGeneratedImportDestruction(
|
||||
data[0] as Int, data[1] as String, data[2] as Int
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_LOGGING_STARTED, SecurityLog.TAG_LOGGING_STOPPED -> null
|
||||
SecurityLog.TAG_LOG_BUFFER_SIZE_CRITICAL -> null
|
||||
SecurityLog.TAG_MAX_PASSWORD_ATTEMPTS_SET -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("target_user_id", payload[2] as Int)
|
||||
put("value", payload[3] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.MaxPasswordAttemptsSet(
|
||||
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_MAX_SCREEN_LOCK_TIMEOUT_SET -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("target_user_id", payload[2] as Int)
|
||||
put("timeout", payload[3] as Long)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.MaxScreenLockTimeoutSet(
|
||||
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Long
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_MEDIA_MOUNT, SecurityLog.TAG_MEDIA_UNMOUNT -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("mount_point", payload[0] as String)
|
||||
put("volume_label", payload[1] as String)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.MediaMountUnmount(data[0] as String, data[1] as String)
|
||||
}
|
||||
SecurityLog.TAG_NFC_ENABLED, SecurityLog.TAG_NFC_DISABLED -> null
|
||||
SecurityLog.TAG_OS_SHUTDOWN -> null
|
||||
SecurityLog.TAG_OS_STARTUP -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("verified_boot_state", payload[0] as String)
|
||||
put("dm_verify_state", payload[1] as String)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.OsStartup(data[0] as String, data[1] as String)
|
||||
}
|
||||
SecurityLog.TAG_PACKAGE_INSTALLED, SecurityLog.TAG_PACKAGE_UNINSTALLED, SecurityLog.TAG_PACKAGE_UPDATED -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("name", payload[0] as String)
|
||||
put("version", payload[1] as Long)
|
||||
put("user_id", payload[2] as Int)
|
||||
}
|
||||
SecurityLog.TAG_PACKAGE_INSTALLED, SecurityLog.TAG_PACKAGE_UPDATED,
|
||||
SecurityLog.TAG_PACKAGE_UNINSTALLED -> {
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.PackageInstalledUninstalledUpdated(
|
||||
data[0] as String, data[1] as Long, data[2] as Int
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_PASSWORD_CHANGED -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("complexity", payload[0] as Int)
|
||||
put("user_id", payload[1] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.PasswordChanged(data[0] as Int, data[1] as Int)
|
||||
}
|
||||
SecurityLog. TAG_PASSWORD_COMPLEXITY_REQUIRED -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("target_user_id", payload[2] as Int)
|
||||
put("complexity", payload[3] as Int)
|
||||
}
|
||||
SecurityLog.TAG_PASSWORD_COMPLEXITY_REQUIRED -> {
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.PasswordComplexityRequired(
|
||||
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_PASSWORD_COMPLEXITY_SET -> {
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.PasswordComplexitySet(
|
||||
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int, data[4] as Int,
|
||||
data[5] as Int, data[6] as Int, data[7] as Int, data[8] as Int, data[9] as Int,
|
||||
data[10] as Int
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_PASSWORD_COMPLEXITY_SET -> null //Deprecated
|
||||
SecurityLog.TAG_PASSWORD_EXPIRATION_SET -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("target_user_id", payload[2] as Int)
|
||||
put("timeout", payload[3] as Long)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.PasswordExpirationSet(
|
||||
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Long
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_PASSWORD_HISTORY_LENGTH_SET -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("target_user_id", payload[2] as Int)
|
||||
put("length", payload[3] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.PasswordHistoryLengthSet(
|
||||
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_REMOTE_LOCK -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("target_user_id", payload[2] as Int)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.RemoteLock(data[0] as String, data[1] as Int, data[2] as Int)
|
||||
}
|
||||
SecurityLog.TAG_SYNC_RECV_FILE, SecurityLog.TAG_SYNC_SEND_FILE -> JsonPrimitive(event.data as String)
|
||||
SecurityLog.TAG_SYNC_RECV_FILE, SecurityLog.TAG_SYNC_SEND_FILE ->
|
||||
SecurityEventData.SyncRecvSendFile(payload as String)
|
||||
SecurityLog.TAG_USER_RESTRICTION_ADDED, SecurityLog.TAG_USER_RESTRICTION_REMOVED -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("admin", payload[0] as String)
|
||||
put("admin_user_id", payload[1] as Int)
|
||||
put("restriction", payload[2] as String)
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.UserRestrictionAddedRemoved(
|
||||
data[0] as String, data[1] as Int, data[2] as String
|
||||
)
|
||||
}
|
||||
SecurityLog.TAG_WIFI_CONNECTION -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("bssid", payload[0] as String)
|
||||
put("type", payload[1] as String)
|
||||
(payload[2] as String).let { if(it != "") put("failure_reason", it) }
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.WifiConnection(data[0] as String, data[1] as String, data[2] as String)
|
||||
}
|
||||
SecurityLog.TAG_WIFI_DISCONNECTION -> {
|
||||
val payload = event.data as Array<*>
|
||||
buildJsonObject {
|
||||
put("bssid", payload[0] as String)
|
||||
(payload[1] as String).let { if(it != "") put("reason", it) }
|
||||
}
|
||||
val data = payload as Array<*>
|
||||
SecurityEventData.WifiDisconnection(data[0] as String, data[1] as String)
|
||||
}
|
||||
SecurityLog.TAG_WIPE_FAILURE -> null
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(24)
|
||||
fun retrieveSecurityLogs(app: MyApplication) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val logs = Privilege.DPM.retrieveSecurityLogs(Privilege.DAR) ?: return@launch
|
||||
app.myRepo.writeSecurityLogs(logs)
|
||||
NotificationUtils.sendBasicNotification(
|
||||
app, NotificationType.SecurityLogsCollected, MyNotificationChannel.SecurityLogging,
|
||||
app.getString(R.string.n_logs_in_total, logs.size)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setDefaultAffiliationID() {
|
||||
if (VERSION.SDK_INT < 26) return
|
||||
if(!SP.isDefaultAffiliationIdSet) {
|
||||
|
||||
@@ -1567,49 +1567,6 @@ fun NetworkLoggingScreen(onNavigateUp: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable object WifiAuthKeypair
|
||||
|
||||
@RequiresApi(31)
|
||||
@Composable
|
||||
fun WifiAuthKeypairScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var keyPair by remember { mutableStateOf("") }
|
||||
MyScaffold(R.string.wifi_auth_keypair, onNavigateUp) {
|
||||
OutlinedTextField(
|
||||
value = keyPair,
|
||||
label = { Text(stringResource(R.string.alias)) },
|
||||
onValueChange = { keyPair = it },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
val isExist = try {
|
||||
Privilege.DPM.isKeyPairGrantedToWifiAuth(keyPair)
|
||||
} catch(e: java.lang.IllegalArgumentException) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
Text(stringResource(R.string.already_exist)+":$isExist")
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Button(
|
||||
onClick = { context.showOperationResultToast(Privilege.DPM.grantKeyPairToWifiAuth(keyPair)) },
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.grant))
|
||||
}
|
||||
Button(
|
||||
onClick = { context.showOperationResultToast(Privilege.DPM.revokeKeyPairFromWifiAuth(keyPair)) },
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.revoke))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable object PreferentialNetworkService
|
||||
|
||||
@RequiresApi(33)
|
||||
|
||||
@@ -88,7 +88,6 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -111,11 +110,11 @@ import com.bintianqi.owndroid.MyViewModel
|
||||
import com.bintianqi.owndroid.Privilege
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.SP
|
||||
import com.bintianqi.owndroid.formatFileSize
|
||||
import com.bintianqi.owndroid.formatDate
|
||||
import com.bintianqi.owndroid.popToast
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.CircularProgressDialog
|
||||
import com.bintianqi.owndroid.ui.ErrorDialog
|
||||
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
|
||||
@@ -132,7 +131,9 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
@@ -178,7 +179,7 @@ fun SystemManagerScreen(
|
||||
if(VERSION.SDK_INT >= 31) {
|
||||
FunctionItem(R.string.nearby_streaming_policy, icon = R.drawable.share_fill0) { onNavigate(NearbyStreamingPolicy) }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && privilege.device) {
|
||||
if (VERSION.SDK_INT >= 28 && privilege.device && !privilege.dhizuku) {
|
||||
FunctionItem(R.string.lock_task_mode, icon = R.drawable.lock_fill0) { onNavigate(LockTaskMode) }
|
||||
}
|
||||
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { onNavigate(CaCert) }
|
||||
@@ -676,7 +677,7 @@ fun ChangeTimeZoneScreen(setTimeZone: (String) -> Boolean, onNavigateUp: () -> U
|
||||
var dialog by remember { mutableStateOf(false) }
|
||||
val availableIds = TimeZone.getAvailableIDs()
|
||||
val validInput = inputTimezone in availableIds
|
||||
MyScaffold(R.string.change_timezone, onNavigateUp) {
|
||||
MyScaffold(R.string.change_timezone, onNavigateUp) {
|
||||
OutlinedTextField(
|
||||
value = inputTimezone,
|
||||
label = { Text(stringResource(R.string.timezone_id)) },
|
||||
@@ -1147,7 +1148,7 @@ fun NearbyStreamingPolicyScreen(
|
||||
fun LockTaskModeScreen(
|
||||
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
|
||||
lockTaskPackages: StateFlow<List<AppInfo>>, getLockTaskPackages: () -> Unit,
|
||||
setLockTaskPackage: (String, Boolean) -> Unit, startLockTaskMode: (String, String) -> Unit,
|
||||
setLockTaskPackage: (String, Boolean) -> Unit, startLockTaskMode: (String, String) -> Boolean,
|
||||
getLockTaskFeatures: () -> Int, setLockTaskFeature: (Int) -> String?, onNavigateUp: () -> Unit
|
||||
) {
|
||||
val coroutine = rememberCoroutineScope()
|
||||
@@ -1202,9 +1203,10 @@ fun LockTaskModeScreen(
|
||||
@RequiresApi(28)
|
||||
@Composable
|
||||
private fun StartLockTaskMode(
|
||||
startLockTaskMode: (String, String) -> Unit,
|
||||
startLockTaskMode: (String, String) -> Boolean,
|
||||
chosenPackage: Channel<String>, onChoosePackage: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var packageName by rememberSaveable { mutableStateOf("") }
|
||||
var activity by rememberSaveable { mutableStateOf("") }
|
||||
@@ -1244,7 +1246,8 @@ private fun StartLockTaskMode(
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 5.dp),
|
||||
onClick = {
|
||||
startLockTaskMode(packageName, activity)
|
||||
val result = startLockTaskMode(packageName, activity)
|
||||
if (!result) context.showOperationResultToast(false)
|
||||
},
|
||||
enabled = packageName.isNotBlank() && (!specifyActivity || activity.isNotBlank())
|
||||
) {
|
||||
@@ -1525,77 +1528,104 @@ fun CaCertScreen(
|
||||
|
||||
@RequiresApi(24)
|
||||
@Composable
|
||||
fun SecurityLoggingScreen(onNavigateUp: () -> Unit) {
|
||||
fun SecurityLoggingScreen(
|
||||
getEnabled: () -> Boolean, setEnabled: (Boolean) -> Unit, exportLogs: (Uri, () -> Unit) -> Unit,
|
||||
getCount: () -> Int, deleteLogs: () -> Unit, getPRLogs: () -> Boolean,
|
||||
exportPRLogs: (Uri, () -> Unit) -> Unit, onNavigateUp: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val logFile = context.filesDir.resolve("SecurityLogs.json")
|
||||
var fileSize by remember { mutableLongStateOf(0) }
|
||||
LaunchedEffect(Unit) { fileSize = logFile.length() }
|
||||
var preRebootSecurityLogs by remember { mutableStateOf(byteArrayOf()) }
|
||||
val exportPreRebootSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
|
||||
if(uri != null) context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
preRebootSecurityLogs.inputStream().copyTo(outStream)
|
||||
var enabled by remember { mutableStateOf(false) }
|
||||
var logsCount by remember { mutableIntStateOf(0) }
|
||||
var exporting by remember { mutableStateOf(false) }
|
||||
var dialog by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
enabled = getEnabled()
|
||||
logsCount = getCount()
|
||||
}
|
||||
val exportLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("application/json")
|
||||
) {
|
||||
if (it != null) {
|
||||
exporting = true
|
||||
exportLogs(it) {
|
||||
exporting = false
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
val exportSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
|
||||
if(uri != null) context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
outStream.write("[".toByteArray())
|
||||
logFile.inputStream().use { it.copyTo(outStream) }
|
||||
outStream.write("]".toByteArray())
|
||||
context.showOperationResultToast(true)
|
||||
val exportPRLogsLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("application/json")
|
||||
) {
|
||||
if (it != null) {
|
||||
exporting = true
|
||||
exportPRLogs(it) {
|
||||
exporting = false
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.security_logging, onNavigateUp) {
|
||||
MyScaffold(R.string.security_logging, onNavigateUp, 0.dp) {
|
||||
SwitchItem(
|
||||
R.string.enable,
|
||||
getState = { Privilege.DPM.isSecurityLoggingEnabled(Privilege.DAR) },
|
||||
onCheckedChange = { Privilege.DPM.setSecurityLoggingEnabled(Privilege.DAR, it) },
|
||||
padding = false
|
||||
R.string.enable, enabled, {
|
||||
setEnabled(it)
|
||||
enabled = it
|
||||
}
|
||||
)
|
||||
Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize)))
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
Button(
|
||||
onClick = {
|
||||
exportSecurityLogs.launch("SecurityLogs.json")
|
||||
},
|
||||
enabled = fileSize > 0,
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.export_logs))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
logFile.delete()
|
||||
fileSize = logFile.length()
|
||||
},
|
||||
enabled = fileSize > 0,
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.delete_logs))
|
||||
}
|
||||
Text(
|
||||
stringResource(R.string.n_logs_in_total, logsCount),
|
||||
Modifier.padding(HorizontalPadding)
|
||||
)
|
||||
Button(
|
||||
{
|
||||
val date = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(Date())
|
||||
exportLauncher.launch("security_logs_$date")
|
||||
},
|
||||
Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding),
|
||||
logsCount > 0
|
||||
) {
|
||||
Text(stringResource(R.string.export_logs))
|
||||
}
|
||||
Notes(R.string.info_security_log)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if (logsCount > 0) FilledTonalButton(
|
||||
{ dialog = true },
|
||||
Modifier.fillMaxWidth().padding(HorizontalPadding, 4.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.delete_logs))
|
||||
}
|
||||
Notes(R.string.info_security_log, HorizontalPadding)
|
||||
Button(
|
||||
onClick = {
|
||||
val logs = Privilege.DPM.retrievePreRebootSecurityLogs(Privilege.DAR)
|
||||
if(logs == null) {
|
||||
context.popToast(R.string.no_logs)
|
||||
return@Button
|
||||
if (getPRLogs()) {
|
||||
val date = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(Date())
|
||||
exportPRLogsLauncher.launch("pre_reboot_security_logs_$date")
|
||||
} else {
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
outputStream.write("[".encodeToByteArray())
|
||||
processSecurityLogs(logs, outputStream)
|
||||
outputStream.write("]".encodeToByteArray())
|
||||
preRebootSecurityLogs = outputStream.toByteArray()
|
||||
exportPreRebootSecurityLogs.launch("PreRebootSecurityLogs.json")
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth().padding(HorizontalPadding, 15.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.pre_reboot_security_logs))
|
||||
}
|
||||
Notes(R.string.info_pre_reboot_security_log)
|
||||
Notes(R.string.info_pre_reboot_security_log, HorizontalPadding)
|
||||
}
|
||||
if (exporting) CircularProgressDialog { exporting = false }
|
||||
if (dialog) AlertDialog(
|
||||
text = { Text(stringResource(R.string.delete_logs)) },
|
||||
confirmButton = {
|
||||
TextButton({
|
||||
deleteLogs()
|
||||
logsCount = 0
|
||||
dialog = false
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton({ dialog = false }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
onDismissRequest = { dialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable object DisableAccountManagement
|
||||
|
||||
Reference in New Issue
Block a user