You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不删除现有账户的前提下将Android应用设为Device Owner?

Android设备所有者设置与恢复出厂设置阻止问题

问题描述

我正在开发一款用于阻止用户将手机恢复出厂设置或重置系统的Android应用。目前应用可激活为设备管理员,但会提示权限不足,且点击“Block Wipe Data”按钮时应用会崩溃。我尝试过多种方式将应用从Profile Owner转为Device Owner均未成功,使用adb命令:

adb shell 
dpm set-device-owner com.foo.deviceowner/.DeviceAdminRcvr

可成功将应用设为Device Owner,使应用正常阻止恢复出厂设置,但该操作会删除设备上的所有现有账户。请问是否存在无需删除现有账户的方法,将应用设为Device Owner,使其在已投入使用的设备上正常工作?

相关代码

MainActivity.kt

package com.example.test2

import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.UserManager
import androidx.appcompat.app.AppCompatActivity
import com.example.test2.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var devicePolicyManager: DevicePolicyManager
    private lateinit var componentName: ComponentName
    private val disallowFactoryReset = "no_factory_reset"


    private lateinit var binding: ActivityMainBinding

    @SuppressLint("WrongConstant")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        devicePolicyManager = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
        componentName = ComponentName(this, MyDeviceAdminReceiver::class.java)

        // 设置按钮文本
        binding.activateButton.setText(R.string.activate_button_text)
        binding.deactivateButton.setText(R.string.deactivate_button_text)
        binding.blockWipeDataButton.setText(R.string.block_wipe_data_button_text)
        binding.unblockWipeDataButton.setText(R.string.unblock_wipe_data_button_text)

        binding.activateButton.setOnClickListener {
            if (!devicePolicyManager.isAdminActive(componentName)) {
                val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
                intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName)
                startActivity(intent)
            }
        }

        binding.deactivateButton.setOnClickListener {
            if (devicePolicyManager.isAdminActive(componentName)) {
                devicePolicyManager.removeActiveAdmin(componentName)
            }
        }

        binding.blockWipeDataButton.setOnClickListener {
            // 阻止恢复出厂设置
                devicePolicyManager.addUserRestriction(componentName, UserManager.DISALLOW_FACTORY_RESET)
            }


        binding.unblockWipeDataButton.setOnClickListener {
            if (devicePolicyManager.isAdminActive(componentName)) {
                // 解除恢复出厂设置阻止
                devicePolicyManager.clearUserRestriction(componentName, disallowFactoryReset)
            }
        }
    }
}

MyDeviceAdminReceiver.kt

package com.example.test2

import android.app.admin.DeviceAdminReceiver
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.widget.Toast

class MyDeviceAdminReceiver : DeviceAdminReceiver() {
    override fun onEnabled(context: Context, intent: Intent) {
        super.onEnabled(context, intent)
        showToast(context, "Device Admin Enabled")
        setAsProfileOwner(context)
    }

    override fun onDisabled(context: Context, intent: Intent) {
        super.onDisabled(context, intent)
        showToast(context, "Device Admin Disabled")
    }

    private fun showToast(context: Context, msg: String) {
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
    }

    private fun setAsProfileOwner(context: Context) {
        val intent = Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)
        intent.putExtra(
            DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
            ComponentName(context, MyDeviceAdminReceiver::class.java)
        )
        context.startActivity(intent)
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.test2">

    <uses-permission android:name="android.permission.BIND_DEVICE_ADMIN"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Test2">

        <receiver
            android:name=".MyDeviceAdminReceiver"
            android:label="@string/device_admin"
            android:description="@string/device_admin_description"
            android:permission="android.permission.BIND_DEVICE_ADMIN"
            android:exported="true">
            <meta-data
                android:name="android.app.device_admin"
                android:resource="@xml/device_admin_receiver" />
            <intent-filter>
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
            </intent-filter>
        </receiver>

        <activity android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

解决方案与说明

核心结论

不存在无需删除现有账户即可将应用设置为Device Owner的方法。Android系统的设计逻辑中,Device Owner是为全新设备或已恢复出厂设置的设备准备的企业管理角色,设置时会清除设备上的所有用户数据和账户,确保设备处于可控的初始状态。已投入使用的设备(存在现有用户账户)无法绕过这个限制直接设置Device Owner。

替代方案

  1. 使用Profile Owner(工作资料)
    Profile Owner可以在不清除设备数据的情况下设置,但它仅能管理工作资料内的应用和行为,无法全局阻止设备的恢复出厂设置操作,因此无法满足你的核心需求。

  2. 引导用户重置设备后设置
    如果必须使用Device Owner权限,只能引导用户先备份设备数据,恢复出厂设置后,通过企业注册流程或adb命令设置应用为Device Owner。此时设备处于初始状态,设置后再恢复用户数据即可,不会丢失新添加的账户。

  3. 修复当前应用崩溃问题
    当前应用点击“Block Wipe Data”崩溃的原因是:普通设备管理员(Device Admin)没有权限调用addUserRestriction设置UserManager.DISALLOW_FACTORY_RESET,只有Device Owner或Profile Owner才有该权限。可以在代码中增加权限判断,避免崩溃:

    binding.blockWipeDataButton.setOnClickListener {
        if (devicePolicyManager.isDeviceOwnerApp(packageName)) {
            devicePolicyManager.addUserRestriction(componentName, UserManager.DISALLOW_FACTORY_RESET)
        } else {
            Toast.makeText(this, "需要设备所有者权限", Toast.LENGTH_SHORT).show()
        }
    }
    

    另外,unblockWipeDataButton的代码中使用了自定义的disallowFactoryReset字符串,应该替换为UserManager.DISALLOW_FACTORY_RESET,否则可能无法正确解除限制。

内容的提问来源于stack exchange,提问作者tomczaxo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 10:52:02