Activity.startLockTask()偶尔会抛出IllegalArgumentException异常。

16

我目前有一个周期性问题,当调用Activity.startLockTask()时,我会遇到IllegalArgumentException的错误。我的应用程序有一个设备所有者应用程序安装,允许我的包自动锁定。

下面的代码正在检查以确保我的包可以锁定自身。如果可以,那么它将锁定自己。

代码:

if (dpm.isLockTaskPermitted(getPackageName())) {
    super.startLockTask();
}

日志记录:

java.lang.IllegalArgumentException: Invalid task, not in foreground
    at android.os.Parcel.readException(Parcel.java:1544)
    at android.os.Parcel.readException(Parcel.java:1493)
    at android.app.ActivityManagerProxy.startLockTaskMode(ActivityManagerNative.java:5223)
    at android.app.Activity.startLockTask(Activity.java:6163)
问题是我的应用需要偶尔重新启动自身。所以我们取消固定,结束活动并使用新任务重新启动它,然后退出我们的进程。当活动再次出现时,它会尝试将自己固定 - 有时它会成功 - 有时不会。我认为我们重新启动的方式可能是抛出异常的原因,但这不应该影响,因为新活动在前台并且具有焦点。
一旦活动无法固定,只要尝试就会继续失败:如果我坐在那里每5秒尝试固定任务,每次都会失败。我已经尝试在onCreateonWindowFocusChangedonResumeonStart中固定。
是否有人知道问题可能是什么?
参考资料:
第8853行:https://android.googlesource.com/platform/frameworks/base/+/android-5.0.2_r1/services/core/java/com/android/server/am/ActivityManagerService.java

ActivityManagerService中使用mFocusedActivity似乎意味着onWindowFocusChanged(true)确实是正确的位置来执行此操作。免责声明:我尚未尝试使用任务锁定功能。 - j__m
1
这基本上就是我们正在做的。我将创建一个简单的应用程序,看看是否能够让它做同样的事情。 - Randy
1
不要在从 finish() 调用中获取 onStop() 之前退出。有了这个澄清,我无法想象一个更有序的重启过程。 - j__m
你最终确定了问题吗? - Kevin Krumwiede
1
还没有。我还没有时间。 - Randy
显示剩余2条评论
6个回答

8

我有同样的问题,但是目前还没有找到合适的解决方案。不过这是我目前采取的方法。

Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        try {
            if (dpm.isLockTaskPermitted(getPackageName())) {
                super.startLockTask();
            }
        }catch (Exception exception) {
            Log.v("KioskActivity","startLockTask - Invalid task, not in foreground");
        }
    }
},1500);

似乎请求锁定的应用程序尚未获得焦点,即使onWindowFocusChanged已触发。延迟调用startLocktask一段时间后它将起作用。但是,在应用程序无法被固定/锁定的那段时间内存在一小段时间。我通过一些额外的安全措施解决了这个问题(我有一个长时间运行的后台服务,防止通知栏下拉并在打开时关闭设置窗口)。
顺便问一下,你是否曾经以适当的方式解决过这个问题?

3
我曾遇到这个问题,并通过参考此帖子中的答案解决了它:如何判断布局已经被绘制? 基本上,它只是确保 UI 先被绘制,然后再尝试固定。
示例代码(将其放入 onCreate 方法中):
> findViewById(R.id.your_view).post( new Runnable() {
>             @Override
>             public void run() {
> 
>                 // Run pinning code here
>             }
>         });

这是一个非常好的解决方法,一切都正确,加1! - Antonio Vlasic
这对我来说有效 - 在运行API级别25和27的两个设备上。 - bddckr

1
我知道这是一个相当老的问题,但我也遇到了这个问题,以下是我的解决方法。关键在于startLockTask()文档中的这句话(同样适用于stopLockTask()):

注意:此方法只能在活动处于前台时调用,即在onResume()和onPause()之间

我有一些代码路径最终尝试在onResume()之前调用startLockTask()。通过确保正确的活动状态(使用AndroidX生命周期),我修复了它。
if(lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
    startLockTask()
}

这对我的情况已经足够了。你可能需要做一些类似于以下的额外逻辑(伪代码):
if(not yet resumed) {
    doWhenResumed { // Implement this as a helper function that executes when your Activity is resumed
        startLockTask()
    }
}

0

这与@Schtibb的答案类似,但我并不感到只硬编码一个1500毫秒的延迟而没有任何重试逻辑很舒适。它仍然可能偶尔失败。

但我发现的方法是在调用startLockTask()时使用try,如果失败了,就捕获错误并在短暂的延迟后再次尝试,直到成功为止:

void startLockTaskDelayed () {
    final Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            // start lock task mode if its not already active
            try {
                ActivityManager am = (ActivityManager) getSystemService(
                        Context.ACTIVITY_SERVICE);

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    if (am.getLockTaskModeState() ==
                            ActivityManager.LOCK_TASK_MODE_NONE) {
                        startLockTask();
                    }
                }

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    if (!am.isInLockTaskMode()) {
                        startLockTask();
                    }
                }
            } catch(IllegalArgumentException e) {
                Log.d("SVC0", "Was not in foreground yet, try again..");
                startLockTaskDelayed();
            }
        }
    }, 10);
}

我觉得这种方法更加动态,几乎尽快固定屏幕。


0

我已经为展示器模式创建了一个单独的活动

主要思路是从其他活动中执行此任务,并始终将锁定任务保持在堆栈的根部

View.post(...) 和类似的神奇延迟对我不起作用

onResume() 方法并检查 isFinishing() 运行良好,但请注意清除任务标志

<style name="AppTheme.Transparent" parent="android:style/Theme.Translucent.NoTitleBar.Fullscreen">
    <item name="android:colorPrimary">@color/colorPrimary</item>
    <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="android:colorAccent">@color/colorAccent</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android: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>

class LockActivity : Activity() {

    private lateinit var adminManager: AdminManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        adminManager = AdminManager(applicationContext)
    }

    override fun onStart() {
        super.onStart()
        when (activityManager.lockTaskModeState) {
            ActivityManager.LOCK_TASK_MODE_NONE -> {
                if (true/*IT IS NEEDED*/) {
                    if (adminManager.setKioskMode(true)) {
                        // need startLockTask on resume
                        return
                    } else {
                        //toast("HAVE NO OWNER RIGHTS")
                    }
                }
                launchActivity()
                finish()
            }
            ActivityManager.LOCK_TASK_MODE_LOCKED -> {
                if (false/*IT IS NOT NEEDED*/) {
                    if (adminManager.setKioskMode(false)) {
                        stopLockTask()
                        launchActivity()
                        finish()
                        return
                    } else {
                        //toast("HAVE NO OWNER RIGHTS")
                    }
                }
                launchActivity()
            }
            else -> finishAffinity()
        }
    }

    override fun onResume() {
        super.onResume()
        if (!isFinishing) {
            if (activityManager.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE) {
                startLockTask()
                launchActivity()
            }
        }
    }

    private fun launchActivity() {
        // todo startActivity depending on business logic
    }

    override fun onBackPressed() {}
}

class AdminManager(context: Context) {

    private val adminComponent = ComponentName(context, AdminReceiver::class.java)

    private val deviceManager = context.devicePolicyManager

    private val packageName = context.packageName

    @Suppress("unused")
    val isAdmin: Boolean
        get() = deviceManager.isAdminActive(adminComponent)

    val isDeviceOwner: Boolean
        get() = deviceManager.isDeviceOwnerApp(packageName)

    fun setKioskMode(enable: Boolean): Boolean {
        if (isDeviceOwner) {
            setRestrictions(enable)
            deviceManager.setKeyguardDisabled(adminComponent, enable)
            setLockTask(enable)
            return true
        }
        return false
    }

    /**
     * @throws SecurityException if {@code admin} is not a device or profile owner.
     */
    private fun setRestrictions(enable: Boolean) {
        arrayOf(
            UserManager.DISALLOW_FACTORY_RESET,
            UserManager.DISALLOW_SAFE_BOOT,
            UserManager.DISALLOW_ADD_USER
        ).forEach {
            if (enable) {
                deviceManager.addUserRestriction(adminComponent, it)
            } else {
                deviceManager.clearUserRestriction(adminComponent, it)
            }
        }
    }

    /**
     * @throws SecurityException if {@code admin} is not the device owner, the profile owner of an
     * affiliated user or profile, or the profile owner when no device owner is set.
     */
    private fun setLockTask(enable: Boolean) {
        if (enable) {
            deviceManager.setLockTaskPackages(adminComponent, arrayOf(packageName))
        } else {
            deviceManager.setLockTaskPackages(adminComponent, arrayOf())
        }
    }
}

你能否添加一条注释,解释为什么要在一个单独的半透明活动上设置锁定任务,然后启动一个新的活动(LoginActivity)?由于锁定任务附加到一个活动上,那么当你结束LockActivity并打开LoginActivity时,它是如何工作的呢?你要么不能打开LoginActivity,要么就会失去锁定。我错了吗? - Greg Rynkowski
如果您在锁定任务完成后结束活动,则会发生错误导致应用程序崩溃。我仅在应用程序不应处于展示模式时关闭“LockActivity”。另外,在恢复时调用“startLockTask()”也很重要。 - Vlad
@GregRynkowski 还要注意长按返回按钮)) 这将关闭展示模式。我通过在 override fun onKeyLongPress(keyCode: Int, event?)if (keyCode == KeyEvent.KEYCODE_BACK) 触发时切换回来来解决了这个问题。 - Vlad
根据你的描述,我理解为: 1)你有一个 LoginActivity 2)你在其上放了一个透明的 LockActivity 3)长按返回键可以解锁。所以...当你启用 kiosk 模式(即 Lock Activity 处于顶部时),用户能否与 LoginActivity 进行交互? - Greg Rynkowski

0
错误显示应用程序不在前台,因此我在onStart方法中创建了一个循环来检查应用程序是否在前台。
 boolean completed = false;
                while (!completed)
                    if (isAppInForeground(this)) {
                        startLockTask();
                        completed = true;
                    }

函数名为 isAppInForeground

  private boolean isAppInForeground(Context context) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
        ActivityManager.RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
        String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName();

        return foregroundTaskPackageName.toLowerCase().equals(context.getPackageName().toLowerCase());
    } else {
        ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
        ActivityManager.getMyMemoryState(appProcessInfo);
        if (appProcessInfo.importance == IMPORTANCE_FOREGROUND 
                || appProcessInfo.importance == IMPORTANCE_VISIBLE) {
            return true;
        }

        KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
        // App is foreground, but screen is locked, so show notification
        return km.inKeyguardRestrictedInputMode();
    }
}

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