aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/moe/yuuta/workmode/access/AccessLayer.kt
diff options
context:
space:
mode:
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.kt86
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