Adjust for Android U

This commit is contained in:
Jonas Lochmann 2023-06-12 02:00:00 +02:00
parent 37b3ea08e9
commit 43db6f4d2e
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
14 changed files with 74 additions and 18 deletions

View file

@ -27,7 +27,7 @@ android {
defaultConfig { defaultConfig {
applicationId "io.timelimit.android" applicationId "io.timelimit.android"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 33 targetSdkVersion 34
versionCode 205 versionCode 205
versionName "6.14.2" versionName "6.14.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -30,6 +30,7 @@
tools:ignore="ProtectedPermissions" /> tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
<uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- for the categories which are limited to a wifi network --> <!-- for the categories which are limited to a wifi network -->
@ -136,6 +137,7 @@
</receiver> </receiver>
<service <service
android:foregroundServiceType="systemExempted"
android:name=".integration.platform.android.BackgroundService" android:name=".integration.platform.android.BackgroundService"
android:exported="false" /> android:exported="false" />

View file

@ -361,7 +361,13 @@ data class Device(
(currentProtectionLevel != ProtectionLevel.DeviceOwner) (currentProtectionLevel != ProtectionLevel.DeviceOwner)
@Transient @Transient
val isImportant = hasAnyManipulation || missingPermissionAtQOrLater || didReportUninstall val missingDeviceAdminPermission =
platformType == DevicePlatform.ANDROID &&
platformLevel >= 2 &&
currentProtectionLevel == ProtectionLevel.None
@Transient
val isImportant = hasAnyManipulation || missingPermissionAtQOrLater || missingDeviceAdminPermission || didReportUninstall
} }
enum class NetworkTime { enum class NetworkTime {

View file

@ -1,5 +1,5 @@
/* /*
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann * TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -18,12 +18,17 @@ package io.timelimit.android.integration.platform.android
import android.app.ActivityManager import android.app.ActivityManager
import android.app.NotificationManager import android.app.NotificationManager
import android.app.Service import android.app.Service
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.IBinder import android.os.IBinder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import io.timelimit.android.R import io.timelimit.android.R
import io.timelimit.android.integration.platform.AppStatusMessage import io.timelimit.android.integration.platform.AppStatusMessage
import io.timelimit.android.logic.DefaultAppLogic import io.timelimit.android.logic.DefaultAppLogic
@ -35,9 +40,10 @@ class BackgroundService: Service() {
fun setStatusMessage(status: AppStatusMessage?, context: Context) { fun setStatusMessage(status: AppStatusMessage?, context: Context) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val intent = Intent(context, BackgroundService::class.java) val intent = Intent(context, BackgroundService::class.java)
val isRestricted = isBackgroundActivityRestricted(context)
if (status != null) { if (status != null) {
if (isBackgroundActivityRestricted(context)) { if (isRestricted) {
val notification = buildNotification(status, context) val notification = buildNotification(status, context)
notificationManager.notify(NotificationIds.APP_STATUS, notification) notificationManager.notify(NotificationIds.APP_STATUS, notification)
@ -50,7 +56,7 @@ class BackgroundService: Service() {
} else { } else {
context.stopService(intent) context.stopService(intent)
if (isBackgroundActivityRestricted(context)) { if (isRestricted) {
notificationManager.cancel(NotificationIds.APP_STATUS) notificationManager.cancel(NotificationIds.APP_STATUS)
} }
} }
@ -59,11 +65,19 @@ class BackgroundService: Service() {
fun isBackgroundActivityRestricted(context: Context): Boolean { fun isBackgroundActivityRestricted(context: Context): Boolean {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (VERSION.SDK_INT >= VERSION_CODES.P) {
return activityManager.isBackgroundRestricted if (activityManager.isBackgroundRestricted) {
} else { return true
return false }
} }
if (VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE) {
if (!context.getSystemService<DevicePolicyManager>()!!.isAdminActive(ComponentName(context, AdminReceiver::class.java))) {
return true
}
}
return false
} }
private fun buildNotification(appStatusMessage: AppStatusMessage, context: Context) = NotificationCompat.Builder(context, NotificationChannels.APP_STATUS) private fun buildNotification(appStatusMessage: AppStatusMessage, context: Context) = NotificationCompat.Builder(context, NotificationChannels.APP_STATUS)

View file

@ -37,6 +37,7 @@ import io.timelimit.android.extensions.MinuteOfDay
import io.timelimit.android.extensions.nextBlockedMinuteOfWeek import io.timelimit.android.extensions.nextBlockedMinuteOfWeek
import io.timelimit.android.integration.platform.* import io.timelimit.android.integration.platform.*
import io.timelimit.android.integration.platform.android.AccessibilityService import io.timelimit.android.integration.platform.android.AccessibilityService
import io.timelimit.android.integration.platform.android.BackgroundService
import io.timelimit.android.livedata.* import io.timelimit.android.livedata.*
import io.timelimit.android.logic.blockingreason.AppBaseHandling import io.timelimit.android.logic.blockingreason.AppBaseHandling
import io.timelimit.android.logic.blockingreason.CategoryHandlingCache import io.timelimit.android.logic.blockingreason.CategoryHandlingCache
@ -178,6 +179,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
private suspend fun backgroundServiceLoop() { private suspend fun backgroundServiceLoop() {
val realTime = RealTime.newInstance() val realTime = RealTime.newInstance()
var skipBackgroundRestrictionChecks = 0
while (true) { while (true) {
val backgroundServiceInterval = when (slowMainLoop) { val backgroundServiceInterval = when (slowMainLoop) {
@ -244,6 +246,24 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
// loop logic // loop logic
try { try {
if (skipBackgroundRestrictionChecks <= 0) {
if (BackgroundService.isBackgroundActivityRestricted(appLogic.context)) {
commitUsedTimeUpdaters()
undisturbedCategoryUsageCounter.reset()
appLogic.platformIntegration.setShowBlockingOverlay(false)
appLogic.platformIntegration.setAppStatusMessage(
AppStatusMessage(
appLogic.context.getString(R.string.background_logic_error),
appLogic.context.getString(R.string.background_logic_error_permission)
)
)
appLogic.timeApi.sleep(BACKGROUND_SERVICE_INTERVAL_LONG)
continue
} else skipBackgroundRestrictionChecks = 128
} else skipBackgroundRestrictionChecks--
// get the current time // get the current time
appLogic.realTimeLogic.getRealTime(realTime) appLogic.realTimeLogic.getRealTime(realTime)

View file

@ -69,7 +69,7 @@ class NFCU2FManager (val parent: U2fManager, context: Context) {
private val nfcReceiverIntent = PendingIntent.getBroadcast( private val nfcReceiverIntent = PendingIntent.getBroadcast(
context, context,
PendingIntentIds.U2F_NFC_DISCOVERY, PendingIntentIds.U2F_NFC_DISCOVERY,
Intent(nfcReceiverAction), Intent(nfcReceiverAction).setPackage(context.packageName),
PendingIntentIds.PENDING_INTENT_FLAGS_ALLOW_MUTATION PendingIntentIds.PENDING_INTENT_FLAGS_ALLOW_MUTATION
) )

View file

@ -77,7 +77,7 @@ class UsbU2FManager (val parent: U2fManager, context: Context) {
private val permissionResponseIntent = PendingIntent.getBroadcast( private val permissionResponseIntent = PendingIntent.getBroadcast(
context, context,
PendingIntentIds.U2F_USB_RESPONSE, PendingIntentIds.U2F_USB_RESPONSE,
Intent(permissionResponseAction), Intent(permissionResponseAction).setPackage(context.packageName),
PendingIntentIds.PENDING_INTENT_FLAGS_ALLOW_MUTATION PendingIntentIds.PENDING_INTENT_FLAGS_ALLOW_MUTATION
) )

View file

@ -38,7 +38,8 @@ fun PermissionGoals(status: PermissionScreenContent.Status) {
PermissionGoal( PermissionGoal(
stringResource(R.string.manage_device_permission_goal_limit_title), stringResource(R.string.manage_device_permission_goal_limit_title),
status.usageStats != RuntimePermissionStatus.NotGranted && status.usageStats != RuntimePermissionStatus.NotGranted &&
(!status.isQOrLater || status.overlay == RuntimePermissionStatus.Granted || status.accessibility), (!status.isQOrLater || status.overlay == RuntimePermissionStatus.Granted || status.accessibility) &&
(status.androidPlatformLevel < 2 || status.protectionLevel != ProtectionLevel.None)
) { ) {
if (status.usageStats != RuntimePermissionStatus.NotRequired) FlowRow { if (status.usageStats != RuntimePermissionStatus.NotRequired) FlowRow {
Text(stringResource(R.string.manage_device_permission_goal_needs)) Text(stringResource(R.string.manage_device_permission_goal_needs))
@ -52,8 +53,13 @@ fun PermissionGoals(status: PermissionScreenContent.Status) {
PermissionIcon(SystemPermission.AccessibilityService, status.accessibility) PermissionIcon(SystemPermission.AccessibilityService, status.accessibility)
} }
if (status.usageStats == RuntimePermissionStatus.NotRequired && !status.isQOrLater) FlowRow {
Text(stringResource(R.string.manage_device_permission_goal_reached_by_old_android)) Text(stringResource(
if (status.androidPlatformLevel >= 2) R.string.manage_device_permission_goal_needs
else R.string.manage_device_permission_goal_eventually_needs_future
))
PermissionIcon(SystemPermission.DeviceAdmin, status.protectionLevel != ProtectionLevel.None)
}
} }
PermissionGoal( PermissionGoal(

View file

@ -32,7 +32,8 @@ data class PermissionScreenContent(
val usageStats: RuntimePermissionStatus, val usageStats: RuntimePermissionStatus,
val overlay: RuntimePermissionStatus, val overlay: RuntimePermissionStatus,
val accessibility: Boolean, val accessibility: Boolean,
val isQOrLater: Boolean val isQOrLater: Boolean,
val androidPlatformLevel: Int
) )
data class Dialog( data class Dialog(

View file

@ -412,7 +412,9 @@ object OverviewHandling {
data class UserList(val list: List<UserItem>, val canAdd: Boolean, val canShowMore: Boolean) data class UserList(val list: List<UserItem>, val canAdd: Boolean, val canShowMore: Boolean)
data class DeviceItem(val device: Device, val userName: String?, val userType: UserType?, val isCurrentDevice: Boolean, val isConnected: Boolean) { data class DeviceItem(val device: Device, val userName: String?, val userType: UserType?, val isCurrentDevice: Boolean, val isConnected: Boolean) {
val isMissingRequiredPermission = userType == UserType.Child && ( val isMissingRequiredPermission = userType == UserType.Child && (
device.currentUsageStatsPermission == RuntimePermissionStatus.NotGranted || device.missingPermissionAtQOrLater) device.currentUsageStatsPermission == RuntimePermissionStatus.NotGranted ||
device.missingPermissionAtQOrLater ||
device.missingDeviceAdminPermission)
} }
data class DeviceList(val list: List<DeviceItem>, val canAdd: Boolean, val canShowMore: OverviewState.DeviceList?) data class DeviceList(val list: List<DeviceItem>, val canAdd: Boolean, val canShowMore: OverviewState.DeviceList?)
} }

View file

@ -16,6 +16,7 @@
package io.timelimit.android.ui.model.managedevice package io.timelimit.android.ui.model.managedevice
import io.timelimit.android.data.model.Device import io.timelimit.android.data.model.Device
import io.timelimit.android.data.model.DevicePlatform
import io.timelimit.android.logic.AppLogic import io.timelimit.android.logic.AppLogic
import io.timelimit.android.ui.manage.device.manage.permission.PermissionScreenContent import io.timelimit.android.ui.manage.device.manage.permission.PermissionScreenContent
import io.timelimit.android.ui.model.ActivityCommand import io.timelimit.android.ui.model.ActivityCommand
@ -82,7 +83,8 @@ object ManageDevicePermissions {
usageStats = device.currentUsageStatsPermission, usageStats = device.currentUsageStatsPermission,
overlay = device.currentOverlayPermission, overlay = device.currentOverlayPermission,
accessibility = device.accessibilityServiceEnabled, accessibility = device.accessibilityServiceEnabled,
isQOrLater = device.qOrLater isQOrLater = device.qOrLater,
androidPlatformLevel = if (device.platformType == DevicePlatform.ANDROID) device.platformLevel else 0
) )
}.distinctUntilChanged() }.distinctUntilChanged()
} }

View file

@ -108,7 +108,8 @@ object SetupLocalModePermissions {
usageStats = platformIntegration.getForegroundAppPermissionStatus(), usageStats = platformIntegration.getForegroundAppPermissionStatus(),
overlay = platformIntegration.getDrawOverOtherAppsPermissionStatus(true), overlay = platformIntegration.getDrawOverOtherAppsPermissionStatus(true),
accessibility = platformIntegration.isAccessibilityServiceEnabled(), accessibility = platformIntegration.isAccessibilityServiceEnabled(),
isQOrLater = AndroidVersion.qOrLater isQOrLater = AndroidVersion.qOrLater,
androidPlatformLevel = AndroidVersion.platformLevel
) )
) )

View file

@ -1033,6 +1033,7 @@
<string name="manage_device_permission_goal_manipulation_protection_unavailable">in der aktuellen Umgebung nicht verfügbar</string> <string name="manage_device_permission_goal_manipulation_protection_unavailable">in der aktuellen Umgebung nicht verfügbar</string>
<string name="manage_device_permission_goal_manipulation_protection_check_remotely">öffnen Sie diese Ansicht am entsprechenden Gerät</string> <string name="manage_device_permission_goal_manipulation_protection_check_remotely">öffnen Sie diese Ansicht am entsprechenden Gerät</string>
<string name="manage_device_permission_goal_eventually_needs">benötigt evtl.</string> <string name="manage_device_permission_goal_eventually_needs">benötigt evtl.</string>
<string name="manage_device_permission_goal_eventually_needs_future">benötigt zukünftig</string>
<string name="manage_device_permission_goal_needs">benötigt</string> <string name="manage_device_permission_goal_needs">benötigt</string>
<string name="manage_device_permission_goal_or">oder</string> <string name="manage_device_permission_goal_or">oder</string>
<string name="manage_device_permission_goal_reached_by_old_android">benötigt wegen alter Android-Version Nichts weiter</string> <string name="manage_device_permission_goal_reached_by_old_android">benötigt wegen alter Android-Version Nichts weiter</string>

View file

@ -1082,6 +1082,7 @@
<string name="manage_device_permission_goal_manipulation_protection_unavailable">unavailable in the current environment</string> <string name="manage_device_permission_goal_manipulation_protection_unavailable">unavailable in the current environment</string>
<string name="manage_device_permission_goal_manipulation_protection_check_remotely">open this screen at the device</string> <string name="manage_device_permission_goal_manipulation_protection_check_remotely">open this screen at the device</string>
<string name="manage_device_permission_goal_eventually_needs">eventually needs</string> <string name="manage_device_permission_goal_eventually_needs">eventually needs</string>
<string name="manage_device_permission_goal_eventually_needs_future">needs in the future</string>
<string name="manage_device_permission_goal_needs">needs</string> <string name="manage_device_permission_goal_needs">needs</string>
<string name="manage_device_permission_goal_or">or</string> <string name="manage_device_permission_goal_or">or</string>
<string name="manage_device_permission_goal_reached_by_old_android">needs nothing due to old Android version</string> <string name="manage_device_permission_goal_reached_by_old_android">needs nothing due to old Android version</string>