Wednesday, March 23, 2011

More laziness with the XBMC Remote Android App

The XBMC android app is great. I mean really really great. It might just be the best android app on my shiny new Atrix. There is/was one problem though. While the wake on LAN properly wakes my box up, the app has no suspend feature.

Hmm... eerily similar to the situation I ran into a few weeks ago. Luckily the app is GPL and I can have a go at fixing things.

XBMC has a secure auth http port exposed and a second unauthed raw command port for LAN only use. The patch below adds a suspend function that sends json to the raw port, and adds a button on the home screen to call the new function. For now this is just a simple get it done hack (hardcoded hostname/port, not following the code style/convention), but it would be cool to build a nice full featured "Shutdown" menu that enables exit, shutdown, suspend, etc. functionality.

Maybe later. Need sleep now... Self.Suspend()


$ svn di src/org/xbmc/android/remote/presentation/controller/HomeController.java
Index: src/org/xbmc/android/remote/presentation/controller/HomeController.java
===================================================================
--- src/org/xbmc/android/remote/presentation/controller/HomeController.java (revision 759)
+++ src/org/xbmc/android/remote/presentation/controller/HomeController.java (working copy)
@@ -21,6 +21,11 @@

package org.xbmc.android.remote.presentation.controller;

+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;
@@ -103,8 +108,8 @@
private static final int HOME_ACTION_RECONNECT = 5;
private static final int HOME_ACTION_WOL = 6;
private static final int HOME_ACTION_TVSHOWS = 7;
+ private static final int HOME_ACTION_SUSPEND = 8;

-
private IInfoManager mInfoManager;

private static final String TAG = "HomeController";
@@ -229,6 +234,7 @@
prefs.registerOnSharedPreferenceChangeListener(this);
homeItems.add(new HomeItem(HOME_ACTION_NOWPLAYING, R.drawable.icon_home_playing, "Now Playing", "See what's"));
homeItems.add(remote);
+ homeItems.add(new HomeItem(HOME_ACTION_SUSPEND, R.drawable.icon_home_power, "Suspend", "Invoke"));

final ArrayList offlineItems = new ArrayList();
offlineItems.add(remote);
@@ -310,6 +316,9 @@
mWolCounter.start();
}
break;
+ case HOME_ACTION_SUSPEND:
+ doSuspend();
+ break;
}
if (intent != null) {
mActivity.startActivity(intent);
@@ -318,6 +327,18 @@
};
}

+ private void doSuspend() {
+ try {
+ final Socket mySocket = new Socket("myth.lan", 9090);
+ final PrintWriter out = new PrintWriter(mySocket.getOutputStream(), true);
+ out.println("{\"jsonrpc\": \"2.0\", \"method\": \"System.Suspend\", \"id\": \"1\"}'jsonrpc': \"2.0\", 'method': \"VideoLibrary.ScanForContent\", 'id': \"1\"");
+ out.close();
+ mySocket.close();
+ } catch (IOException e) {
+ }
+}
+
+
public void update(Observable observable, Object data) {
if (data instanceof BroadcastListener.Event) {
BroadcastListener.Event event = (BroadcastListener.Event)data;

Wednesday, March 9, 2011

XBMC, /proc/acpi/wakeup, and instant resume

I got one of these guys http://www.ipazzport.com/show_10A.html to control my Fedora XBMC HTPC without wires and without having a large keyboard on the coffee table.

There was one nagging issue though. To be completely lazy I needed a remote power switch. Specifically I needed a way to wakeup the box from suspend.

/proc/acpi/wakeup to the rescue! I quickly found that USB2 was the bus (terminology?) that my wireless keyboards dongle was attached to. Great! But do to the way /proc/acpi/wakeup and the wireless dongle work I was left with a problem... as soon as the computer suspended it resumed. No amount of acpi conflict resolution seemed to work for me (as discussed here http://wiki.xbmc.org/?title=Enable_Wake-On-Device).

Whats a hacker to do? Hack of course :-D

I patched the XBMC code a few times to provide a delay when the suspend function was called (I figured the remote needed to be fully powered off). No good. So I patched XBMC again to not use its built in suspend functionality, but instead call out to an external shell script. No more compiling/test/compiling/etc. Yay!

--- xbmc-10.0-Dharma/xbmc/utils/Builtins.cpp 2010-12-17 01:17:41.000000000 -0500
+++ xbmc-10.0-Dharma-delay/xbmc/utils/Builtins.cpp 2011-03-06 14:05:57.747094861 -0500
@@ -84,6 +84,10 @@
#endif

#include
+#include
+#include
+#include
+#include

using namespace std;
using namespace XFILE;
@@ -254,7 +258,16 @@
}
else if (execute.Equals("suspend"))
{
- g_application.getApplicationMessenger().Suspend();
+ pid_t child_pid = fork();
+ if (child_pid == 0) {
+ char *args[] = {"/usr/bin/xbmc-delay", (char *) 0};
+ execvp (args[0], args);
+ return(0);
+ } else {
+ wait(NULL);
+ }
+ kill(child_pid, SIGKILL);
+ //g_application.getApplicationMessenger().Suspend();
}
else if (execute.Equals("quit"))
{


Now I could edit the shell script and test rather quickly (no need to rebuild and install). I found that with one suspend the dongle was still active enough to wake the box. Even large delays (so I could turn the remote off before the box was going down) did not help.

What works? Two back to back suspends. Why? I think it has to do with the way that usb subsystems come back online after a suspend. The dongle needs to stop mucking around on the usb bus to stop waking the machine up. While suspended the dongle certainly quits doing stuff. Thus when the second suspend occurs the dongle has settled down, and it does not wake the box up until I power the remote on.

The bash script is stupid simple
#!/bin/bash
sleep 1
sudo pm-suspend
sudo pm-suspend

Fun stuff...