My company produces an SDK delivered as an Android Library aar file. As part of that SDK, we define a service:
<service
android:name=".session.internal.ChatSession"
android:description="@string/description"
android:enabled="true"
android:exported="false"
android:label="Network communication service"
/>
This service is then started and bound by more code in the SDK:
public boolean bindService(final Runnable whenBound) {
if (connection == null) {
// Make sure the service is running
boolean success = startService();
if(BuildConfig.DEBUG && !success) {
throw new AssertionError("startService failed");
}
connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
chatSession = (IChatSession) service;
if(whenBound != null) {
whenBound.run();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
chatSession = null;
connection = null;
}
};
success = context.bindService(new Intent(context, ChatSession.class), connection, Context.BIND_IMPORTANT);
if(BuildConfig.DEBUG && !success) {
throw new AssertionError("bindService failed");
}
return success;
}
if(whenBound != null) {
whenBound.run();
}
return true;
}
boolean startService() {
boolean success = true;
if(!isServiceRunning()) {
success = context.startService(new Intent(context, ChatSession.class)) != null;
}
return success;
}
This all works fine as long as there is only one application using the SDK installed on the mobile device.
Since the service is both explicitly not exported (android:exported="false"
) and implicitly not exported (there is no <intent-filter>
defined) we expected this to work fine with multiple applications installed as well, with each application getting it's own, private, instance of the service when bindService
is called.
What actually happens is that neither application works any more as neither ServiceConnection.onServiceConnected
or ServiceConnected.onServiceDisconnected
is ever called, although the calls to context.startService
and context.bindService
both return success.
Once both applications have been installed, the only way to get either of them to work is to uninstall both, and then reinstall only one. It's not sufficient to uninstall either independently.
It turns out, the problem was actually this bit of code that was preventing the service from being started in the first place:
if(!isServiceRunning()) {
success = context.startService(new Intent(context, ChatSession.class)) != null;
}
I had cribbed code for isServiceRunning
that was incorrectly determining that the service was running based only on the name:
public boolean isServiceRunning() {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> services = manager.getRunningServices(Integer.MAX_VALUE);
if(services == null) {
return false;
}
for (ActivityManager.RunningServiceInfo service : services) {
if (ChatSession.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Replacing the if with:
if (Process.myPid() == service.pid && ChatSession.class.getName().equals(service.service.getClassName())) {
seems to resolve the issue.
I'm still not thrilled with this as it seems like there should be a better way of checking.