Getting Android Sensor Events While The Screen is Off

April 3, 2012

So you want to continue to get sensor events in your Android service, even after the screen turns off? Well that’s too damn bad, kid, cause you can’t. No, I’m just kidding.

But you’ll probably need a work-around for Android Issue 3708. In this document I describe what I had to do to get it working on my platform. My assumption is you’ve already been Googling around, and so have a little background on this, but are still scratching your head as to why it’s not working. Hopefully, you won’t be in that position after reading this!

Hold a PARTIAL_WAKE_LOCK

Some relic platforms of the past might have skated by holding just a PARTIAL_WAKE_LOCK. For me this was necessary, but not sufficient. Even while holding the lock, my service ceased to receive notifications via onSensorChanged() nor onAccuracyChanged().

Re-register Your Listeners When The Screen Goes Off

Some then propose to take action when the screen turns off. In this post on StackOverflow, for example, the author proposes un-registering and re-registering to receive sensor events at that time.

Use a Delayed Thread, for the Above

The above two techniques are ultimately what I’ve used to solve the problem, except I had to make one additional modification. When the BroadcastReceiver receives an ACTION_SCREEN_OFF Intent, I queue a thread to run 500ms in the future, instead of running immediately. This ensures that most other activities surrounding the screen off will have completed. So at that time, 500ms later, we un-register and re-register our service as a sensor event listener.

This is how I’ve implemented the BroadcastReceiver:

    public BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "onReceive("+intent+")");

            if (!intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 
                return;
            }

            Runnable runnable = new Runnable() {
                public void run() {
                    Log.i(TAG, "Runnable executing.");
                    unregisterListener();
                    registerListener();
                }
            };

            new Handler().postDelayed(runnable, SCREEN_OFF_RECEIVER_DELAY);
        }
    };

You may have to play with the value of SCREEN_OFF_RECEIVER_DELAY — for me, 500 (milliseconds) was the minimal value that would get it working.

See the Code In An Example App

I’ve posted an example app which includes the above code. It shows a foreground service holding a PARTIAL_WAKE_LOCK, doing the re-registration business mentioned above. The result is a service which continues to receive sensor events even after the screen goes off (for whatever reason.)

https://github.com/jamesonwilliams/AndroidPersistentSensors

Well, it worked for me at least. Does it work for you? (Please comment below.)

References

15 Responses to “Getting Android Sensor Events While The Screen is Off”

  1. Qi Zhenyu says:

    Thanks your sharing, it works for me.
    On my HTC G14 phone, it only works after using the “Delayed Thread”.

    I search the web for whole day, and even want to give up, now seems it works again.

    Thanks very much again.

  2. T-T says:

    It doesn’t works for me.
    my phone is meizu MX,android 2.3.5

    05-02 20:26:31.003: DEBUG/com.whh.test.sensor.TheService(22091): ———–onSensorChanged————-
    05-02 20:26:31.003: DEBUG/com.whh.test.sensor.TheService|ACCELEROMETER(22091): -5.161899
    05-02 20:26:31.028: DEBUG/PhoneInterfaceManager(226): [PhoneIntfMgr] processKeyEvent…
    05-02 20:26:31.028: DEBUG/PhoneInterfaceManager(226): [PhoneIntfMgr] makeProcessKeyEventResult: handled = false, hungup = false
    05-02 20:26:31.193: DEBUG/com.whh.test.sensor.TheService(22091): ———–onSensorChanged————-
    05-02 20:26:31.193: DEBUG/dalvikvm(143): GC_EXPLICIT freed 33K, 49% free 6775K/13255K, external 6729K/9009K, paused 155ms
    05-02 20:26:31.203: DEBUG/com.whh.test.sensor.TheService(22091): ———–onSensorChanged————-
    05-02 20:26:31.203: DEBUG/com.whh.test.sensor.TheService|ACCELEROMETER(22091): -5.2959743
    05-02 20:26:31.393: DEBUG/com.whh.test.sensor.TheService(22091): ———–onSensorChanged————-
    05-02 20:26:31.403: DEBUG/com.whh.test.sensor.TheService(22091): ———–onSensorChanged————-
    05-02 20:26:31.403: DEBUG/com.whh.test.sensor.TheService|ACCELEROMETER(22091): -5.2385135
    05-02 20:26:31.488: DEBUG/dalvikvm(143): GC_EXPLICIT freed 2K, 49% free 6772K/13255K, external 9009K/9009K, paused 60ms
    05-02 20:26:31.553: DEBUG/dalvikvm(143): GC_EXPLICIT freed activate(handle: 3, enabled: 0)
    05-02 20:26:31.833: DEBUG/Sensors(143): gp2ap:enable():handle is 3, what is 0, en is 0.
    05-02 20:26:31.843: DEBUG/Sensors(143): enable():close device.
    05-02 20:26:31.848: DEBUG/SurfaceFlinger(143): About to give-up screen, flinger = 0x2c27a0
    05-02 20:26:31.868: DEBUG/TestForLock(143): onScreenTurnedOff(2)—PowerManagerService1335961591871
    05-02 20:26:31.868: DEBUG/TestForLock(143): doKeyguard: showing the lock screen—PowerManagerService1335961591871
    05-02 20:26:31.868: DEBUG/Pms-Moses(143): Am going to Sleep, reson:2—PowerManagerService1335961591870
    05-02 20:26:31.873: DEBUG/TestForLock(143): handleShow: systemReady = true, mBootComplete = true
    05-02 20:26:31.873: DEBUG/TestForLock(143): show begin
    05-02 20:26:31.873: DEBUG/TestForLock(143): keyguard view is null, creating it…
    05-02 20:26:32.003: DEBUG/dalvikvm(143): GC_EXTERNAL_ALLOC freed 32K, 49% free 6784K/13255K, external 9009K/9009K, paused 88ms
    05-02 20:26:32.018: INFO/LedTest(143): buttonlight breakFlashLight
    05-02 20:26:32.033: INFO/com.whh.test.sensor.TheService(22091): *****onReceive(Intent { act=android.intent.action.SCREEN_OFF flg=0×40000000 })——
    05-02 20:26:32.163: VERBOSE/LockViewBase(143): ***** LOCK ATTACHED TO WINDOW
    05-02 20:26:32.163: VERBOSE/LockViewBase(143): Cur orient=1, new config={ scale=1.0 themeChanged=0 themeChangedFlags=0 imsi=460/1 loc=zh_CN touch=3 keys=1/1/2 nav=1/1 orien=1 layout=18 uiMode=17 seq=48}
    05-02 20:26:32.163: DEBUG/Sliding(143): onAttachedToWindow:25
    05-02 20:26:32.163: DEBUG/ChargingEdotView(143): resetViewStatus, mPluggedIn = true
    05-02 20:26:32.198: DEBUG/TestForLock(143): show end
    05-02 20:26:32.213: DEBUG/WindowManager(143): Input focus has changed to Window{406d3950 Keyguard paused=false}
    05-02 20:26:32.218: DEBUG/ContactsApp(223): updateScreenShotBuffer()…
    05-02 20:26:32.228: DEBUG/LedLightsService(143): Intent.ACTION_KEYGUARD_LOCK0
    05-02 20:26:32.533: INFO/com.whh.test.sensor.TheService(22091): Runnable executing.
    05-02 20:26:32.533: DEBUG/Sensors(143): sensors_poll_context_t->activate(handle: 2, enabled: 0)
    05-02 20:26:32.533: DEBUG/Sensors(143): akm:enable() handle 2, en 0.
    05-02 20:26:32.533: DEBUG/AKMD2(143): akm_enable_sensor():enable sensor type 2.
    05-02 20:26:32.563: INFO/AKMD2(143): AK8975/B for Android end.
    05-02 20:26:32.563: DEBUG/AKMD2(143): akmd2 : Compass Closed.
    05-02 20:26:32.563: DEBUG/AKMD2(143): akm_set_delay():200000000 ns.
    05-02 20:26:32.563: DEBUG/Sensors(143): sensors_poll_context_t->activate(handle: 0, enabled: 0)
    05-02 20:26:32.563: DEBUG/Sensors(143): akm:enable() handle 0, en 0.
    05-02 20:26:32.563: DEBUG/AKMD2(143): akm_enable_sensor():enable sensor type 1.
    05-02 20:26:32.633: DEBUG/AKMD2(143): acc thread end…
    05-02 20:26:32.633: DEBUG/AKMD2(143): acc Closed.
    05-02 20:26:32.638: INFO/com.whh.test.sensor.TheService(22091): *****************registerListener()**************
    05-02 20:26:32.638: DEBUG/Sensors(143): sensors_poll_context_t->activate(handle: 0, enabled: 1)
    05-02 20:26:32.638: DEBUG/Sensors(143): akm:enable() handle 0, en 1.
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_enable_sensor():enable sensor type 1.
    05-02 20:26:32.638: DEBUG/AKMD2(143): acc_enable(): acc phread create success!
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_set_delay():200000000 ns.
    05-02 20:26:32.638: DEBUG/Sensors(143): akm:setDelay():handle 0, delay 200000000 ns.
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_set_delay():200000000 ns.
    05-02 20:26:32.638: DEBUG/Sensors(143): akm:setDelay():handle 0, delay 200000000 ns.
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_set_delay():200000000 ns.
    05-02 20:26:32.638: DEBUG/Sensors(143): sensors_poll_context_t->activate(handle: 2, enabled: 1)
    05-02 20:26:32.638: DEBUG/Sensors(143): akm:enable() handle 2, en 1.
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_enable_sensor():enable sensor type 2.
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_enable(): phread create success!
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_set_delay():200000000 ns.
    05-02 20:26:32.638: DEBUG/Sensors(143): akm:setDelay():handle 2, delay 200000000 ns.
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_set_delay():200000000 ns.
    05-02 20:26:32.638: DEBUG/Sensors(143): akm:setDelay():handle 2, delay 200000000 ns.
    05-02 20:26:32.638: DEBUG/AKMD2(143): akm_set_delay():200000000 ns.
    05-02 20:26:32.648: DEBUG/AKMD2(143): acc_thread_main: ***acc*** thread start with delay = 200000000
    05-02 20:26:32.663: DEBUG/AKMD2(143): akm_thread_main: ***akm*** thread start with delay = 200000000
    05-02 20:26:32.663: INFO/AKMD2(143): AK8975/B for Android v 2.0.0.—- (Library: v1.2.3.114) started.
    05-02 20:27:05.128: DEBUG/dalvikvm(308): GC_EXPLICIT freed 90K, 46% free 4378K/8007K, external 14900K/16847K, paused 91ms

  3. Martin says:

    Does not work on (most) HTCs. The darn thing seems to be handled on the driver level and hotwired to the screen.

  4. I recently solved a similar issue using a similar path used by you. The problem is that it drains the battery.

  5. Masthaka says:

    Worked without issues on 4.0.4 Karbonn device!

    Thanks!

  6. Marco says:

    I must admit that after spending DAYS searching for a solution to this issue, all hope appeared to be lost. That is until I saw this. Thank you so much for taking the time to try to help others like me. You’ve made my day!

    Note: tested on Samsung Galaxy S3 running android version 4.1.1.

  7. frank says:

    Do we install the zip in recovery..like doing a flash? Or how do we implement this?

  8. Jameson says:

    @Frank — not sure what you mean?

  9. frank says:

    my apologies, still very much a noob at this. the link “https://github.com/jamesonwilliams/AndroidPersistentSensors” has a zip file download link. is this zip installable like a rom or software via custom recovery is what i meant to ask. again i apologize for the dumb question, just want this fix bad and not sure how to do it.

  10. Jameson says:

    Ah gotcha — https://github.com/jamesonwilliams/AndroidPersistentSensors/archive/master.zip ?

    That is a link to source code for an Android application, which demonstrates the method described in this blog post. You would have to compile the code into an application in order to see it working. But the code is meant only to be demonstrative — in and of itself, it’s not particularly useful.

  11. sam says:

    Looks like after 2 years people are still finding this code useful. Unfortunately it’s only effective on a handful of my tested devices. Has anyome found a permanent solution?

  12. Chris says:

    Great Solution,
    realy simple but works like a charm.
    Thank you

  13. Roberto says:

    GREAT!! It works on Samsung Galaxy Ace, OS 4.3.6
    THAKS!!!

  14. Rakesh says:

    Thanks a lot…..:)

  15. Sean says:

    Thank You!

    It still works great.

    Tested on Galaxy Note 2, Android 4.3.

Leave a Reply