I have a Service which resides inside a lib project like this
public abstract class MyService extends Service{
//service body here
}
I have my aidl file set up to communicate with the remote service which also included in the lib project copying the aidl file here
package mypackage;
// Declare the communication interface which holds all of our exposed functions.
interface IMyService {
//interface body here
}
Inside the lib manifest I have declared the service like this
<service
android:name="mypackage.core.MyService"
android:enabled="true"
android:exported="true"
android:process=":remote" >
<intent-filter>
<action android:name="mypackage.IMyService" />
</intent-filter>
</service>
I have included this lib in my app but when I try to bind the service from the app it is not getting instantiated. Can any one suggest me what I am doing wrong and if so guid me a way out. The service in is starting inside another class which belongs to the lib like this
try{
Intent i = new Intent(MyService.class.getName());
i.setPackage("mypackage");
// start the service explicitly.
// otherwise it will only run while the IPC connection is up.
mAppContext.startService(i);
boolean ret = mAppContext.bindService(i,
mConnection, Service.BIND_AUTO_CREATE);
if(!ret){
MyLogger.log("Error");
}
}catch(Exception e){
MyLogger.log("err");
}
The service bound API always returns false what would be the issue. Is this the prominent way of creating a RemoteService ? Do I need to add this service in the app manifest if so how ?
what would be the issue
First, your Intent does not match your <service>.
Intent i = new Intent(MyService.class.getName());
You are passing in an action String that looks something like mypackage.core.MyService. However, that is not the <action> for your Service:
<action android:name="mypackage.IMyService" />
Hence, your Intent is not matching anything, and you cannot bind.
Second, your Service is very insecure. Any app that wants to can bind to it. If you want other apps to bind to it, that's fine, but please secure it with some permission, so the user gets a vote for what apps can bind to it.
Third, you are binding using an implicit Intent, one that uses stuff like an action string. That won't work on Android 5.0+, as you can no longer bind to a service using an implicit Intent. Using an implicit Intent to discover a service is fine, but you then need to convert the Intent to an explicit one, that incorporates the component name. Here is how I do this in this sample app:
#Override
public void onAttach(Activity host) {
super.onAttach(host);
appContext=(Application)host.getApplicationContext();
Intent implicit=new Intent(IDownload.class.getName());
List<ResolveInfo> matches=host.getPackageManager()
.queryIntentServices(implicit, 0);
if (matches.size()==0) {
Toast.makeText(host, "Cannot find a matching service!",
Toast.LENGTH_LONG).show();
}
else if (matches.size()>1) {
Toast.makeText(host, "Found multiple matching services!",
Toast.LENGTH_LONG).show();
}
else {
Intent explicit=new Intent(implicit);
ServiceInfo svcInfo=matches.get(0).serviceInfo;
ComponentName cn=new ComponentName(svcInfo.applicationInfo.packageName,
svcInfo.name);
explicit.setComponent(cn);
appContext.bindService(explicit, this, Context.BIND_AUTO_CREATE);
}
}
Is this the prominent way of creating a RemoteService ?
Few people create remote services. They are difficult to secure, difficult to deal with version changes in protocol, etc. And, in your case, I do not know why you think you need a remote service, since you are clearly binding to the service from your own app.
Related
I have a background service running and I would like to use AIDL to be able to communicate with 3rd party apps .
I experimented with an app communication using AIDL and it's working great .
my question is what if I want to communicate with another app,
how do I filter which app is binding at the moment?
I have tried filtering with:
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d("INTENT",intent.getDataString())
return (new IpPortBinder(getApplicationContext()));
}
but it crashes, because intent.getDataString() is null for some reason. so which method should I use here?
You can filter the intents in the androidmanifest file of your service project.
steps as below:
1) define the filter in service's manifest file.
<service android:name="com.x.y.servicename">
<intent-filter >
<action android:name="getdata" />
</intent-filter>
</service>
2)
while calling binding the service call the bindservice from the client app with this intent
Intent i=new Intent();
i.setAction("getdata");
ret=actContext.bindService(i, AddServiceConnection, Service.BIND_AUTO_CREATE);
the step 2 can be called from instance definition of ServiceConnection in the activity code.
I am trying to bind a service from a library project that is added to another library project that is added to an application project.
So library A is referenced by library B and library B is added to my app. The app starts Service A. Another app starts Service B. Service B binds to service A but fails
ActivityManager(593): Unable to start service Intent { act=com.xx.yy.zz/.Service } U=0: not found
-I can find the jar containing the service in the application (in android dependencies).
-I declared the app in the manifest (application).
<service android:name="com.xx.yy.zz.Service"
android:exported="true"
android:enabled="true">
</service>
-The service declaration is within the application tag.
-The service extends Service
-Both services produce Logs so they are both started
This is how I try to bind the service
private void bindToService() {
Toast.makeText(getApplicationContext(), "Binding service", Toast.LENGTH_SHORT).show();
Intent intent = new Intent();
intent.setClassName("com.xx.yy.zz", "com.xx.yy.zz.Service");
mBound = getApplicationContext().bindService(intent, this, BIND_AUTO_CREATE);
Log.d(TAG, "bindService returned " + mBound);
}
mBound always returns false.
The services have been binded before (in a testapp which uses allot of the same code).
the services bind using AIDL (nothing changed to that code).
I cant paste more code so I hope this is enough to get me on the way.
Thanks in advance.
I fixed it, it turns out i had to provide the application package name and the library package name.
So instead of:
intent.setClassName("com.library.package.name", "com.library.package.name.Service");
I had to do:
intent.setClassName("com.application.package.name", "com.library.package.name.Service");
I hope this helps someone else! I wasted allot of time on this.
I work in a company that produces several apps, not all those apps have the same signature or more like it we have at least 5-6 apps certificates for the time being.
We tried to create a mechanism in which all the companie's apps on the same device share the same is,
For example if user installed from the market App A and no app installed, a new ID will be generated, if now he installs App A, app B should have the same id as App A(id is just a generated UUID type #4) etc...
We are using broadcast at the moment and only apps with our permission can receive that broadcast and send back the id with another broadcast(explicit this time).
The broadcast and the responses are protected with our permission with signature level, this is of course not helping since we have more than one signature.
I tried to write an intent broadcast and recover that can have it's own mechanism of protection that will not be limited to only one signature but several, the problem is that things like Binder.getSenderUID() doesn't work for broadcasts and i get my own uid.
it looks like i have no way to get the identity of my snder unless he itself writes his id in the intent, which is NOT something i can trust as it can be easily faked.
Using encryption requires the apps to come with a key on them, which is not secured once more, turning to a server for validation takes too much time and on mobile not guaranteed to success since not 100% sure there is network around.
Anyone has any idea how can one get a validate\secure message from one app to another ?(all my apps but may have different signatures).
As always with a challenging question here i never get a proper, if ANY!' answer, so i'm forced to find it myself.
The problem with Intents is that it's not possible to get the sender as they are the parallel to mulicast in a network where there sender's address is not important.
If i wish to get the snder's UID i need to make a "remote" process even if he is local, instead of using broadcast IPC i need to use AIDL with IBInder implementation.
Once i have a Binder object i can call in my service getCallingUid() and get the uid of the caller, this will allow me to ask PackageManager to give me it's public certificate (without asking the process itself, i ask the OS) and compare it to a set of certificates i prepared in advance in the apk.
The calling application on the other side(the other process that sends me it's ID) just has to use the bindService(service, conn, flags) method to bind to me.
The disadvantage to this approach is ofcourse the time consuming process, Bind takes time, it's an async call that pass through the kernel and is not as fast as binding to a local service. Moreover since i might have several applications i need to synchronize the access my internal ID so only the first binding call that didn't fail will set and ID for me.
I still need to check if i can use the Messanger method that prevents the multi thread issues.
Hopes this helps someone else.
Sorry for late response...
Bind takes time, and more importantly, its asynchronous.
However, there is a way to make a synchronous bind - assuming of course the service you attempt to contact is already started at the time.
Android allowed for this more for BroadcastReceivers (which are async in nature, and thus can't use normal bindService) a BroadcastReceiver has a "peekService" method.
If you want to use it without listening to a broadcast, you can by doing:
final IBinder[] b = new IBinder[1];
new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
b[0] = peekService(context, intent);
}
}.onReceiver(context, intent);
IMyInterface i = IMyInterface.Stub.asInterface(b[0);
note that you don't bind to the service, so make sure to peek at on each use.
As already stated, binding is probably the best solution to this. However, you could consider switching to an Activity rather than a BroadcastReceiver then you can use getCallingActivity(), assuming you launched with startActivityForResult().
Declare you Activity as follows to make it "silent" like a BroadcastReceiver:
<activity
android:name=".FauxReceiver"
android:theme="#android:style/Theme.NoDisplay"
android:excludeFromRecents="true"
android:noHistory="true"
>
<intent-filter>
...
</intent-filter>
</activity>
Inspiration: How to get the sender of an Intent?
I was looking for a way to verify the package name of the application that sent the intent received by my intent-filter. That activity in my app that handles the intent-filter requires that the intent sender includes their process id in an Intent Extras field. My receiving activity can then get the associated application package name from the ActivityManager.
Here is some example code I found while shifting through StackOverflow.
Constants needed for both Apps
public static final String EXTRA_APP_ID;
public static final String ACTION_VERIFY = "com.example.receivingapp.action.VERIFY";
Calling Activity
Intent verifyIntent = new Intent();
verifyIntent.setAction(Consts.ACTION_VERIFY);
verifyIntent.putExtra(EXTRA_APP_ID, android.os.Process.myPid());
// Verify that the intent will resolve to an activity
if (verifyIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(verifyIntent, Consts.REQUEST_VERIFY);
} else {
Log.d(TAG, "Application not found.");
}
Receiving App
Manifest
<activity
android:name="com.example.receivingapp.ReceivingActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="com.example.receivingapp.VERIFY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
ReceivingActivity
if (getIntent().hasExtra(OnyxCoreConsts.EXTRA_APP_ID)) {
string appName = null;
// Resolve intent
if (getIntent().getAction().equals(ACTION_VERIFY) {
int appPid = getIntent().getIntExtra(EXTRA_APP_ID, -1);
if (-1 != mAppPid) {
appName = Utils.getAppNameByPID(mContext, mAppPid);
}
if (null != appName && !"".equalsIgnoreCase(appName)) {
// Do something with the application package name
}
}
}
Utils class
public static String getAppNameByPID(Context context, int pid){
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == pid) {
return processInfo.processName;
}
}
return "";
}
I've got two separate applications that use a common background service (which is in a common library which both applications include) to collect Bluetooth data. Everything works fine on first installation of the applications.
The problem is that when one of the applications is re-deployed after having the common service changed, it still uses the service previously installed.
Here's some info and an example to clarify things:
The 2 applications are named BioSound, and BioZen. Each of them includes a common library called AndroidSpineServerLib, which in terns includes the common library AndroidBTService (which contains the background service)
For example, on first installation AndroidBTService has a version number of, say 1.0. When the applications are deployed everything is fine, Both BioZen and BioSound uses the V1.0 service.
Then I make a change to BioSound, and AndroidBTService, incrementing it's version to V1.1. When I deploy BioSound after this I would expect it to use the newly changed service V1.1, but it continues to use the V1.0 service. The only way to fix this is to remove the BioZen , then BioSound the correct service ( I don't even have to reinstall BioSound).
Programatically, when I start each application I bind to the service, and when each application exits I unbind the service.
Obviously I'm missing something but can't figure it out. Any ideas?
Your problem may be that you are using an implicit intent to communicate with your service that doesn't specific an application, only an action. That intent (which you're using in both applications) ends up getting resolved to one particular application's service. Instead, make sure to specify the explicit intent with the particular component or class. See the Intent docs for details.
I'm sure this is it but I'm having difficulty with the details.
Here's the existing code:
Mainfest for the main Application:
<application android:icon="#drawable/biosound_icon" android:label="#string/app_name" android:debuggable="true">
<service
android:name="com.t2.biofeedback.BioFeedbackService">
<intent-filter>
<action android:name="com.t2.biofeedback.IBioFeedbackService"/>
</intent-filter>
</service>
</application>
The call to bind the service was in the library AndroidSpineServerLib
Intent intent2 = new Intent("com.t2.biofeedback.IBioFeedbackService");
bindService(intent2, mConnection, Context.BIND_AUTO_CREATE);
So I can see how the service was bound to a specific application
To fix this I tried:
Intent intent2 = new Intent(this, BioFeedbackService.class);
bindService(intent2, mConnection, Context.BIND_AUTO_CREATE);
The problem is that the service was not created.
I also tried the following
In the manifest of the service:
<service
android:class=".service.BioFeedbackService">
<intent-filter>
<action android:name="com.t2.biofeedback.IBioFeedbackService"/>
</intent-filter>
</service>
And in the library where I bind the service:
Intent intent2 = new Intent();
intent2.setAction("com.t2.biofeedback.IBioFeedbackService");
bindService(intent2, mConnection, Context.BIND_AUTO_CREATE);
Still no luck.
Maybe I'm putting the service definition in the wrong manifest.
Remember I have the following hierarchy
Main Application references:
AndroidBTService (Library), which references
AndroidSpineServerLib (Library)
So I have 3 manifests and not sure which to update.
Good Morning.
I have a question about how to create an application without GUI. It should start when the user pushes the icon. Reading other posts, seems that the natural way of doing this would be a Service.
Since the app has no GUI, it makes no sense to add any Activity. For this reason, the Service has to be unbinded. So, if there is no component calling startService, and no external component is sending an intent, ¿how does the service start?
Is there any attribute in the manifest to achieve this? Or maybe extending Application and using onCreate to start the service?
Thanks.
UPDATES:
-There's no way to start a Service in the same app without an Intent. Other options would be autostart or Broadcast receivers, but these don't fit my requirements.
-Tried a test app without Activities, and the icon isn't even showing in the launcher. Don't know the reason of this, maybe related to the manifest not having a LAUNCHER activity.
The list of applications shown in the Android launcher is basically the list of all activities in the system that have a LAUNCHER intent filter:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
If you put this intent filter on a <service>, it will not work (just tried). Thus, the only way to do what you want to do is through an Activity. I think the cleanest way is something like this:
public void onCreate(Bundle savedInstanceState) {
Intent service = new Intent(this, MyService.class);
startService(service);
Toast.makeText(this, "Service started.", Toast.LENGTH_SHORT).show();
finish();
}
The user will not see anything except a small message at the bottom of the screen saying "Service started." that will automatically disappear in a couple of seconds. It's clean and user-friendly.
The service is started either when somebody calls startService() or when somebody calls bindService(). Note that if service is only started via bindService() it will be automatically stopped when Activity either explicitly unbinds from it or is destroyed (and it was the only binder).
You can declare BOOT_COMPLETED_ACTION broadcast receiver in your AndroidManifest.xml and start your service on system boot. But you service will only start on next device reboot. And there are some issues with applications without activities and this broadcast event in Android 3.1. More info can be found here.
In general, its good to have at least one activity in your application, even if your primary component is service. This activity will start the service when user launches it, and also may expose some ability to configure the service behavior.
Example of activity that starts service:
public class ServiceStarterActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
startService(new Intent(this, ServiceA.class));
finish();
}
}