Unity3D Android Lockscreen not shown when screen times out. (Wakelock?)
Asked Answered
T

3

5

I am building an android app using Unity and it is coming along very well. However, I'm having an odd issue relating to the screen timing out and the lockscreen not showing.

What Should Happen

  • User stops playing
  • Screen Times out and turns off
  • Later, player returns and turns their phone back on
  • Lockscreen shows, user can enter their password, or otherwise unlock their phone
  • App regains focus and continues

What IS happening

  • User stops playing
  • Screen Times out and turns off
  • Later, player returns and turns their phone back on
  • Lockscreen does NOT show! The app is right in focus, bypassing the lockscreen all together
  • Users get mad that their security is compromised :(

Notes

  • This happens regardless of whether or not I'm using Android Plugins
  • I'm using Unity 4.2.0f4 (Though the changelogs for the more recent versions have nothing about this issue)
  • It even happens on a blank Android project
  • I've tested this on 5 different devices, all have the same problem

I suspect that this is caused by a wakelock that Unity does not give up when the screen times out. This causes the app to hold focus and the lockscreen never gets to 'load'. This is a pretty serious problem.

Does anyone know of any way to fix this?

Note: I've already asked this question on Unity Answers a little over a week ago and haven't gotten any responses yet. I thought maybe I'd have better luck here.

Tuneless answered 7/11, 2013 at 21:14 Comment(1)
Ive verified that my nexus 10 has the same exact behavior as wellBentham
T
2

First I'd like to say thanks to MichaelTaylor3D's Answer for pointing me in the right direction.

There were a few issues with the way he solved the problem that I think I have corrected:

The first issue is with the KeyguardManager approach to locking the screen. This was depreciated in API version 8 and won't work on API 9+. The new solution uses the Device Admin API which seems very intrusive for a game.

I looked through the UnityPlayer definition in eclipse and found a function called setWakeLock(boolean), however it is private.

I made a custom android Activity. In it, I access a protected UnityPlayer function setWakeLock(boolean) and calls it in the onPause function.

I admit this is less than ideal, but it does seem to work with no side effects. I've submitted a bug report to Unity, so hopefully this work around will not be required for long.

public class UnityPlayerWithLockscreen extends UnityPlayerNativeActivity {


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

    //Provides access to the private UnityPlayer.setWakeLock(boolean) method.
    public void setWakeLock(boolean useWakelock) {
        Method setWakeLockMethod;

        //Use Reflection to get the setWakeLock(boolean) method from the UnityPlayer class
        setWakeLockMethod = mUnityPlayer.getClass().getDeclaredMethod("setWakeLock");

        //Set the method to me accessible
        setWakeLockMethod.setAccessible(true);

        //Invoke the method with our argument
        setWakeLockMethod.invoke(mUnityPlayer, useWakelock);
    }


    @Override
    protected void onPause()
    {
        super.onPause();

        //Force unity to release the wakelock
        setWakeLock(false);     
    }
}

Then you need to set this activity as the main activity in the AndroidManifest file:

<?xml version="1.0" encoding="utf-8"?>
<application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".UnityPlayerWithLockscreen"
              android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

This solution also doesn't require any C# scripts or C# <-> Java interop.

Again, I know this is sort of a hacky solution, but it seems to work on several devices regardless of API level with no side effects. Hopefully this gets fixed by Unity soon and will no longer require an ugly fix.

Tuneless answered 12/11, 2013 at 14:33 Comment(1)
do you have a reference to the unity bug that you reported?Graupel
M
0

I do not have any experience from Unity but try using this http://developer.android.com/reference/android/os/PowerManager.WakeLock.html

To see if unity is forcing the wakelock somehow with isHeld(); and try to override it within your activity

EDIT: I am using wakelock when downloading and this makes the screen go black but not lock the device, so this is probably the case so try to release it, this might give some kind of conflict but it's always worth a try.

Marche answered 11/11, 2013 at 12:2 Comment(0)
B
0

This is untested but you can try this,

Basically you want to try to access the Android SDK directly, Unity has some classes that allow you to do this.

Try writing an external Java class and place it in the Plugins/Android Folder.

The Java class method might be something like this: referenced from this question make sure that its compiled into a .jar file

package com.yourcompany.yourgamename;

import com.unity3d.player.UnityPlayerActivity;

import android.content.Context;
import android.os.Bundle;
import android.util.Config;
import android.util.Log;
import android.app.Activity;   

public class MainActivity extends UnityPlayerActivity 
{    
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
         super.onCreate(savedInstanceState);
    }

    public static void lockScreen()
    {
        KeyguardManager keyguardManager = (KeyguardManager)getSystemService(Activity.KEYGUARD_SERVICE); 
        KeyguardLock lock = keyguardManager.newKeyguardLock(KEYGUARD_SERVICE);  
        lock.reenableKeyguard();  
    }
}

Also make sure this is in the Android Manifest file so that Unity loads this activity instead of the main one and put it in \Plugins\Android folder:

<?xml version="1.0" encoding="utf-8"?>

<application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".MainActivity"
              android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

Then create a Unity C# script that invokes the external Java Class: referenced here

public static void InvokeAndroidLockScreen()
{
    #if UNITY_ANDROID
    using (AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    {
         using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
         {
              obj_Activity .CallStatic("lockScreen");
         }
    }
    #else
    Debug.LogWarning("Cant invoke lockscreen on a non Android Device");
    #endif

}

Then finally in an active monobehavior invoke the lockscreen on the application pause event: referenced here

void OnApplicationPause(bool pauseStatus)
{
     InvokeAndroidLockScreen();
}

You would have to make sure that the following flag is set to false

Application.runInBackground = false;

I would think or else the pause event wont get triggered when the device turns off. (This is off by default)

Even if this is a work around, I still think this is a Unity Bug that should be reported to them.

Bentham answered 11/11, 2013 at 15:0 Comment(2)
Thank you for the suggestion. I'll give it a shot after work. I agree that its a unity bug and I'll file a report.Tuneless
Thank you! This answer was extremely helpful in solving my problem. So I'm awarding you the bounty. However I did end up doing it a different way so I'm going to post another answer with my final solution. If its possible to accept both I will.Tuneless

© 2022 - 2024 — McMap. All rights reserved.