Do I need to explicitely specify class using intent.setClass? - android

I want to use AlarmManager to schedule a repeating task. Basically, I have this code:
Intent intent = new Intent(INTENT_ACTION_TICK);
// The following line prevents the broadcast receiver from being notified:
intent.setClass(context, MyScheduler.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, intervalInMs, intervalInMs, pendingIntent);
I register MyScheduler as a broadcast receiver in its constructor:
context.registerReceiver(this, new IntentFilter(INTENT_ACTION_TICK));
Everything works as expected (receiver is triggered) unless I add the intent.setClass. Fine with me, however, I distinctly remember reading that you should use explicit intents (intent.setClass) for security reasons.
Is this something I have to consider for my use case?

Related

Explicit Intent not received by manifest registered receiver

Am trying some example alarm clock and trying to separate the data from the ui using the viewModel examples.
Basically I'm trying to set an alarm and as the docs say when it's time comes the alarmManager should fire the intent supplied and it should be received by a broadcast receiver.
This works all nice and dandy if setting the alarm inside a fragment/activity. But if I try to move the code in another class which is a singleton and it's instance is used in a ViewModelProvider the the broadcast receiver is not receiving the intent from AlarmManager.
The code is as simple as :
Intent intent = new Intent(mContext, AlarmReceiver.class);
Log.d(LOG_TAG, "====+++ " + mContext);
intent.putExtra(ALARM_UNIQUE_ID, mId);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getBroadcast(mContext, 123, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendarAlarma = Calendar.getInstance();
calendarAlarma.set(Calendar.HOUR_OF_DAY, 11);
calendarAlarma.set(Calendar.MINUTE, 33);
AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(calendarAlarma.getTimeInMillis(),
null);
AlarmManager mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mAlarmManager.setAlarmClock(alarmClockInfo, pi);
The broadcast receiver is registered in the manifest file. The context object seems to be the same but somehow the intent is never launched or never received.
The receiver part is defined as:
<receiver
android:name=".ui.alarma.AlarmReceiver"
android:exported="true"
android:enabled="true"
>
</receiver>
What is it I am missing ?
PS1:
The alarm icon in the status bar is showing (set) as expected and it disappears when the time for the alarm to start is right. Also the I can see in logcat the following:
onReceive:android.app.action.NEXT_ALARM_CLOCK_CHANGE
But my receiver receives nothing, why ?

Intent : Direct binding Activity to BroadcastReceiver without service?

First of all I wanted to give codes here but the codes are way to long, it can be found here
http://www.c-sharpcorner.com/article/creating-and-scheduling-alarms-in-android/
The codes in the article doesn't create service.
As per my understanding, BroadcastReceiver is used to serve an activity based on its corresponding Service ( via an intent ).
Normally we would declare such :
Intent serviceIntent = new Intent(MainActivity.this, CustomeService.class);
startService(serviceIntent);
registerReceiver(mReceiver, mIntentFilter);
where mReceiver will be something like :
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
}
}
But there's something confusing me in AlarmManager class in the example that I shared by the link :
intent = new Intent(this, MyBroadcastReceiver.class);
pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(), 280192, intent, PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + (i * 1000), 10000
, pendingIntent);
The line that is confusing me is this :
intent = new Intent(this, MyBroadcastReceiver.class);
the type of this MyBroadcastReceiver.class is BroadcastReceiver itself, and not Service.
So where's the Service that this MyBroadcastReceiver.class received broadcast from then?
Does BroadcastReceiver somehow create its own service?
the type of this MyBroadcastReceiver.class is BroadcastReceiver itself, and not Service
Correct. It is being used with PendingIntent.getBroadcast(), notgetService()`.
So where's the Service that this MyBroadcastReceiver.class received broadcast from then?
Few broadcasts are sent by some Service, though that is certainly possible. In this case, the broadcast is being sent from a system process, as part of sending the PendingIntent, when the alarm time comes around.
Does BroadcastReceiver somehow create its own service?
I am not sure what you mean by "create" here. A BroadcastReceiver may delegate its work to a Service. That is a common pattern with AlarmManager: have the alarms trigger a WakefulBroadcastReceiver, which in turn delegates work to an IntentService. In that case, the reason for the indirection stems from the way that AlarmManager works with WakeLocks.

Android Pending Intent to start service

So I have a service, in the onCreate() I setup 3 pending intents to start the same service, each having different extra data to specify the action. I'm creating a notification, and I want to use one pending intent as the click action, one as the dismiss action, and the third is for an alarm.
Intent iCancel = new Intent(this, BootService.class);
Intent iAlarm = new Intent(this, BootService.class);
Intent iDismiss = new Intent(this, BootService.class);
// each will have actions
iAlarm.putExtra(INTENT_ACTION, INTENT_ALARM_FIRED);
iCancel.putExtra(INTENT_ACTION, INTENT_CANCEL);
iDismiss.putExtra(INTENT_ACTION, INTENT_DISMISS);
PendingIntent piCancel = PendingIntent.getService(
this,
0,
iCancel,
Intent.FILL_IN_DATA);
mPiAlarm = PendingIntent.getService(this, 0, iAlarm, Intent.FILL_IN_DATA);
PendingIntent piDismiss = PendingIntent.getService(this, 0, iDismiss, Intent.FILL_IN_DATA);
mNotifyBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_action_about)
.setContentTitle(getString(R.string.app_name))
.setContentIntent(piCancel)
.setDeleteIntent(piDismiss);
The problem is all pending intents seem that have the same intent extra data, so when onStartCommand is launched no matter whether the notification was clicked or dismissed or neither, constant INTENT_CANCEL is received from intent.getIntExtra(INTENT_ACTION)
I believe it has something to do with the flags used in PendingIntent.getService(), i'm confused about which to use. I've tried using PendingIntent.FLAG_CANCEL_CURRENT, and UPDATE_CURRENT, neither seem to fix the issue but the result is different, I receive constant INTENT_ALARM_FIRED for every action.
How can I get each pending intent to have its own intent extra data?
Solution
I discovered an exact warning about this scenario right in the PendingIntent doc. http://developer.android.com/reference/android/app/PendingIntent.html
just changed my request codes and it works
This has to do with Intents being considered the same. See PendingIntent for more information.
One way to get around this would be to just vary the requestCode for each Intent. (The 2nd parameter in the getService call)

onReceiver of BroadcastReceiver not called, AlarmManager

I am building a cab booking app, I need current location of the cab every 20 seconds.
I have defined a AlarmManager and need it to repeat itself every 20 seconds. But its not repeating itself regularly. Instead it repeated itself after 233 seconds, and just once. What am I doing wrong here ?
My HomeScreen has a inner class OnAlarmReceiver, in the onCreate of my HomeScreen I am calling AlarmManager
AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, OnAlarmReceiver.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 20);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
cal.getTimeInMillis(), God.UPDATE_PENDING_INTERVAL, pi);
Inner class in HomeScreen
public class OnAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// PullPendingRequests.acquireStaticLock(context);
Toast.makeText(context, "Don't panik but your time is up!!!!.", Toast.LENGTH_LONG)
.show();
Log.d("Taxeeta:PullPendingRequets", "CallService Location");
context.startService(new Intent(context, PullPendingRequests.class));
}
}
My AndroidManifest file has
<service
android:name="com.taxeeta.support.PullPendingRequests"
android:enabled="true"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:theme="#android:style/Theme.Light.NoTitleBar" />
<receiver android:name=".com.taxeeta.HomeScreen.OnAlarmReceiver" />
</application>
Output of adb shell dumpsys alarm
com.taxeeta
51471ms running, 5248 wakeups
5248 alarms: flg=0x4 cmp=com.taxeeta/.HomeScreen$OnAlarmReceiver
Output of adb shell dumpsys alarm | grep taxeeta
ELAPSED_WAKEUP #7: Alarm{409303b0 type 2 com.taxeeta}
operation=PendingIntent{408ba2d8: PendingIntentRecord{40887be8 com.taxeeta broadcastIntent}}
com.taxeeta
5248 alarms: flg=0x4 cmp=com.taxeeta/.HomeScreen$OnAlarmReceiver
To fix it, I removed the inner class OnAlarmReceiver and fixed the androidmanifest.xml file.
<receiver
android:name="com.taxeeta.support.OnAlarmReceiver"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.NOTIFY" />
</intent-filter>
</receiver>
If the answer above doesn't work for you then there is another way to not receive any callbacks when AlarmManager fires an expired alarm. You simply need to check this one out: by sending the wrong Intent on instantiation of PendingIntent. For example you wanted to receive a call onReceive on one of your receivers but you instantiated a PendingIntent via getActivity or getService, but what you actually meant is getReceiver.
When creating instance of PendingIntent, there are many ways to create it (getService, getActivity,getReceiver, getForegroundService:
if you want Activity the receiver of the intent then you:
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_*);
if you want BroadcastReceiver the receiver of the intent:
PendingIntent.getReceiver(this, 0, intent, PendingIntent.FLAG_*);
if you want a foreground Service the receiver of the intent:
PendingIntent.getForegroundService(this, 0, intent, PendingIntent.FLAG_*);
if you want a Service the receiver of the intent:
PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_*);
Also, make sure you intents are pointing to the correct class. (e.g. creating intents for Activity, Service etc.). You will not receive any call if you pass wrongfully like this:
Intent intent = new Intent(this, MyReceiver.class); // You wanted receiver
// PendingIntent was created in such a way
// you wanted this to be received by an activity.
// you will not receive any call if you set it up like this.
PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_*);
I also posted similar answer here.
HTH
This piece of code worked for me and make sure you have added reciever in android manifest file.
AlarmManager service = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, OnAlarmReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(context, 0, i,
PendingIntent.FLAG_CANCEL_CURRENT);
Calendar cal = Calendar.getInstance();
// Start 20 seconds after boot completed
cal.add(Calendar.SECOND, 20);
//
// Fetch every 20 seconds
// InexactRepeating allows Android to optimize the energy consumption
service.setInexactRepeating(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis(), 1000*20, pending);
The above solutions didn't work for me.
Additionally, registering dynamically via code did the trick:
Intent intent = new Intent();
intent.setAction("android.intent.action.NOTIFY");
//Register the receiver
context.registerReceiver(new OnAlarmReceiver(),new IntentFilter());
For anyone still stuck - make sure your broadcast receiver is not crashing in the background. Make sure to check your LogCat!

How to properly cancel pending alarms

I can't seem to find an answer to this, but what is the criteria for passing in a matching PendingIntent to be removed from an alarm? It it based on the just the name like com.blah.package.myclass or do the Extras matter?
For example, I'm doing something like this and all alarms fire the same intent:
public void setAlarm(Context context, long nextAlarmMillis) {
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra("alarm", this);
PendingIntent sender = PendingIntent.getBroadcast(context, 1234 /* unused */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
am.cancel(sender);
am.set(AlarmManager.RTC_WAKEUP, nextAlarmMillis, sender);
}
What happens is that the Alarm class can be altered, etc and passed in as an Extra. I am canceling the previous alarm, but I'm not sure if it's doing anything or if any left over alarms will remain.
Anyone know for sure?
Following is mentioned in AlarmManager documentation
Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled.
Which, the filterEquals() is defined as:
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.
I think the easiest to do is you keep the reference of PendingIntent passed to AlarmManager, and pass it to cancel. Or have a factory method to construct such pi.

Categories

Resources