Improve overlay permission check

This commit is contained in:
Jonas Lochmann 2022-03-21 01:00:00 +01:00
parent c6e1a2b7c1
commit fadb22b90c
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
2 changed files with 107 additions and 3 deletions

View file

@ -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
}
}

View file

@ -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
} }
} }