Activity.startLockTask () sometimes raises IllegalArgumentException

I currently have a periodic problem when I get an IllegalArgumentException when I call Activity.startLockTask() . My application has the device owner’s application installed, which allowed my package to automatically connect.

Below is the code certifying that my package can block itself. If he can then, he goes in cycles.

code:

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

Logcat:

 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) 

The problem is that my application needs to be restarted periodically. Thus, we free, complete the work and start it with a new task, and then exit our process. When activity returns, he tries to consolidate himself - sometimes it works - sometimes it is not. I believe that the way we restart is probably the reason the exception is thrown, but that doesn’t matter, since the new IS activity is in the foreground and is focused on the OS.

As soon as the action is completed, he will continue to fail while he tries: if I sit there and try to establish a connection every 5 seconds, he will continue to fail every time. I tried binding in onCreate , onWindowFocusChanged , onResume and onStart .

Does anyone know what the problem is?

For reference:
Line 8853: https://android.googlesource.com/platform/frameworks/base/+/android-5.0.2_r1/services/core/java/com/android/server/am/ActivityManagerService.java

+10
source share
6 answers

I have the same problem, I have not yet found a suitable solution. But this is what I am doing now.

 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); 

It seems that the application requesting the lock has not yet received focus, even if onWindowFocusChanged is fired. By delaying the startLocktask call, it will work. Anyway, a little time when the application will not be pinned / blocked. I solved this with some additional security measures (I have a long service in the background that prevents notifications from falling out and closes the settings window if it is open).

By the way, could you ever solve this properly?

+5
source

I had this problem and it was solved using logic taken from the answers in this post: How can you determine when the layout was drawn?

This basically just ensures that the user interface has been drawn first and then will try to render it.

Sample code (put this in your onCreate method):

 > findViewById(R.id.your_view).post( new Runnable() { > @Override > public void run() { > > // Run pinning code here > } > }); 
+2
source

The error says that the application is not in the foreground, so I made a loop in the onStart method, which checks if the application is in the foreground

  boolean completed = false; while (!completed) if (isAppInForeground(this)) { startLockTask(); completed = true; } 

Function 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(); } } 
0
source

This is similar to @Schtibb's answer, however, I did not feel very comfortable with hard-coding only one 1500 ms delay without any repetition logic. It could still sometimes fail.

But what I discovered was startLockTask() to do a try when calling startLockTask() , and if it fails, just catch the error and try again after a short delay until it succeeds:

 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); } 

I feel this approach is a bit more dynamic and attaches the screen (almost) as soon as possible.

0
source

I know this is a pretty old question, but I ran into it, and this is how I solved it. The key is this suggestion in the docs for startLockTask() (the same goes for stopLockTask() )

Note: this method can only be called when the action is in the foreground. That is, between onResume () and onPause ()

I had several code paths that eventually tried to call startLockTask() before onResume() . I fixed this by ensuring the correct state of activity (using the AndroidX life cycle)

 if(lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { startLockTask() } 

That was enough for my case. You may need to perform additional logic, such as (pseudocode):

 if(not yet resumed) { doWhenResumed { // Implement this as a helper function that executes when your Activity is resumed startLockTask() } } 
0
source

I created a separate activity for kiosk mode

The main idea is to rid this task of other actions and keep the lock task always in the root of the stack

View.post(...) and similar ones with magic delay do not work for me

onResume() with isFinishing() works fine, but be careful with a clear task and / or with a transparent top

 <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 (/*IT IS NEEDED*/) { if (adminManager.setKioskMode(true)) { return } else { toast("HAVE NO OWNER RIGHTS") } } } ActivityManager.LOCK_TASK_MODE_LOCKED -> { if (/*IT IS NOT NEEDED*/) { if (adminManager.setKioskMode(false)) { stopLockTask() } else { toast("HAVE NO OWNER RIGHTS") } } } } startActivity<LoginActivity>() finish() } override fun onResume() { super.onResume() if (!isFinishing) { startLockTask() startActivity<LoginActivity>() } } 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) setKeyGuardEnabled(enable) setLockTask(enable) return true } return false } /** * @throws SecurityException if {@code admin} is not a device or profile owner. */ private fun setRestrictions(disallow: Boolean) { arrayOf( UserManager.DISALLOW_FACTORY_RESET, UserManager.DISALLOW_SAFE_BOOT, UserManager.DISALLOW_USER_SWITCH, UserManager.DISALLOW_ADD_USER ).forEach { if (disallow) { deviceManager.addUserRestriction(adminComponent, it) } else { deviceManager.clearUserRestriction(adminComponent, it) } } } /** * @throws SecurityException if {@code admin} is not the device owner, or a profile owner of * secondary user that is affiliated with the device. */ private fun setKeyGuardEnabled(enable: Boolean) { deviceManager.setKeyguardDisabled(adminComponent, !enable) } /** * @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()) } } } 
0
source

Source: https://habr.com/ru/post/980668/


All Articles