diff --git a/app/src/main/java/io/timelimit/android/integration/platform/DeviceOwnerApi.kt b/app/src/main/java/io/timelimit/android/integration/platform/DeviceOwnerApi.kt index 99049cc..563b6c4 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/DeviceOwnerApi.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/DeviceOwnerApi.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2023 Jonas Lochmann + * TimeLimit Copyright 2019 - 2024 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,4 +25,7 @@ interface DeviceOwnerApi { fun setOrganizationName(name: String) fun transferOwnership(packageName: String, dryRun: Boolean = false) + + // returns true on success; never throws + fun grantLocationAccess(): Boolean } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/integration/platform/android/AndroidDeviceOwnerApi.kt b/app/src/main/java/io/timelimit/android/integration/platform/android/AndroidDeviceOwnerApi.kt index 5420d96..ef8e12b 100644 --- a/app/src/main/java/io/timelimit/android/integration/platform/android/AndroidDeviceOwnerApi.kt +++ b/app/src/main/java/io/timelimit/android/integration/platform/android/AndroidDeviceOwnerApi.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2023 Jonas Lochmann + * TimeLimit Copyright 2019 - 2024 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 @@ -15,6 +15,7 @@ */ package io.timelimit.android.integration.platform.android +import android.Manifest import android.app.admin.DeviceAdminReceiver import android.app.admin.DevicePolicyManager import android.content.ComponentName @@ -124,4 +125,14 @@ class AndroidDeviceOwnerApi( devicePolicyManager.setDelegatedScopes(componentName, packageName, emptyList()) devicePolicyManager.transferOwnership(componentName, targetComponentName, null) } + + override fun grantLocationAccess(): Boolean { + if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) return false + if (!devicePolicyManager.isDeviceOwnerApp(componentName.packageName)) return false + + return devicePolicyManager.setPermissionGrantState( + componentName, componentName.packageName, Manifest.permission.ACCESS_FINE_LOCATION, + DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED + ) + } } \ No newline at end of file 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 7e40e5b..bb43054 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 @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2023 Jonas Lochmann + * TimeLimit Copyright 2019 - 2024 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 @@ -15,6 +15,7 @@ */ package io.timelimit.android.integration.platform.android +import android.Manifest import android.annotation.TargetApi import android.app.ActivityManager import android.app.Application @@ -40,6 +41,7 @@ import android.widget.Toast import androidx.collection.LruCache import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentActivity import androidx.lifecycle.LiveData import io.timelimit.android.BuildConfig @@ -511,6 +513,40 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { policyManager.addUserRestriction(deviceAdmin, UserManager.DISALLOW_SAFE_BOOT) } + + policyManager.getPermissionGrantState( + deviceAdmin, + context.packageName, + Manifest.permission.ACCESS_FINE_LOCATION, + ).let { + if (it == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) { + policyManager.setPermissionGrantState( + deviceAdmin, + context.packageName, + Manifest.permission.ACCESS_FINE_LOCATION, + if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) + DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED + else + DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED + ) + } + } + + policyManager.setPermissionGrantState( + deviceAdmin, + context.packageName, + Manifest.permission.CALL_PHONE, + DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED + ) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + policyManager.setPermissionGrantState( + deviceAdmin, + context.packageName, + Manifest.permission.POST_NOTIFICATIONS, + DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED + ) + } } else /* disable lockdown */ { // enable problematic features policyManager.clearUserRestriction(deviceAdmin, UserManager.DISALLOW_ADD_USER) @@ -520,6 +556,29 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio policyManager.clearUserRestriction(deviceAdmin, UserManager.DISALLOW_SAFE_BOOT) } + policyManager.setPermissionGrantState( + deviceAdmin, + context.packageName, + Manifest.permission.ACCESS_FINE_LOCATION, + DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT + ) + + policyManager.setPermissionGrantState( + deviceAdmin, + context.packageName, + Manifest.permission.CALL_PHONE, + DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT + ) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + policyManager.setPermissionGrantState( + deviceAdmin, + context.packageName, + Manifest.permission.POST_NOTIFICATIONS, + DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT + ) + } + enableSystemApps() stopSuspendingForAllApps() setBlockedFeatures(emptySet()) 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 4c86606..7e7e909 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 @@ -207,5 +207,7 @@ class DummyIntegration( override fun setOrganizationName(name: String) = throw SecurityException() override fun transferOwnership(packageName: String, dryRun: Boolean) = throw IllegalStateException("unsupported operation") + + override fun grantLocationAccess(): Boolean = false } } diff --git a/app/src/main/java/io/timelimit/android/ui/lock/LockActionFragment.kt b/app/src/main/java/io/timelimit/android/ui/lock/LockActionFragment.kt index 45805d4..a069ee9 100644 --- a/app/src/main/java/io/timelimit/android/ui/lock/LockActionFragment.kt +++ b/app/src/main/java/io/timelimit/android/ui/lock/LockActionFragment.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2022 Jonas Lochmann + * TimeLimit Copyright 2019 - 2024 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 @@ -124,7 +124,7 @@ class LockActionFragment : Fragment() { override fun setThisDeviceAsCurrentDevice() = this@LockActionFragment.setThisDeviceAsCurrentDevice() override fun requestLocationPermission() { - RequestWifiPermission.doRequest(this@LockActionFragment, LOCATION_REQUEST_CODE) + RequestWifiPermission.doRequest(this@LockActionFragment, LOCATION_REQUEST_CODE, auth.logic.platformIntegration) } override fun disableLimitsTemporarily() { diff --git a/app/src/main/java/io/timelimit/android/ui/manage/category/settings/networks/ManageCategoryNetworksView.kt b/app/src/main/java/io/timelimit/android/ui/manage/category/settings/networks/ManageCategoryNetworksView.kt index ef4aec8..fe5be35 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/category/settings/networks/ManageCategoryNetworksView.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/category/settings/networks/ManageCategoryNetworksView.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2023 Jonas Lochmann + * TimeLimit Copyright 2019 - 2024 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 @@ -117,7 +117,7 @@ object ManageCategoryNetworksView { } view.grantPermissionButton.setOnClickListener { - RequestWifiPermission.doRequest(fragment, permissionRequestCode) + RequestWifiPermission.doRequest(fragment, permissionRequestCode, auth.logic.platformIntegration) } isFullVersionLive.observe(lifecycleOwner, Observer { isFullVersion -> diff --git a/app/src/main/java/io/timelimit/android/ui/manage/category/settings/networks/RequestWifiPermission.kt b/app/src/main/java/io/timelimit/android/ui/manage/category/settings/networks/RequestWifiPermission.kt index 44be1f7..73f29db 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/category/settings/networks/RequestWifiPermission.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/category/settings/networks/RequestWifiPermission.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2020 Jonas Lochmann + * TimeLimit Copyright 2019 - 2024 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 @@ -26,6 +26,7 @@ import android.widget.Toast import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import io.timelimit.android.R +import io.timelimit.android.integration.platform.PlatformIntegration object RequestWifiPermission { private fun isLocationEnabled(context: Context): Boolean = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) @@ -36,8 +37,10 @@ object RequestWifiPermission { locationManager.isLocationEnabled } - fun doRequest(fragment: Fragment, permissionRequestCode: Int) { + fun doRequest(fragment: Fragment, permissionRequestCode: Int, platformIntegration: PlatformIntegration) { if (ContextCompat.checkSelfPermission(fragment.requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + if (platformIntegration.deviceOwner.grantLocationAccess()) return + fragment.requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), permissionRequestCode) } else if (!isLocationEnabled(fragment.requireContext())) { Toast.makeText(fragment.requireContext(), R.string.category_networks_toast_enable_location_service, Toast.LENGTH_SHORT).show()