Add FGS task manager manipulation type

This commit is contained in:
Jonas Lochmann 2022-07-11 02:00:00 +02:00
parent 5f61efbd14
commit 7021a9c699
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
19 changed files with 1461 additions and 82 deletions

File diff suppressed because it is too large Load diff

View file

@ -293,6 +293,12 @@ object DatabaseMigrations {
}
}
private val MIGRATE_TO_V42 = object: Migration(41, 42) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE device ADD COLUMN manipulation_flags INTEGER NOT NULL DEFAULT 0")
}
}
val ALL = arrayOf(
MIGRATE_TO_V2,
MIGRATE_TO_V3,
@ -333,6 +339,7 @@ object DatabaseMigrations {
MIGRATE_TO_V38,
MIGRATE_TO_V39,
MIGRATE_TO_V40,
MIGRATE_TO_V41
MIGRATE_TO_V41,
MIGRATE_TO_V42
)
}

View file

@ -53,7 +53,7 @@ import java.util.concurrent.TimeUnit
CategoryNetworkId::class,
ChildTask::class,
CategoryTimeWarning::class
], version = 41)
], version = 42)
abstract class RoomDatabase: RoomDatabase(), io.timelimit.android.data.Database {
companion object {
private val lock = Object()

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 Jonas Lochmann
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -92,7 +92,9 @@ data class Device(
@ColumnInfo(name = "enable_activity_level_blocking")
val enableActivityLevelBlocking: Boolean,
@ColumnInfo(name = "q_or_later")
val qOrLater: Boolean
val qOrLater: Boolean,
@ColumnInfo(name = "manipulation_flags")
val manipulationFlags: Long
): JsonSerializable {
companion object {
private const val ID = "id"
@ -126,6 +128,7 @@ data class Device(
private const val WAS_ACCESSIBILITY_SERVICE_ENABLED = "wase"
private const val ENABLE_ACTIVITY_LEVEL_BLOCKING = "ealb"
private const val Q_OR_LATER = "qol"
private const val MANIPULATION_FLAGS = "mf"
fun parse(reader: JsonReader): Device {
var id: String? = null
@ -159,6 +162,7 @@ data class Device(
var wasAccessibilityServiceEnabled = false
var enableActivityLevelBlocking = false
var qOrLater = false
var manipulationFlags = 0L
reader.beginObject()
@ -195,6 +199,7 @@ data class Device(
WAS_ACCESSIBILITY_SERVICE_ENABLED -> wasAccessibilityServiceEnabled = reader.nextBoolean()
ENABLE_ACTIVITY_LEVEL_BLOCKING -> enableActivityLevelBlocking = reader.nextBoolean()
Q_OR_LATER -> qOrLater = reader.nextBoolean()
MANIPULATION_FLAGS -> manipulationFlags = reader.nextLong()
else -> reader.skipValue()
}
}
@ -232,7 +237,8 @@ data class Device(
accessibilityServiceEnabled = accessibilityServiceEnabled,
wasAccessibilityServiceEnabled = wasAccessibilityServiceEnabled,
enableActivityLevelBlocking = enableActivityLevelBlocking,
qOrLater = qOrLater
qOrLater = qOrLater,
manipulationFlags = manipulationFlags
)
}
}
@ -303,6 +309,7 @@ data class Device(
writer.name(WAS_ACCESSIBILITY_SERVICE_ENABLED).value(wasAccessibilityServiceEnabled)
writer.name(ENABLE_ACTIVITY_LEVEL_BLOCKING).value(enableActivityLevelBlocking)
writer.name(Q_OR_LATER).value(qOrLater)
writer.name(MANIPULATION_FLAGS).value(manipulationFlags)
writer.endObject()
}
@ -331,7 +338,7 @@ data class Device(
manipulationOfAccessibilityService
@Transient
val hasAnyManipulation = hasActiveManipulationWarning || hadManipulation
val hasAnyManipulation = hasActiveManipulationWarning || hadManipulation || manipulationFlags != 0L
@Transient
val missingPermissionAtQOrLater = qOrLater &&
@ -382,3 +389,7 @@ object HadManipulationFlag {
const val OVERLAY_PERMISSION = 1L shl 4
const val ACCESSIBILITY_SERVICE = 1L shl 5
}
object ManipulationFlag {
const val USED_FGS_KILLER = 1L shl 0
}

View file

@ -89,7 +89,7 @@ abstract class PlatformIntegration(
confirmationLevel: SystemPermissionConfirmationLevel = SystemPermissionConfirmationLevel.None
): Boolean
abstract fun getExitLog(): List<ExitLogItem>
abstract fun getExitLog(length: Int): List<ExitLogItem>
var installedAppsChangeListener: Runnable? = null
var systemClockChangeListener: Runnable? = null

View file

@ -815,9 +815,9 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio
}
}
override fun getExitLog(): List<ExitLogItem> {
override fun getExitLog(length: Int): List<ExitLogItem> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
activityManager.getHistoricalProcessExitReasons(context.packageName, 0, 0)
activityManager.getHistoricalProcessExitReasons(context.packageName, 0, length)
.map { ExitLogItem.fromApplicationExitInfo(it) }
} else emptyList()
}

View file

@ -190,5 +190,5 @@ class DummyIntegration(
confirmationLevel: SystemPermissionConfirmationLevel
): Boolean = false
override fun getExitLog(): List<ExitLogItem> = emptyList()
override fun getExitLog(length: Int): List<ExitLogItem> = emptyList()
}

View file

@ -109,7 +109,8 @@ class AppSetupLogic(private val appLogic: AppLogic) {
accessibilityServiceEnabled = false,
wasAccessibilityServiceEnabled = false,
enableActivityLevelBlocking = false,
qOrLater = AndroidVersion.qOrLater
qOrLater = AndroidVersion.qOrLater,
manipulationFlags = 0
)
appLogic.database.device().addDeviceSync(device)

View file

@ -26,16 +26,14 @@ import io.timelimit.android.coroutines.runAsync
import io.timelimit.android.coroutines.runAsyncExpectForever
import io.timelimit.android.data.backup.DatabaseBackup
import io.timelimit.android.data.model.ExperimentalFlags
import io.timelimit.android.data.model.ManipulationFlag
import io.timelimit.android.data.model.UserType
import io.timelimit.android.data.model.derived.UserRelatedData
import io.timelimit.android.date.DateInTimezone
import io.timelimit.android.date.getMinuteOfWeek
import io.timelimit.android.extensions.MinuteOfDay
import io.timelimit.android.extensions.nextBlockedMinuteOfWeek
import io.timelimit.android.integration.platform.AppStatusMessage
import io.timelimit.android.integration.platform.ForegroundApp
import io.timelimit.android.integration.platform.NetworkId
import io.timelimit.android.integration.platform.ProtectionLevel
import io.timelimit.android.integration.platform.*
import io.timelimit.android.integration.platform.android.AccessibilityService
import io.timelimit.android.livedata.*
import io.timelimit.android.logic.blockingreason.AppBaseHandling
@ -76,6 +74,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
runAsyncExpectForever { syncDeviceStatusLoop() }
runAsyncExpectForever { backupDatabaseLoop() }
runAsyncExpectForever { annoyUserOnManipulationLoop() }
runAsync { checkForceKilled() }
runAsync {
// this is effective after an reboot
@ -924,6 +923,33 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
}
}
private suspend fun checkForceKilled() {
appLogic.platformIntegration.getExitLog(1).singleOrNull()?.let { item ->
if (
item.reason == ExitReason.UserRequest &&
item.description != null &&
item.description.startsWith("fully stop ") &&
item.description.endsWith("by user request")
) {
appLogic.isInitialized.waitUntilValueMatches { it == true }
try {
ApplyActionUtil.applyAppLogicAction(
action = UpdateDeviceStatusAction.empty.copy(
addedManipulationFlags = ManipulationFlag.USED_FGS_KILLER
),
appLogic = appLogic,
ignoreIfDeviceIsNotConfigured = true
)
} catch (ex: Exception) {
if (BuildConfig.DEBUG) {
Log.w(LOG_TAG, "could not save a forced kill notification", ex)
}
}
}
}
}
private val syncDeviceStatusLock = Mutex()
fun reportDeviceReboot() {

View file

@ -188,7 +188,8 @@ object ApplyServerDataStatus {
accessibilityServiceEnabled = newDevice.accessibilityServiceEnabled,
wasAccessibilityServiceEnabled = newDevice.wasAccessibilityServiceEnabled,
enableActivityLevelBlocking = newDevice.enableActivityLevelBlocking,
qOrLater = newDevice.qOrLater
qOrLater = newDevice.qOrLater,
manipulationFlags = newDevice.manipulationFlags
))
} else {
// eventually update old entry
@ -222,7 +223,8 @@ object ApplyServerDataStatus {
accessibilityServiceEnabled = newDevice.accessibilityServiceEnabled,
wasAccessibilityServiceEnabled = newDevice.wasAccessibilityServiceEnabled,
enableActivityLevelBlocking = newDevice.enableActivityLevelBlocking,
qOrLater = newDevice.qOrLater
qOrLater = newDevice.qOrLater,
manipulationFlags = newDevice.manipulationFlags
)
if (updatedDeviceEntry != oldDeviceEntry) {

View file

@ -1081,7 +1081,8 @@ data class UpdateDeviceStatusAction(
val newAccessibilityServiceEnabled: Boolean?,
val newAppVersion: Int?,
val didReboot: Boolean,
val isQOrLaterNow: Boolean
val isQOrLaterNow: Boolean,
val addedManipulationFlags: Long
): AppLogicAction() {
companion object {
const val TYPE_VALUE = "UPDATE_DEVICE_STATUS"
@ -1093,6 +1094,7 @@ data class UpdateDeviceStatusAction(
private const val NEW_APP_VERSION = "appVersion"
private const val DID_REBOOT = "didReboot"
private const val IS_Q_OR_LATER_NOW = "isQOrLaterNow"
private const val ADDED_MANIPULATION_FLAGS = "addedManipulationFlags"
val empty = UpdateDeviceStatusAction(
newProtectionLevel = null,
@ -1102,7 +1104,8 @@ data class UpdateDeviceStatusAction(
newAccessibilityServiceEnabled = null,
newAppVersion = null,
didReboot = false,
isQOrLaterNow = false
isQOrLaterNow = false,
addedManipulationFlags = 0L
)
}
@ -1159,6 +1162,10 @@ data class UpdateDeviceStatusAction(
writer.name(IS_Q_OR_LATER_NOW).value(true)
}
if (addedManipulationFlags != 0L) {
writer.name(ADDED_MANIPULATION_FLAGS).value(addedManipulationFlags)
}
writer.endObject()
}
}
@ -1174,7 +1181,8 @@ data class IgnoreManipulationAction(
val ignoreAccessibilityServiceManipulation: Boolean,
val ignoreReboot: Boolean,
val ignoreHadManipulation: Boolean,
val ignoreHadManipulationFlags: Long
val ignoreHadManipulationFlags: Long,
val ignoreManipulationFlags: Long
): ParentAction() {
companion object {
const val TYPE_VALUE = "IGNORE_MANIPULATION"
@ -1189,6 +1197,7 @@ data class IgnoreManipulationAction(
private const val IGNORE_HAD_MANIPULATION = "hadManipulation"
private const val IGNORE_REBOOT = "reboot"
private const val IGNORE_HAD_MANIPULATION_FLAGS = "ignoreHadManipulationFlags"
private const val IGNORE_MANIPULATION_FLAGS = "ignoreManipulationFlags"
}
init {
@ -1204,7 +1213,8 @@ data class IgnoreManipulationAction(
(!ignoreAccessibilityServiceManipulation) &&
(!ignoreReboot) &&
(!ignoreHadManipulation) &&
(ignoreHadManipulationFlags == 0L)
(ignoreHadManipulationFlags == 0L) &&
(ignoreManipulationFlags == 0L)
override fun serialize(writer: JsonWriter) {
writer.beginObject()
@ -1222,6 +1232,8 @@ data class IgnoreManipulationAction(
writer.name(IGNORE_REBOOT).value(ignoreReboot)
writer.name(IGNORE_HAD_MANIPULATION_FLAGS).value(ignoreHadManipulationFlags)
if (ignoreManipulationFlags != 0L) writer.name(IGNORE_MANIPULATION_FLAGS).value(ignoreManipulationFlags)
writer.endObject()
}
}

View file

@ -305,6 +305,10 @@ object LocalDatabaseAppLogicActionDispatcher {
device = device.copy(qOrLater = true)
}
if (action.addedManipulationFlags != 0L) {
device = device.copy(manipulationFlags = device.manipulationFlags or action.addedManipulationFlags)
}
database.device().updateDeviceEntry(device)
null

View file

@ -465,6 +465,12 @@ object LocalDatabaseParentActionDispatcher {
}
}
if (action.ignoreManipulationFlags != 0L) {
deviceEntry = deviceEntry.copy(
manipulationFlags = deviceEntry.manipulationFlags and (action.ignoreManipulationFlags.inv())
)
}
database.device().updateDeviceEntry(deviceEntry)
}
is SetKeepSignedInAction -> {

View file

@ -298,7 +298,8 @@ data class ServerDeviceData(
val accessibilityServiceEnabled: Boolean,
val wasAccessibilityServiceEnabled: Boolean,
val enableActivityLevelBlocking: Boolean,
val qOrLater: Boolean
val qOrLater: Boolean,
val manipulationFlags: Long
) {
companion object {
private const val DEVICE_ID = "deviceId"
@ -331,6 +332,7 @@ data class ServerDeviceData(
private const val WAS_ACCESSIBILITY_SERVICE_ENABLED = "wasAsEnabled"
private const val ENABLE_ACTIVITY_LEVEL_BLOCKING = "activityLevelBlocking"
private const val Q_OR_LATER = "qOrLater"
private const val MANIPULATION_FLAGS = "mFlags"
fun parse(reader: JsonReader): ServerDeviceData {
var deviceId: String? = null
@ -363,6 +365,7 @@ data class ServerDeviceData(
var wasAccessibilityServiceEnabled: Boolean? = null
var enableActivityLevelBlocking = false
var qOrLater = false
var manipulationFlags = 0L
reader.beginObject()
while (reader.hasNext()) {
@ -397,6 +400,7 @@ data class ServerDeviceData(
WAS_ACCESSIBILITY_SERVICE_ENABLED -> wasAccessibilityServiceEnabled = reader.nextBoolean()
ENABLE_ACTIVITY_LEVEL_BLOCKING -> enableActivityLevelBlocking = reader.nextBoolean()
Q_OR_LATER -> qOrLater = reader.nextBoolean()
MANIPULATION_FLAGS -> manipulationFlags = reader.nextLong()
else -> reader.skipValue()
}
}
@ -432,7 +436,8 @@ data class ServerDeviceData(
accessibilityServiceEnabled = accessibilityServiceEnabled!!,
wasAccessibilityServiceEnabled = wasAccessibilityServiceEnabled!!,
enableActivityLevelBlocking = enableActivityLevelBlocking,
qOrLater = qOrLater
qOrLater = qOrLater,
manipulationFlags = manipulationFlags
)
}

View file

@ -31,7 +31,7 @@ import io.timelimit.android.ui.main.FragmentWithCustomTitle
class DiagnoseExitReasonFragment: Fragment(), FragmentWithCustomTitle {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = DiagnoseExitReasonFragmentBinding.inflate(inflater, container, false)
val data = DefaultAppLogic.with(requireContext()).platformIntegration.getExitLog()
val data = DefaultAppLogic.with(requireContext()).platformIntegration.getExitLog(0)
val recycler = binding.recycler
val adapter = DiagnoseExitReasonAdapter()

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 Jonas Lochmann
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -25,6 +25,7 @@ import com.google.android.material.snackbar.Snackbar
import io.timelimit.android.R
import io.timelimit.android.data.model.Device
import io.timelimit.android.data.model.HadManipulationFlag
import io.timelimit.android.data.model.ManipulationFlag
import io.timelimit.android.databinding.ManageDeviceManipulationViewBinding
import io.timelimit.android.livedata.map
import io.timelimit.android.sync.actions.IgnoreManipulationAction
@ -62,7 +63,7 @@ object ManageDeviceManipulation {
entries.forEach { warning ->
container.addView(
createCheckbox().apply {
setText(ManipulationWarningTypeLabel.getLabel(warning))
setText(warning.labelResourceId)
isChecked = selection.contains(warning)
setOnCheckedChangeListener { _, newIsChecked ->
@ -128,60 +129,74 @@ data class ManipulationWarnings(val current: List<ManipulationWarningType>, val
fun isFlagSet(flag: Long) = (manipulationFlags and flag) == flag
if (device.manipulationTriedDisablingDeviceAdmin) {
current.add(ManipulationWarningType.TriedDisablingDeviceAdmin)
current.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.TriedDisablingDeviceAdmin))
}
if (device.manipulationOfAppVersion) {
current.add(ManipulationWarningType.AppDowngrade)
current.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.AppDowngrade))
}
if (isFlagSet(HadManipulationFlag.APP_VERSION)) {
past.add(ManipulationWarningType.AppDowngrade)
past.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.AppDowngrade))
}
if (device.manipulationOfProtectionLevel) {
current.add(ManipulationWarningType.DeviceAdmin)
current.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.DeviceAdmin))
}
if (isFlagSet(HadManipulationFlag.PROTECTION_LEVEL)) {
past.add(ManipulationWarningType.DeviceAdmin)
past.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.DeviceAdmin))
}
if (device.manipulationOfUsageStats) {
current.add(ManipulationWarningType.UsageStats)
current.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.UsageStats))
}
if (isFlagSet(HadManipulationFlag.USAGE_STATS_ACCESS)) {
past.add(ManipulationWarningType.UsageStats)
past.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.UsageStats))
}
if (device.manipulationOfNotificationAccess) {
current.add(ManipulationWarningType.NotificationAccess)
current.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.NotificationAccess))
}
if (isFlagSet(HadManipulationFlag.NOTIFICATION_ACCESS)) {
past.add(ManipulationWarningType.NotificationAccess)
past.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.NotificationAccess))
}
if (device.manipulationOfOverlayPermission) {
current.add(ManipulationWarningType.OverlayPermission)
current.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.OverlayPermission))
}
if (isFlagSet(HadManipulationFlag.OVERLAY_PERMISSION)) {
past.add(ManipulationWarningType.OverlayPermission)
past.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.OverlayPermission))
}
if (device.wasAccessibilityServiceEnabled) {
if (!device.accessibilityServiceEnabled) {
current.add(ManipulationWarningType.AccessibilityService)
current.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.AccessibilityService))
}
}
if (isFlagSet(HadManipulationFlag.ACCESSIBILITY_SERVICE)) {
past.add(ManipulationWarningType.AccessibilityService)
past.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.AccessibilityService))
}
if (device.manipulationDidReboot) {
current.add(ManipulationWarningType.DidReboot)
current.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.DidReboot))
}
if (device.hadManipulation) {
if (past.isEmpty()) {
past.add(ManipulationWarningType.Unknown)
past.add(ManipulationWarningType.Classic(ClassicManipulationWarningType.Unknown))
}
}
if (device.manipulationFlags != 0L) {
var remainingFlags = device.manipulationFlags
if (remainingFlags and ManipulationFlag.USED_FGS_KILLER == ManipulationFlag.USED_FGS_KILLER) {
past.add(ManipulationWarningType.Flag(ManipulationFlag.USED_FGS_KILLER, R.string.manage_device_manipulation_fgs_killer))
remainingFlags = remainingFlags.and(ManipulationFlag.USED_FGS_KILLER.inv())
}
if (remainingFlags != 0L) {
past.add(ManipulationWarningType.Flag(remainingFlags, R.string.manage_device_manipulation_unknown))
}
}
@ -197,38 +212,69 @@ data class ManipulationWarnings(val current: List<ManipulationWarningType>, val
fun buildIgnoreAction(deviceId: String): IgnoreManipulationAction {
var ignoreHadManipulationFlags = 0L
var ignoreManipulationFlags = 0L
past.forEach { type ->
ignoreHadManipulationFlags = ignoreHadManipulationFlags or when(type) {
ManipulationWarningType.TriedDisablingDeviceAdmin -> throw IllegalArgumentException()
ManipulationWarningType.AppDowngrade -> HadManipulationFlag.APP_VERSION
ManipulationWarningType.DeviceAdmin -> HadManipulationFlag.PROTECTION_LEVEL
ManipulationWarningType.UsageStats -> HadManipulationFlag.USAGE_STATS_ACCESS
ManipulationWarningType.NotificationAccess -> HadManipulationFlag.NOTIFICATION_ACCESS
ManipulationWarningType.OverlayPermission -> HadManipulationFlag.OVERLAY_PERMISSION
ManipulationWarningType.AccessibilityService -> HadManipulationFlag.ACCESSIBILITY_SERVICE
ManipulationWarningType.DidReboot -> throw IllegalArgumentException()
ManipulationWarningType.Unknown -> 0L // handled at an other location
when (type) {
is ManipulationWarningType.Classic -> {
ignoreHadManipulationFlags = ignoreHadManipulationFlags or when(type.type) {
ClassicManipulationWarningType.TriedDisablingDeviceAdmin -> throw IllegalArgumentException()
ClassicManipulationWarningType.AppDowngrade -> HadManipulationFlag.APP_VERSION
ClassicManipulationWarningType.DeviceAdmin -> HadManipulationFlag.PROTECTION_LEVEL
ClassicManipulationWarningType.UsageStats -> HadManipulationFlag.USAGE_STATS_ACCESS
ClassicManipulationWarningType.NotificationAccess -> HadManipulationFlag.NOTIFICATION_ACCESS
ClassicManipulationWarningType.OverlayPermission -> HadManipulationFlag.OVERLAY_PERMISSION
ClassicManipulationWarningType.AccessibilityService -> HadManipulationFlag.ACCESSIBILITY_SERVICE
ClassicManipulationWarningType.DidReboot -> throw IllegalArgumentException()
ClassicManipulationWarningType.Unknown -> 0L // handled at an other location
}
}
is ManipulationWarningType.Flag -> {
ignoreManipulationFlags = ignoreManipulationFlags or type.mask
}
}.let {/* require handling all cases */}
}
val currentClassic = current.filterIsInstance<ManipulationWarningType.Classic>().map { it.type }
return IgnoreManipulationAction(
deviceId = deviceId,
ignoreUsageStatsAccessManipulation = current.contains(ManipulationWarningType.UsageStats),
ignoreNotificationAccessManipulation = current.contains(ManipulationWarningType.NotificationAccess),
ignoreDeviceAdminManipulationAttempt = current.contains(ManipulationWarningType.TriedDisablingDeviceAdmin),
ignoreDeviceAdminManipulation = current.contains(ManipulationWarningType.DeviceAdmin),
ignoreOverlayPermissionManipulation = current.contains(ManipulationWarningType.OverlayPermission),
ignoreAccessibilityServiceManipulation = current.contains(ManipulationWarningType.AccessibilityService),
ignoreAppDowngrade = current.contains(ManipulationWarningType.AppDowngrade),
ignoreReboot = current.contains(ManipulationWarningType.DidReboot),
ignoreHadManipulation = past.contains(ManipulationWarningType.Unknown),
ignoreHadManipulationFlags = ignoreHadManipulationFlags
ignoreUsageStatsAccessManipulation = currentClassic.contains(ClassicManipulationWarningType.UsageStats),
ignoreNotificationAccessManipulation = currentClassic.contains(ClassicManipulationWarningType.NotificationAccess),
ignoreDeviceAdminManipulationAttempt = currentClassic.contains(ClassicManipulationWarningType.TriedDisablingDeviceAdmin),
ignoreDeviceAdminManipulation = currentClassic.contains(ClassicManipulationWarningType.DeviceAdmin),
ignoreOverlayPermissionManipulation = currentClassic.contains(ClassicManipulationWarningType.OverlayPermission),
ignoreAccessibilityServiceManipulation = currentClassic.contains(ClassicManipulationWarningType.AccessibilityService),
ignoreAppDowngrade = currentClassic.contains(ClassicManipulationWarningType.AppDowngrade),
ignoreReboot = currentClassic.contains(ClassicManipulationWarningType.DidReboot),
ignoreHadManipulation = currentClassic.contains(ClassicManipulationWarningType.Unknown),
ignoreHadManipulationFlags = ignoreHadManipulationFlags,
ignoreManipulationFlags = ignoreManipulationFlags
)
}
}
enum class ManipulationWarningType {
sealed class ManipulationWarningType {
abstract val labelResourceId: Int
data class Classic(val type: ClassicManipulationWarningType): ManipulationWarningType() {
override val labelResourceId = when (type) {
ClassicManipulationWarningType.TriedDisablingDeviceAdmin -> R.string.manage_device_manipulation_device_admin_disable_attempt
ClassicManipulationWarningType.AppDowngrade -> R.string.manage_device_manipulation_app_version
ClassicManipulationWarningType.DeviceAdmin -> R.string.manage_device_manipulation_device_admin_disabled
ClassicManipulationWarningType.UsageStats -> R.string.manage_device_manipulation_usage_stats_access
ClassicManipulationWarningType.NotificationAccess -> R.string.manage_device_manipulation_notification_access
ClassicManipulationWarningType.OverlayPermission -> R.string.manage_device_manipulation_overlay_permission
ClassicManipulationWarningType.AccessibilityService -> R.string.manage_device_manipulation_accessibility_service
ClassicManipulationWarningType.DidReboot -> R.string.manage_device_manipulation_reboot
ClassicManipulationWarningType.Unknown -> R.string.manage_device_manipulation_existed
}
}
data class Flag(val mask: Long, override val labelResourceId: Int): ManipulationWarningType()
}
enum class ClassicManipulationWarningType {
TriedDisablingDeviceAdmin,
AppDowngrade,
DeviceAdmin,
@ -240,20 +286,6 @@ enum class ManipulationWarningType {
Unknown
}
object ManipulationWarningTypeLabel {
fun getLabel(type: ManipulationWarningType) = when (type) {
ManipulationWarningType.TriedDisablingDeviceAdmin -> R.string.manage_device_manipulation_device_admin_disable_attempt
ManipulationWarningType.AppDowngrade -> R.string.manage_device_manipulation_app_version
ManipulationWarningType.DeviceAdmin -> R.string.manage_device_manipulation_device_admin_disabled
ManipulationWarningType.UsageStats -> R.string.manage_device_manipulation_usage_stats_access
ManipulationWarningType.NotificationAccess -> R.string.manage_device_manipulation_notification_access
ManipulationWarningType.OverlayPermission -> R.string.manage_device_manipulation_overlay_permission
ManipulationWarningType.AccessibilityService -> R.string.manage_device_manipulation_accessibility_service
ManipulationWarningType.DidReboot -> R.string.manage_device_manipulation_reboot
ManipulationWarningType.Unknown -> R.string.manage_device_manipulation_existed
}
}
class ManageDeviceManipulationStatus {
val selectedCurrent = mutableListOf<ManipulationWarningType>()
val selectedPast = mutableListOf<ManipulationWarningType>()

View file

@ -35,7 +35,6 @@ import io.timelimit.android.ui.backdoor.BackdoorDialogFragment
import io.timelimit.android.ui.login.NewLoginFragment
import io.timelimit.android.ui.main.ActivityViewModel
import io.timelimit.android.ui.main.ActivityViewModelHolder
import io.timelimit.android.ui.manage.device.manage.ManipulationWarningTypeLabel
import io.timelimit.android.ui.manage.device.manage.ManipulationWarnings
import io.timelimit.android.util.TimeTextUtil
@ -95,7 +94,7 @@ class AnnoyActivity : AppCompatActivity(), ActivityViewModelHolder {
logic.deviceEntry.map {
val reasonItems = (it?.let { ManipulationWarnings.getFromDevice(it) } ?: ManipulationWarnings.empty)
.current
.map { getString(ManipulationWarningTypeLabel.getLabel(it)) }
.map { getString(it.labelResourceId) }
if (reasonItems.isEmpty()) {
null

View file

@ -862,6 +862,8 @@
<string name="manage_device_manipulation_app_version">ältere App-Version installiert</string>
<string name="manage_device_manipulation_reboot">Gerät wurde neu gestartet</string>
<string name="manage_device_manipulation_existed">Es gab eine Manipulation, die wieder beendet wurde</string>
<string name="manage_device_manipulation_unknown">unbekannte Manipulation</string>
<string name="manage_device_manipulation_fgs_killer">mittels Task-Manager beendet</string>
<string name="manage_device_manipulation_btn_ignore">Warnungen bestätigen und ausblenden</string>
<string name="manage_device_manipulation_toast_nothing_selected">Sie müssen zuerst Warnungen wählen, die Sie ignorieren möchten</string>

View file

@ -907,6 +907,8 @@
<string name="manage_device_manipulation_app_version">older App version installed</string>
<string name="manage_device_manipulation_reboot">device was rebooted</string>
<string name="manage_device_manipulation_existed">there was a manipulation which was stopped again</string>
<string name="manage_device_manipulation_unknown">unknown Manipulation</string>
<string name="manage_device_manipulation_fgs_killer">killed using task manager</string>
<string name="manage_device_manipulation_btn_ignore">Confirm and hide warnings</string>
<string name="manage_device_manipulation_toast_nothing_selected">You have to select warnings to ignore first</string>