Greetings!

If any of the info on this website was useful for your projects or made your head spin with creative ideas, and you would like to share a token of your appreciation- a donation would be massively appreciated!

Your donation will go to my Robotics Fund, straight towards more sensors, actuators, books, and journeys. It takes a lot to continue building these robots, and I’m really thankful of everyone who helps encourage me along the way.


USD


CAD

“A wise robot once said to me through Serial.println- that robots teach us about ourselves.”

Android ADK Background Service

I was playing around with the ADK and wondering how it would be possible to make the connection work while the app was in the background. The first way I tried was to not close the connection in onPause(). This worked, since the file input and output streams were able to continue … until they were garbage collected. When other Android apps run in the background, they use a Service. I tried this out with the ADK, and it works! Check out the video demonstration below.



Watch video on YouTube

The initialization and opening of the ADK is still done in the main activity. We use the Application class as a friendly singleton to transfer the streams, file descriptor, and usb accessory over to the Service.

  1. private void enableControls(boolean b) {
  2.         ((ServiceADKApplication) getApplication()).setInputStream(mInputStream);
  3.         ((ServiceADKApplication) getApplication()).setOutputStream(mOutputStream);
  4.         ((ServiceADKApplication) getApplication()).setFileDescriptor(mFileDescriptor);
  5.         ((ServiceADKApplication) getApplication()).setUsbAccessory(mAccessory);
  6.         // … snip …
  7. }

Creating the Service is rather straight forward. In ServiceADKActivity:

onCreate()

  1. startService(new Intent(this, ADKService.class));

onPause()

  1. try {
  2.         ADKService.self.startUpdater();
  3. } catch(Exception e) {
  4.         Log.d(TAG, "Starting the updater failed");
  5. }

onResume()

  1. try {
  2.         ADKService.self.stopUpdater();
  3. } catch(Exception e) {
  4.         Log.d(TAG, "Stopping the updater failed");
  5. }

When the Service is started, it creates an Updater thread, which runs every second.

  1. if (!updater.isRunning()) {
  2.         Log.d(TAG, "updater not running");
  3.         updater = new Updater();
  4.         updater.start();
  5. } else {
  6.         Log.d(TAG, "updater running");
  7. }

In background, this can run for a very long time. I let it be for 12 hours before I stopped it.

However… there is a very bizarre bug. Sometimes when the app returns to foreground, it can’t reconnect to the ADK. The main problem is with the USB Manager trying to open the accessory for some reason. The app has permissions to the accessory, and it prints out the connected accessory correctly. Here is my trace of the bug:

  1. @Override
  2.         public void onResume() {
  3.        
  4.         Log.v(TAG, "onResume");
  5.  
  6. // —- onResume is called —-
  7.        
  8.                 super.onResume();
  9.                
  10.                 try {
  11.                         ADKService.self.stopUpdater();
  12.                 } catch(Exception e) {
  13.                         Log.d(TAG, "Stopping the updater failed");
  14.                 }
  15.                
  16. // —- updater is indeed stopped —-
  17.  
  18.                 Intent intent = getIntent();
  19.                
  20.                 if (mInputStream != null && mOutputStream != null) {
  21.                         Log.v(TAG, "input and output stream weren’t null!");
  22.                         enableControls(true);
  23.                         return;
  24.                 }
  25.  
  26. // —- the file i&o streams are null —-
  27.                
  28.                 UsbAccessory[] accessories = mUsbManager.getAccessoryList();
  29.                
  30.                 Log.v(TAG, "all the accessories: " + accessories);
  31.  
  32. // —- shows the connected accessory —-
  33.                
  34.                 UsbAccessory accessory = (accessories == null ? null : accessories[0]);
  35.                 if (accessory != null) {
  36.                         if (mUsbManager.hasPermission(accessory)) {
  37.  
  38. // —- there is permission, going to open the accessory —-
  39.  
  40.                                 Log.v(TAG, "mUsbManager does have permission");
  41.                                 openAccessory(accessory);
  42.                         } else {
  43.                                 Log.v(TAG, "mUsbManager did not have permission");
  44.                                 synchronized (mUsbReceiver) {
  45.                                         if (!mPermissionRequestPending) {
  46.                                                 mUsbManager.requestPermission(accessory,
  47.                                                                 mPermissionIntent);
  48.                                                 mPermissionRequestPending = true;
  49.                                         }
  50.                                 }
  51.                         }
  52.                 } else {
  53.                         Log.d(TAG, "mAccessory is null");
  54.                 }
  55.                
  56.                
  57.         }
  58.  
  59.  
  60. private void openAccessory(UsbAccessory accessory) {
  61.                
  62.                 Log.e(TAG, "openAccessory: " + accessory);
  63.                
  64.                 Log.d(TAG, "this is mUsbManager: " + mUsbManager);
  65.                
  66. // —- prints out the address of usb manager fine —-
  67.  
  68.                 mFileDescriptor = mUsbManager.openAccessory(accessory);
  69.                
  70. // —- Error in log from UsbService: E/UsbService( 110): could not open /dev/usb_accessory
  71.  
  72.                 Log.d(TAG, "Tried to open");
  73.                
  74.                 if (mFileDescriptor != null) {
  75.                         mAccessory = accessory;
  76.                         FileDescriptor fd = mFileDescriptor.getFileDescriptor();
  77.                         mInputStream = new FileInputStream(fd);
  78.                         mOutputStream = new FileOutputStream(fd);
  79.                         mThread = new Thread(null, this, "DemoKit"); // meep
  80.                         mThread.start(); // meep
  81.                         Log.d(TAG, "accessory opened");
  82.                         enableControls(true);
  83.                 } else {
  84.                         Log.d(TAG, "accessory open fail");
  85.                         enableControls(false);
  86.                 }
  87. }

Does anyone know why this may be? I have tried to work around it numerous times but to no luck yet.

You can grab the code off of my GitHub to play around and test it.

It would be great if we could fix this bug!

4 CommentsLeave a Comment


  • Valim

    3 years ago

    can you share the Streaming.h arduino library?

  • Erin, the RobotGrrl

    3 years ago

  • Phillip Givens

    2 years ago

    Hello Robotgrrl, thanks for this excellent post. I am doing something similar and ran into the same problem that you were with the /dev/usb_accessory. I found this answer on SO and it helped me figure out that I was not calling closeAccessory everywhere that I should have been. I think I was modifying the same code from the book ArduinoADK and I think you commented out the onPause call to closeAccessory so that the file descriptor would hang around after the activity went away.

    http://stackoverflow.com/questions/8275730/proper-way-to-close-a-usb-accessory-connection

    I hope that this helps.

  • Mohammad

    1 year ago

    Hey thanks for your great post, I’ve been looking for a solution for this issue for a while and finally I got it here. Great job.

    Could you share your complete project (service and activity code) with me? I appreciate it.

    -Mohammad

Leave a CommentPlease be polite. We appreciate that.

Your Comment