Playhi/FreezeYou

View on GitHub
app/src/main/java/cf/playhi/freezeyou/fuf/FUFSinglePackage.kt

Summary

Maintainability
C
1 day
Test Coverage
package cf.playhi.freezeyou.fuf

import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.IBinder
import android.system.Os
import cf.playhi.freezeyou.DeviceAdminReceiver.getComponentName
import cf.playhi.freezeyou.utils.DevicePolicyManagerUtils.*
import cf.playhi.freezeyou.utils.FUFUtils.checkMRootFrozen
import cf.playhi.freezeyou.utils.FUFUtils.isSystemApp
import cf.playhi.freezeyou.utils.ProcessUtils.fAURoot
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.ShizukuProvider
import rikka.shizuku.SystemServiceHelper
import java.lang.reflect.InvocationTargetException

open class FUFSinglePackage(
    open val context: Context,
    open val singlePackageName: String,
    open val actionMode: Int,
    open val apiMode: Int
) {

    open suspend fun commit(): Int {
        if (singlePackageName.isBlank()) return ERROR_SINGLE_PACKAGE_NAME_IS_BLANK

        if ("cf.playhi.freezeyou" == singlePackageName) {
            return ERROR_OPERATION_ON_FREEZEYOU_IS_NOT_ALLOWED
        }

        return when (apiMode) {
            @Suppress("DEPRECATION")
            API_FREEZEYOU_LEGACY_AUTO ->
                pureExecuteAPIAutoAction()
            API_FREEZEYOU_MROOT_DPM ->
                pureExecuteAPIDPMAction()
            API_FREEZEYOU_MROOT_PROFILE_OWNER ->
                pureExecuteAPIProfileOwnerAction()
            API_FREEZEYOU_ROOT_DISABLE_ENABLE ->
                pureExecuteAPIRootAction(false)
            API_FREEZEYOU_ROOT_UNHIDE_HIDE ->
                pureExecuteAPIRootAction(true)
            API_FREEZEYOU_SYSTEM_APP_ENABLE_DISABLE ->
                pureExecuteAPISystemAppDisabledAction()
            API_FREEZEYOU_SYSTEM_APP_ENABLE_DISABLE_UNTIL_USED ->
                pureExecuteAPISystemAppDisabledUntilUsedAction()
            API_FREEZEYOU_SYSTEM_APP_ENABLE_DISABLE_USER ->
                pureExecuteAPISystemAppDisabledUserAction()
            API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE,
            API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE_UNTIL_USED,
            API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE_USER ->
                pureExecuteAPIShizukuAction()
            else ->
                ERROR_NO_SUCH_API_MODE
        }
    }

    private fun pureExecuteAPIAutoAction(): Int {
        return if (actionMode == ACTION_MODE_FREEZE) {
            if (Build.VERSION.SDK_INT >= 21 && isDeviceOwner(context)) {
                pureExecuteAPIDPMAction()
            } else {
                pureExecuteAPIRootAction()
            }
        } else {
            if (checkMRootFrozen(context, singlePackageName)) {
                pureExecuteAPIDPMAction()
            } else {
                pureExecuteAPIRootAction()
            }
        }
    }

    private fun pureExecuteAPIDPMAction(): Int {

        if (Build.VERSION.SDK_INT < 21) return ERROR_DEVICE_ANDROID_VERSION_TOO_LOW
        if (!isDeviceOwner(context)) return ERROR_NOT_DEVICE_POLICY_MANAGER

        val hidden = actionMode == ACTION_MODE_FREEZE
        if (!hidden &&
            !getDevicePolicyManager(context)
                .isApplicationHidden(getComponentName(context), singlePackageName)
        ) {
            return ERROR_NO_ERROR_SUCCESS
        }

        return if (getDevicePolicyManager(context).setApplicationHidden(
                getComponentName(context),
                singlePackageName,
                hidden
            )
        ) {
            ERROR_NO_ERROR_SUCCESS
        } else {
            ERROR_DPM_EXECUTE_FAILED_FROM_SYSTEM
        }

    }

    private fun pureExecuteAPIRootAction(): Int {
        return when (apiMode) {
            @Suppress("DEPRECATION")
            API_FREEZEYOU_LEGACY_AUTO, API_FREEZEYOU_ROOT_DISABLE_ENABLE ->
                pureExecuteAPIRootAction(false)
            API_FREEZEYOU_ROOT_UNHIDE_HIDE ->
                pureExecuteAPIRootAction(true)
            else ->
                ERROR_OTHER
        }
    }

    private fun pureExecuteAPIRootAction(hideMode: Boolean): Int {

        var returnValue = ERROR_OTHER
        val enable = actionMode == ACTION_MODE_UNFREEZE
        try {
            val exitValue = fAURoot(singlePackageName, enable, hideMode)
            returnValue = if (exitValue == 0) {
                ERROR_NO_ERROR_SUCCESS
            } else {
                ERROR_OTHER
            }
        } catch (e: Exception) {
            e.printStackTrace()
            val eMsg = e.message
            if (eMsg != null &&
                (eMsg.contains("permission denied", true)
                        || eMsg.contains("not found", true))
            ) {
                returnValue = ERROR_NO_ROOT_PERMISSION
            }
        }
        return returnValue
    }

    private fun pureExecuteAPISystemAppDisabledUntilUsedAction(): Int {

        if (Build.VERSION.SDK_INT < 18) return ERROR_DEVICE_ANDROID_VERSION_TOO_LOW
        if (!isSystemApp(context)) return ERROR_NOT_SYSTEM_APP
        val freeze = actionMode == ACTION_MODE_FREEZE
        context.packageManager.setApplicationEnabledSetting(
            singlePackageName,
            if (freeze)
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
            else
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            0
        )
        return ERROR_NO_ERROR_CAUGHT_UNKNOWN_RESULT
    }

    private fun pureExecuteAPISystemAppDisabledUserAction(): Int {

        if (!isSystemApp(context)) return ERROR_NOT_SYSTEM_APP
        val freeze = actionMode == ACTION_MODE_FREEZE
        context.packageManager.setApplicationEnabledSetting(
            singlePackageName,
            if (freeze)
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
            else
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            0
        )
        return ERROR_NO_ERROR_CAUGHT_UNKNOWN_RESULT
    }

    private fun pureExecuteAPISystemAppDisabledAction(): Int {

        if (!isSystemApp(context)) return ERROR_NOT_SYSTEM_APP
        val freeze = actionMode == ACTION_MODE_FREEZE
        context.packageManager.setApplicationEnabledSetting(
            singlePackageName,
            if (freeze)
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED
            else
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            0
        )
        return ERROR_NO_ERROR_CAUGHT_UNKNOWN_RESULT
    }

    private fun pureExecuteAPIProfileOwnerAction(): Int {

        if (Build.VERSION.SDK_INT < 21) return ERROR_DEVICE_ANDROID_VERSION_TOO_LOW
        if (!isProfileOwner(context)) return ERROR_NOT_PROFILE_OWNER

        val hidden = actionMode == ACTION_MODE_FREEZE
        if (!hidden &&
            !getDevicePolicyManager(context)
                .isApplicationHidden(getComponentName(context), singlePackageName)
        ) {
            return ERROR_NO_ERROR_SUCCESS
        }

        return if (getDevicePolicyManager(context).setApplicationHidden(
                getComponentName(context),
                singlePackageName,
                hidden
            )
        ) {
            ERROR_NO_ERROR_SUCCESS
        } else {
            ERROR_PROFILE_OWNER_EXECUTE_FAILED_FROM_SYSTEM
        }

    }

    private fun pureExecuteAPIShizukuAction(): Int {

        if (Build.VERSION.SDK_INT < 23) return ERROR_DEVICE_ANDROID_VERSION_TOO_LOW

        try {
            ShizukuProvider.requestBinderForNonProviderProcess(context)
            val freeze = actionMode == ACTION_MODE_FREEZE

            @SuppressLint("PrivateApi")
            val cls = Class.forName("android.content.pm.IPackageManager\$Stub")
                .getMethod("asInterface", IBinder::class.java)
                .invoke(null, ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")))
            cls::class.java.getMethod(
                "setApplicationEnabledSetting",
                String::class.java, // packageName
                Int::class.java, // newState
                Int::class.java, // flags
                Int::class.java, // userId
                String::class.java // callingPackage
            ).invoke(
                cls,
                singlePackageName,
                if (freeze) {
                    when (apiMode) {
                        API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE_UNTIL_USED ->
                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
                        API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE_USER ->
                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
                        API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE ->
                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED
                        else ->
                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                    }
                } else {
                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                },
                0,
                /**
                 * .../private/android_filesystem_config.h
                 * #define AID_USER 100000        /* \T\O\D\O: switch users over to AID_USER_OFFSET */
                 * #define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
                 */
                Os.getuid() / 100000,
                "cf.playhi.freezeyou"
            )
            return ERROR_NO_ERROR_CAUGHT_UNKNOWN_RESULT
        } catch (e: InvocationTargetException) {
            e.printStackTrace()
            if (e.cause is SecurityException) {
                return ERROR_INSUFFICIENT_PERMISSION
            }
        } catch (e: SecurityException) {
            e.printStackTrace()
            return ERROR_INSUFFICIENT_PERMISSION
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return ERROR_OTHER
    }

    companion object {
        const val ACTION_MODE_FREEZE = 0
        const val ACTION_MODE_UNFREEZE = 1
        const val ERROR_NO_ERROR_CAUGHT_UNKNOWN_RESULT = 1
        const val ERROR_NO_ERROR_SUCCESS = 0
        const val ERROR_OTHER = -1
        const val ERROR_SINGLE_PACKAGE_NAME_IS_BLANK = -2
        const val ERROR_DEVICE_ANDROID_VERSION_TOO_LOW = -3
        const val ERROR_NO_ROOT_PERMISSION = -4
        const val ERROR_DPM_EXECUTE_FAILED_FROM_SYSTEM = -5
        const val ERROR_NOT_DEVICE_POLICY_MANAGER = -6
        const val ERROR_NO_SUCH_API_MODE = -7
        const val ERROR_NOT_SYSTEM_APP = -8
        const val ERROR_NOT_PROFILE_OWNER = -9
        const val ERROR_PROFILE_OWNER_EXECUTE_FAILED_FROM_SYSTEM = -10
        const val ERROR_OPERATION_ON_FREEZEYOU_IS_NOT_ALLOWED = -11
        const val ERROR_USER_SET_NOT_ALLOWED_TO_FREEZE_FOREGROUND_APPLICATION = -12
        const val ERROR_USER_SET_NOT_ALLOWED_TO_FREEZE_NOTIFYING_APPLICATION = -13
        const val ERROR_NO_SUFFICIENT_PERMISSION_TO_START_THIS_ACTIVITY = -14
        const val ERROR_CANNOT_FIND_THE_LAUNCH_INTENT_OR_UNFREEZE_FAILED = -15
        const val ERROR_INSUFFICIENT_PERMISSION = -16

        /**
         * 使用 FreezeYou 的 自动(免ROOT(DPM)/ROOT(DISABLE)) 模式
         *
         * 推荐直接使用 [API_FREEZEYOU_MROOT_DPM]
         * 与 [API_FREEZEYOU_ROOT_DISABLE_ENABLE]
         * 与 [API_FREEZEYOU_ROOT_UNHIDE_HIDE]
         */
        @Deprecated("Use API_FREEZEYOU_MROOT_DPM or API_FREEZEYOU_ROOT_DISABLE_ENABLE instead.")
        const val API_FREEZEYOU_LEGACY_AUTO = 0

        /**
         * 使用 FreezeYou 的 免ROOT(DPM) 模式
         */
        const val API_FREEZEYOU_MROOT_DPM = 1

        /**
         * 使用 FreezeYou 的 ROOT(DISABLE) 模式
         */
        const val API_FREEZEYOU_ROOT_DISABLE_ENABLE = 2

        /**
         * 使用 FreezeYou 的 ROOT(UNHIDE) 模式
         */
        const val API_FREEZEYOU_ROOT_UNHIDE_HIDE = 3

        /**
         * 使用 FreezeYou 的 System App (DISABLE_UNTIL_USED) 模式
         */
        const val API_FREEZEYOU_SYSTEM_APP_ENABLE_DISABLE_UNTIL_USED = 4

        /**
         * 使用 FreezeYou 的 System App (DISABLE_USER) 模式
         */
        const val API_FREEZEYOU_SYSTEM_APP_ENABLE_DISABLE_USER = 5

        /**
         * 使用 FreezeYou 的 System App (DISABLE) 模式
         */
        const val API_FREEZEYOU_SYSTEM_APP_ENABLE_DISABLE = 6

        /**
         * 使用 FreezeYou 的 免ROOT (Profile Owner) 模式
         */
        const val API_FREEZEYOU_MROOT_PROFILE_OWNER = 7

        /**
         * 使用 FreezeYou 的借助 Shizuku 的 System App (DISABLE_UNTIL_USED) 模式
         */
        const val API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE_UNTIL_USED = 8

        /**
         * 使用 FreezeYou 的借助 Shizuku 的 System App (DISABLE_USER) 模式
         */
        const val API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE_USER = 9

        /**
         * 使用 FreezeYou 的借助 Shizuku 的 System App (DISABLE) 模式
         */
        const val API_FREEZEYOU_SHIZUKU_SYSTEM_APP_ENABLE_DISABLE = 10

    }
}