multiple proximity alert based on a service - android

I have my application, which uses ProximityAlerts to fire up when the user enters on the designated radios.
My proximityAlert fires up a service which shows a Toast telling me that I've entered the designated radio of the events.
The problem comes that I cannot make my application to fire several registered locations, it only react to the last one that was registered and ignore the earlier registered events
Any help please? I have seen people using broadcast receiver but in my case I use a service instead.

To build an Android app with proximity alert, the main ingredient are Broadcast Receiver, Intent, PendingIntent, and AddProximityAlert.
The workflow should be as following: -
Register an IntentFilter with a Broadcast Receiver
Create an Intent
Get a PendingIntent that will perform a broadcast
Set proximity alert
In order to make your app fire several registered locations, your must first register the broadcast receiver with correct IntentFilter and create the respective Intent for each Proximity Alert. Remember to use unique intent action for each location as follows
.....
IntentFilter filter = new IntentFilter(PROX_ALERT_INTENT + uniqueID);
.....
Intent mIntent = new Intent(PROX_ALERT_INTENT + uniqueID);
......
For more information, you can read my post on the topic

There are a couple points to be made here:
Use a broadcast receiver to handle the PendingIntent that fires for a proximityAlert.
When creating the PendingIntent for a proximityAlert, the PendingIntent must be unique for each location. The easiest way to do this is to create a unique action string for each location.
Here's an example of a PendingIntent you can create for a proximityAlert:
int uniqueID = <unique_id_for_location>;
String intentAction = "PROXIMITY_ALERT." + uniqueID;
PendingIntent proximityIntent = PendingIntent.getBroadcast(getApplicationContext(),
uniqueID, new Intent(intentAction), PendingIntent.FLAG_CANCEL_CURRENT);
Then, add the proximityAlert for your location (I'm assuming you know how to do this part).
Next, register your broadcast receiver to handle the intent action string you created for your pendingIntent:
IntentFilter filter = new IntentFilter(intentAction);
registerReceiver( new ProximityItentReceiver(), filter );
Here, ProximityIntentReceiver is the name of the BroadcastReceiver class you have created.

It's all about the request_id in pendingIntent, you assign a requestid for pendingIntent..and if your first service uses the request id 1 means and second service uses the request id 2..so the first service not be killed and the service occurs concurrently ..
the count i used as request id in pending intent
int COUNT=Integer.parseInt(some_text.getText().toString());
if(COUNT==1)
{
PendingIntent proxi_pi=PendingIntent.getService(class_name.this,COUNT,service_class_intent,Intent.FLAG_ACTIVITY_NEW_TASK);
location_manager.addProximityAlert(location.getLatitude(), location.getLongitude(), radius, -1, proxi_pi);
}
else if(COUNT==2)
{
PendingIntent proxi_pi=PendingIntent.getService(class_name.this,count,service_class_intent,Intent.FLAG_ACTIVITY_NEW_TASK);
location_manager.addProximityAlert(location.getLatitude(), location.getLongitude(), radius, -1, proxi_pi);
}
else if(COUNT==so..on){
}
i hope this helps you..

Have you considered using a broadcast receiver then launching a service from the receiver?
When I first used them I found the system cached the results somewhat so had to set an ID (Which the docs claim not to be used). By doing this the intents weren't cached and I got the appropriate information to handle multiple proximity alerts.
Source code is available through github (See the bottom of this post) Gaunt Face - Multiple Proximity Alerts

Related

How to check for Proximity Alerts and find them

I have a question regarding proximity alerts.
In all tutorials I ve read they are created and destroyed while the activity that create them is still running.
But what happens if say an activity creates n proximity alerts and then the activity itself is destroyed (the PA are not)
Then if I want to build another activity that finds these Proximity Alerts, how can I do that? Is that even possible?
You have to maintain your own list of proximity alerts. There is no way to get them back. However, #Mercato is correct when he says that you can remove a PA using only pending intents, but you don't have to store them. According to the docs:
A PendingIntent itself is simply a reference to a token maintained by the system describing the original data used to retrieve it. This means that, even if its owning application's process is killed, the PendingIntent itself will remain usable from other processes that have been given it. If the creating application later re-retrieves the same kind of PendingIntent (same operation, same Intent action, data, categories, and components, and same flags), it will receive a PendingIntent representing the same token if that is still valid, and can thus call cancel() to remove it.
This means that the system will store your PendingIntent for you between app restarts, and you can retrieve it by passing the same Intent you used to create it. So for example, if you created the following PendingIntent:
Intent intent = new Intent(context, Foo.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Then all you have to store is the requestId (1) and the Class or class name (Foo.class or Foo.class.getName()). Then if you want to retrieve that same PendingIntent without creating a new one, you can do the following:
Class<Foo> className = retrieveClass(); //You implement this
//String clazz = retrieveClassName(); //This is another option
int requestId = retrieveId(); //You implement this
Intent intent = new Intent(context, className);
//The flag given attempts to retrieve the PendingIntent if it exists, returns null if it doesn't.
PendingIntent pi = PendingIntent.getBroadcast(context, requestId, intent, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
//This pending intent was registered once before.
//Go ahead and call the function to remove the PA. Also, go ahead and call pi.cancel() on this.
}
else {
//This pending intent was not registered, and therefore can't have a PA registered to it.
}
Technically, all proximity alerts need a PendingIntent defined and used as a parameter. Android's Documentation shows that if you know the list of PendingIntents then you can remove them as well.
removeProximityAlert(PendingIntent intent) Removes the proximity alert
with the given PendingIntent.
Since PendingIntent is Parecelable see here then you could add it as an Extra to any Intent. This means, that on starting another Activity, you can create an Parcelable[] array to hold all these PendingIntent, then
putExtra(String name, Parcelable[] value)
Add extended data to the intent.
then retrieve them in the next Activity via getIntent() and it's relevant methods.

AlarmManager and WakefullBroadcastReceiver how private are the passed Extras?

I am trying to implement some alarm scheduling by using AlarmManager. Since when a alarm is triggered, I want to use a WakefulBroadcastReceiver which starts an IntentService to do some background job.
I have some questions related to security/privacy of the parameters passed for alarm's intents.
When setting a PendingIntent for a alarm I do something like:
Intent myIntent = new Intent(context, MyReceiver.class);
myIntent.putExtra("test", "testValue");
Bundle bundle = new Bundle();
bundle.putParcelable("bundleValue", bundleTestValue2);
myIntent.putExtra("test3", bundle);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 323,
myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
My question is: how private are the values set as Extra for the pendingIntent of the alarm? Is there a chance of them getting read by some other app since is being used by Android Alarm's Manager after it is scheduled?
By having a receiver like
public class MyReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
startWakefulService(context, MyIntentService);
}
And on android manifest
<receiver
android:name=".receivers.MyReceiver"
android:exported="false"/>
<service
android:name=".MyIntentService"
android:exported="false"/>
And the service
public class MyIntentService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
try {
//logic here
} catch{
} finaly{
MyReceiver.completeWakefulIntent(intent);
}
}
Call from within my Activity
sendBroadcast(new Intent(context, MyReceiver.class).putExtra(...);
Schedule a pending intent from an alarm
Intent myIntent = new Intent(context, MyReceiver.class);
myIntent.putExtra("test", "testValue");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 323,
myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
how exposed is this receiver to other apps? Can it react to other apps except mine? Does this rise any security possible issues?
Thank you.
Later edit:
Since the WakefullBroadcastReceiver seems the only way that guarantees that my service will get a partial wakelock, how can I make sure 100% that no other apps will be aware of my receiver and that my receiver won't get any other calls except ones made from my activity or from my set Alarm?
How would a WakefullBroadcastReceiver pattern works versus CommonsWare's WakefulIntentService ?
Later Edit:
I've finally managed to finish my implementation.
As stated before, both my WakefulBroadcastReceiver and IntentService are declared as exported="false" in my Android Manifest which from what I understand means that only my app can access them. Since the receiver is not exported, does it receive broadcasts from outside the app?
When setting an alarm I use this PendingIntent:
Intent myIntent = new Intent(context, MyReceiver.class);
myIntent.putExtra("databaseId", "1");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 323,
myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
When calling from my Activity I do:
sendBroadcast(new Intent(context, MyReceiver.class).putExtra("databaseId", "1"));
Is this enough?
Privacy considerations related to Intent extras
In general, I would say that it is an insecure practice to put sensitive data in Intent.
In theory, if Intent can only be consumed by specific application (discussed later) then only that application should be able to see its contents. However, given a vast amount of Android devices and OS versions (incl. rooted devices and custom ROMs), I wouldn't count on it.
You did not specify the kind of sensitive data you'd like to pass in Intent extras, therefore I can only give these general recommendations:
Make sure you understand the "confidentiality level" of the data in question: is it secret, or just restricted? Or, maybe, it is public (in which case no protection needed)?
Try to find another approach that doesn't involve passing sensitive data in Intent (I myself never encountered such a need).
If you absolutely must pass sensitive data in Intent extras - consider encrypting it. The encryption model should be adequate to "confidentiality level" of the data, and to potential harm which could be done if that data is being intercepted (it can go all the way up to "server side" encryption).
Privacy/security considerations related to BroadcastReceiver
In general, BroadcastReceiver is a component which receives "system wide" broadcasts. The fact that the broadcasts are "system wide" should speak by itself about the level of privacy associated with them.
That being said, there is one mechanism by which developers can restrict broadcasts' scopes: custom permissions. Usage of custom permissions allows for two "levels of control" over broadcasts:
If broadcast requires specific permission then only if BroadcastReceiver has that permission will it receive the broadcast.
If BroadcastReceiver filters the incoming broadcasts by specific permissions, then only broadcasts carrying that permission will be delivered to that receiver.
While the above points can seem similar on the first sight, these are distinct schemes that can be used separately, or combined. The first scheme associates a broadcast with a permission (and the sender of that broadcast doesn't necessarily have that permission by himself), while the second scheme filters all broadcasts by specific permission (and the receiver must have that permission).
A better approach in your case
EDIT: this COULD BE a better approach if "wakefullness" wouldn't be part of the requirements. But it is. Since there is no guarantee that Service started by AlarmManager will get a chance to acquire a wake lock - this approach is not suitable for OP's case.
Please note that broadcasts and custom permissions were designed in order to introduce "decoupling" at application level - this scheme allows for sender application to be completely agnostic of the receiving application, as long as they agree on one custom permission (well, the same scheme is employed for pre-installed public permissions as well, but you wouldn't want to guard your sensitive data with a public permission).
In your case, however, sender and receiver are the same application. In such setting you don't really need all the trouble associated with broadcasts - just construct PendingIntent that starts the required Service inside your app, and you get it all at once:
PendingIntent and the associated Intent start a specific Service in your application (by name), therefore no other application can intercept it (theoretically, remember the above discussion).
Target Service can be non-exported, therefore no other application can access it in any way.
You welcome :)

Can you use pending intents with localbroadcasts?

I am interested in using pending intents with local broadcasts.
To make myself clear, I am using the following for registering receivers and sending broadcast: android.support.v4.content.LocalBroadcastManager.
I have a local broadcast receiver in a service which works. I am trying to send local broadcasts from a custom notification layout which includes click-able items.
The local broadcast receiver - just receives simple action intents.
I was trying something like this to no avail:
Intent backintent = new Intent("GOTO_START_BROADCAST");
PendingIntent backIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, backintent, 0);
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification);
contentView.setOnClickPendingIntent(R.id.imageView1, backIntent);
I am interested in using pending intents with local broadcasts.
That is not possible.
The point behind a PendingIntent is to allow some other process to perform an action you request, such as sending a broadcast.
The point behind LocalBroadcastManager is to keep broadcast within your process.
Hence, a PendingIntent can issue a regular broadcast, but not one via LocalBroadcastManager.

Removing and adding multiple proximity alerts

My use case:
-My app has for example 3 lon/lat locations that i fetch from a server. These may change between fetches.
-I want to add proximityalerts for all three, but when i re-fetch the locations i wish to re-create the proximityalerts, in case the lon/lats have changed.
I have seen the methods in locationmanager and have found some examples, but i cannot see a "removeAll" method anywhere, nor have i found a way to do it. All deal with that i have to create the pendingintent, alert and register the receiver for each location, with a unique ID every time...
I had hoped to basically use the same intent/pendingintent for all my proximityalerts, so that when i later call the remove, i only need to call the method once, and all my proximityalerts would be removed, like the code below.
Any tips? Is this totally impossible?
//CREATE (i would use the same way to create intent for all my proximity alerts, and have one broadcastreciever that receives all for the intent with that ID):
Intent intent = new Intent(PROX_ALERT_INTENT_ID);
PendingIntent proximityIntent = PendingIntent.getBroadcast(activity.getApplicationContext(), 0, intent, 0);
lm.addProximityAlert(
latitude,
longitude,
POINT_RADIUS,
PROX_ALERT_EXPIRATION,
proximityIntent
);
//REMOVE:
Intent intent = new Intent(PROX_ALERT_INTENT_ID);
PendingIntent proximityIntent = PendingIntent.getBroadcast(activity.getApplicationContext(), 0, intent, PendingIntent.FLAG_NO_CREATE);
//i had hoped that this call would remove all alerts at once?
lm.removeProximityAlert(proximityIntent);

How to cancel a PendingIntent

I've got a navigation app. I want to get position information that may not arrive for a while, depending on how long it takes GPS to lock up -- or ever if reception is bad.
I was planning to use LocationManager.requestLocationUpdates() to request location information to be sent to a BroadcastReciever whenever it becomes available, and to also set a timeout via AlarmManager.set().
If the location update arrives, I want to cancel the timeout. If the timeout arrives, I want to cancel the location update. Assuming that my app could be killed before either happens, I'll have lost the PendingIntent for the thing I want to cancel.
Is there a way to save the PendingIntent somehow, so I can use them to cancel the timeout and/or location update later? Or is there a better way to go about this?
You don't need to save the PendingIntent instance itself. The documentation for AlarmManager.cancel(PendingIntent operation) says,
Remove any alarms with a matching Intent. Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled.
If you look at Intent.filterEquals(Intent), it 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.
So you can just create a PendingIntent with the same action and do am.cancel() with that new pending intent, and it will cancel the previous pending intent as well.
Here's a quick code sample:
private static final String ALARM_ACTION = "foo.bar.MY_ALARM_ACTION";
private PendingIntent getAlarmIntent() {
Intent alarmIntent = new Intent(ALARM_ACTION);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); // or whatever flag you need
return pi;
}
And now you can call am.set() with the PendingIntent returned from the above function, and also call am.cancel() with the PendingIntent returned from the same function as well. It doesn't matter whether the PendingIntent is the same instance or not, it just has to match the Intent.filterEquals() test (so basically just the Intent action has to match only).
So basically just use the same action to create the intent to set/cancel the alarm and it will work.

Categories

Resources