Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android AlarmManager not working while phone asleep

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 ?

like image 868
mcourpot Avatar asked Nov 30 '16 20:11

mcourpot


People also ask

How AlarmManager works in android?

AlarmManager is a bridge between application and Android system alarm service. It can send a broadcast to your app (which can be completely terminated by user) at a scheduled time and your app can then perform any task accordingly.

What is RTC alarm in android?

There are two general clock types for alarms: "elapsed real time" and "real time clock" (RTC). Elapsed real time uses the "time since system boot" as a reference, and real time clock uses UTC (wall clock) time.

How to make alarm in Android studio?

Navigate to the app > res > layout > activity_main. xml and add the below code to that file. In this file, we have added two items 'TimePicker' and 'ToggleButton'. TimePicker is used to capture the alarm time and ToggleButton is added to set the alarm on or off.


2 Answers

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

like image 173
mcourpot Avatar answered Oct 14 '22 11:10

mcourpot


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.

like image 34
Vikram Rao Avatar answered Oct 14 '22 13:10

Vikram Rao