8

I'm playing around with the task locking API on a Nexus 4 running Android 5.0.1. My device owner app consists of two buttons: "Lock" and "Unlock", which simply call startLockTask() and stopLockTask(). That's really all there is to it, but since some people insist on seeing the boilerplate:

public class MainActivity extends Activity {    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.lockButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startLockTask();
            }           
        });

        findViewById(R.id.unlockButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopLockTask();
            }           
        });
    }
}

Pressing the "Lock" button when the task is already locked is harmless. But if I press the "Unlock" button when the task is not locked, I get a NPE:

02-12 22:58:11.942: E/AndroidRuntime(12888): java.lang.NullPointerException: Attempt to read from field 'android.content.Intent com.android.server.am.TaskRecord.intent' on a null object reference
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.os.Parcel.readException(Parcel.java:1546)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.os.Parcel.readException(Parcel.java:1493)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.app.ActivityManagerProxy.stopLockTaskMode(ActivityManagerNative.java:5245)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.app.Activity.stopLockTask(Activity.java:6179)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at com.chalcodes.kiosk.MainActivity$2.onClick(MainActivity.java:44)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.view.View.performClick(View.java:4756)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.view.View$PerformClick.run(View.java:19749)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.os.Handler.handleCallback(Handler.java:739)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.os.Handler.dispatchMessage(Handler.java:95)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.os.Looper.loop(Looper.java:135)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at android.app.ActivityThread.main(ActivityThread.java:5221)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at java.lang.reflect.Method.invoke(Native Method)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at java.lang.reflect.Method.invoke(Method.java:372)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
02-12 22:58:11.942: E/AndroidRuntime(12888):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Is there a method somewhere to test whether the current task is locked?

This is kind of like how unregisterReceiver(...) explodes if you try to unregister a receiver that isn't registered. I think it's a poorly-behaved API that forces you to keep track of its internal state for it. But stopLockTask() is even worse, because whereas you'd normally unregister your receivers when your activity is paused, you would not normally unlock your task. So the next activity is created locked, but it has no way to know it's locked. So you basically have to pass the locked state around in intents and saved instance bundles. Or else just surround every call to stopLockTask() with an ugly try/catch...

Am I making any sense here?

Edit: Created issue #150089.

Kevin Krumwiede
  • 9,868
  • 4
  • 34
  • 82
  • Show related part of code `MainActivity` class – ρяσѕρєя K Feb 13 '15 at 06:20
  • 1
    @ρяσѕρєяK The button literally just calls `stopLockTask()`. – Kevin Krumwiede Feb 13 '15 at 06:20
  • @Gaskoin I *have* shown you the entirety of the relevant code. But since you insist, I'll show you the boilerplate as well. – Kevin Krumwiede Feb 13 '15 at 16:56
  • the doc says `This can only be called by activities that have successfully called startLockTask previously.` so I guess the behavior is otherwise unspecified – njzk2 Feb 13 '15 at 17:15
  • Well, that's technically not even true. It crashes if I call `startLockTask()` and then call `stopLockTask()` twice. :) – Kevin Krumwiede Feb 13 '15 at 17:20
  • In newer Android versions it seems to be allowed to use stopLockTask even if the Activity is not locked. That doesn't make things much better, because I now ran into the problem only after I published my app (fortunately on beta). – user2808624 Feb 28 '18 at 16:27

2 Answers2

6

Here is method to check current state of current task if locked or not in case of screen pinning:

!mActivityManager.isInLockTaskMode()

For more information visit this link: http://developer.android.com/reference/android/app/ActivityManager.html#isInLockTaskMode%28%29

Vikasdeep Singh
  • 20,983
  • 15
  • 78
  • 104
  • 3
    Why that method is in `ActivityManager` when all the other related methods are in `Activity` is yet another Android API mystery. – Kevin Krumwiede Apr 20 '15 at 02:10
-2

You need to implement the other methods of the lifecycle (OnResume, OnStop and so on).

Put your logic to unlock the Application inside other method which you'll find out which one is better according to your expected behaviour. Check it out this [link].1

Victor Viola
  • 575
  • 1
  • 4
  • 14
  • I'm in a hurry right now but if you do not get the point I'll try to explain better later. – Victor Viola Feb 13 '15 at 17:15
  • It certainly cannot be done in `onCreate()`/`onDestroy()`, because then the task is left unlocked when navigating back in the activity stack. Doing it in `onResume()`/`onPause()` or `onStart()`/`onStop()` leaves the task unlocked when the screen is asleep, which gives you access to the lock screen and all the settings in the pull-down menu. Preventing the screen from sleeping may not always be desirable. And if you do it in any lifecycle method, you're constantly spammed with toasts every time you navigate between activities or rotate the device. – Kevin Krumwiede Feb 13 '15 at 17:50