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 {
applicationId "io.timelimit.android"
minSdkVersion 21
targetSdkVersion 33
targetSdkVersion 34
versionCode 205
versionName "6.14.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

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

View file

@ -361,7 +361,13 @@ data class Device(
(currentProtectionLevel != ProtectionLevel.DeviceOwner)
@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 {

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
* 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.NotificationManager
import android.app.Service
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import io.timelimit.android.R
import io.timelimit.android.integration.platform.AppStatusMessage
import io.timelimit.android.logic.DefaultAppLogic
@ -35,9 +40,10 @@ class BackgroundService: Service() {
fun setStatusMessage(status: AppStatusMessage?, context: Context) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val intent = Intent(context, BackgroundService::class.java)
val isRestricted = isBackgroundActivityRestricted(context)
if (status != null) {
if (isBackgroundActivityRestricted(context)) {
if (isRestricted) {
val notification = buildNotification(status, context)
notificationManager.notify(NotificationIds.APP_STATUS, notification)
@ -50,7 +56,7 @@ class BackgroundService: Service() {
} else {
context.stopService(intent)
if (isBackgroundActivityRestricted(context)) {
if (isRestricted) {
notificationManager.cancel(NotificationIds.APP_STATUS)
}
}
@ -59,11 +65,19 @@ class BackgroundService: Service() {
fun isBackgroundActivityRestricted(context: Context): Boolean {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return activityManager.isBackgroundRestricted
} else {
return false
if (VERSION.SDK_INT >= VERSION_CODES.P) {
if (activityManager.isBackgroundRestricted) {
return true
}
}
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)

View file

@ -37,6 +37,7 @@ import io.timelimit.android.extensions.MinuteOfDay
import io.timelimit.android.extensions.nextBlockedMinuteOfWeek
import io.timelimit.android.integration.platform.*
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.logic.blockingreason.AppBaseHandling
import io.timelimit.android.logic.blockingreason.CategoryHandlingCache
@ -178,6 +179,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
private suspend fun backgroundServiceLoop() {
val realTime = RealTime.newInstance()
var skipBackgroundRestrictionChecks = 0
while (true) {
val backgroundServiceInterval = when (slowMainLoop) {
@ -244,6 +246,24 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
// loop logic
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
appLogic.realTimeLogic.getRealTime(realTime)

View file

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

View file

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

View file

@ -38,7 +38,8 @@ fun PermissionGoals(status: PermissionScreenContent.Status) {
PermissionGoal(
stringResource(R.string.manage_device_permission_goal_limit_title),
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 {
Text(stringResource(R.string.manage_device_permission_goal_needs))
@ -52,8 +53,13 @@ fun PermissionGoals(status: PermissionScreenContent.Status) {
PermissionIcon(SystemPermission.AccessibilityService, status.accessibility)
}
if (status.usageStats == RuntimePermissionStatus.NotRequired && !status.isQOrLater)
Text(stringResource(R.string.manage_device_permission_goal_reached_by_old_android))
FlowRow {
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(

View file

@ -32,7 +32,8 @@ data class PermissionScreenContent(
val usageStats: RuntimePermissionStatus,
val overlay: RuntimePermissionStatus,
val accessibility: Boolean,
val isQOrLater: Boolean
val isQOrLater: Boolean,
val androidPlatformLevel: Int
)
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 DeviceItem(val device: Device, val userName: String?, val userType: UserType?, val isCurrentDevice: Boolean, val isConnected: Boolean) {
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?)
}

View file

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

View file

@ -108,7 +108,8 @@ object SetupLocalModePermissions {
usageStats = platformIntegration.getForegroundAppPermissionStatus(),
overlay = platformIntegration.getDrawOverOtherAppsPermissionStatus(true),
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_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_future">benötigt zukünftig</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_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_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_future">needs in the future</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_reached_by_old_android">needs nothing due to old Android version</string>