diff options
Diffstat (limited to 'app/src/main/java/moe/yuuta/workmode/access/AccessLayer.kt')
-rw-r--r-- | app/src/main/java/moe/yuuta/workmode/access/AccessLayer.kt | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/app/src/main/java/moe/yuuta/workmode/access/AccessLayer.kt b/app/src/main/java/moe/yuuta/workmode/access/AccessLayer.kt new file mode 100644 index 0000000..78ef5dd --- /dev/null +++ b/app/src/main/java/moe/yuuta/workmode/access/AccessLayer.kt @@ -0,0 +1,86 @@ +package moe.yuuta.workmode.access + +import android.content.Context +import android.content.pm.LauncherApps +import android.content.pm.PackageManager +import android.os.Bundle +import android.os.PersistableBundle +import android.os.Process +import android.os.UserHandle +import android.system.Os +import moe.yuuta.workmode.BuildConfig +import java.lang.reflect.Field +import java.lang.reflect.Method + +/** + * An layer to access package suspending related APIs, it is a low-level layer which is used to call System APIs directly. + * + * TODO: Multi-user support + */ +internal class AccessLayer(private val mContext: Context) { + private val mPM: PackageManager = mContext.packageManager + + fun setPackagesSuspended(packageNames: Array<String>, suspended: Boolean, + appExtras: PersistableBundle, launcherExtras: PersistableBundle, + dialogMessage: String): Array<String> { + // ApplicationPackageManager ALWAYS uses context.getOpPackageName() as the argument "callingPackage" + // My callingPackage MUSTN'T equals to 'android' + // If we are using packageName of 'android', system will show disabled + // by admin dialog instead of suspended dialog + // F**k Google + val func: Method = Class.forName("android.content.pm.IPackageManager") + .getDeclaredMethod("setPackagesSuspendedAsUser", + Array<String>::class.java, + Boolean::class.java, + PersistableBundle::class.java, + PersistableBundle::class.java, + String::class.java, + String::class.java, + Int::class.java) + + // It's an unstable design + val iPM: Field = mPM::class.java.getDeclaredField("mPM") + iPM.isAccessible = true + + return func.invoke(iPM.get(mPM), + packageNames, + suspended, + appExtras, + launcherExtras, + dialogMessage, + BuildConfig.APPLICATION_ID, + UserHandle.getUserHandleForUid(mPM.getPackageUid(mContext.packageName, 0)).hashCode()) as Array<String> + } + + /** + * This method will SET your UID and you WON'T BE ABLE TO GO BACK. + * Create a new process and access it. + */ + fun getSuspendedPackageAppExtras(packageName: String): PersistableBundle? { + Os.setuid(mPM.getPackageUid(packageName, 0)) + // ApplicationPackageManager ALWAYS uses context.getOpPackageName() as the package name + // F**k Google + val func: Method = Class.forName("android.content.pm.IPackageManager") + .getDeclaredMethod("getSuspendedPackageAppExtras", + String::class.java, + Int::class.java) + + // It's an unstable design + val iPM: Field = mPM::class.java.getDeclaredField("mPM") + iPM.isAccessible = true + + return func.invoke(iPM.get(mPM), + packageName, + UserHandle.getUserHandleForUid(mPM.getPackageUid(packageName, 0)).hashCode()) as PersistableBundle? + } + + @Throws(PackageManager.NameNotFoundException::class) + fun isPackageSuspended(packageName: String): Boolean { + val func: Method = PackageManager::class.java.getDeclaredMethod("isPackageSuspended", + String::class.java) + return func.invoke(mPM, packageName) as Boolean + } + + fun getSuspendedPackageLauncherExtras(packageName: String): Bundle? = + mContext.getSystemService(LauncherApps::class.java).getSuspendedPackageLauncherExtras(packageName, Process.myUserHandle()) +}
\ No newline at end of file |