0

Following the docs I managed to setup an alarm with AlarmManager, which is working nicely as soon as I don't turn off the phone's screen.

This is the output of adb shell dumpsys alarm when the screen is on:

Batch{1304da7 num=1 start=190708622 end=190708622 flgs=0x1}:
    RTC_WAKEUP #0: Alarm{f642254 type 0 when 1480059825231 alarm.poc.app}
      tag=*walarm*:alarm.poc.app.ACTION
      type=0 whenElapsed=+5m49s424ms when=2016-11-25 02:43:45
      window=0 repeatInterval=0 count=0 flags=0x1
      operation=PendingIntent{f78d3a6: PendingIntentRecord{f52e1e7 alarm.poc.app broadcastIntent}}

A couple of seconds after I turn the screen off my alarm disappears from the command output and never gets called (it works with the screen on).

So I have these questions:

  • What other apps do to keep alarms alive? I can see in the output of adb shell dumpsys alarm a gazillion of other alarms not being removed even if I clear all recent apps. I can see there alarms of Whatsapp, Google and a lot of other random apps.
  • Maybe all those apps have a foreground service? I ask this because I managed to "workaround" the problem creating a nasty foreground service that writes something to the log every 5 seconds. It looks like having a running service prevents the problem but it's completely ugly and I think is not a good idea to keep the processor awake all the time.

This is how I setup the alarm:

AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);    
Intent intent = new Intent(AlarmReceiver.MY_ACTION);
intent.putExtra("text", editText.getText().toString());
PendingIntent pending = PendingIntent.getBroadcast(this, 42,
    intent, PendingIntent.FLAG_UPDATE_CURRENT);
// also tried with getBroadcast(this, 0, intent, 0)
manager.setExact(AlarmManager.RTC_WAKEUP, millis, pending);

I have a WakefulBroadcastReceiver (not called with screen off):

public void onReceive(Context context, Intent receivedIntent) {
  if (MY_ACTION.equals(receivedIntent.getAction())) {
    System.out.println("good!!!");
    String text = receivedIntent.getStringExtra("text");
    Intent intentService = new Intent(context, TestService.class);
    intentService.putExtra("text", text);
    startWakefulService(context, intentService);
    System.out.println("service started");
  }
}

And the manifest:

...
<uses-permission android:name="android.permission.WAKE_LOCK"/>
...
  <service android:name="alarm.poc.app.TestService"
      android:exported="false"/>

  <receiver android:name="alarm.poc.app.AlarmReceiver"
            android:process=":remote">
    <intent-filter>
      <action android:name="alarm.poc.app.ACTION"/>
    </intent-filter>
  </receiver>
...

I'm testing on a marshmallow phone, minSdkVersion 19 and targetSdkVersion 24

carrizo
  • 689
  • 6
  • 15

2 Answers2

1

I found the answer to my question.

The thing is that I'm testing on a Huawei phone and those come with a "Protected Apps" feature that kills all apps when the screen goes off except those configured to be protected.

The reason why there are so many other apps in the dumpsys output is that Huawei comes by default with some popular apps already "protected".

The only solution I've found is detecting Huawei phones and ask the user to protect the app. This answer covers that approach:

"Protected Apps" setting on Huawei phones, and how to handle it

Community
  • 1
  • 1
carrizo
  • 689
  • 6
  • 15
1

You should use WakeLocker to wake up the device when the alarm is received. Make a new class called WakeLocker with this source code:

package your.packagename;

import android.content.Context; import android.os.PowerManager;

public abstract class WakeLocker { 

    private static PowerManager.WakeLock wakeLock;

    public static void acquire(Context ctx) {
    if (wakeLock != null) wakeLock.release();

    PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
    wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
            PowerManager.ACQUIRE_CAUSES_WAKEUP |
            PowerManager.ON_AFTER_RELEASE, MainActivity.APP_TAG);
    wakeLock.acquire();
    }

    public static void release() {
    if (wakeLock != null) wakeLock.release(); wakeLock = null;
    }
} 

and in your receiver call WakeLocker.acquire(context); as the 1st thing. Extra: it would also be neat to callWakeLocker.release(); once your alarm has done its thing.

Also you require permission: <uses-permission android:name="android.permission.WAKE_LOCK" />

Guilherme Golfetto
  • 510
  • 2
  • 5
  • 25
Patrick R
  • 6,621
  • 1
  • 24
  • 27