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, suspended: Boolean, appExtras: PersistableBundle, launcherExtras: PersistableBundle, dialogMessage: String): Array { // 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::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 } /** * 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()) }