I'm creating an app that reads every Service that has an IntentFilter that matches a custom action by using this:
Intent intent = new Intent(ACTION);
List<ResolveInfo> infos = getActivity().getPackageManager()
.queryIntentServices(intent, 0);
I have no problem retrieving the Services, but when I try to bind them to the current Activity, my IBinder implementation is not passed to onServiceConnected(ComponentName, IBinder). Instead, a BinderProxyis being passed.
How can I get my LocalBinder from that BinderProxy?
EDIT:
Here's my implementation:
public abstract class LocalService extends Service {
private LocalBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
public LocalService getServiceInstance() {
return LocalService.this;
}
}
#Override
public final IBinder onBind(Intent intent) {
return mBinder;
}
public abstract List<Category> getInitialCategories();
public abstract void onObjectSelected(Item object,
LocalCallback callback);
}
What I want to do is to be able to call the last 2 methods from other people's implementations of my LocalService.
I don't think you can do that.
You're binding to a service in a different process. It would be impossible to get a reference to your actual LocalBinder instance, because processes cannot share object instances.
I haven't tested this scenario myself, but according to the documentation if you need IPC for Services you have two alternatives, either a Messenger or AIDL. The first one seems much simpler.
If you need your interface to work across different processes, you can
create an interface for the service with a Messenger. In this manner,
the service defines a Handler that responds to different types of
Message objects. This Handler is the basis for a Messenger that can
then share an IBinder with the client, allowing the client to send
commands to the service using Message objects. Additionally, the
client can define a Messenger of its own so the service can send
messages back.
This is the simplest way to perform interprocess communication (IPC),
because the Messenger queues all requests into a single thread so that
you don't have to design your service to be thread-safe.
Related
Lets say I have a String test = "value" available in a service and want to send it to my running mainactivity.
How does one send that data safely?
Im looking for the most simple example possible, where the data can not be seen by other apps.
When I look at intents it says:
A broadcast is a message that any app can receive. The system delivers
various broadcasts for system events, such as when the system boots up
or the device starts charging. You can deliver a broadcast to >>>>other
apps<<< by passing an Intent to sendBroadcast() or
sendOrderedBroadcast().
However I want to send it to my app only as it contains private data.
In my service I tried:
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(getString(R.string.notification_intent), "6");
startActivity(intent);
Where MainActivity.class is the class that should receive the intent, but in this way I get:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
How can I safely send data from a service to an activity?
Q: How do I send data from an Android service to my activity?
A: You have several alternatives:
1. Use intents:
How to get data from service to activity
Send msg from service:
private static void sendMessageToActivity(Location l, String msg) {
Intent intent = new Intent("GPSLocationUpdates");
// You can also include some extra data.
intent.putExtra("Status", msg);
Bundle b = new Bundle();
b.putParcelable("Location", l);
intent.putExtra("Location", b);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
Register to receive message in activity:
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
mMessageReceiver, new IntentFilter("GPSLocationUpdates"));
Custom message receiver in activity:
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
String message = intent.getStringExtra("Status");
Bundle b = intent.getBundleExtra("Location");
lastKnownLoc = (Location) b.getParcelable("Location");
...
I would NOT characterize this as "unsafe" - it can be a perfectly reasonable approach.
2. Have the activity bind to the service
https://developer.android.com/guide/components/bound-services
Service:
public class LocalService extends Service {
// Binder given to clients
private final IBinder binder = new LocalBinder();
...
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
Activity:
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
...
#Override
protected void onStop() {
super.onStop();
unbindService(connection);
mBound = false;
...
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
...
// To use the service, your client would call mService.someMethod()...
I'm not sure exactly what you're looking for, but example 1 is probably your best bet.
Here's a tutorial that might help give you more details/more ideas:
Basics Of Services In Android:
Part 1: Basics
Part 2: Binding services to an activity
Part 3: Using Messenger
https://developer.android.com/guide/components/broadcasts
Android provides three ways for apps to send broadcast:
The sendOrderedBroadcast(Intent, String) method sends broadcasts to one receiver at a time.
sendBroadcast(Intent) method sends broadcasts to all receivers in an undefined order. This is called a Normal Broadcast. This is more
efficient, but means that receivers cannot read results from other
receivers, propagate data received from the broadcast, or abort the
broadcast.
LocalBroadcastManager.sendBroadcast method sends broadcasts to receivers that are in the same app as the sender. If you don't need to
send broadcasts across apps, use local broadcasts. The implementation
is much more efficient (no interprocess communication needed) and you
don't need to worry about any security issues related to other apps
being able to receive or send your broadcasts.
Additionally:
https://developer.android.com/guide/components/broadcasts#restrict-broadcasts-permissions
Restricting broadcasts with permissions
Permissions allow you to restrict broadcasts to the set of apps that hold certain permissions. You can enforce restrictions on either
the sender or receiver of a broadcast.
Sending with permissions
When you call sendBroadcast(Intent, String) or sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int,
String, Bundle), you can specify a permission parameter. Only
receivers who have requested that permission with the tag in their
manifest (and subsequently been granted the permission if it is
dangerous) can receive the broadcast. For example, the following code
sends a broadcast:
Personally, I don't see any "security" issue at all with simply using an intent.
But if you want or need to, you can use the above techniques to further lock down communications.
'Hope that helps!
I am totally confused with bound services. My questions are:
What is the meaning of binding?
What does the Binder class do?
What is meant by "returns an IBinder for interacting with the service"?
What is the IBinder object?
How does the onBind() method work?
These are the a questions on bound services. Please explain in detail. I have already read the documentation, but it is still unclear to me.
Bound service:
A bound service is one that allows application components to bind to it by calling bindService() to create a long-standing connection.
Create a bound service when you want to interact with the service from activities and other components in your application or to expose some of your application's functionality to other applications through interprocess communication (IPC).
To create a bound service, implement the onBind() callback method to return an IBinder that defines the interface for communication with the service. Other application components can then call bindService() to retrieve the interface and begin calling methods on the service. The service lives only to serve the application component that is bound to it, so when there are no components bound to the service, the system destroys it. You do not need to stop a bound service in the same way that you must when the service is started through onStartCommand().
IBinder:
To create a bound service, you must define the interface that specifies how a client can communicate with the service. This interface between the service and a client must be an implementation of IBinder and is what your service must return from the onBind() callback method. After the client receives the IBinder, it can begin interacting with the service through that interface.
onBind():
The system invokes this method by calling bindService() when another component wants to bind with the service (such as to perform RPC). In your implementation of this method, you must provide an interface that clients use to communicate with the service by returning an IBinder. You must always implement this method; however, if you don't want to allow binding, you should return null.
this is an example works as completion to the answer above
inside your service class, initialize the IBinder interface with the object created by our inner class (check step 2)
create an inner class extends Binder that has a getter function, to gain access to the service class
in your service class ovveride onBind function, and use it to return the instance we created in step 1
**The code will clear it for you **
public class MyServiceClass extends Service {
//binder given to client
private final IBinder mBinder = new LocalBinder();
//our inner class
public LocalBinder extends Binder {
public MyServiceClass getService() {
return MyServiceClass.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void doSomeWork(int time){ //we will call this function from outside, which is the whole idea of this **Binding**}
}
Next step is binding itself
in your MainClass or whatever where you want to bind your service,
Defines callbacks for service binding, passed to bindService()
private ServiceConnection serviceConnection = new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyServiceClass.LocalBinder binder =(MyServiceClass.LocalBinder)service;
timerService = binder.getService();
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
//what to do if service diconnect
}
};
the moment of binding
Intent intent = new Intent(this, MyServiceClass.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
to unbind the service
unbindService(serviceConnection);
then you call the public function we created before in the Service class using the help of [timerService = binder.getService();]
timerService.doSomeWork(50);
In my application I am using an IntentService to download a file from a cloud. And showing the progress in NotificationManager. I need to show the status (Downloading/Completed or Failed) in the Activity which stared the IntentService too.
My problem is once I closed the app and open it back, I want to get the status of downloading from IntentService.
Which is the best way to do this?
You can let your Activity bind to your Service, by calling bindService() in your Activity. As per the documentation:
A service is "bound" when an application component binds to it by
calling bindService(). A bound service offers a client-server
interface that allows components to interact with the service, send
requests, get results, and even do so across processes with
interprocess communication (IPC). A bound service runs only as long as
another application component is bound to it. Multiple components can
bind to the service at once, but when all of them unbind, the service
is destroyed.
Also:
You should create a bound service when you want to interact with the
service from activities and other components in your application or to
expose some of your application's functionality to other applications,
through interprocess communication (IPC).
The documentation provides a fully functional example of this. Below is taken from the provided link.
Service class:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
Activity class:
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
In your Service, you can define public methods that your Activity can call, such as polling for your download progress. Please refer to the documentation for explanation in detail.
There are couple of ways to have communication connection between Service and Activity. I would suggest these 2
First, you can use the great library Otto. With Otto, you can also have #Produce annotated method. With this method you will return the latest information about the download. When you #Subscribe in your Activity you will get the latest info immediately. https://github.com/square/otto
If you are using Android built-in DownloadManager it returns the updates and results with a Broadcast, you can register to that Broadcast both in your Service and Activity. This way you will be able to update both of them. I suggest you to use DownloadManager, it is awesome.
http://developer.android.com/reference/android/app/DownloadManager.html
I have a network Service which runs in the background. I have this global variable mConnection inside the Activity
protected ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
serviceobject = ((NetworkService.MyBinder) binder).getService();
}
public void onServiceDisconnected(ComponentName className) {
serviceobject = null;
}
};
and then I bind the Service in the Activity's onCreate(..) using
bindService(new Intent(this, NetworkService.class),
mConnection,Context.BIND_AUTO_CREATE);
The NetworkService class has an inner class MyBinder
public class MyBinder extends Binder {
NetworkService getService() {
return NetworkService.this;
}
}
Now to invoke any Service method from the Activity, i use the serviceobject and I create an AsyncTask for each method invocation.(I know that invoking Service methods from the Activity nullifies the use of having Services.I use this for light methods which doesn't involve much computation)
This helps me to directly deal with the data from the Service using the serviceobject .
I unbind the Service in the Activity's onDestroy()
#Override
protected void onDestroy()
{
unbindService(mConnection);
super.onDestroy();
}
Is this the best way of doing it or am I wrong somewhere?
I think what you wanna do is to run a Remote Service. That's what ServiceConnection and bindService is used to. The idea is that your service runs in the baackground and any activity can "bind" to it and interact through in interface you define in AIDL.
The access to the service is fast so you can call method from your service from the UI thread without the use ofAsyncTask. That's one benefit.
However the implementation is a bit tedious because you must write this AIDL interface.
I recommend you to read Google's tutorial here: http://developer.android.com/guide/developing/tools/aidl.html
And then to google "Remote Service AIDL" with "tutorial" or "example".
Good Luck.
I'm trying to remote control a live wallpaper from a widget. They're in the same APK, but obviously different processes. Calling an "activity" of the live wallpaper is of little use to me since it is a different process. The widget has simple buttons that, when pressed,
So what (I think) I need is IPC and AIDL.
First I created the AIDL on the wallpaper side, which worked fine. It has three methods with no extra parameters. But when I added the clientside to the widget, I got an error telling me that I cannot bind to that remote interface because the widget is already a BroadcastListener. I tried getting button handling in without needing the Widget to be a BroadcastListener, but that seems to be impossible.
Well no problem, right? I just created a service within the widget that binds to the remote interface, because while the widget is a BroadcastListener, the service is not, and everything should be fine.
Or so I thought.
Well, I'm getting the widget's buttons to trigger the widget service. Binding to the remote service yields me the following warning:
Unable to start service Intent (act=com.blabla.IRemoteService): not found.
I am using getApplicationContext() within the service of the widget to bind to the remote stuff. I do have the widget service in the manifest, but I don't have the remote service in there. When I do put it in there, I get a nonspecific InstantiationException.
In the Widget's Service onStart() I am doing this:
getApplicationContext().bindService(new Intent(IWallpaperRemote.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
I also have...
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
mService = IWallpaperRemote.Stub.asInterface(service);
isBound = true;
Log.i("WidgetServiceConnection", "binding to service succeeded");
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
isBound = false;
Log.i("WidgetServiceConnection", "binding to service lost!");
}
};
My question is this: Has anyone ever successfully done a remote call from a widget into another application? Considering I am talking about a live wallpaper here, and the fact that I'm not interested in calling an activity within the widget process but cause function calls within the live wallpaper, what options do I have other than IPC, if any?
And if IPC is the way to go here, what am I doing wrong?
I found the answer to my own question. To make things easier for others, here's the solution:
When doing a remote service, one has to write the AIDL which will be compiled into a sort of stub interface, the implementation of that interface (i.e. the code that is executed when someone calls the remote methods), and a class that extends "Service" which returns the implementation class in the onBind() method. (A normal local service would return null in that method)
Now what I had not understood is that you MUST have a service definition in the manifest - WITH INTENT FILTER!
Let's say your AIDL is called IRemoteService.aidl, then you have a class called RemoteService which looks like this:
public class RemoteService extends Service {
public IBinder onBind(Intent intent) {
Log.i("RemoteService", "onBind() called");
return new RemoteServiceImpl();
}
/**
* The IRemoteInterface is defined through IDL
*/
public class RemoteServiceImpl extends IRemoteService.Stub {
public void remoteDetonateBirthdayCake() throws RemoteException {
//your code here
}
};
}
In your android manifest, you want this:
<service android:name="RemoteService">
<intent-filter>
<action android:name="com.sofurry.favorites.IRemoteService"></action>
</intent-filter>
</service>
Note the service name: It's "RemoteService", not "IRemoteService" or even "RemoteServiceImpl". You need the name of the class that extends "Service", whose onBind method we overrode.
To complete the thing, here's the code on the client side -and yes this code also works from within another service, for example one you started from your widget ;)
IRemoteService mService;
RemoteServiceConnection mConnection = new RemoteServiceConnection();
getApplicationContext().bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE);
...where RemoteServiceConnection can be an inner class like so:
class RemoteServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName className,
IBinder service ) {
mService = IRemoteService.Stub.asInterface(service);
isBound = true;
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
isBound = false;
}
};
And now, you're free to call..
mService.remoteDetonateBirthdayCake();
In summary: Be sure to have a service stanza in the android manifest, set "name" to the class that returns the actual implementation in its onBind() method, and you must also have an intent filter with an action definiton that points to the AIDL interface.
Hint: If you are calling remote services from an app inside a different APK, add a "category" element to the intent filter too, and set it to DEFAULT.