Android AlarmManager not working while phone asleep
Asked Answered
E

3

8

I've got a problem with AlarmManager.

In short, I plan an alarmManager :

Intent intent = new Intent(context, MyActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delayInMs, pendingIntent);

And the activity MyActivity appears at the specified time. Just when the device is plugged. It also works when it is in my pocket, or when the delay is a few minutes. But when I set the alarmManager before night, it won't work in the morning. However, it will work as soon as I take the phone or unlock the screen.

So, I suppose it is due to a sleep mode of the device, but how solve this ?

1) I added a log in every methods of myActivity, and I'm sure no one is called before I manually wake the device. 2) I tried PowerManagement's wake lock (with the WAKE_LOCK permission in manifest), but nothing changed :

alarmManager.setExact(.........);
wakeLock = ((PowerManager)contexte.getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "MyActivity");
wakeLock.acquire();

Please, help ! I'm sure I'm so close...

EDIT Dec 04'16 : Thanks to Nick Friskel and Vikram Rao, I changed my initial code to call a broadcastReceiver and acquire my wakeLock in the onReceive. Unfortunately, it doesn't seem to work. It perfectly works when the phone is plugged or when the alarm is planned 35 minutes later, but for a complete night, the onReceive isn't even called. I tried that night, with an alarm planned at 9:00 AM, but the onReceive was only executed at 9:46 AM, that means the moment where I unlocked the device. Here's my new code :

Intent intent = new Intent("com.blah.something.ALARM_RECEIVED");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delayInMs, pendingIntent);

That said, my log writing "start of onReceive" wasn't really at the beginning of the listener for some reason. I just managed to put it at the real beginning, so I'll see if the listener is called or not.

EDIT Dec 05'16 : So, I changed the log writing, at the top of the onReceive, and the same problem happened : the start of the onReceive is called as soon as I manually wake the device. I can implement the wakefulBroadcastReceiver, but I fear it won't solve anything. If I understood correctly, the wakefulBroadcastReceiver is useful to prevent the device to sleep between onReceive and the launch of the activity or service. But what if the onReceive isn't even called ? I'm a bit desperate... Maybe should I directly ask Sony. Furthermore, my phone has stamina mode, but it isn't activated.

EDIT Dec 11'16 : So, with more tests, I'm now sure that I understand nothing.... I set a broadcastReceiver which activates every 5 minutes (the onReceive resets the alarmManager 5 minutes later), and I can see it's perfectly working... sometimes. It can last several hours, and sleep for two hours, then ok for 30 minutes, then back to sleep. (all that when my phone is on, unplugged and idle). I'm going to remove ALL the code but what interests us. It will be easier to understand and I will be able to write here all the active code.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.par.hasard.mysimpleapplication">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="com.par.hasard.mysimpleapplication.MySimpleReceiver">
            <intent-filter android:priority="1">
                <action android:name="com.par.hasard.mysimpleapplication.REGULAR_ALARM" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

MainActivity.java

package com.par.hasard.mysimpleapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button)findViewById(R.id.myExportButton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MyLogManager.copyLogToClipboard(view.getContext());
                MyLogManager.emptyLogFile(view.getContext());
            }
        });
        try {
            MyLogManager.createLogFile(this);
            MyLogManager.write(this, "Application launched\n");
            MyAlarmPlanner.planAlarm(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

MySimpleReceiver.java

package com.par.hasard.mysimpleapplication;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MySimpleReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            MyLogManager.write(context, "Beginning of onReceive\n");
            MyAlarmPlanner.planAlarm(context);
            MyLogManager.write(context, "End of onReceive\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

MyAlarmPlanner.java

package com.par.hasard.mysimpleapplication;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import java.io.IOException;

public class MyAlarmPlanner {
    public static void planAlarm(Context context) throws IOException {
        MyLogManager.write(context, "Beginning of alarm planning\n");
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent("com.par.hasard.mysimpleapplication.REGULAR_ALARM");
        PendingIntent pendingIntent =  PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        alarmManager.cancel(pendingIntent);
        alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 300000, pendingIntent);
        MyLogManager.write(context, "End of alarm planning\n");
    }
}

I don't think MyLogManager.java is useful, it's just boring files management methods.

Content of log file after a long idle time :

12/12 15h33m23s380 => Beginning of onReceive
12/12 15h33m23s381 => Beginning of alarm planning
12/12 15h33m23s383 => End of alarm planning
12/12 15h33m23s384 => End of onReceive
12/12 15h38m24s337 => Beginning of onReceive
12/12 15h38m24s339 => Beginning of alarm planning
12/12 15h38m24s375 => End of alarm planning
12/12 15h38m24s376 => End of onReceive
12/12 15h43m24s375 => Beginning of onReceive
12/12 15h43m24s376 => Beginning of alarm planning
12/12 15h43m24s380 => End of alarm planning
12/12 15h43m24s381 => End of onReceive
12/12 15h48m25s301 => Beginning of onReceive
12/12 15h48m25s304 => Beginning of alarm planning
12/12 15h48m25s307 => End of alarm planning
12/12 15h48m25s308 => End of onReceive
12/12 15h53m25s316 => Beginning of onReceive
12/12 15h53m25s318 => Beginning of alarm planning
12/12 15h53m25s328 => End of alarm planning
12/12 15h53m25s329 => End of onReceive
12/12 15h58m25s328 => Beginning of onReceive
12/12 15h58m25s329 => Beginning of alarm planning
12/12 15h58m25s331 => End of alarm planning
12/12 15h58m25s333 => End of onReceive
12/12 16h3m26s336 => Beginning of onReceive
12/12 16h3m26s351 => Beginning of alarm planning
12/12 16h3m26s379 => End of alarm planning
12/12 16h3m26s380 => End of onReceive
12/12 16h8m26s397 => Beginning of onReceive
12/12 16h8m26s401 => Beginning of alarm planning
12/12 16h8m26s404 => End of alarm planning
12/12 16h8m26s405 => End of onReceive
12/12 16h13m26s406 => Beginning of onReceive
12/12 16h13m26s407 => Beginning of alarm planning
12/12 16h13m26s410 => End of alarm planning
12/12 16h13m26s411 => End of onReceive
12/12 16h18m27s328 => Beginning of onReceive
12/12 16h18m27s329 => Beginning of alarm planning
12/12 16h18m27s346 => End of alarm planning
12/12 16h18m27s348 => End of onReceive
12/12 16h23m28s298 => Beginning of onReceive
12/12 16h23m28s299 => Beginning of alarm planning
12/12 16h23m28s303 => End of alarm planning
12/12 16h23m28s304 => End of onReceive
12/12 16h28m29s308 => Beginning of onReceive
12/12 16h28m29s310 => Beginning of alarm planning
12/12 16h28m29s323 => End of alarm planning
12/12 16h28m29s324 => End of onReceive
12/12 16h33m29s339 => Beginning of onReceive
12/12 16h33m29s340 => Beginning of alarm planning
12/12 16h33m29s355 => End of alarm planning
12/12 16h33m29s361 => End of onReceive
12/12 16h38m29s356 => Beginning of onReceive
12/12 16h38m29s357 => Beginning of alarm planning
12/12 16h38m29s360 => End of alarm planning
12/12 16h38m29s361 => End of onReceive
12/12 16h43m29s364 => Beginning of onReceive
12/12 16h43m29s365 => Beginning of alarm planning
12/12 16h43m29s367 => End of alarm planning
12/12 16h43m29s369 => End of onReceive
12/12 16h48m29s376 => Beginning of onReceive
12/12 16h48m29s380 => Beginning of alarm planning
12/12 16h48m29s390 => End of alarm planning
12/12 16h48m29s394 => End of onReceive
12/12 16h53m29s392 => Beginning of onReceive
12/12 16h53m29s394 => Beginning of alarm planning
12/12 16h53m29s402 => End of alarm planning
12/12 16h53m29s403 => End of onReceive
12/12 17h43m33s986 => Beginning of onReceive      //problem, the 16'58 onReceive wasn't called
12/12 17h43m33s988 => Beginning of alarm planning
12/12 17h43m33s996 => End of alarm planning
12/12 17h43m34s4 => End of onReceive
12/12 17h48m34s535 => Beginning of onReceive
12/12 17h48m34s536 => Beginning of alarm planning
12/12 17h48m34s539 => End of alarm planning
12/12 17h48m34s540 => End of onReceive
12/12 18h29m49s635 => Beginning of onReceive     //the moment I turned on my device
12/12 18h29m49s648 => Beginning of alarm planning
12/12 18h29m49s667 => End of alarm planning
12/12 18h29m49s668 => End of onReceive

Can someone tell me where is my mistake ?

Expansile answered 30/11, 2016 at 20:0 Comment(2)
Which android version and what is the phone make? Different versions of android have slightly differing behaviour in my experience. Some phones with customisations by maker also behave differently for sleep/wake related actions primarily to conserve battery at the cost of limiting other functions.Lakeishalakeland
It's a Sony XPERIA E5. Yes, I'm aware of this, but I hope that, if I can do it working on my device, it would be OK on most others. I hope...Expansile
E
6

Thanks to CommonsWare, the problem is solved ! This fail is due to doze mode (https://developer.android.com/training/monitoring-device-state/doze-standby.html) In short, since Android 6.0, AlarmManager is impacted and cannot fire if the device is in this doze mode. But you can replace setExact by setExactAndAllowWhileIdle. There are limitations but we have to deal with. There's the link to the post where CommonsWare answered : sendWakefulWork not always called with cwac-wakeful-1.1.0

Expansile answered 25/12, 2016 at 12:40 Comment(0)
L
1

AlarmManager api in android has its limitations. For -

  1. It is cleaned up on restart of the device (all alarms lost)
  2. Its behaviour is inconsistent between makers and android versions during device lock/sleep states

They way I have worked around these is -

  1. Create alarm that has a broadcast intent and then add listener to that broadcast to do the necessary action.

Like this -

Intent intent = new Intent("com.blah.something.ALARM_RECIEVED");
PendingIntent pendingIntent =  PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Html5Activity.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, triggerTimeInMillis, pendingIntent);

manifest file -

<receiver android:name=".receiver.BackgroundScheduledAlarmReceiver">
     <intent-filter android:priority="1">
          <action android:name="com.blah.something.ALARM_RECIEVED" />
     </intent-filter>
</receiver>
  1. Save alarms in sqlite or somewhere (not shown here) and recreate them on device restart by listening to device boot like this -

manifest file -

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
...
<receiver
    android:name=".receiver.RecreateAlarmsAtBootReceiver"
    android:enabled="true"
    android:exported="true"
    android:label="RecreateAlarmsAtBootReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.QUICKBOOT_POWERON" />
    </intent-filter>
</receiver>

In RecreateAlarmsAtBootReceiver, read the sqlite storing alarms and adds them to alarm manager again.

Lakeishalakeland answered 2/12, 2016 at 3:13 Comment(2)
For second point, I already added a RECEIVE_BOOT_COMPLETED, which initializes alarms with saved datas. It seems to work, although I didn't spend lot of time on testing it. For first point, I'll try, and get you in touch with resultsExpansile
The broadcastReceiver didn't solve the problem for now, the listener doesn't seem to be called when the device is unplugged and inactive for a long time. As I said in my edited question, I'll make sure of it.Expansile
P
0

The proper way to do this would be to have your AlarmManager fire a BroadcastReceiver instead of the direct Activity. You would then put your wakelock inside of the broadcast receiver class, and then either run your activity from the BroadcastReceiver or from an IntentService that is started from the BroadcastReceiver.

Your intent would be changed to this:

Intent intent = new Intent(context, MyActivityReceiver.class);

and then make your broadcast receiver:

package com.yourpackage (change this to your package)

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyActivityReceiver extends BroadcastReceiver {
    public static final int REQUEST_CODE = 0; //only necessary if you have more receivers 

    // when the alarm is triggered
    @Override
    public void onReceive(Context context, Intent intent) {
    wakeLock = ((PowerManager)contexte.getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "MyActivity");
    wakeLock.acquire();
    Intent i = new Intent(context, MyActivity.class);
    context.startService(i);
    }
}

and add the receiver to your Manifest:

<receiver
            android:name="service.TimeService"
            android:enabled="true"
            android:exported="false"
            >
</receiver>

If this does not work then you need to start your activity through a WakefulService.

Pupa answered 2/12, 2016 at 2:28 Comment(3)
Thanks for your answer. I just edited my question. So I'll confirm that my onReceive isn't called, and if that's confirmed, I'll try your WakefulService idea.Expansile
So the onReceive is not called ONLY when the phone is plugged in?Pupa
No, the onReceive is perfectly called when the phone is plugged in or when it is idle for a few minutes. That's when it is unplugged and idle for a long time that it won't be called.Expansile

© 2022 - 2024 — McMap. All rights reserved.