how to retrieve the result code from a PendingIntent starting a service? - android

I have provide two objects of type PendingIntent to be passed to SmsManager.sendDataSms(). These two pendingIntents are used to trigger a service at a later time.
According to documentation:
this PendingIntent is
broadcast when the message is successfully
sent, or failed. The result code will be
Activity.RESULT_OK
The question is how can I retrieve this 'result code' inside my service?
Basically, except for Activity (through onActivityResult() or something), none of the application components has a mean to retrieve this 'result code' passed to different variations of PendingIntent.send().

First of all, at least in my head, a resultCode for service would not make much sense as it i suppose to be a self contained process. Again, that is just my perspective.
Secondly, the documentation of the getResultCode() for BroadcastReceiver says:
Retrieve the current result code, as set by the previous receiver.
which implies a dependency to an earlier receiver that Services does not have.
As I suggest in the comments to the question, I think the way to go would be to register a BroadcastReceiver inside the Service.
A final note to your comment:
However, I (and I'm sure lots of other people) still wanna know if there's a way of retrieving the result code from inside a service or not. If not, whay such a thing hasn't been explicitely mentioned in documentation?
The fact that the documentation does not state that you can get a resultCode in a Service is a good indication that it is not possible. Furthermore, it is fairly uncommon to document explicitly what some code cannot do.

I think you would retrieve the result code in the BroadcastReceiver and put it in a extra int or something in the intent and send it to the service through the startService.

Related

How can I securely transmit data between applications

I want to transmit a small amount of data between a Service and an Activity. These are separate applications. Ideally, I'd just pack my data (and a nonce for confirmation) into an Intent and use that. But per Mark Murphy's blog post Warning: Activity Intent Extras Can Be Public I know this is not safe.
Is ResultReceiver safe?
Is there a better way to do what I want?
Here is my use case:
Service A is going to launch Activity B with an Intent and ask the question "Is operation X authorized?". User action may be required, so that's why I'm launching an Activity from a Service.
I want Activity B to answer simply "yes" or "no". I want to make sure a malicious app on the device can't spoof that response. My current code uses an Intent to send the response.
I'm taking a defense-in-depth approach, and part of that is that I don't want the request from A to B to be readable by anybody else. Among other things, I intend to pack a nonce into the original request
Answering #mwhs' questions:
Service is only asking a yes/no question. I want to be very sure that the response comes from who I'm expecting it from. A nonce should do the trick, but it would need to be transmitted securely.
I'm not a security expert other than a little dabbling. What do you mean by "receipt of that very nonce on the initial sender side must carry a proof, that the intended party actually replied with that nonce, ideally by binding the nonce (or a decrement of it) cryptographically to a proof of identity (which can be usage of a shared good key for instance)"?
Assuming I can get the nonce securely to the other party, doesn't the fact that the other party responds with it constitute proof that they're the intended party? How do I "bind" the nonce to proof of identity? Some sort of HMAC? And why decrement it; is that some sort of hash-buster?
I'm a little leery of embedding shared secrets into an app that could theoretically be reverse-engineered.
Service A is going to launch Activity B with an Intent and ask the question "Is operation X authorized?". User action may be required, so that's why I'm launching an Activity from a Service.
For ordinary apps on ordinary devices, this is not a recommended approach, unless you are damn sure that the user won't regret you taking over the foreground with your activity.
I want Activity B to answer simply "yes" or "no". I want to make sure a malicious app on the device can't spoof that response.
The blog post that you cite refers to a bug that used to exist in Android, for extras packaged on an Intent used for startActivity(). Your scenario, as described, does not involve this, as you did not mention any extras on the Intent passed from Service A to Activity B. Plus, that bug has since been fixed, though I forget exactly where in the 4.x series it was fixed.
My current code uses an Intent to send the response.
That's not a particularly informative statement. That's akin to saying that you use a string to send the response. A string or an Intent may be the format of the response, but somehow you need to use inter-process communication (IPC) to get that response back to Service A.
One non-option is startActivityForResult(), as that is only available to activities, not services.
Possible IPC options include:
Calling startService() on the service, passing the Intent, using a custom signature permission to ensure that only your apps can call startService() on that service.
Calling bindService() on the service, waiting to get the binder, then passing the Intent (or whatever) to some method that you implement on the Binder (and using the same custom signature permission as above).
Passing a PendingIntent from Service A to Activity B as an extra, and having Activity B fill and execute that PendingIntent. You would use a signature permission or validating the signing key of Activity B's app to ensure that Service A only sends this to the correct app. And, you'd need to research the exact point when the problem that I outlined in the blog post got fixed.
Passing a ResultReceiver from Service A to Activity B (which otherwise works much as would the PendingIntent scenario above)
Having Service A register a BroadcastReceiver via registerReceiver(), then have Activity B send a broadcast matching the receiver's IntentFilter. You would need to go the custom signature permission route.
Doing something outside the Android component system (e.g., using an HTTP daemon in the service that the activity talks to). Ick.
Note that custom permissions suck, at least before Android 5.0.
Other than the now-fixed recent-activities Intent leak, I am not aware of any current means for spying on IPC, except:
rooted devices
custom ROMs
impersonation (e.g., Service A talks to Activity M from a malware author that is masquerading as Activity B)
The latter scenario is where signature permissions or validating the signing key of Activity B's app come into play.
Anything that is exported (scenarios #1, #2, #5) is at risk of spoofing. signature permissions help, except that custom permissions suck. I would lean towards #3 or #4.
Depending on what other communications are going on between these apps, there may be other options (e.g., long-term bound service, with callback objects to provide bi-directional communication).

how to call certain method of activity via appwidget

i'm trying to call a method in my main activity via a button of a widget.
the widget has different buttons with different values. i want the method to do its job without showing up the gui attached to the main activity. (send http request with button value)
so far i searched for some tutorials but i didn't quite understand them, my code got all clumsy and it barely worked. i think a mix of intents, services and broadcasts are needed to realize this?! i really don't know, can somebody post a understandable description or tutorial which covers all aspects of how to do this?
i think there is no code or pictures to provide because there is nothing look at right now.
thanks in advance for any answer.
Android AppWidgets don't need to have any relation at all to an Activity, although it is certainly possible to have a click or something start an activity.
As you probably know, the primary interface to AppWidgets is through your AppWidgetProvider (if you don't know what any of this is about, take a look at the official android guide).
You can tell a button to use a PendingIntent to do one of several things when clicked:
To start a service (probably your best option for time consuming tasks like http requests), using PendingIntent.getService.
To start an activity, use PendingIntent.getActivity.
To broadcast a message to a BroadcastReceiver, create it using PendingIntent.getBroadcast.
In all of these cases, you would tell the button to run the PendingIntent with the
setOnClickPendingIntent(int id, PendingIntent operation) method of RemoteViews.
The Intent you pass to any of these methods should typically be created using the Intent(Context, Class) constructor, where the second argument is the service, activity or receiver class you want the pending intent to be sent to.

How Can I call startIntentSenderForResult in Service properly?

I'm working on a Service which gets a location and I'm using Google Play Services for it.
According to http://developer.android.com/reference/com/google/android/gms/common/ConnectionResult.html#hasResolution%28%29, if hasResolution() returns true, calling startResolutionForResult may resolve an error. But it needs an Activity as first parameter.
Of course ConnectionResult returns a PendingIntent by getResolution() but the Service doesn't have a startIntentSenderForResult() like an Activity.
As far as I know, there is no way to get a result back in a Service.
How can I get a result in the Service? Or is there another proper way?
Edit:
Google Play Service SDK provides GooglePlayServicesUtil.showErrorNotification for background tasks. Of course accepted answer is good solution.
Edit 2:
showErrorNotification is only for a return value of isGooglePlayServicesAvailable.
I would proceed this way:
Show a notification indicating that there is a problem doing task X
(actually, interacting with Google Play Services, but you may say something else
more specific to your app).
For this notification, provide a PendingIntent that starts your activity. As part of the extras for this PendingIntent, pass the PendingIntent provided by ConnectionResult.getResolution(). PendingIntents are parcelable so this shouldn't pose a problem.
In onCreate() for this Activity, obtain the original PendingIntent from the extras, then call startIntentSenderForResult() with it. This will automatically redirect the user to wherever Google Play Services needed him to go (possibly log-in?)
Then, in onActivityResult(), finish the activity, having first notified your service (via an Intent) that the problem is resolved (or not). The transient activity will have been invisible to the user.
I admit this solution is theoretical, but it should work.

How to cancel alarms?

I have the following situation.
I need to set many alarms to start the same Activity at different specific dates.
In order to accomplish this, I obviously use an AlarmManager.
Since the PendingIntents given to the AlarmManager are all equivalent, in order to have Android create all of them (and not just 1) I use a different requestCode to differentate among them.
All of this works fine, all alarms are created correctly.
The point is that sometimes I have to cancel them!
If I do not do it with the same requestCode the AlarmManager does not cancel them.
It is very difficult to retrieve the original requestCode in the code, since they are created at runtime at different moments/days...
Any suggestion on how to address this situation?
You have the right approach, as the only way I know to cancel the alarms would be cancel the pending intents with the same requestCode, however your issue is retrieving the same request codes made at run time. You would have to store these codes some how either by SQLite, or shared preferences perhaps to have them stored on the device then retrieve them as needed. Alternatively you could pass the requestCode as a bundle in the intent and then cancel it immediately after it fires off, or when ever through the alarms life cycle you choose. Hope this helped a bit.

Best practice for Broadcasts, ContentProviders, and ContentRecievers

Okay so up front I will say that I am new to Android. I have made a few small projects and played with a few things and done tons and tons of reading. So here is the task I am trying to accomplish:
I have a remote service that runs in the foreground of my phone that can send and receive information with other android devices over a wifi network. APK's on the phone that are built to use this service contain multiple SQLite DB's. I want these APK's to register there content providers with the service so the service always know where to put data that it recieves (I have accomplished this task). When data comes in I will identify where it belongs and place it in the right DB (no problem here). So what would be the best way to notify the correct activity that new data has been received. Do I register Broadcast Receivers for my service to call? Will Broadcast Receivers work if the APK has been killed by the OS? Once the correct activity is loaded would I use a content observer to show the new data? Why would I use IntentServices?
I'm sorry for so many questions. Like I said I am learning fast but Android's SKD is massive and there are so many cool things. I want to make sure I am thinking things through right with best practices and making this very easily maintainable and expandable. Thanks for any help or advice I can get.
If you are asking what the best way is for a current Activity to know data has changed in the ContentProvider it is reading from, than the answer is a ContentObserver.
Lets say, for examples sake, you have a set of Activities and ContentProviders. Activity A is a list view of items from Content Provider A, and that list view is populated via a CursorAdapter of some kind. Whenever you have a query on a ContentProviders CONTENT_URI, the cursor should automatically monitor the CONTENT_URI for changes via a ContentObserver.
The reason this works is because in the ContentProvider:
Your query method calls setNotificationUri on your cursor to the URI that was queried.
Whenever a change happens in an insert/update/delete method, you are calling notifyChange on the URI.
If there is additional data that can change in your service that is not tied to a ContentProvider, than you have a few choices on how to pass data. If the Activity is in the same APK as your service, than you could use some sort of static variable, or the application context, or some other custom form of communication. The methods are rather liberal here because you are running in the same VM, so you have a lot of things available to you.
In your situation, it sounds like most activities will be in separate APKs. If that is the case, you will probably want to send out broadcast Intents to let the entire system knwo about the changes. If these changes only matter when an activity is running, you can register a BroadcastReceiver for the life of the Activity that cares about the change. Alternatively, you could put a BroadcastReceiver in the applications manifest, and always receive that change.
That brings us to IntentServices. IntentServices are the "easiest" way to handle long running tasks in Android. All they do, is create a background thread that will handle intents being sent into the service. A common use case is as follows:
You receive a broadcast intent that you need to react to (and it will take a while)
Send a directed intent (intent with a set component name) to an IntentService with some info from the broadcast intent.
IntentService either spawns a new BG thread, or adds the intent to the processing queue on the existing BG thread
The onHandleIntent function is called in the IntentService so that you can now process that change using as much processing time as you want.

Categories

Resources