mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Improve overlay permission check
This commit is contained in:
parent
c6e1a2b7c1
commit
fadb22b90c
2 changed files with 107 additions and 3 deletions
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.integration.platform.android
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.AppOpsManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Process
|
||||||
|
import java.lang.ClassCastException
|
||||||
|
import java.lang.IllegalArgumentException
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
|
object AppOps {
|
||||||
|
internal class ReflectionData (
|
||||||
|
val getOpsForPackage: Method,
|
||||||
|
val getOps: Method,
|
||||||
|
val getMode: Method
|
||||||
|
)
|
||||||
|
|
||||||
|
private val reflectionData by lazy {
|
||||||
|
try {
|
||||||
|
val getOpsForPackage = AppOpsManager::class.java.getMethod("getOpsForPackage", Int::class.java, String::class.java, Array<String>::class.java)
|
||||||
|
val packageOpsClass = Class.forName("android.app.AppOpsManager\$PackageOps")
|
||||||
|
val getOps = packageOpsClass.getMethod("getOps")
|
||||||
|
val opEntryClass = Class.forName("android.app.AppOpsManager\$OpEntry")
|
||||||
|
val getMode = opEntryClass.getMethod("getMode")
|
||||||
|
|
||||||
|
ReflectionData(
|
||||||
|
getOpsForPackage = getOpsForPackage,
|
||||||
|
getOps = getOps,
|
||||||
|
getMode = getMode
|
||||||
|
)
|
||||||
|
} catch (ex: ReflectiveOperationException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOpMode(op: String, appOpsManager: AppOpsManager, context: Context): Mode {
|
||||||
|
try {
|
||||||
|
val reflectionData = reflectionData ?: return Mode.Unknown
|
||||||
|
|
||||||
|
val uid: Int = Process.myUid()
|
||||||
|
val pkg: String = context.packageName
|
||||||
|
val ops: Array<String> = arrayOf(op)
|
||||||
|
|
||||||
|
val packageOpsList =
|
||||||
|
reflectionData.getOpsForPackage.invoke(appOpsManager, uid, pkg, ops) as List<*>
|
||||||
|
|
||||||
|
val packageOpsItem = packageOpsList.singleOrNull() ?: return Mode.Unknown
|
||||||
|
|
||||||
|
val opEntryList = reflectionData.getOps.invoke(packageOpsItem) as List<*>
|
||||||
|
|
||||||
|
val opEntryItem = opEntryList.singleOrNull() ?: return Mode.Unknown
|
||||||
|
|
||||||
|
val mode = reflectionData.getMode.invoke(opEntryItem) as Int
|
||||||
|
|
||||||
|
return when (mode) {
|
||||||
|
AppOpsManager.MODE_ALLOWED -> Mode.Allowed
|
||||||
|
AppOpsManager.MODE_DEFAULT -> Mode.Default
|
||||||
|
AppOpsManager.MODE_IGNORED -> Mode.Ignored
|
||||||
|
AppOpsManager.MODE_ERRORED -> Mode.Blocked
|
||||||
|
else -> Mode.Unknown
|
||||||
|
}
|
||||||
|
} catch (ex: ReflectiveOperationException) {
|
||||||
|
return Mode.Unknown
|
||||||
|
} catch (ex: ClassCastException) {
|
||||||
|
return Mode.Unknown
|
||||||
|
} catch (ex: IllegalArgumentException) {
|
||||||
|
return Mode.Unknown
|
||||||
|
} catch (ex: InvocationTargetException) {
|
||||||
|
if (ex.cause is SecurityException) return Mode.Unknown
|
||||||
|
else throw ex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
Unknown,
|
||||||
|
Allowed,
|
||||||
|
Blocked,
|
||||||
|
Default,
|
||||||
|
Ignored
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2022 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
|
||||||
|
@ -100,8 +100,14 @@ class OverlayUtil(private var application: Application) {
|
||||||
private fun checkAppOp(): Boolean {
|
private fun checkAppOp(): Boolean {
|
||||||
if (systemOverlayOp == null) return false
|
if (systemOverlayOp == null) return false
|
||||||
|
|
||||||
val status = appsOpsManager.checkOpNoThrow(systemOverlayOp, Process.myUid(), application.packageName)
|
val mode1 = AppOps.getOpMode(systemOverlayOp, appsOpsManager, application)
|
||||||
|
|
||||||
return status == AppOpsManager.MODE_ALLOWED || status == AppOpsManager.MODE_IGNORED
|
if (mode1 != AppOps.Mode.Unknown) {
|
||||||
|
return mode1 == AppOps.Mode.Allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
val mode2 = appsOpsManager.checkOpNoThrow(systemOverlayOp, Process.myUid(), application.packageName)
|
||||||
|
|
||||||
|
return mode2 == AppOpsManager.MODE_ALLOWED || mode2 == AppOpsManager.MODE_IGNORED
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue