9

I've got an app that registers itself as the default launcher and pins itself automatically when started.

This all works fine when installing the app. It pins itself and only the back button is visible.

The problem is that when the device first boots up, it does not pin properly. I see a series of toasts "Screen pinned" and "Screen unpinned" multiple times. The "Home" and "Recent Tasks" buttons are still visible as well.

--

Running "adb shell dumpsys activity activities" - the last lines indicate that it is not pinned:

mLockTaskModeState=NONE mLockTaskPackages (userId:packages)=
0:[com.example.myapp]
mLockTaskModeTasks[]

--

Testing device Asus ZenPad running Marshmallow/6.0/23

I'm relying on the MainActivity manifest attribute "lockTaskMode" to pin (rather than activity.startLockTask()):

<activity
    android:name=".MainActivity"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:label="@string/launcher_main"
    android:launchMode="singleTask"
    android:lockTaskMode="if_whitelisted"
    android:screenOrientation="landscape">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.HOME"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

Any help or pointers would be appreciated

Greg T
  • 3,278
  • 3
  • 37
  • 39
  • What happens if you use the default `lockTaskMode` and call `startLockTask()`? – Kevin Krumwiede Oct 10 '16 at 19:32
  • Seems to behave the same way – Greg T Oct 10 '16 at 19:47
  • I wonder if there's a race condition between starting the launcher and establishing task pinning permissions. I wrote an app that pins itself automatically when started, but instead of being the launcher, it starts in response to `BOOT_COMPLETED`. This is broadcast several seconds after the launcher appears. – Kevin Krumwiede Oct 10 '16 at 21:07
  • 1
    You're right about the BOOT_COMPLETED broadcast being delayed. It seems to get delivered to the system apps first and user apps sometime after. I'm not sure that's the problem here though... In the DeviceAdminReceiver, I see onLockTaskModeEntering then onLockTaskModeExiting. I just don't know what's triggering it to break out – Greg T Oct 11 '16 at 17:13

3 Answers3

3

I had the same problem and I could really only find one solution. I'm not sure why but yeah, something in android prevents task locking when booting up which boggles my mind since the task lock was designed to create these "kiosk" type of applications. The only solution I could find was to detect for a case when it didn't lock then restart the application. Its a little "hacky" but what else can you do?

To detect for the case where it didn't lock I created a state variable and assigning states (Locking, Locked, Unlocking, Unlocked). Then in the device admin receiver in onTaskModeExiting if the state isn't "Unlocking" then I know it unlocked on its own. So if this case happened where it failed, I then restart the application using this method (which schedules the application in the alarm manager then kills the application):

how to programmatically "restart" android app?

Here is some sample code:

DeviceAdminReceiver

@Override
public void onLockTaskModeEntering(Context context, Intent intent, String pkg) {
    super.onLockTaskModeEntering(context, intent, pkg);
    Lockdown.LockState = Lockdown.LOCK_STATE_LOCKED;
}

@Override
public void onLockTaskModeExiting(Context context, Intent intent) {
    super.onLockTaskModeExiting(context, intent);

    if (Lockdown.LockState != Lockdown.LOCK_STATE_UNLOCKING) {
        MainActivity.restartActivity(context);
    }
    Lockdown.LockState = Lockdown.LOCK_STATE_UNLOCKED;
}

MainActivity

public static void restartActivity(Context context) {
    if (context != null) {
        PackageManager pm = context.getPackageManager();
        if (pm != null) {
            Intent intent = pm.getLaunchIntentForPackage(context.getPackageName());
            if (intent != null) {
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                int pendingIntentId = 223344;
                PendingIntent pendingIntent = PendingIntent.getActivity(context, pendingIntentId, intent, PendingIntent.FLAG_CANCEL_CURRENT);
                AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
                mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent);
                System.exit(0);
            }
        }
    }
}

private void lock() {
    Lockdown.LockState = Lockdown.LOCK_STATE_LOCKING;
    startLockTask();
}

private void unlock() {
    ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    if (am.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED) {
        Lockdown.LockState = Lockdown.LOCK_STATE_UNLOCKING;
        stopLockTask();
    }
}

In truth this is a simplified version of what I implemented. But it should hopefully get you pointed towards a solution.

Community
  • 1
  • 1
Will Young
  • 86
  • 6
  • Not an ideal solution, but this does protect against a user escaping the kiosk mode. The app will be killed and launch again (assuming it's the default launcher). – Greg T May 18 '17 at 18:26
  • Hopefully this gets cleaned up in future Android versions – Greg T May 18 '17 at 18:27
  • For me it doesnt work. My activity rebooting like always, in infinity loop ;( Any more solutions please? – LionisIAm Nov 09 '17 at 17:43
0

The only solution I found as for now : make another launcher app, without locktask, which will trigger main app every time when launcher appears. This prevent user for waiting few more seconds before LockTasked app is being called with on BOOT_COMPLETED receiver. So we can meet this problem only when lockTask app has launcher properties for some activity in manifest.

LionisIAm
  • 751
  • 3
  • 17
0

Sorry for late answering, but...
Anyone has this problem can do this tricky work in first (LAUNCHER/HOME) activity (e.g. MainActivity):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (mSharedPreferences.getBoolean(KEY_PREF_RECREATED, false)) {
        mSharedPreferences.edit().putBoolean(KEY_PREF_RECREATED, false).apply();

        // start LOCK TASK here
    } else {
        mSharedPreferences.edit().putBoolean(KEY_PREF_RECREATED, true).apply();

        finish(); // close the app
        startActivity(new Intent(this, MainActivity.class)); // reopen the app
        return;
    }

    setContentView(R.layout.activity_main);

    // other codes
}
Pourqavam
  • 50
  • 1
  • 7