AlarmManager sends intent with empty Bundle in Android N - android

I am scheduling 2 alarms for an Event. First in one hour before and the second in the Event time.
It works in Android M and lower.
In Android N the Bundle(extras) comes empty.
This is how I create an intent
public static Intent createEventIntent(final Context context, final Event event) {
final Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra(EXTRA_ALARM_TYPE, EXTRA_EVENT_TYPE);
intent.putExtra(EXTRA_EVENT, event);
return intent;
}
Event implements Serializable.
Then, I create a pending Intent
final int broadCastId = event.hashCode();
final int broadCastId2 = broadCastId - 1; //to ensure 2 alarms
final PendingIntent alarmIntent =
PendingIntent.getBroadcast(mContext, broadCastId, intent, FLAG_UPDATE_CURRENT);
final PendingIntent alarmIntentOneHourBefore =
PendingIntent.getBroadcast(mContext, broadCastId2, intent, FLAG_UPDATE_CURRENT);
Then I schedule the alarms:
mAlarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), alarmIntent);
...
mAlarmManager.set(AlarmManager.RTC_WAKEUP, calendarOneHourBefore.getTimeInMillis(),
alarmIntentOneHourBefore);
In the method onReceive, the extras are null:
#Override
public void onReceive(final Context context, final Intent intent) {
final String type = intent.getStringExtra(EXTRA_ALARM_TYPE); // null in 7.0
}
This is happening only in Android N.
What am I missing?
I have tried:
Event implements parcelable and then send a parcelable.
Create a Bundle and add the extras to the Bundle.
Set and intent action.

I remember seeing something about tightening restrictions on use of custom Parcelable objects in AlarmManager Intents. I can't find the reference now. You should figure out a way to rearchitect your app so that you don't send a custom Parcelable to the AlarmManager.
You can serialize your custom Parcelable to a simple String or a byte[] and put that in the Intent and that should solve your problem.

Related

Recurring alarm BroadcastReceiver Intent string extras not getting updated

I have the below class to check if my alarms are being triggered at the exact time which I scheduled or is it differing.
I will call SetAlarm method inside the BroadCast Reciever from MainActivity. The consecutive alarms will be set by the Receiver itself by setting current time as its new string extra.
Alarms are working fine except the issue Intent string extras are not getting updated. ScheduledTime will always hold the initial value regardless of what I have set it in the setAlarm method.
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Date currentTime = new Date();
String ScheduledTime = "";
if (null != intent) { //Null Checking
ScheduledTime = intent.getStringExtra("ScheduledTime");
//intent.removeExtra("ScheduledTime");
}
String message = "Current Time" + currentTime + ", Scheduled Time was: " + ScheduledTime ;
//Show Notification
long alarmMillis =(10*60*1000) ; //Set Alarm after 10 minutes
Long newTimeInMillis = System.currentTimeMillis() + alarmMillis;
currentTime.setTime(newTimeInMillis );
setAlarm(context, newTimeInMillis , currentTime.toString());
}
public void setAlarm(Context context, Long timeMillis, String ScheduledTime)
{
AlarmManager am =( AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, AlarmReceiver.class);
i.putExtra("ScheduledTime", ScheduledTime);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
am.setExact(AlarmManager.RTC_WAKEUP, timeMillis, pi);
}
}
From Android official site
public static final int FLAG_UPDATE_CURRENT
Flag indicating that if the described PendingIntent already exists,
then keep it but replace its extra data with what is in this new
Intent. For use with getActivity(Context, int, Intent, int),
getBroadcast(Context, int, Intent, int), and getService(Context, int,
Intent, int).
This can be used if you are creating intents where only the extras
change, and don't care that any entities that received your previous
PendingIntent will be able to launch it with your new extras even if
they are not explicitly given to it.
So change your code from
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
to
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);

Android cannot pass intent extras to AlarmManager

I thought this would solve my problem, but it doesn't.
I have this code to send my alarm:
public void triggerAlarm() {
AlarmManager alarmManager = (AlarmManager) getContext().getSystemService(ALARM_SERVICE);
alarmManager = (AlarmManager) getContext().getSystemService(ALARM_SERVICE);
Intent intent = new Intent(getContext(), AlarmReceiver.class);
intent.putExtra("Id", nextDue.id.get() + "");
String passed = intent.getStringExtra("Id");
Log.d("DEBRRUG", "The extra im passing: " + passed);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getContext(), i++, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, soonest.dueTime.get(), pendingIntent);
}
My DEBRRUG statement indicates that the extra being passed is 8.
This is my alarm receiver:
#Override
public void onReceive(Context context, Intent intent) {
String passed = intent.getStringExtra("Id");
Log.d("DEBRRUG", "The extra im receiving: " + passed);
}
Here, my DEBRRUG statemnt indicates that the extra im receiving is NULL.
Note: Something that could, possibly, be interesting is that my triggerAlarm method is being called from within my ContentProvider. Don't know if that helps you understand my problem better or not.
Use
intent.getExtras().getString("id")
I found it. Wow! I was sending another alarm in another area of my code, where I wasn't putting the extra on.
Doing ctrl f -> ".set(Alar" let me track down the little bastard.

Android 7 intent extras missing

Does anyone know if there are any changes to how Android 7.0 (Nougat) handles intent extras compared to Android 6.0 (Lollipop)?
Long story short: my app works as intended on all versions from 4.1(16) to 6.0(23) but crashes on android 7.0(24)!
The app creates a pending intent with an intent to a custom broadcast receiver which has extras. However, on android 7 none of the extras are present in the intent received by the broadcast receiver.
MainActivity.java
Intent intent = new Intent(context, PollServerReceiver.class);
// TODO: Remove after DEBUGGING is completed!
intent.putExtra("TESTING1", "testing1");
intent.putExtra("TESTING2", "testing2");
intent.putExtra("TESTING3", "testing3");
// PendingIntent to be triggered when the alarm goes off.
final PendingIntent pIntent = PendingIntent.getBroadcast(context,
PollServerReceiver.REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Setup alarm to schedule our service runs.
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, firstRun, freqMilis, pIntent);
PollServerReceiver.java
Bundle extras = intent.getExtras();
Log.d(TAG, "onReceive: TESTING1 = " + extras.getString("TESTING1")); // null here
// None of the three "TESTING*" keys are there!
for (String key : extras.keySet()) {
Object value = extras.get(key);
Log.d(TAG, String.format("onReceive extra keys: %s %s (%s)", key, value.toString(), value.getClass().getName()));
}
Stack trace obviously gives the NullPointerException as the cause of crash.
It would not be so weird if it would crash among all versions, but in this case its the latest android only. Has anyone got any ideas please?
Note: I have tried creating pending intents with different flags including (0, PendingIntent.FLAG_UPDATE_CURRENT, PendingIntent.FLAG_CANCEL_CURRENT) still got the exact same result.
Putting a custom Parcelable in a PendingIntent has never been especially reliable, and it flat-out will not work in an AlarmManager PendingIntent on Android 7.0. Other processes may need to fill in values into the Intent, and that involves manipulating the extras, and that can't be done in any process but your own, since no other process has your custom Parcelable class.
This SO answer has a workaround, in the form of converting the Parcelable yourself to/from a byte[].
I had a similar problem but I think I found an easy solution. Put your data inside a Bundle and send that Bundle with your intent. In my case I wanted to send a serializable object with my intent.
Setup the alarm:
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReciever.class);
Bundle bundle = new Bundle();
//creating an example object
ExampleClass exampleObject = new ExampleClass();
//put the object inside the Bundle
bundle.putSerializable("example", exampleObject);
//put the Bundle inside the intent
intent.putExtra("bundle",bundle);
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//setup the alarm
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), alarmIntent);
Receive the alarm:
public class AlarmReciever extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// get the Bundle
Bundle bundle = intent.getBundleExtra("bundle");
// get the object
ExampleClass exampleObject = (ExampleClass)bundle.getSerializable("example");
}
}
It worked fine for me. Hope it helps :)

How to pass data from Service to Broadcastreceiver

I am calling broadcast receiver from service through this method.
Intent intent = new Intent(NotifyService.this, AlarmReciever.class);
intent.putExtra("TIME",ttime);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 34324243, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP,timee,pendingIntent);
In Broadcastreceiver page I am receiveing through this method.
Bundle b = in.getExtras();
date = b.getString("TIME");
Problem is In Broadcastreceiver page I am getting null value.
My Question is :-
How to pass data from service to broadcastreceiver ?
In your main activity start your service as
i = new Intent(this, SimpleService.class);
startService(i);
registerReceiver(broadcastReceiver, new IntentFilter(SimpleService.BROADCAST_ACTION));
use send broadcast to send any value from service class to mainactiviy class as
intent.putExtra("textval", s);
sendBroadcast(intent);
and then in your main activity class receive it as
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//do your coding here using intent
}
};
OK i assume the "ttime" is a bundle ( from the code your wrote it seems to make sense)
Try and recieve the data like this
Bundle b= in.getBundleExtra("TIME");
In case the "tttime" is of other format say a String or Int you can get the data in onReceive of Broadcastreceiverlike this
String :
in.getStringExtra("TIME");
Integer :
in.getIntExtra("TIME", -1) // -1 is default value which will be return incase no matching integer found with the key "TIME" in intent "in"
For full details and how to get data from intent of different type refer to android documentation
you missed to do like this:-
Intent intent = new Intent(NotifyService.this, AlarmReciever.class);
sendBroadcast(intent);
You can call pendingIntent Like this:-
PendingIntent operation = PendingIntent.getBroadcast(Service.this,
0, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(),5* 60 * 1000, operation);
If you are passing any time data to broadcast class,then you can receive that data in receiver class as
String mTime = NotifyService.time;
Try this. Inform me also.

Get list of registered Pending Intents in Android OS

I register alarms which I schedule to execute at given time, and it can be many alarms depending on the size of the scheduled list. But I have two questions which remains unclear to me:
1) How can I query the OS for the Pending Intents I registers? I need this for testing. The psudo code for what I want would be something like this:
List<PendingIntent> intentsInOS = context.getAllPendingIntentsOfType(AppConstants.INTENT_ALARM_SCHEDULE));
2) See the pending intent I create, I provide an action and extra data (the schedule id).
private Intent getSchedeuleIntent(Integer id) {
Intent intent = new Intent(AppConstants.INTENT_ALARM_SCHEDULE);
intent.putExtra(AppConstants.INTENT_ALARM_SCHEDULE_EXTRA, id);
return intent;
}
But we also say that the intent have FLAG_CANCEL_CURRENT. Will it cancel all pending intents with same action, or does it have to both same action AND extra data?
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, getSchedeuleIntent(schedule.id), PendingIntent.FLAG_CANCEL_CURRENT);
My code
#Override
public void run() {
List<ScheduledLocation> schedules = dbManager.getScheduledLocations();
if(schedules == null || schedules.isEmpty()){
return;
}
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
//alarmManager.
// we need to get the number of milliseconds from current time till next hour:minute the next day.
for(ScheduledLocation schedule : schedules){
long triggerAtMillis = DateUtils.millisecondsBetweenNowAndNext(now, schedule.hour, schedule.minute, schedule.day);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, getSchedeuleIntent(schedule.id), PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, MILLISECONDS_IN_WEEK, pendingIntent);
}
// List<PendingIntent> intentsInOS = context.getAllPendingIntentsOfType(AppConstants.INTENT_ALARM_SCHEDULE));
}
private Intent getSchedeuleIntent(Integer id) {
Intent intent = new Intent(AppConstants.INTENT_ALARM_SCHEDULE);
intent.putExtra(AppConstants.INTENT_ALARM_SCHEDULE_EXTRA, id);
return intent;
}
1 How can I query the OS for the Pending Intents I registers?
I'm not sure you can, but you can check if a specific PendingIntent is registered or not like this :
private boolean checkIfPendingIntentIsRegistered() {
Intent intent = new Intent(context, RingReceiver.class);
// Build the exact same pending intent you want to check.
// Everything has to match except extras.
return (PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE) != null);
}
2 Will it cancel all pending intents with same action, or does it have to both same action AND extra data?
It will cancel all the PendingIntent that are resolved to be equal.
What exactly does equal means ?
The java doc of android says:
Determine if two intents are the same for the purposes of intent
resolution (filtering). That is, if their action, data, type, class,
and categories are the same. This does not compare any extra
data included in the intents.
You can read at the 7391 line here : https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/Intent.java
To sum up, all PendingIntent that are build exactly the same except extras will be cancelled.

Categories

Resources