Android服务是否可以检测到设备何时被锁定?

特雷弗·威利

我有一个Android服务,想在设备锁定时采取任何措施。

我想澄清一下:

  • 我对屏幕的开/关状态不感兴趣。
  • 我知道如何使用带有Intent.ACTION_USER_PRESENT和KeyguardManager.inKeyguardRestrictedInputMode的BroadcastReceiver来检查设备何时解锁。
  • 我对需要不断检查锁定状态的解决方案不感兴趣。
  • 我不想自己锁定或解锁设备。
  • 我不能依靠处于前台的Activity来处理onResume。

我补充这些要点,是因为我发现没有人问同样的问题并得到其中的一种答复。我对他们的所作所为感到高兴,因为答案很有用和/或具有教育意义,但它们并不是我现在想要的。

特雷弗·威利

我已经提出了一个潜在的解决方案,但是随之而来的是一些主要警告。

通用方法:检测“屏幕关闭”事件,然后定期检查设备是否已锁定。这远非理想,但似乎没有任何方法可以检测设备何时被锁定。基本上,“没有正确的方法来执行此操作,因此您需要一起破解一些东西”。

致谢这是基于@Markus在评论中的建议,并结合了所链接问题的答案中的一些代码以及我自己的一些额外工作。

注意事项

  • 其他制造商可能具有不同的锁定时间。
  • 在我们先前确定设备在该期间内不会锁定(例如:设备可能突然锁定并且几分钟内无法检测到)之后,设备政策(例如:Android for Work)可能会更改为强制执行期限。
  • 设备可以被Android设备管理器远程锁定。
  • 设备可能被另一个应用程序锁定(例如:基于蓝牙的锁定机制)。
  • 未经测试,但我怀疑如果用户多次快速打开和关闭设备,此代码中会有问题。
  • 未经打Do测试。
  • 未经测试,但怀疑切换用户可能存在问题。
  • 我没有在愤怒中对此进行过测试,很可能还有其他问题。
  • 实际使用这种方法的任何人都应该做一些重新架构。下面介绍的只是概念证明。

AndroidManifest.xml

添加启动活动:

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

添加广播接收器:

<receiver android:name=".StateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

添加主要服务:

<service
    android:name=".LockMonitor"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="com.sample.screenmonitor.LockMonitor.ACTION_START_SERVICE"/>
    </intent-filter>
</service>

添加权限:

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

res / values / styles.xml

添加透明样式:

<style name="Theme.Transparent" parent="android:Theme">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:backgroundDimEnabled">false</item>
</style>

res / values / colors.xml

添加透明颜色:

<color name="transparent">#00000000</color>

StartLockMonitorActivity.java

这是主要的切入点,它可以启动服务:

package com.sample.screenmonitor;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

public class StartLockMonitorActivity extends AppCompatActivity {

    public static final String TAG = "LockMonitor-SLM";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.w(TAG, "Starting service...");
        final Intent newIntent = new Intent(this, LockMonitor.class);
        newIntent.setAction(LockMonitor.ACTION_CHECK_LOCK);
        startService(newIntent);
        Toast.makeText(this, "Starting Lock Monitor Service", Toast.LENGTH_LONG).show();
        finish();
    }
}

StateReceiver.java

设备重新启动时,这将重新启动服务。首次启动服务时,它会添加一些其他过滤器(请参阅LockMonitor.java说明中为何未在清单中进行说明的注释)。

package com.sample.screenmonitor;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class StateReceiver extends BroadcastReceiver {

    public static final String TAG = "LockMonitor-SR";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "onReceive: redirect intent to LockMonitor");
        final Intent newIntent = new Intent(context, LockMonitor.class);
        newIntent.setAction(LockMonitor.ACTION_CHECK_LOCK);
        newIntent.putExtra(LockMonitor.EXTRA_STATE, intent.getAction());
        context.startService(newIntent);
    }
}

LockMonitor.java

package com.sample.screenmonitor;

import android.app.KeyguardManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.Timer;
import java.util.TimerTask;

public class LockMonitor extends Service {

    public static final String TAG = "LockMonitor";

    public static final String ACTION_CHECK_LOCK = "com.sample.screenmonitor.LockMonitor.ACTION_CHECK_LOCK";
    public static final String EXTRA_CHECK_LOCK_DELAY_INDEX = "com.sample.screenmonitor.LockMonitor.EXTRA_CHECK_LOCK_DELAY_INDEX";
    public static final String EXTRA_STATE = "com.sample.screenmonitor.LockMonitor.EXTRA_STATE";

    BroadcastReceiver receiver = null;
    static final Timer timer = new Timer();
    CheckLockTask checkLockTask = null;

    public LockMonitor() {
        Log.d(TAG, "LockMonitor constructor");
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "LM.onDestroy");
        super.onDestroy();

        if (receiver != null) {
            unregisterReceiver(receiver);
            receiver = null;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "LM.onStartCommand");

        if (intent != null && intent.getAction() == ACTION_CHECK_LOCK) {
            checkLock(intent);
        }

        if (receiver == null) {
            // Unlike other broad casted intents, for these you CANNOT declare them in the Android Manifest;
            // instead they must be registered in an IntentFilter.
            receiver = new StateReceiver();
            IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
            filter.addAction(Intent.ACTION_SCREEN_ON);
            filter.addAction(Intent.ACTION_SCREEN_OFF);
            filter.addAction(Intent.ACTION_USER_PRESENT);
            registerReceiver(receiver, filter);
        }

        return START_STICKY;
    }

    void checkLock(final Intent intent) {
        KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);

        final boolean isProtected = keyguardManager.isKeyguardSecure();
        final boolean isLocked = keyguardManager.inKeyguardRestrictedInputMode();
        final boolean isInteractive = powerManager.isInteractive();
        final int delayIndex = getSafeCheckLockDelay(intent.getIntExtra(EXTRA_CHECK_LOCK_DELAY_INDEX, -1));
        Log.i(TAG,
                String.format("LM.checkLock with state=%s, isProtected=%b, isLocked=%b, isInteractive=%b, delay=%d",
                        intent != null ? intent.getStringExtra(EXTRA_STATE) : "",
                        isProtected, isLocked, isInteractive, checkLockDelays[delayIndex])
        );

        if (checkLockTask != null) {
            Log.i(TAG, String.format("LM.checkLock: cancelling CheckLockTask[%x]", System.identityHashCode(checkLockTask)));
            checkLockTask.cancel();
        }

        if (isProtected && !isLocked && !isInteractive) {
            checkLockTask = new CheckLockTask(this, delayIndex);
            Log.i(TAG, String.format("LM.checkLock: scheduling CheckLockTask[%x] for %d ms", System.identityHashCode(checkLockTask), checkLockDelays[delayIndex]));
            timer.schedule(checkLockTask, checkLockDelays[delayIndex]);
        } else {
            Log.d(TAG, "LM.checkLock: no need to schedule CheckLockTask");
            if (isProtected && isLocked) {
                Log.e(TAG, "Do important stuff here!");
            }
        }
    }

    static final int SECOND = 1000;
    static final int MINUTE = 60 * SECOND;
    // This tracks the deltas between the actual options of 5s, 15s, 30s, 1m, 2m, 5m, 10m
    // It also includes an initial offset and some extra times (for safety)
    static final int[] checkLockDelays = new int[] { 1*SECOND, 5*SECOND, 10*SECOND, 20*SECOND, 30*SECOND, 1*MINUTE, 3*MINUTE, 5*MINUTE, 10*MINUTE, 30*MINUTE };
    static int getSafeCheckLockDelay(final int delayIndex) {
        final int safeDelayIndex;
        if (delayIndex >= checkLockDelays.length) {
            safeDelayIndex = checkLockDelays.length - 1;
        } else if (delayIndex < 0) {
            safeDelayIndex = 0;
        } else {
            safeDelayIndex = delayIndex;
        }
        Log.v(TAG, String.format("getSafeCheckLockDelay(%d) returns %d", delayIndex, safeDelayIndex));
        return safeDelayIndex;
    }

    class CheckLockTask extends TimerTask {
        final int delayIndex;
        final Context context;
        CheckLockTask(final Context context, final int delayIndex) {
            this.context = context;
            this.delayIndex = delayIndex;
        }
        @Override
        public void run() {
            Log.i(TAG, String.format("CLT.run [%x]: redirect intent to LockMonitor", System.identityHashCode(this)));
            final Intent newIntent = new Intent(context, LockMonitor.class);
            newIntent.setAction(ACTION_CHECK_LOCK);
            newIntent.putExtra(EXTRA_CHECK_LOCK_DELAY_INDEX, getSafeCheckLockDelay(delayIndex + 1));
            context.startService(newIntent);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "LM.onBind");
        return null;
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何检测android设备屏幕何时即将超时或“锁定”?

来自分类Dev

adb是否可以检测到最大数量的设备

来自分类Dev

检测到A设备(电话)时是否可以解锁窗口?

来自分类Dev

蓝牙无法检测到设备,但hcitool可以检测到吗?

来自分类Dev

是否有可能检测到任何应用程序何时在android中全屏显示?

来自分类Dev

Android蓝牙检测设备何时连接问题

来自分类Dev

是否可以检测到JBoss / WildFly PID?

来自分类Dev

是否可以检测到JBoss / WildFly PID?

来自分类Dev

有什么工具可以检测到何时忘记关闭goroutine?

来自分类Dev

node.js可以检测到何时关闭吗?

来自分类Dev

您可以检测到何时关闭或弹出UIViewController吗?

来自分类Dev

您可以检测到iCloud何时更新吗?

来自分类Dev

是否可以在加载所有指定模块之前检测到requirejs何时由于用户导航而生成脚本错误?

来自分类Dev

是否可以检测到浏览器何时使用后备字体而不是CSS中指定的主要字体?

来自分类Dev

在加载所有指定模块之前,我是否可以检测到requirejs何时由于用户导航而生成脚本错误?

来自分类Dev

是否检测到异步JMS MessageConsumer何时发生异常?

来自分类Dev

是否可以检测何时下载了文件?

来自分类Dev

Bluez Code无法检测到蓝牙设备,但“设置”可以

来自分类Dev

检查Android用户的“设备何时被锁定”通知设置

来自分类Dev

在Android上,是否可以不看屏幕就能检测到屏幕扭曲?

来自分类Dev

检测设备是否可以接收短信

来自分类Dev

aplay列出了设备,但pulseaudio认为它已锁定并且无法检测到它

来自分类Dev

aplay列出了该设备,但pulseaudio认为它已锁定并且无法检测到它

来自分类Dev

Android上的VideoCast Sample App无法检测到投射设备

来自分类Dev

减少BLE startScan在Android 5.0 Lollipop上检测到的设备

来自分类Dev

Android上的VideoCast Sample App无法检测到投射设备

来自分类Dev

fastboot无法检测到我的android设备-Ubuntu

来自分类Dev

juju可以检测到下降服务吗?

来自分类Dev

如何检测到服务器的连接是否随机断开

Related 相关文章

  1. 1

    如何检测android设备屏幕何时即将超时或“锁定”?

  2. 2

    adb是否可以检测到最大数量的设备

  3. 3

    检测到A设备(电话)时是否可以解锁窗口?

  4. 4

    蓝牙无法检测到设备,但hcitool可以检测到吗?

  5. 5

    是否有可能检测到任何应用程序何时在android中全屏显示?

  6. 6

    Android蓝牙检测设备何时连接问题

  7. 7

    是否可以检测到JBoss / WildFly PID?

  8. 8

    是否可以检测到JBoss / WildFly PID?

  9. 9

    有什么工具可以检测到何时忘记关闭goroutine?

  10. 10

    node.js可以检测到何时关闭吗?

  11. 11

    您可以检测到何时关闭或弹出UIViewController吗?

  12. 12

    您可以检测到iCloud何时更新吗?

  13. 13

    是否可以在加载所有指定模块之前检测到requirejs何时由于用户导航而生成脚本错误?

  14. 14

    是否可以检测到浏览器何时使用后备字体而不是CSS中指定的主要字体?

  15. 15

    在加载所有指定模块之前,我是否可以检测到requirejs何时由于用户导航而生成脚本错误?

  16. 16

    是否检测到异步JMS MessageConsumer何时发生异常?

  17. 17

    是否可以检测何时下载了文件?

  18. 18

    Bluez Code无法检测到蓝牙设备,但“设置”可以

  19. 19

    检查Android用户的“设备何时被锁定”通知设置

  20. 20

    在Android上,是否可以不看屏幕就能检测到屏幕扭曲?

  21. 21

    检测设备是否可以接收短信

  22. 22

    aplay列出了设备,但pulseaudio认为它已锁定并且无法检测到它

  23. 23

    aplay列出了该设备,但pulseaudio认为它已锁定并且无法检测到它

  24. 24

    Android上的VideoCast Sample App无法检测到投射设备

  25. 25

    减少BLE startScan在Android 5.0 Lollipop上检测到的设备

  26. 26

    Android上的VideoCast Sample App无法检测到投射设备

  27. 27

    fastboot无法检测到我的android设备-Ubuntu

  28. 28

    juju可以检测到下降服务吗?

  29. 29

    如何检测到服务器的连接是否随机断开

热门标签

归档