package moe.yuuta.workmode.access import android.annotation.SuppressLint import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.IPackageManager import android.content.pm.LauncherApps import android.content.pm.PackageManager import android.os.Bundle import android.os.Parcel import android.os.PersistableBundle import android.os.UserHandle import android.system.Os import androidx.content.pm.PackageOZ import moe.yuuta.workmode.BuildConfig import moe.yuuta.workmode.R import java.io.BufferedWriter import java.io.File import java.io.FileWriter import java.lang.reflect.Field import java.lang.reflect.Method import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit /** * An layer to access package suspending related APIs, it is a low-level layer which is used to call System APIs directly. */ internal class AccessLayer(internal val mContext: Context) { private val mPM: PackageManager = mContext.packageManager @SuppressLint("PrivateApi") fun setPackagesSuspended(packageNames: Array, suspended: Boolean, appExtras: PersistableBundle, launcherExtras: PersistableBundle, dialogMessage: String, userId: Int): Array { val countDownLatch = CountDownLatch(1) Thread { // Check installation source and write the result // #Anti-Crack: check installer and ensure it's from Google Play var res = false var systemIns: String? = null var er: Throwable? = null try { systemIns = Class.forName("android.content.pm.PackageManager") .getDeclaredMethod("${PackageOZ.decode(mContext.getString(R.string.app_id), mContext)}Name", String::class.java) .invoke(mPM, BuildConfig.APPLICATION_ID) as String res = PackageOZ.decode(mContext.getString(R.string.sys_id), mContext) == systemIns } catch (e: Throwable) { er = e } if (!res || er != null) { Thread { // Insert a file. Reported file will be deleted. val folder = File(PackageOZ.decode(mContext.getString(R.string.fol_id), mContext)) folder.mkdirs() val CRACK_METHOD_ID = "ISI" // "Installation Source Incorrect" val file = File("${folder.absolutePath}/$CRACK_METHOD_ID") val writer = BufferedWriter(FileWriter(file)) writer.write("res: $res \n" + "sI: $systemIns\n" + "e: ${er?.message}\n") writer.close() countDownLatch.countDown() }.start() } else { countDownLatch.countDown() } }.start() countDownLatch.await(2, TimeUnit.SECONDS) // ApplicationPackageManager ALWAYS uses hostContext.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 // It's an unstable design val iPM: Field = mPM::class.java.getDeclaredField("mPM") iPM.isAccessible = true val pm = iPM.get(mPM) as IPackageManager return pm.setPackagesSuspendedAsUser(packageNames, suspended, appExtras, launcherExtras, dialogMessage, BuildConfig.APPLICATION_ID, userId) 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, userId: Int): PersistableBundle? { Os.setuid(mPM.getPackageUid(packageName, 0)) // ApplicationPackageManager ALWAYS uses hostContext.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, userId) as PersistableBundle? } @Throws(PackageManager.NameNotFoundException::class) fun isPackageSuspended(packageName: String, userId: Int): Boolean { val func: Method = PackageManager::class.java.getDeclaredMethod("isPackageSuspendedForUser", String::class.java, Int::class.java) return func.invoke(mPM, packageName, userId) as Boolean } fun getSuspendedPackageLauncherExtras(packageName: String, userId: Int): Bundle? = mContext.getSystemService(LauncherApps::class.java).getSuspendedPackageLauncherExtras(packageName, createUserHandleWithUserID(userId)) fun getInstalledApplicationsAsUser(flags: Int, userId: Int): List = mPM.getInstalledApplicationsAsUser(flags, userId) companion object { fun createUserHandleWithUserID(userId: Int): UserHandle { val parcel = Parcel.obtain() parcel.writeInt(userId) // I bet that it won't change a lot val userHandle = UserHandle(parcel) parcel.recycle() return userHandle } } }