mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-27 04:16:00 +00:00
Refactor security logging
Bump version number
This commit is contained in:
@@ -26,8 +26,8 @@ android {
|
|||||||
applicationId = "com.bintianqi.owndroid"
|
applicationId = "com.bintianqi.owndroid"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 40
|
versionCode = 41
|
||||||
versionName = "7.1"
|
versionName = "7.2"
|
||||||
multiDexEnabled = false
|
multiDexEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.bintianqi.owndroid
|
package com.bintianqi.owndroid
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
@@ -242,7 +243,10 @@ class MainActivity : FragmentActivity() {
|
|||||||
val locale = context.resources?.configuration?.locale
|
val locale = context.resources?.configuration?.locale
|
||||||
zhCN = locale == Locale.SIMPLIFIED_CHINESE || locale == Locale.CHINESE || locale == Locale.CHINA
|
zhCN = locale == Locale.SIMPLIFIED_CHINESE || locale == Locale.CHINESE || locale == Locale.CHINA
|
||||||
val vm by viewModels<MyViewModel>()
|
val vm by viewModels<MyViewModel>()
|
||||||
if (VERSION.SDK_INT >= 33) {
|
if (
|
||||||
|
VERSION.SDK_INT >= 33 &&
|
||||||
|
checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {}
|
val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {}
|
||||||
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
}
|
}
|
||||||
@@ -379,7 +383,11 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
|||||||
CaCertScreen(vm.installedCaCerts, vm::getCaCerts, vm::installCaCert, vm::parseCaCert,
|
CaCertScreen(vm.installedCaCerts, vm::getCaCerts, vm::installCaCert, vm::parseCaCert,
|
||||||
vm::exportCaCert, vm::uninstallCaCert, vm::uninstallAllCaCerts, ::navigateUp)
|
vm::exportCaCert, vm::uninstallCaCert, vm::uninstallAllCaCerts, ::navigateUp)
|
||||||
}
|
}
|
||||||
composable<SecurityLogging> { SecurityLoggingScreen(::navigateUp) }
|
composable<SecurityLogging> {
|
||||||
|
SecurityLoggingScreen(vm::getSecurityLoggingEnabled, vm::setSecurityLoggingEnabled,
|
||||||
|
vm::exportSecurityLogs, vm::getSecurityLogsCount, vm::deleteSecurityLogs,
|
||||||
|
vm::getPreRebootSecurityLogs, vm::exportPreRebootSecurityLogs, ::navigateUp)
|
||||||
|
}
|
||||||
composable<DisableAccountManagement> {
|
composable<DisableAccountManagement> {
|
||||||
DisableAccountManagementScreen(vm.mdAccountTypes, vm::getMdAccountTypes,
|
DisableAccountManagementScreen(vm.mdAccountTypes, vm::getMdAccountTypes,
|
||||||
vm::setMdAccountType, ::navigateUp)
|
vm::setMdAccountType, ::navigateUp)
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ import android.content.Context
|
|||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
|
||||||
class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 1) {
|
class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 2) {
|
||||||
override fun onCreate(db: SQLiteDatabase) {
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
db.execSQL("CREATE TABLE dhizuku_clients (uid INTEGER PRIMARY KEY," +
|
db.execSQL("CREATE TABLE dhizuku_clients (uid INTEGER PRIMARY KEY," +
|
||||||
"signature TEXT, permissions TEXT)")
|
"signature TEXT, permissions TEXT)")
|
||||||
}
|
}
|
||||||
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
|
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
if (oldVersion < 2) {
|
||||||
|
db.execSQL("CREATE TABLE security_logs (id INTEGER, tag INTEGER, level INTEGER," +
|
||||||
|
"time INTEGER, data TEXT)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,18 @@
|
|||||||
package com.bintianqi.owndroid
|
package com.bintianqi.owndroid
|
||||||
|
|
||||||
|
import android.app.admin.SecurityLog
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
|
import android.database.DatabaseUtils
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.os.Build.VERSION
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.database.getStringOrNull
|
||||||
|
import com.bintianqi.owndroid.dpm.SecurityEvent
|
||||||
|
import com.bintianqi.owndroid.dpm.SecurityEventWithData
|
||||||
|
import com.bintianqi.owndroid.dpm.transformSecurityEventData
|
||||||
|
import kotlinx.serialization.json.ClassDiscriminatorMode
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
class MyRepository(val dbHelper: MyDbHelper) {
|
class MyRepository(val dbHelper: MyDbHelper) {
|
||||||
fun getDhizukuClients(): List<DhizukuClientInfo> {
|
fun getDhizukuClients(): List<DhizukuClientInfo> {
|
||||||
@@ -43,4 +54,104 @@ class MyRepository(val dbHelper: MyDbHelper) {
|
|||||||
fun deleteDhizukuClient(info: DhizukuClientInfo) {
|
fun deleteDhizukuClient(info: DhizukuClientInfo) {
|
||||||
dbHelper.writableDatabase.delete("dhizuku_clients", "uid = ${info.uid}", null)
|
dbHelper.writableDatabase.delete("dhizuku_clients", "uid = ${info.uid}", null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSecurityLogsCount(): Long {
|
||||||
|
return DatabaseUtils.queryNumEntries(dbHelper.readableDatabase, "security_logs")
|
||||||
|
}
|
||||||
|
@RequiresApi(24)
|
||||||
|
fun writeSecurityLogs(events: List<SecurityLog.SecurityEvent>) {
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
val json = Json {
|
||||||
|
classDiscriminatorMode = ClassDiscriminatorMode.NONE
|
||||||
|
}
|
||||||
|
val statement = db.compileStatement("INSERT INTO security_logs VALUES (?, ?, ?, ?, ?)")
|
||||||
|
db.beginTransaction()
|
||||||
|
events.forEach { event ->
|
||||||
|
try {
|
||||||
|
if (VERSION.SDK_INT >= 28) {
|
||||||
|
statement.bindLong(1, event.id)
|
||||||
|
statement.bindLong(3, event.logLevel.toLong())
|
||||||
|
} else {
|
||||||
|
statement.bindNull(1)
|
||||||
|
statement.bindNull(3)
|
||||||
|
}
|
||||||
|
statement.bindLong(2, event.tag.toLong())
|
||||||
|
statement.bindLong(4, event.timeNanos / 1000000)
|
||||||
|
val dataObject = transformSecurityEventData(event.tag, event.data)
|
||||||
|
if (dataObject == null) {
|
||||||
|
statement.bindNull(5)
|
||||||
|
} else {
|
||||||
|
statement.bindString(5, json.encodeToString(dataObject))
|
||||||
|
}
|
||||||
|
statement.executeInsert()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
statement.clearBindings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
db.endTransaction()
|
||||||
|
statement.close()
|
||||||
|
}
|
||||||
|
fun exportSecurityLogs(stream: OutputStream) {
|
||||||
|
var offset = 0
|
||||||
|
val json = Json {
|
||||||
|
explicitNulls = false
|
||||||
|
}
|
||||||
|
var addComma = false
|
||||||
|
val bw = stream.bufferedWriter()
|
||||||
|
bw.write("[")
|
||||||
|
while (true) {
|
||||||
|
dbHelper.readableDatabase.rawQuery(
|
||||||
|
"SELECT * FROM security_logs LIMIT ? OFFSET ?",
|
||||||
|
arrayOf(100.toString(), offset.toString())
|
||||||
|
).use { cursor ->
|
||||||
|
if (cursor.count == 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
if (addComma) bw.write(",")
|
||||||
|
addComma = true
|
||||||
|
val event = SecurityEvent(
|
||||||
|
cursor.getLong(0), cursor.getInt(1), cursor.getInt(2), cursor.getLong(3),
|
||||||
|
cursor.getStringOrNull(4)?.let { json.decodeFromString(it) }
|
||||||
|
)
|
||||||
|
bw.write(json.encodeToString(event))
|
||||||
|
}
|
||||||
|
offset += 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bw.write("]")
|
||||||
|
bw.close()
|
||||||
|
}
|
||||||
|
@RequiresApi(24)
|
||||||
|
fun exportPRSecurityLogs(logs: List<SecurityLog.SecurityEvent>, stream: OutputStream) {
|
||||||
|
val bw = stream.bufferedWriter()
|
||||||
|
bw.write("[")
|
||||||
|
val json = Json {
|
||||||
|
explicitNulls = false
|
||||||
|
classDiscriminatorMode = ClassDiscriminatorMode.NONE
|
||||||
|
}
|
||||||
|
var addComma = false
|
||||||
|
logs.forEach { log ->
|
||||||
|
try {
|
||||||
|
if (addComma) bw.write(",")
|
||||||
|
addComma = true
|
||||||
|
val event = SecurityEventWithData(
|
||||||
|
if (VERSION.SDK_INT >= 28) log.id else null, log.tag,
|
||||||
|
if (VERSION.SDK_INT >= 28) log.logLevel else null, log.timeNanos / 1000000,
|
||||||
|
transformSecurityEventData(log.tag, log.data)
|
||||||
|
)
|
||||||
|
bw.write(json.encodeToString(event))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bw.write("]")
|
||||||
|
bw.close()
|
||||||
|
}
|
||||||
|
fun deleteSecurityLogs() {
|
||||||
|
dbHelper.writableDatabase.execSQL("DELETE FROM security_logs")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ import android.app.admin.FactoryResetProtectionPolicy
|
|||||||
import android.app.admin.IDevicePolicyManager
|
import android.app.admin.IDevicePolicyManager
|
||||||
import android.app.admin.PackagePolicy
|
import android.app.admin.PackagePolicy
|
||||||
import android.app.admin.PreferentialNetworkServiceConfig
|
import android.app.admin.PreferentialNetworkServiceConfig
|
||||||
|
import android.app.admin.SecurityLog
|
||||||
import android.app.admin.SystemUpdateInfo
|
import android.app.admin.SystemUpdateInfo
|
||||||
import android.app.admin.SystemUpdatePolicy
|
import android.app.admin.SystemUpdatePolicy
|
||||||
import android.app.admin.WifiSsidPolicy
|
import android.app.admin.WifiSsidPolicy
|
||||||
@@ -756,9 +757,12 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
getLockTaskPackages()
|
getLockTaskPackages()
|
||||||
}
|
}
|
||||||
@RequiresApi(28)
|
@RequiresApi(28)
|
||||||
fun startLockTaskMode(packageName: String, activity: String): Int {
|
fun startLockTaskMode(packageName: String, activity: String): Boolean {
|
||||||
if (!NotificationUtils.checkPermission(application)) return 0
|
if (!DPM.isLockTaskPermitted(packageName)) {
|
||||||
if (!DPM.isLockTaskPermitted(packageName)) return 1
|
val list = lockTaskPackages.value.map { it.name } + packageName
|
||||||
|
DPM.setLockTaskPackages(DAR, list.toTypedArray())
|
||||||
|
getLockTaskPackages()
|
||||||
|
}
|
||||||
val options = ActivityOptions.makeBasic().setLockTaskEnabled(true)
|
val options = ActivityOptions.makeBasic().setLockTaskEnabled(true)
|
||||||
val intent = if(activity.isNotEmpty()) {
|
val intent = if(activity.isNotEmpty()) {
|
||||||
Intent().setComponent(ComponentName(packageName, activity))
|
Intent().setComponent(ComponentName(packageName, activity))
|
||||||
@@ -766,9 +770,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
application.startActivity(intent, options.toBundle())
|
application.startActivity(intent, options.toBundle())
|
||||||
return 0
|
return true
|
||||||
} else {
|
} else {
|
||||||
return 2
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@RequiresApi(28)
|
@RequiresApi(28)
|
||||||
@@ -913,6 +917,53 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
DPM.installSystemUpdate(DAR, uri, application.mainExecutor, callback)
|
DPM.installSystemUpdate(DAR, uri, application.mainExecutor, callback)
|
||||||
}
|
}
|
||||||
|
@RequiresApi(24)
|
||||||
|
fun getSecurityLoggingEnabled(): Boolean {
|
||||||
|
return DPM.isSecurityLoggingEnabled(DAR)
|
||||||
|
}
|
||||||
|
@RequiresApi(24)
|
||||||
|
fun setSecurityLoggingEnabled(enabled: Boolean) {
|
||||||
|
DPM.setSecurityLoggingEnabled(DAR, enabled)
|
||||||
|
}
|
||||||
|
fun exportSecurityLogs(uri: Uri, callback: () -> Unit) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
application.contentResolver.openOutputStream(uri)?.use {
|
||||||
|
myRepo.exportSecurityLogs(it)
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun getSecurityLogsCount(): Int {
|
||||||
|
return myRepo.getSecurityLogsCount().toInt()
|
||||||
|
}
|
||||||
|
fun deleteSecurityLogs() {
|
||||||
|
myRepo.deleteSecurityLogs()
|
||||||
|
}
|
||||||
|
var preRebootSecurityLogs = emptyList<SecurityLog.SecurityEvent>()
|
||||||
|
@RequiresApi(24)
|
||||||
|
fun getPreRebootSecurityLogs(): Boolean {
|
||||||
|
if (preRebootSecurityLogs.isNotEmpty()) return true
|
||||||
|
return try {
|
||||||
|
val logs = DPM.retrievePreRebootSecurityLogs(DAR)
|
||||||
|
if (logs != null && logs.isNotEmpty()) {
|
||||||
|
preRebootSecurityLogs = logs
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
} catch (_: SecurityException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@RequiresApi(24)
|
||||||
|
fun exportPreRebootSecurityLogs(uri: Uri, callback: () -> Unit) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val stream = application.contentResolver.openOutputStream(uri) ?: return@launch
|
||||||
|
myRepo.exportPRSecurityLogs(preRebootSecurityLogs, stream)
|
||||||
|
stream.close()
|
||||||
|
withContext(Dispatchers.Main) { callback() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
fun isCreatingWorkProfileAllowed(): Boolean {
|
fun isCreatingWorkProfileAllowed(): Boolean {
|
||||||
|
|||||||
@@ -1,49 +1,36 @@
|
|||||||
package com.bintianqi.owndroid
|
package com.bintianqi.owndroid
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Notification
|
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import androidx.core.app.NotificationChannelCompat
|
||||||
import android.os.Build
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
|
||||||
object NotificationUtils {
|
object NotificationUtils {
|
||||||
fun checkPermission(context: Context): Boolean {
|
|
||||||
return if(Build.VERSION.SDK_INT >= 33)
|
|
||||||
context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
|
|
||||||
else false
|
|
||||||
}
|
|
||||||
fun createChannels(context: Context) {
|
fun createChannels(context: Context) {
|
||||||
if (Build.VERSION.SDK_INT < 26) return
|
val channels = MyNotificationChannel.entries.map {
|
||||||
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
NotificationChannelCompat.Builder(it.id, it.importance)
|
||||||
val lockTaskMode = NotificationChannel(
|
.setName(context.getString(it.text))
|
||||||
MyNotificationChannel.LockTaskMode.id,
|
.build()
|
||||||
context.getString(MyNotificationChannel.LockTaskMode.text),
|
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
|
||||||
)
|
|
||||||
val events = NotificationChannel(
|
|
||||||
MyNotificationChannel.Events.id,
|
|
||||||
context.getString(MyNotificationChannel.Events.text),
|
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
|
||||||
)
|
|
||||||
nm.createNotificationChannels(listOf(lockTaskMode, events))
|
|
||||||
}
|
}
|
||||||
fun notifyEvent(context: Context, type: NotificationType, text: String) {
|
NotificationManagerCompat.from(context).createNotificationChannelsCompat(channels)
|
||||||
val notification = NotificationCompat.Builder(context, MyNotificationChannel.Events.id)
|
}
|
||||||
|
fun sendBasicNotification(
|
||||||
|
context: Context, type: NotificationType, channel: MyNotificationChannel, text: String
|
||||||
|
) {
|
||||||
|
val notification = NotificationCompat.Builder(context, channel.id)
|
||||||
.setSmallIcon(type.icon)
|
.setSmallIcon(type.icon)
|
||||||
.setContentTitle(context.getString(type.text))
|
.setContentTitle(context.getString(type.text))
|
||||||
.setContentText(text)
|
.setContentText(text)
|
||||||
.build()
|
.build()
|
||||||
notify(context, type, notification)
|
|
||||||
}
|
|
||||||
fun notify(context: Context, type: NotificationType, notification: Notification) {
|
|
||||||
val enabledNotifications = SP.notifications?.split(',')?.mapNotNull { it.toIntOrNull() }
|
|
||||||
if (enabledNotifications == null || type.id in enabledNotifications) {
|
|
||||||
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
nm.notify(type.id, notification)
|
nm.notify(type.id, notification)
|
||||||
}
|
}
|
||||||
|
fun notifyEvent(context: Context, type: NotificationType, text: String) {
|
||||||
|
val enabledNotifications = SP.notifications?.split(',')?.mapNotNull { it.toIntOrNull() }
|
||||||
|
if (enabledNotifications == null || type.id in enabledNotifications) {
|
||||||
|
sendBasicNotification(context, type, MyNotificationChannel.Events, text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fun cancel(context: Context, type: NotificationType) {
|
fun cancel(context: Context, type: NotificationType) {
|
||||||
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
@@ -62,10 +49,12 @@ enum class NotificationType(val id: Int, val text: Int, val icon: Int) {
|
|||||||
BugReportShared(8, R.string.bug_report_shared, R.drawable.bug_report_fill0),
|
BugReportShared(8, R.string.bug_report_shared, R.drawable.bug_report_fill0),
|
||||||
BugReportSharingDeclined(9, R.string.bug_report_sharing_declined, R.drawable.bug_report_fill0),
|
BugReportSharingDeclined(9, R.string.bug_report_sharing_declined, R.drawable.bug_report_fill0),
|
||||||
BugReportFailed(10, R.string.bug_report_failed, R.drawable.bug_report_fill0),
|
BugReportFailed(10, R.string.bug_report_failed, R.drawable.bug_report_fill0),
|
||||||
SystemUpdatePending(11, R.string.system_update_pending, R.drawable.system_update_fill0)
|
SystemUpdatePending(11, R.string.system_update_pending, R.drawable.system_update_fill0),
|
||||||
|
SecurityLogsCollected(12, R.string.security_logs_collected, R.drawable.description_fill0),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class MyNotificationChannel(val id: String, val text: Int) {
|
enum class MyNotificationChannel(val id: String, val text: Int, val importance: Int) {
|
||||||
LockTaskMode("LockTaskMode", R.string.lock_task_mode),
|
LockTaskMode("LockTaskMode", R.string.lock_task_mode, NotificationManagerCompat.IMPORTANCE_HIGH),
|
||||||
Events("Events", R.string.events)
|
Events("Events", R.string.events, NotificationManagerCompat.IMPORTANCE_LOW),
|
||||||
|
SecurityLogging("SecurityLogging", R.string.security_logging, NotificationManagerCompat.IMPORTANCE_MIN)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.bintianqi.owndroid
|
package com.bintianqi.owndroid
|
||||||
|
|
||||||
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.admin.DeviceAdminReceiver
|
import android.app.admin.DeviceAdminReceiver
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
@@ -12,7 +13,7 @@ import android.os.UserManager
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import com.bintianqi.owndroid.dpm.handleNetworkLogs
|
import com.bintianqi.owndroid.dpm.handleNetworkLogs
|
||||||
import com.bintianqi.owndroid.dpm.handlePrivilegeChange
|
import com.bintianqi.owndroid.dpm.handlePrivilegeChange
|
||||||
import com.bintianqi.owndroid.dpm.processSecurityLogs
|
import com.bintianqi.owndroid.dpm.retrieveSecurityLogs
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -21,11 +22,10 @@ class Receiver : DeviceAdminReceiver() {
|
|||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
super.onReceive(context, intent)
|
super.onReceive(context, intent)
|
||||||
if(VERSION.SDK_INT >= 26 && intent.action == "com.bintianqi.owndroid.action.STOP_LOCK_TASK_MODE") {
|
if(VERSION.SDK_INT >= 26 && intent.action == "com.bintianqi.owndroid.action.STOP_LOCK_TASK_MODE") {
|
||||||
val dpm = getManager(context)
|
|
||||||
val receiver = ComponentName(context, this::class.java)
|
val receiver = ComponentName(context, this::class.java)
|
||||||
val packages = dpm.getLockTaskPackages(receiver)
|
val packages = Privilege.DPM.getLockTaskPackages(receiver)
|
||||||
dpm.setLockTaskPackages(receiver, arrayOf())
|
Privilege.DPM.setLockTaskPackages(receiver, arrayOf())
|
||||||
dpm.setLockTaskPackages(receiver, packages)
|
Privilege.DPM.setLockTaskPackages(receiver, packages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,29 +56,23 @@ class Receiver : DeviceAdminReceiver() {
|
|||||||
|
|
||||||
override fun onSecurityLogsAvailable(context: Context, intent: Intent) {
|
override fun onSecurityLogsAvailable(context: Context, intent: Intent) {
|
||||||
super.onSecurityLogsAvailable(context, intent)
|
super.onSecurityLogsAvailable(context, intent)
|
||||||
if(VERSION.SDK_INT >= 24) {
|
if (VERSION.SDK_INT >= 24) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
retrieveSecurityLogs(context.applicationContext as MyApplication)
|
||||||
val events = getManager(context).retrieveSecurityLogs(MyAdminComponent) ?: return@launch
|
|
||||||
val file = context.filesDir.resolve("SecurityLogs.json")
|
|
||||||
val fileExists = file.exists()
|
|
||||||
file.outputStream().use {
|
|
||||||
if(fileExists) it.write(",".encodeToByteArray())
|
|
||||||
processSecurityLogs(events, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLockTaskModeEntering(context: Context, intent: Intent, pkg: String) {
|
override fun onLockTaskModeEntering(context: Context, intent: Intent, pkg: String) {
|
||||||
super.onLockTaskModeEntering(context, intent, pkg)
|
super.onLockTaskModeEntering(context, intent, pkg)
|
||||||
if (!NotificationUtils.checkPermission(context)) return
|
val stopIntent = Intent(context, this::class.java)
|
||||||
val intent = Intent(context, this::class.java).setAction("com.bintianqi.owndroid.action.STOP_LOCK_TASK_MODE")
|
.setAction("com.bintianqi.owndroid.action.STOP_LOCK_TASK_MODE")
|
||||||
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
val pendingIntent = PendingIntent.getBroadcast(context, 0, stopIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
val builder = NotificationCompat.Builder(context, MyNotificationChannel.LockTaskMode.id)
|
val notification = NotificationCompat.Builder(context, MyNotificationChannel.LockTaskMode.id)
|
||||||
.setContentTitle(context.getText(R.string.lock_task_mode))
|
.setContentTitle(context.getText(R.string.lock_task_mode))
|
||||||
.setSmallIcon(R.drawable.lock_fill0)
|
.setSmallIcon(R.drawable.lock_fill0)
|
||||||
.addAction(NotificationCompat.Action.Builder(null, context.getString(R.string.stop), pendingIntent).build())
|
.addAction(NotificationCompat.Action.Builder(null, context.getString(R.string.stop), pendingIntent).build())
|
||||||
NotificationUtils.notify(context, NotificationType.LockTaskMode, builder.build())
|
.build()
|
||||||
|
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
nm.notify(NotificationType.LockTaskMode.id, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLockTaskModeExiting(context: Context, intent: Intent) {
|
override fun onLockTaskModeExiting(context: Context, intent: Intent) {
|
||||||
|
|||||||
@@ -14,21 +14,28 @@ import android.content.pm.PackageInstaller
|
|||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
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.Privilege
|
||||||
import com.bintianqi.owndroid.R
|
import com.bintianqi.owndroid.R
|
||||||
import com.bintianqi.owndroid.SP
|
import com.bintianqi.owndroid.SP
|
||||||
import com.bintianqi.owndroid.ShortcutUtils
|
import com.bintianqi.owndroid.ShortcutUtils
|
||||||
import com.rosan.dhizuku.api.Dhizuku
|
import com.rosan.dhizuku.api.Dhizuku
|
||||||
import com.rosan.dhizuku.api.DhizukuBinderWrapper
|
import com.rosan.dhizuku.api.DhizukuBinderWrapper
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
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.Json
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
|
||||||
import kotlinx.serialization.json.add
|
import kotlinx.serialization.json.add
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
import kotlinx.serialization.json.putJsonArray
|
import kotlinx.serialization.json.putJsonArray
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager? {
|
fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager? {
|
||||||
@@ -166,230 +173,336 @@ fun handleNetworkLogs(context: Context, batchToken: Long) {
|
|||||||
buffer.close()
|
buffer.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(24)
|
@Serializable
|
||||||
fun processSecurityLogs(securityEvents: List<SecurityLog.SecurityEvent>, outputStream: OutputStream) {
|
class SecurityEvent(
|
||||||
val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
|
val id: Long?, val tag: Int, val level: Int?, val time: Long, val data: JsonObject?
|
||||||
val buffer = outputStream.bufferedWriter()
|
)
|
||||||
securityEvents.forEachIndexed { index, event ->
|
|
||||||
val item = buildJsonObject {
|
@Serializable
|
||||||
put("time", event.timeNanos / 1000)
|
class SecurityEventWithData(
|
||||||
put("tag", event.tag)
|
val id: Long?, val tag: Int, val level: Int?, val time: Long, val data: SecurityEventData?
|
||||||
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) }
|
@Serializable
|
||||||
}
|
sealed class SecurityEventData {
|
||||||
buffer.write(json.encodeToString(item))
|
@Serializable
|
||||||
if(index < securityEvents.size - 1) buffer.write(",")
|
class AdbShellCmd(val command: String): SecurityEventData()
|
||||||
}
|
@Serializable
|
||||||
buffer.close()
|
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 transformSecurityEventData(tag: Int, payload: Any): SecurityEventData? {
|
||||||
fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
|
return when(tag) {
|
||||||
return when(event.tag) {
|
SecurityLog.TAG_ADB_SHELL_CMD -> SecurityEventData.AdbShellCmd(payload as String)
|
||||||
SecurityLog.TAG_ADB_SHELL_CMD -> JsonPrimitive(event.data as String)
|
|
||||||
SecurityLog.TAG_ADB_SHELL_INTERACTIVE -> null
|
SecurityLog.TAG_ADB_SHELL_INTERACTIVE -> null
|
||||||
SecurityLog.TAG_APP_PROCESS_START -> {
|
SecurityLog.TAG_APP_PROCESS_START -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.AppProcessStart(
|
||||||
put("name", payload[0] as String)
|
data[0] as String, data[1] as Long, data[2] as Int, data[3] as Int,
|
||||||
put("time", payload[1] as Long)
|
data[4] as String, data[5] as String
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_BACKUP_SERVICE_TOGGLED -> {
|
SecurityLog.TAG_BACKUP_SERVICE_TOGGLED -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.BackupServiceToggled(data[0] as String, data[1] as Int, data[2] as Int)
|
||||||
put("admin", payload[0] as String)
|
|
||||||
put("admin_user_id", payload[1] as Int)
|
|
||||||
put("state", payload[2] as Int)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_BLUETOOTH_CONNECTION -> {
|
SecurityLog.TAG_BLUETOOTH_CONNECTION -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.BluetoothConnection(data[0] as String, data[1] as Int, data[2] as String)
|
||||||
put("mac", payload[0] as String)
|
|
||||||
put("successful", payload[1] as Int)
|
|
||||||
(payload[2] as String).let { if(it != "") put("failure_reason", it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_BLUETOOTH_DISCONNECTION -> {
|
SecurityLog.TAG_BLUETOOTH_DISCONNECTION -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.BluetoothDisconnection(data[0] as String, data[1] as String)
|
||||||
put("mac", payload[0] as String)
|
|
||||||
(payload[1] as String).let { if(it != "") put("reason", it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_CAMERA_POLICY_SET -> {
|
SecurityLog.TAG_CAMERA_POLICY_SET -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.CameraPolicySet(
|
||||||
put("admin", payload[0] as String)
|
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||||
put("admin_user_id", payload[1] as Int)
|
)
|
||||||
put("target_user_id", payload[2] as Int)
|
|
||||||
put("disabled", payload[3] as Int)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_CERT_AUTHORITY_INSTALLED, SecurityLog.TAG_CERT_AUTHORITY_REMOVED -> {
|
SecurityLog.TAG_CERT_AUTHORITY_INSTALLED, SecurityLog.TAG_CERT_AUTHORITY_REMOVED -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.CaInstalledRemoved(data[0] as Int, data[1] as String, data[2] as Int)
|
||||||
put("result", payload[0] as Int)
|
|
||||||
put("subject", payload[1] as String)
|
|
||||||
if(VERSION.SDK_INT >= 30) put("user", payload[2] as Int)
|
|
||||||
}
|
}
|
||||||
}
|
SecurityLog.TAG_CERT_VALIDATION_FAILURE ->
|
||||||
SecurityLog.TAG_CERT_VALIDATION_FAILURE -> JsonPrimitive(event.data as String)
|
SecurityEventData.CertValidationFailure(payload as String)
|
||||||
SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED -> JsonPrimitive(event.data as Int)
|
SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED ->
|
||||||
|
SecurityEventData.CryptoSelfTestCompleted(payload as Int)
|
||||||
SecurityLog.TAG_KEYGUARD_DISABLED_FEATURES_SET -> {
|
SecurityLog.TAG_KEYGUARD_DISABLED_FEATURES_SET -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.KeyguardDisabledFeaturesSet(
|
||||||
put("admin", payload[0] as String)
|
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||||
put("admin_user_id", payload[1] as Int)
|
)
|
||||||
put("target_user_id", payload[2] as Int)
|
|
||||||
put("mask", payload[3] as Int)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_KEYGUARD_DISMISSED -> null
|
SecurityLog.TAG_KEYGUARD_DISMISSED -> null
|
||||||
SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT -> {
|
SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.KeyguardDismissAuthAttempt(data[0] as Int, data[1] as Int)
|
||||||
put("result", payload[0] as Int)
|
|
||||||
put("strength", payload[1] as Int)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_KEYGUARD_SECURED -> null
|
SecurityLog.TAG_KEYGUARD_SECURED -> null
|
||||||
SecurityLog.TAG_KEY_DESTRUCTION, SecurityLog.TAG_KEY_GENERATED, SecurityLog.TAG_KEY_IMPORT -> {
|
SecurityLog.TAG_KEY_GENERATED, SecurityLog.TAG_KEY_IMPORT, SecurityLog.TAG_KEY_DESTRUCTION -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.KeyGeneratedImportDestruction(
|
||||||
put("result", payload[0] as Int)
|
data[0] as Int, data[1] as String, data[2] 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_LOGGING_STARTED, SecurityLog.TAG_LOGGING_STOPPED -> null
|
SecurityLog.TAG_LOGGING_STARTED, SecurityLog.TAG_LOGGING_STOPPED -> null
|
||||||
SecurityLog.TAG_LOG_BUFFER_SIZE_CRITICAL -> null
|
SecurityLog.TAG_LOG_BUFFER_SIZE_CRITICAL -> null
|
||||||
SecurityLog.TAG_MAX_PASSWORD_ATTEMPTS_SET -> {
|
SecurityLog.TAG_MAX_PASSWORD_ATTEMPTS_SET -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.MaxPasswordAttemptsSet(
|
||||||
put("admin", payload[0] as String)
|
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||||
put("admin_user_id", payload[1] as Int)
|
)
|
||||||
put("target_user_id", payload[2] as Int)
|
|
||||||
put("value", payload[3] as Int)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_MAX_SCREEN_LOCK_TIMEOUT_SET -> {
|
SecurityLog.TAG_MAX_SCREEN_LOCK_TIMEOUT_SET -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.MaxScreenLockTimeoutSet(
|
||||||
put("admin", payload[0] as String)
|
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Long
|
||||||
put("admin_user_id", payload[1] as Int)
|
)
|
||||||
put("target_user_id", payload[2] as Int)
|
|
||||||
put("timeout", payload[3] as Long)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_MEDIA_MOUNT, SecurityLog.TAG_MEDIA_UNMOUNT -> {
|
SecurityLog.TAG_MEDIA_MOUNT, SecurityLog.TAG_MEDIA_UNMOUNT -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.MediaMountUnmount(data[0] as String, data[1] as String)
|
||||||
put("mount_point", payload[0] as String)
|
|
||||||
put("volume_label", payload[1] as String)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
SecurityLog.TAG_NFC_ENABLED, SecurityLog.TAG_NFC_DISABLED -> null
|
||||||
SecurityLog.TAG_OS_SHUTDOWN -> null
|
SecurityLog.TAG_OS_SHUTDOWN -> null
|
||||||
SecurityLog.TAG_OS_STARTUP -> {
|
SecurityLog.TAG_OS_STARTUP -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.OsStartup(data[0] as String, data[1] as String)
|
||||||
put("verified_boot_state", payload[0] as String)
|
|
||||||
put("dm_verify_state", payload[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 -> {
|
SecurityLog.TAG_PASSWORD_CHANGED -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.PasswordChanged(data[0] as Int, data[1] as Int)
|
||||||
put("complexity", payload[0] as Int)
|
|
||||||
put("user_id", payload[1] 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_REQUIRED -> {
|
SecurityLog.TAG_PASSWORD_COMPLEXITY_SET -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.PasswordComplexitySet(
|
||||||
put("admin", payload[0] as String)
|
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int, data[4] as Int,
|
||||||
put("admin_user_id", payload[1] as Int)
|
data[5] as Int, data[6] as Int, data[7] as Int, data[8] as Int, data[9] as Int,
|
||||||
put("target_user_id", payload[2] as Int)
|
data[10] as Int
|
||||||
put("complexity", payload[3] as Int)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
SecurityLog.TAG_PASSWORD_COMPLEXITY_SET -> null //Deprecated
|
|
||||||
SecurityLog.TAG_PASSWORD_EXPIRATION_SET -> {
|
SecurityLog.TAG_PASSWORD_EXPIRATION_SET -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.PasswordExpirationSet(
|
||||||
put("admin", payload[0] as String)
|
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Long
|
||||||
put("admin_user_id", payload[1] as Int)
|
)
|
||||||
put("target_user_id", payload[2] as Int)
|
|
||||||
put("timeout", payload[3] as Long)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_PASSWORD_HISTORY_LENGTH_SET -> {
|
SecurityLog.TAG_PASSWORD_HISTORY_LENGTH_SET -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.PasswordHistoryLengthSet(
|
||||||
put("admin", payload[0] as String)
|
data[0] as String, data[1] as Int, data[2] as Int, data[3] as Int
|
||||||
put("admin_user_id", payload[1] as Int)
|
)
|
||||||
put("target_user_id", payload[2] as Int)
|
|
||||||
put("length", payload[3] as Int)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_REMOTE_LOCK -> {
|
SecurityLog.TAG_REMOTE_LOCK -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.RemoteLock(data[0] as String, data[1] as Int, data[2] as Int)
|
||||||
put("admin", payload[0] as String)
|
|
||||||
put("admin_user_id", payload[1] as Int)
|
|
||||||
put("target_user_id", payload[2] as Int)
|
|
||||||
}
|
}
|
||||||
}
|
SecurityLog.TAG_SYNC_RECV_FILE, SecurityLog.TAG_SYNC_SEND_FILE ->
|
||||||
SecurityLog.TAG_SYNC_RECV_FILE, SecurityLog.TAG_SYNC_SEND_FILE -> JsonPrimitive(event.data as String)
|
SecurityEventData.SyncRecvSendFile(payload as String)
|
||||||
SecurityLog.TAG_USER_RESTRICTION_ADDED, SecurityLog.TAG_USER_RESTRICTION_REMOVED -> {
|
SecurityLog.TAG_USER_RESTRICTION_ADDED, SecurityLog.TAG_USER_RESTRICTION_REMOVED -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.UserRestrictionAddedRemoved(
|
||||||
put("admin", payload[0] as String)
|
data[0] as String, data[1] as Int, data[2] as String
|
||||||
put("admin_user_id", payload[1] as Int)
|
)
|
||||||
put("restriction", payload[2] as String)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_WIFI_CONNECTION -> {
|
SecurityLog.TAG_WIFI_CONNECTION -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.WifiConnection(data[0] as String, data[1] as String, data[2] as String)
|
||||||
put("bssid", payload[0] as String)
|
|
||||||
put("type", payload[1] as String)
|
|
||||||
(payload[2] as String).let { if(it != "") put("failure_reason", it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_WIFI_DISCONNECTION -> {
|
SecurityLog.TAG_WIFI_DISCONNECTION -> {
|
||||||
val payload = event.data as Array<*>
|
val data = payload as Array<*>
|
||||||
buildJsonObject {
|
SecurityEventData.WifiDisconnection(data[0] as String, data[1] as String)
|
||||||
put("bssid", payload[0] as String)
|
|
||||||
(payload[1] as String).let { if(it != "") put("reason", it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SecurityLog.TAG_WIPE_FAILURE -> null
|
SecurityLog.TAG_WIPE_FAILURE -> null
|
||||||
else -> 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() {
|
fun setDefaultAffiliationID() {
|
||||||
if (VERSION.SDK_INT < 26) return
|
if (VERSION.SDK_INT < 26) return
|
||||||
if(!SP.isDefaultAffiliationIdSet) {
|
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
|
@Serializable object PreferentialNetworkService
|
||||||
|
|
||||||
@RequiresApi(33)
|
@RequiresApi(33)
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableFloatStateOf
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableLongStateOf
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -111,11 +110,11 @@ import com.bintianqi.owndroid.MyViewModel
|
|||||||
import com.bintianqi.owndroid.Privilege
|
import com.bintianqi.owndroid.Privilege
|
||||||
import com.bintianqi.owndroid.R
|
import com.bintianqi.owndroid.R
|
||||||
import com.bintianqi.owndroid.SP
|
import com.bintianqi.owndroid.SP
|
||||||
import com.bintianqi.owndroid.formatFileSize
|
|
||||||
import com.bintianqi.owndroid.formatDate
|
import com.bintianqi.owndroid.formatDate
|
||||||
import com.bintianqi.owndroid.popToast
|
import com.bintianqi.owndroid.popToast
|
||||||
import com.bintianqi.owndroid.showOperationResultToast
|
import com.bintianqi.owndroid.showOperationResultToast
|
||||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||||
|
import com.bintianqi.owndroid.ui.CircularProgressDialog
|
||||||
import com.bintianqi.owndroid.ui.ErrorDialog
|
import com.bintianqi.owndroid.ui.ErrorDialog
|
||||||
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
|
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
|
||||||
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
|
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
|
||||||
@@ -132,7 +131,9 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.Serializable
|
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 java.util.TimeZone
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
|
|
||||||
@@ -178,7 +179,7 @@ fun SystemManagerScreen(
|
|||||||
if(VERSION.SDK_INT >= 31) {
|
if(VERSION.SDK_INT >= 31) {
|
||||||
FunctionItem(R.string.nearby_streaming_policy, icon = R.drawable.share_fill0) { onNavigate(NearbyStreamingPolicy) }
|
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.lock_task_mode, icon = R.drawable.lock_fill0) { onNavigate(LockTaskMode) }
|
||||||
}
|
}
|
||||||
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { onNavigate(CaCert) }
|
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { onNavigate(CaCert) }
|
||||||
@@ -1147,7 +1148,7 @@ fun NearbyStreamingPolicyScreen(
|
|||||||
fun LockTaskModeScreen(
|
fun LockTaskModeScreen(
|
||||||
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
|
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
|
||||||
lockTaskPackages: StateFlow<List<AppInfo>>, getLockTaskPackages: () -> 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
|
getLockTaskFeatures: () -> Int, setLockTaskFeature: (Int) -> String?, onNavigateUp: () -> Unit
|
||||||
) {
|
) {
|
||||||
val coroutine = rememberCoroutineScope()
|
val coroutine = rememberCoroutineScope()
|
||||||
@@ -1202,9 +1203,10 @@ fun LockTaskModeScreen(
|
|||||||
@RequiresApi(28)
|
@RequiresApi(28)
|
||||||
@Composable
|
@Composable
|
||||||
private fun StartLockTaskMode(
|
private fun StartLockTaskMode(
|
||||||
startLockTaskMode: (String, String) -> Unit,
|
startLockTaskMode: (String, String) -> Boolean,
|
||||||
chosenPackage: Channel<String>, onChoosePackage: () -> Unit
|
chosenPackage: Channel<String>, onChoosePackage: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var packageName by rememberSaveable { mutableStateOf("") }
|
var packageName by rememberSaveable { mutableStateOf("") }
|
||||||
var activity by rememberSaveable { mutableStateOf("") }
|
var activity by rememberSaveable { mutableStateOf("") }
|
||||||
@@ -1244,7 +1246,8 @@ private fun StartLockTaskMode(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 5.dp),
|
.padding(bottom = 5.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
startLockTaskMode(packageName, activity)
|
val result = startLockTaskMode(packageName, activity)
|
||||||
|
if (!result) context.showOperationResultToast(false)
|
||||||
},
|
},
|
||||||
enabled = packageName.isNotBlank() && (!specifyActivity || activity.isNotBlank())
|
enabled = packageName.isNotBlank() && (!specifyActivity || activity.isNotBlank())
|
||||||
) {
|
) {
|
||||||
@@ -1525,77 +1528,104 @@ fun CaCertScreen(
|
|||||||
|
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
@Composable
|
@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 context = LocalContext.current
|
||||||
val logFile = context.filesDir.resolve("SecurityLogs.json")
|
var enabled by remember { mutableStateOf(false) }
|
||||||
var fileSize by remember { mutableLongStateOf(0) }
|
var logsCount by remember { mutableIntStateOf(0) }
|
||||||
LaunchedEffect(Unit) { fileSize = logFile.length() }
|
var exporting by remember { mutableStateOf(false) }
|
||||||
var preRebootSecurityLogs by remember { mutableStateOf(byteArrayOf()) }
|
var dialog by remember { mutableStateOf(false) }
|
||||||
val exportPreRebootSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
|
LaunchedEffect(Unit) {
|
||||||
if(uri != null) context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
enabled = getEnabled()
|
||||||
preRebootSecurityLogs.inputStream().copyTo(outStream)
|
logsCount = getCount()
|
||||||
}
|
}
|
||||||
}
|
val exportLauncher = rememberLauncherForActivityResult(
|
||||||
val exportSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
|
ActivityResultContracts.CreateDocument("application/json")
|
||||||
if(uri != null) context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
) {
|
||||||
outStream.write("[".toByteArray())
|
if (it != null) {
|
||||||
logFile.inputStream().use { it.copyTo(outStream) }
|
exporting = true
|
||||||
outStream.write("]".toByteArray())
|
exportLogs(it) {
|
||||||
|
exporting = false
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyScaffold(R.string.security_logging, onNavigateUp) {
|
}
|
||||||
|
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, 0.dp) {
|
||||||
SwitchItem(
|
SwitchItem(
|
||||||
R.string.enable,
|
R.string.enable, enabled, {
|
||||||
getState = { Privilege.DPM.isSecurityLoggingEnabled(Privilege.DAR) },
|
setEnabled(it)
|
||||||
onCheckedChange = { Privilege.DPM.setSecurityLoggingEnabled(Privilege.DAR, it) },
|
enabled = it
|
||||||
padding = false
|
}
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.n_logs_in_total, logsCount),
|
||||||
|
Modifier.padding(HorizontalPadding)
|
||||||
)
|
)
|
||||||
Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize)))
|
|
||||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
{
|
||||||
exportSecurityLogs.launch("SecurityLogs.json")
|
val date = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(Date())
|
||||||
|
exportLauncher.launch("security_logs_$date")
|
||||||
},
|
},
|
||||||
enabled = fileSize > 0,
|
Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding),
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
logsCount > 0
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.export_logs))
|
Text(stringResource(R.string.export_logs))
|
||||||
}
|
}
|
||||||
Button(
|
if (logsCount > 0) FilledTonalButton(
|
||||||
onClick = {
|
{ dialog = true },
|
||||||
logFile.delete()
|
Modifier.fillMaxWidth().padding(HorizontalPadding, 4.dp)
|
||||||
fileSize = logFile.length()
|
|
||||||
},
|
|
||||||
enabled = fileSize > 0,
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.delete_logs))
|
Text(stringResource(R.string.delete_logs))
|
||||||
}
|
}
|
||||||
}
|
Notes(R.string.info_security_log, HorizontalPadding)
|
||||||
Notes(R.string.info_security_log)
|
|
||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
val logs = Privilege.DPM.retrievePreRebootSecurityLogs(Privilege.DAR)
|
if (getPRLogs()) {
|
||||||
if(logs == null) {
|
val date = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(Date())
|
||||||
context.popToast(R.string.no_logs)
|
exportPRLogsLauncher.launch("pre_reboot_security_logs_$date")
|
||||||
return@Button
|
|
||||||
} else {
|
} else {
|
||||||
val outputStream = ByteArrayOutputStream()
|
context.showOperationResultToast(false)
|
||||||
outputStream.write("[".encodeToByteArray())
|
|
||||||
processSecurityLogs(logs, outputStream)
|
|
||||||
outputStream.write("]".encodeToByteArray())
|
|
||||||
preRebootSecurityLogs = outputStream.toByteArray()
|
|
||||||
exportPreRebootSecurityLogs.launch("PreRebootSecurityLogs.json")
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().padding(HorizontalPadding, 15.dp)
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.pre_reboot_security_logs))
|
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
|
@Serializable object DisableAccountManagement
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<string name="time_unit_ms">Время (мс)</string>
|
<string name="time_unit_ms">Время (мс)</string>
|
||||||
<string name="length">Длина</string>
|
<string name="length">Длина</string>
|
||||||
<string name="none">Нет</string>
|
<string name="none">Нет</string>
|
||||||
<string name="no_logs">Нет журналов</string>
|
|
||||||
<string name="default_stringres">По умолчанию</string>
|
<string name="default_stringres">По умолчанию</string>
|
||||||
<string name="apply">Применить</string>
|
<string name="apply">Применить</string>
|
||||||
<string name="decide_by_user">Определять пользователем</string>
|
<string name="decide_by_user">Определять пользователем</string>
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<string name="time_unit_ms">Zaman (ms)</string>
|
<string name="time_unit_ms">Zaman (ms)</string>
|
||||||
<string name="length">Uzunluk</string>
|
<string name="length">Uzunluk</string>
|
||||||
<string name="none">Yok</string>
|
<string name="none">Yok</string>
|
||||||
<string name="no_logs">Kayıt Yok</string>
|
|
||||||
<string name="default_stringres">Varsayılan</string>
|
<string name="default_stringres">Varsayılan</string>
|
||||||
<string name="apply">Uygula</string>
|
<string name="apply">Uygula</string>
|
||||||
<string name="decide_by_user">Kullanıcı Tarafından Karar Ver</string>
|
<string name="decide_by_user">Kullanıcı Tarafından Karar Ver</string>
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
<string name="time_unit_ms">时间(毫秒)</string>
|
<string name="time_unit_ms">时间(毫秒)</string>
|
||||||
<string name="length">长度</string>
|
<string name="length">长度</string>
|
||||||
<string name="none">无</string>
|
<string name="none">无</string>
|
||||||
<string name="no_logs">无日志</string>
|
|
||||||
<string name="default_stringres">默认</string>
|
<string name="default_stringres">默认</string>
|
||||||
<string name="apply">应用</string>
|
<string name="apply">应用</string>
|
||||||
<string name="decide_by_user">由用户决定</string>
|
<string name="decide_by_user">由用户决定</string>
|
||||||
@@ -194,6 +193,8 @@
|
|||||||
<string name="uninstall_all_user_ca_cert">卸载所有用户证书</string>
|
<string name="uninstall_all_user_ca_cert">卸载所有用户证书</string>
|
||||||
<string name="security_logging">安全日志</string>
|
<string name="security_logging">安全日志</string>
|
||||||
<string name="pre_reboot_security_logs">重启前安全日志</string>
|
<string name="pre_reboot_security_logs">重启前安全日志</string>
|
||||||
|
<string name="security_logs_collected">安全日志已收集</string>
|
||||||
|
<string name="n_logs_in_total">总共 %1$d 条日志</string>
|
||||||
<string name="wipe_data">清除数据</string>
|
<string name="wipe_data">清除数据</string>
|
||||||
<string name="wipe_external_storage">清除外部存储</string>
|
<string name="wipe_external_storage">清除外部存储</string>
|
||||||
<string name="wipe_reset_protection_data">清除受保护的数据</string>
|
<string name="wipe_reset_protection_data">清除受保护的数据</string>
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
<string name="time_unit_ms">Time(ms)</string>
|
<string name="time_unit_ms">Time(ms)</string>
|
||||||
<string name="length">Length</string>
|
<string name="length">Length</string>
|
||||||
<string name="none">None</string>
|
<string name="none">None</string>
|
||||||
<string name="no_logs">No logs</string>
|
|
||||||
<string name="default_stringres">Default</string>
|
<string name="default_stringres">Default</string>
|
||||||
<string name="apply">Apply</string>
|
<string name="apply">Apply</string>
|
||||||
<string name="decide_by_user">Decide by user</string>
|
<string name="decide_by_user">Decide by user</string>
|
||||||
@@ -221,6 +220,8 @@
|
|||||||
<string name="uninstall_all_user_ca_cert">Uninstall all user CA certificate</string>
|
<string name="uninstall_all_user_ca_cert">Uninstall all user CA certificate</string>
|
||||||
<string name="security_logging">Security logging</string>
|
<string name="security_logging">Security logging</string>
|
||||||
<string name="pre_reboot_security_logs">Pre-reboot security logs</string>
|
<string name="pre_reboot_security_logs">Pre-reboot security logs</string>
|
||||||
|
<string name="security_logs_collected">Security logs collected</string>
|
||||||
|
<string name="n_logs_in_total">%1$d logs in total</string>
|
||||||
<string name="wipe_data">Wipe data</string>
|
<string name="wipe_data">Wipe data</string>
|
||||||
<string name="wipe_external_storage">Wipe external storage</string>
|
<string name="wipe_external_storage">Wipe external storage</string>
|
||||||
<string name="wipe_reset_protection_data">Wipe protected data</string>
|
<string name="wipe_reset_protection_data">Wipe protected data</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user