Note that there are some explanatory texts on larger screens.

plurals
  1. POBinding to a service from another app
    primarykey
    data
    text
    <p>I wrote two apps (target Gingerbread). Let say app1 and app2. App1 has two services started with "BOOT_COMPLETED" and they are started with the return value START_STICKY. They run in separate threads. To make a long story short. One of the service is watching for incoming data on a serial port (a kind of proxy for app communicating with interfaces on the other end of the serial port). The other has a listener watching some system status and waiting for some "instructions" from other apps. I know they are running well because they are listed in the running services and I added some code that forces them to do some stuff when some specific data come from the serial port. </p> <p>Now the problem: I wrote app2. It tries to bind to one of the service in app1. I used <a href="http://developer.android.com/guide/components/bound-services.html">android-developper</a> documentation and implemented a bidirectional communication between the service in app1 and app2. Since I just have a small amount of very simple data to send, I used a messenger, as suggested. I basically just use the "what, arg1 and arg2" I did not use the AIDL interface as the documentation was suggesting. </p> <p>Here is the section of the androidmanifest declaring the service in app1 I try to bind too.</p> <pre><code> &lt;service android:name=".ModemWatcherService" android:label="@string/app_name" android:exported="true"&gt; &lt;intent-filter&gt; &lt;action android:name="android.intent.action.MAIN" /&gt; &lt;category android:name="android.intent.category.LAUNCHER" /&gt; &lt;!-- Service name --&gt; &lt;action android:name="com.admetric.modemwatcher.Service" /&gt; &lt;/intent-filter&gt; &lt;/service&gt; </code></pre> <p>Then, here are the few method dealing with this issue in app1:</p> <pre><code> @Override public IBinder onBind(Intent intent) { Log.d(TAG, "entering onBind"); return mMessenger.getBinder(); } /** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { String logMessage = "Received meaasge what= %d, arg1= %d, arg2= %d" + String.valueOf(msg.what) + String.valueOf(msg.arg1) + String.valueOf( msg.arg2); Log.d(TAG, logMessage); switch (msg.what) { case MSG_REGISTER_CLIENT: mClients.add(msg.replyTo); break; case MSG_UNREGISTER_CLIENT: mClients.remove(msg.replyTo); break; case ..... more code here for the application default: super.handleMessage(msg); } } } @Override public void onCreate() { mHandler = new Handler(); startSignalLevelListener(); Log.i(TAG, "Just did onCreated"); // Display a notification about us starting. We put an icon in the status bar. // showNotification(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "Received start id " + startId + ": " + intent); // We want this service to continue running until it is explicitly // stopped, so return sticky. return START_STICKY; } </code></pre> <p>For app2, here is the relevant code to establish the binding with the bidirectional communication:</p> <pre><code>public final class ComWithIoMcu extends Service { private static final String TAG = "ComWithIoMcu"; /** Messenger for communicating with service. */ static Messenger mServiceMcu = null; /** Flag indicating whether we have called bind on the service. */ boolean mIsBound; /** * Command to the service to register a client, receiving callbacks * from the service. The Message's replyTo field must be a Messenger of * the client where callbacks should be sent. */ static final int MSG_REGISTER_CLIENT = 1; /** * Command to the service to unregister a client, ot stop receiving callbacks * from the service. The Message's replyTo field must be a Messenger of * the client as previously given with MSG_REGISTER_CLIENT. */ static final int MSG_UNREGISTER_CLIENT = 2; /** * Command to forward a string command to the I/O MCU */ public static final int MSG_SEND_STRING_TO_IOMCU = 3; /** List of supported commands * */ ...... more code .... /** * Handler of incoming messages from service. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_UNSOL_MESSAGE: Log.d(TAG, "Received from service: " + msg.arg1); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); boolean mBound; /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mServiceMcu = new Messenger(service); Log.d(TAG, "Attached."); // We want to monitor the service for as long as we are // connected to it. try { Message msg = Message.obtain(null, MSG_REGISTER_CLIENT); msg.replyTo = mMessenger; mServiceMcu.send(msg); } catch (RemoteException e) { // In this case the service has crashed before we could even // do anything with it; we can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. Log.e(TAG, "ModemWatcherService is not running"); } } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mServiceMcu = null; mBound = false; } }; void doBindService() { // Establish a connection with the service. We use an explicit // class name because there is no reason to be able to let other // applications replace our component. //bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); try { Intent intentForMcuService = new Intent(); Log.d(TAG, "Before init intent.componentName"); intentForMcuService.setComponent(new ComponentName("com.admetric.modemwatcher", "ModemWatcherService")); Log.d(TAG, "Before bindService"); if (bindService(intentForMcuService, mConnection, 0)){ Log.d(TAG, "Binding to Modem Watcher returned true"); } else { Log.d(TAG, "Binding to Modem Watcher returned false"); } } catch (SecurityException e) { Log.e(TAG, "can't bind to ModemWatcherService, check permission in Manifest"); } mIsBound = true; Log.d(TAG, "Binding."); } void doUnbindService() { if (mIsBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. if (mServiceMcu != null) { try { Message msg = Message.obtain(null, MSG_UNREGISTER_CLIENT); msg.replyTo = mMessenger; mServiceMcu.send(msg); } catch (RemoteException e) { // There is nothing special we need to do if the service // has crashed. } } // Detach our existing connection. unbindService(mConnection); mIsBound = false; Log.d(TAG, "Unbinding."); } } </code></pre> <p>Looking at the running services, I can see that the service I created in app2 is running. Logcat shows me that I try to bind the the ModemWatcherService but it is not found. Here is the interesting section of logcat</p> <pre><code>12-05 17:22:59.884 D/ComWithIoMcu( 547): Before init intent.componentName 12-05 17:22:59.884 D/ComWithIoMcu( 547): Before bindService 12-05 17:22:59.888 D/ComWithIoMcu( 547): Binding to Modem Watcher returned false 12-05 17:22:59.888 D/ComWithIoMcu( 547): Binding. 12-05 17:22:59.888 W/ActivityManager( 89): Unable to start service Intent { cmp=com.admetric.modemwatcher/ModemWatcherService }: not found </code></pre> <p>My first thought was that I was missing a permission but bindService() can trow security exceptions and in this case it doesn't so, I checked and it returns false for an unknown reason. Also, I know that in app1, the onBind is never called proving that the binding never occurred. So the logcat message "not found" make sense but I declared that service public in its manifest. It's probably a simple mistake but I've been on this issu for a while now and I did not find why. Any idea why app2 can't find the service in app1 ? I used just cut and paste for names so I would not do stupid mistyping mistakes in names. Am I missing permissions of some sort? Do I need to do some extra step to publish the service for the whole system? That's the first time I try to access something in one app from another app so, I might have missed something. </p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload