Android - 如何完全禁用键盘保护

8
我想在设备上控制启用/禁用键盘锁。 为此,我使用了Android SDK的DevicePolicyManager和KeyguardLock API。
以下是我管理的实现方式:
public class DeviceLocker {

private static DeviceLocker instance;

public static synchronized DeviceLocker getInstance(Context context) {
    if(instance==null) {
        instance = new DeviceLocker(context);
    }
    return instance;
}

private Context context;
private KeyguardLock lock;

private DeviceLocker(Context context) {
    this.context = context;
}

public void lock() {
    lock(true);
}

public void lock(boolean lockNow) {
    getLock().reenableKeyguard();
    DevicePolicyManager devicePolicyManager = getDevicePolicyManager();
    if(devicePolicyManager==null) {
        return;
    }
    LocalStorage storage = LocalStorage.from(context);
    
    boolean result = devicePolicyManager.resetPassword(storage.getPassword(),
            DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
    
    if(lockNow) {
        devicePolicyManager.lockNow();
    }
    storage.setDeviceLocked(true);
}

public void unlock() {
    DevicePolicyManager devicePolicyManager = getDevicePolicyManager();
    if(devicePolicyManager==null) {
        return;
    }
    devicePolicyManager.resetPassword("",0);
    getLock().disableKeyguard();
    LocalStorage.from(context).setDeviceLocked(false);
}

private KeyguardLock getLock() {
    if(lock==null){
        KeyguardManager kgManager = (KeyguardManager)context.getSystemService(Activity.KEYGUARD_SERVICE);
        lock = kgManager.newKeyguardLock(Context.KEYGUARD_SERVICE);
    }
    return lock;
}

private DevicePolicyManager getDevicePolicyManager() {
    DevicePolicyManager devicePolicyManager =
        (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
    ComponentName deviceAdmin = new ComponentName(context, WatchGuardDeviceAdminReceiver.class);
    LocalStorage storage = LocalStorage.from(context);
    if(!devicePolicyManager.isAdminActive(deviceAdmin)) {
        return null;
    }
    if(!storage.isPasswordSet()) {
        UIUtils.showMessage(context, R.string.password_not_set);
        return null;
    }
    devicePolicyManager.setPasswordQuality(deviceAdmin,DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
    
    return devicePolicyManager;
}

关于屏幕锁定的问题,它可以正常工作,但解锁功能存在一些问题:有时它可以按照我的意愿完全删除任何类型的键盘锁屏,但有时会显示“滑动解锁”键盘锁屏。

你知道这里的问题是什么吗?如何使它稳定工作(至少在所有情况下都要么显示“滑动解锁”,要么完全删除键盘锁屏)?

非常感谢您的帮助。

编辑

我只想指出我的解决方案有效,但问题在于它不稳定(有时会完全删除键盘锁屏,有时会显示“滑动”键盘锁屏)。而且我不仅在显示某个活动时使用它来禁用键盘锁屏,还用它来控制设备的锁定/解锁,因此我在服务中使用此代码,因此我无法调用 getWindow().addFlags(..),因为我没有窗口可以应用。

我只是想知道是否有人遇到过这种不稳定的行为。


这里的LocalStorage类是什么?你是从某种Web框架中进行操作吗?另外,你是否已经得到了一个可行的解决方案?我正在寻求在DPC中实现这个。 - Keilaron
5个回答

5
注意:在撰写本文时,我可能没有意识到问题特指设备管理,我的回答适用于普通应用程序,但不适用于设备策略控制场景,因此我划掉了下面无效的陈述,但为了提供任何价值,我仍保留了答案。感谢@Keilaron注意到这一点。

据我所知,这并非普遍可行,否则这将是一个严重的安全漏洞。

在未root的手机上,您最多只能解除非安全的键盘锁屏或在安全键盘锁屏上方显示您的应用程序。

以下是一个组合操作:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

在已经获取了 root 权限的手机上,你可以尝试移除安全锁屏(虽然可能需要重启设备才能真正消失)。
以下是实现方法(我自己没有试过,但我相信 Play 商店里的几个“解锁手机”应用程序使用的就是这种技巧)。
Runtime.getRuntime().exec("sudo rm /data/system/gesture.key");

我建议您先尝试通过adb安装此应用。此外,这里有一个XDA论坛帖子,其中包含更多信息:http://forum.xda-developers.com/showthread.php?t=1800799

1
这只会在标准应用程序场景下违反规定。如果有人正在处理 DPM,他们至少是一个设备管理员应用程序 - 在这种情况下可能仍然如此,但是在我看来,设备所有者应用程序不仅应该而且必须具备此功能。 - Keilaron

5
您需要添加以下代码:
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED|WindowManager.LayoutParams.FLAG_FULLSCREEN|
                WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.lockscreen);

        startService(new Intent(this,LockService.class).setAction(Intent.ACTION_SCREEN_OFF));
    }

使用以下方法创建 LockService:

@Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {

        if(intent == null || intent.getAction() == null)
            return START_NOT_STICKY;

        String action = intent.getAction();

        if(action.equals(ACTION_SCREEN_OFF))
        {
           KeyguardManager.KeyguardLock k1;
           KeyguardManager km =(KeyguardManager)getSystemService(KEYGUARD_SERVICE);
           k1= km.newKeyguardLock("IN");
           k1.disableKeyguard();  
        }

        return START_NOT_STICKY;
    }

实际上,我不知道为什么,但是k1.disableKeyguard();只在服务中起作用。


嘿,谢谢你的提议,但我已经在服务中使用它并且它可以工作。只是它不稳定(完全没有键盘锁或“滑动”键盘锁),我想知道是否有人处理过这种行为。 - kowalski
你只需要解锁,对吧? 请看上面的代码(onCreate方法)。你可以创建一个新的活动,例如DismissLockActivity,当你需要解除键盘锁时,只需启动该活动并完成。我知道这是一个丑陋的hack,但在我的应用程序中,它完美地运行。 - dooplaye

1
KeyguardManager keyguardManager = (KeyguardManager)getSystemService(Activity.KEYGUARD_SERVICE);
KeyguardLock lock = keyguardManager.newKeyguardLock(KEYGUARD_SERVICE);
lock.disableKeyguard();

<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>

or 

getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);

0

0

对于 Android 27+,您可以这样做:

 private fun dismissKeyguard() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            setTurnScreenOn(true)
            setShowWhenLocked(true)
            (getSystemService(Context.KEYGUARD_SERVICE) as? KeyguardManager?)?.requestDismissKeyguard(
                this, object : KeyguardManager.KeyguardDismissCallback() {
                 //log if success or failure
                }
            )
        } else {
            window?.addFlags(
                WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                        or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
            )
        }
    }

我在这个调用中添加了可空性检查 (getSystemService(Context.KEYGUARD_SERVICE) as? KeyguardManager?),因为由于某种原因,一些随机设备并不总是具有此方法,为了避免异常,我只是添加了可空性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接