diff --git a/app/src/main/java/io/timelimit/android/integration/platform/PlatformIntegration.kt b/app/src/main/java/io/timelimit/android/integration/platform/PlatformIntegration.kt index 31dd5d0..53eb1e7 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/PlatformIntegration.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/PlatformIntegration.kt @@ -72,6 +72,8 @@ abstract class PlatformIntegration( // this function requires the device owner permission and a recent android version abstract fun setForceNetworkTime(enable: Boolean) + abstract fun restartApp() + var installedAppsChangeListener: Runnable? = null var systemClockChangeListener: Runnable? = null } diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/AndroidIntegration.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/AndroidIntegration.kt index d57bc88..6596426 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/AndroidIntegration.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/android/AndroidIntegration.kt @@ -39,17 +39,20 @@ import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.LiveData import io.timelimit.android.BuildConfig import io.timelimit.android.R +import io.timelimit.android.async.Threads import io.timelimit.android.coroutines.runAsyncExpectForever import io.timelimit.android.data.model.App import io.timelimit.android.data.model.AppActivity import io.timelimit.android.integration.platform.* import io.timelimit.android.integration.platform.android.foregroundapp.ForegroundAppHelper +import io.timelimit.android.ui.MainActivity import io.timelimit.android.ui.homescreen.HomescreenActivity import io.timelimit.android.ui.lock.LockActivity import io.timelimit.android.ui.manipulation.AnnoyActivity import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.consumeEach import kotlinx.coroutines.delay +import kotlin.system.exitProcess class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectionLevel) { @@ -503,4 +506,20 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio } } } + + override fun restartApp() { + Threads.mainThreadHandler.post { + if (lastAppStatusMessage != null) { + LockActivity.start(context, BuildConfig.APPLICATION_ID, null) + + if (!BackgroundService.isBackgroundActivityRestricted(context)) { + context.startService(Intent(context, BackgroundActionService::class.java)) + } + } + + Threads.mainThreadHandler.post { + exitProcess(0) + } + } + } } diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt index f83960d..2a25a90 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/android/BackgroundService.kt @@ -57,7 +57,7 @@ class BackgroundService: Service() { } } - private fun isBackgroundActivityRestricted(context: Context): Boolean { + fun isBackgroundActivityRestricted(context: Context): Boolean { val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { diff --git a/app/src/main/java/io/timelimit/android/integration/platform/dummy/DummyIntegration.kt b/app/src/main/java/io/timelimit/android/integration/platform/dummy/DummyIntegration.kt index 62f7d38..c7e02e9 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/dummy/DummyIntegration.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/dummy/DummyIntegration.kt @@ -170,4 +170,6 @@ class DummyIntegration( override fun setEnableCustomHomescreen(enable: Boolean) = Unit override fun setForceNetworkTime(enable: Boolean) = Unit + + override fun restartApp() = Unit } diff --git a/app/src/main/java/io/timelimit/android/logic/AppLogic.kt b/app/src/main/java/io/timelimit/android/logic/AppLogic.kt index 34e3099..eee1437 100644 --- a/app/src/main/java/io/timelimit/android/logic/AppLogic.kt +++ b/app/src/main/java/io/timelimit/android/logic/AppLogic.kt @@ -96,6 +96,7 @@ class AppLogic( init { SyncInstalledAppsLogic(this) + WatchdogLogic(this) } val manipulationLogic = ManipulationLogic(this) diff --git a/app/src/main/java/io/timelimit/android/logic/WatchdogLogic.kt b/app/src/main/java/io/timelimit/android/logic/WatchdogLogic.kt new file mode 100644 index 0000000..e349b03 --- /dev/null +++ b/app/src/main/java/io/timelimit/android/logic/WatchdogLogic.kt @@ -0,0 +1,55 @@ +/* + * TimeLimit Copyright 2019 - 2020 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 + * the Free Software Foundation version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.timelimit.android.logic + +import android.widget.Toast +import io.timelimit.android.async.Threads +import io.timelimit.android.coroutines.runAsyncExpectForever +import kotlinx.coroutines.delay +import java.util.concurrent.atomic.AtomicBoolean + +data class WatchdogLogic(private val logic: AppLogic) { + init { + runAsyncExpectForever { + val boolean = AtomicBoolean(false) + val runnable = Runnable { + logic.database.runInUnobservedTransaction { + logic.database.config().getOwnDeviceIdSync() + } + + boolean.compareAndSet(false, true) + } + + while (true) { + Threads.database.execute(runnable) + + for (i in 1..15) { + delay(1000) + } + + if (!boolean.getAndSet(false)) { + Toast.makeText(logic.context, "TimeLimit: watchdog triggered", Toast.LENGTH_SHORT).show() + + while (true) { + delay(3000) + logic.platformIntegration.restartApp() + } + } + } + } + } +} \ No newline at end of file