diff --git a/Makefile.am b/Makefile.am
index 495157e78..fc0670220 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -301,6 +301,7 @@ ANDROID_BUILD_TOOLS_DIR = $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_V
 ANDROID_SDK_PLATFORM_DIR = $(ANDROID_SDK)/platforms/$(ANDROID_SDK_PLATFORM)
 
 JAVAC = javac
+AIDL = $(ANDROID_BUILD_TOOLS_DIR)/aidl
 AAPT = $(ANDROID_BUILD_TOOLS_DIR)/aapt
 DX = $(ANDROID_BUILD_TOOLS_DIR)/dx
 ZIPALIGN = $(ANDROID_BUILD_TOOLS_DIR)/zipalign
@@ -308,11 +309,21 @@ ZIPALIGN = $(ANDROID_BUILD_TOOLS_DIR)/zipalign
 ANDROID_XML_RES := $(wildcard $(srcdir)/android/res/*/*.xml)
 ANDROID_XML_RES_COPIES := $(patsubst $(srcdir)/android/%,android/build/%,$(ANDROID_XML_RES))
 
-JAVA_SOURCE_NAMES = Bridge.java Loader.java Main.java
+JAVA_SOURCE_NAMES = Bridge.java Loader.java Main.java Settings.java
 JAVA_SOURCE_PATHS = $(addprefix $(srcdir)/android/src/,$(JAVA_SOURCE_NAMES))
 
 JAVA_CLASSFILES_DIR = android/build/classes
 
+AIDL_FILES = $(wildcard $(srcdir)/android/src/*.aidl)
+AIDL_JAVA_FILES = $(patsubst $(srcdir)/android/src/%.aidl,android/build/src/org/musicpd/%.java,$(AIDL_FILES))
+
+android/build/src/org/musicpd/IMain.java: android/build/src/org/musicpd/IMainCallback.java
+
+$(AIDL_JAVA_FILES): android/build/src/org/musicpd/%.java: $(srcdir)/android/src/%.aidl
+	@$(MKDIR_P) $(@D)
+	@cp $< $(@D)/
+	$(AIDL) -Iandroid/build/src -oandroid/build/src $(patsubst %.java,%.aidl,$@)
+
 $(ANDROID_XML_RES_COPIES): $(ANDROID_XML_RES)
 	@$(MKDIR_P) $(dir $@)
 	cp $(patsubst android/build/%,$(srcdir)/android/%,$@) $@
@@ -330,7 +341,7 @@ android/build/resources.apk: $(ANDROID_XML_RES_COPIES) android/build/res/drawabl
 # R.java is generated by aapt, when resources.apk is generated
 android/build/gen/org/musicpd/R.java: android/build/resources.apk
 
-android/build/classes.dex: $(JAVA_SOURCE_PATHS) android/build/gen/org/musicpd/R.java
+android/build/classes.dex: $(JAVA_SOURCE_PATHS) $(AIDL_JAVA_FILES) android/build/gen/org/musicpd/R.java
 	@$(MKDIR_P) $(JAVA_CLASSFILES_DIR)
 	$(JAVAC) -source 1.6 -target 1.6 -Xlint:-options \
 		-cp $(ANDROID_SDK_PLATFORM_DIR)/android.jar:$(JAVA_CLASSFILES_DIR) \
diff --git a/NEWS b/NEWS
index 972b7144a..5eb591817 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
 ver 0.20.22 (not yet released)
 * storage
   - curl: URL-encode paths
+* Android
+  - now runs as a service
+  - add button to start/stop MPD
 
 ver 0.20.21 (2018/08/17)
 * database
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index dba4f518f..6095c9460 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -8,14 +8,14 @@
   <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
 
   <application android:icon="@drawable/icon" android:label="@string/app_name">
-    <activity android:name=".Main"
-              android:label="@string/app_name"
-              android:launchMode="singleInstance">
+    <activity android:name=".Settings"
+              android:label="@string/app_name">
       <intent-filter>
         <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
     </activity>
+    <service android:name=".Main" />
   </application>
 
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
diff --git a/android/res/layout/custom_notification_gb.xml b/android/res/layout/custom_notification_gb.xml
new file mode 100644
index 000000000..92a6036e2
--- /dev/null
+++ b/android/res/layout/custom_notification_gb.xml
@@ -0,0 +1,22 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/layout"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:padding="10dp" >
+    <ImageView android:id="@+id/image"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:layout_alignParentLeft="true"
+        android:layout_marginRight="10dp" />
+    <TextView android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/image"
+        style="Custom Notification Title" />
+    <TextView android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/image"
+        android:layout_below="@id/title"
+        style="Custom Notification Text" />
+</RelativeLayout>
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
index 416c8de9f..bcc1ae0c5 100644
--- a/android/res/values/strings.xml
+++ b/android/res/values/strings.xml
@@ -2,4 +2,6 @@
 
 <resources>
   <string name="app_name">MPD</string>
+  <string name="notification_title_mpd_running">Music Player Daemon is running</string>
+  <string name="notification_text_mpd_running">Touch for MPD options.</string>
 </resources>
diff --git a/android/src/IMain.aidl b/android/src/IMain.aidl
new file mode 100644
index 000000000..ba7050d79
--- /dev/null
+++ b/android/src/IMain.aidl
@@ -0,0 +1,12 @@
+package org.musicpd;
+import org.musicpd.IMainCallback;
+
+interface IMain
+{
+    void start();
+    void stop();
+    void setWakelockEnabled(boolean enabled);
+    boolean isRunning();
+    void registerCallback(IMainCallback cb);
+    void unregisterCallback(IMainCallback cb);
+}
diff --git a/android/src/IMainCallback.aidl b/android/src/IMainCallback.aidl
new file mode 100644
index 000000000..c8cdaa4a0
--- /dev/null
+++ b/android/src/IMainCallback.aidl
@@ -0,0 +1,9 @@
+package org.musicpd;
+
+interface IMainCallback
+{
+    void onStarted();
+    void onStopped();
+    void onError(String error);
+    void onLog(int priority, String msg);
+}
diff --git a/android/src/Main.java b/android/src/Main.java
index 816b62cdb..44e2e3d54 100644
--- a/android/src/Main.java
+++ b/android/src/Main.java
@@ -19,57 +19,406 @@
 
 package org.musicpd;
 
-import android.app.Activity;
-import android.os.Bundle;
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
 import android.os.Build;
-import android.os.Handler;
-import android.os.Message;
-import android.widget.TextView;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
 import android.util.Log;
+import android.widget.RemoteViews;
 
-public class Main extends Activity implements Runnable {
-	private static final String TAG = "MPD";
+public class Main extends Service implements Runnable {
+	private static final String TAG = "Main";
+	private static final String REMOTE_ERROR = "MPD process was killed";
+	private static final int MAIN_STATUS_ERROR = -1;
+	private static final int MAIN_STATUS_STOPPED = 0;
+	private static final int MAIN_STATUS_STARTED = 1;
 
-	Thread thread;
+	private static final int MSG_SEND_STATUS = 0;
+	private static final int MSG_SEND_LOG = 1;
 
-	TextView textView;
+	private Thread mThread = null;
+	private int mStatus = MAIN_STATUS_STOPPED;
+	private boolean mAbort = false;
+	private String mError = null;
+	private final RemoteCallbackList<IMainCallback> mCallbacks = new RemoteCallbackList<IMainCallback>();
+	private final IBinder mBinder = new MainStub(this);
+	private PowerManager.WakeLock mWakelock = null;
 
-	final Handler quitHandler = new Handler() {
-			public void handleMessage(Message msg) {
-				textView.setText("Music Player Daemon has quit");
+	static class MainStub extends IMain.Stub {
+		private Main mService;
+		MainStub(Main service) {
+			mService = service;
+		}
+		public void start() {
+			mService.start();
+		}
+		public void stop() {
+			mService.stop();
+		}
+		public void setWakelockEnabled(boolean enabled) {
+			mService.setWakelockEnabled(enabled);
+		}
+		public boolean isRunning() {
+			return mService.isRunning();
+		}
+		public void registerCallback(IMainCallback cb) {
+			mService.registerCallback(cb);
+		}
+		public void unregisterCallback(IMainCallback cb) {
+			mService.unregisterCallback(cb);
+		}
+	}
 
-				// TODO: what now?  restart?
+	private synchronized void sendMessage(int what, int arg1, int arg2, Object obj) {
+		int i = mCallbacks.beginBroadcast();
+		while (i > 0) {
+			i--;
+			final IMainCallback cb = mCallbacks.getBroadcastItem(i);
+			try {
+				switch (what) {
+				case MSG_SEND_STATUS:
+					switch (arg1) {
+					case MAIN_STATUS_ERROR:
+						cb.onError((String)obj);
+						break;
+					case MAIN_STATUS_STOPPED:
+						cb.onStopped();
+						break;
+					case MAIN_STATUS_STARTED:
+						cb.onStarted();
+						break;
+					}
+					break;
+				case MSG_SEND_LOG:
+					cb.onLog(arg1, (String) obj);
+					break;
+				}
+			} catch (RemoteException e) {
+			}
+		}
+		mCallbacks.finishBroadcast();
+	}
+
+	private Bridge.LogListener mLogListener = new Bridge.LogListener() {
+		@Override
+		public void onLog(int priority, String msg) {
+			sendMessage(MSG_SEND_LOG, priority, 0, msg);
+		}
+	};
+
+	@Override
+	public IBinder onBind(Intent intent) {
+		return mBinder;
+	}
+
+	@Override
+	public int onStartCommand(Intent intent, int flags, int startId) {
+		start();
+		if (intent != null && intent.getBooleanExtra("wakelock", false))
+			setWakelockEnabled(true);
+		return START_STICKY;
+	}
+
+	@Override
+	public void run() {
+		if (!Loader.loaded) {
+			final String error = "Failed to load the native MPD libary.\n" +
+				"Report this problem to us, and include the following information:\n" +
+				"SUPPORTED_ABIS=" + String.join(", ", Build.SUPPORTED_ABIS) + "\n" +
+				"PRODUCT=" + Build.PRODUCT + "\n" +
+				"FINGERPRINT=" + Build.FINGERPRINT + "\n" +
+				"error=" + Loader.error;
+			setStatus(MAIN_STATUS_ERROR, error);
+			stopSelf();
+			return;
+		}
+		synchronized (this) {
+			if (mAbort)
+				return;
+			setStatus(MAIN_STATUS_STARTED, null);
+		}
+		Bridge.run(this, mLogListener);
+		setStatus(MAIN_STATUS_STOPPED, null);
+	}
+
+	private synchronized void setStatus(int status, String error) {
+		mStatus = status;
+		mError = error;
+		sendMessage(MSG_SEND_STATUS, mStatus, 0, mError);
+	}
+
+	@TargetApi(Build.VERSION_CODES.GINGERBREAD)
+	private Notification buildNotificationGB(int title, int text, int icon, PendingIntent contentIntent) {
+		final Notification notification = new Notification();
+		notification.icon = R.drawable.icon;
+		notification.contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_gb);
+		notification.contentView.setImageViewResource(R.id.image, icon);
+		notification.contentView.setTextViewText(R.id.title, getText(title));
+		notification.contentView.setTextViewText(R.id.text, getText(text));
+		notification.contentIntent = contentIntent;
+		return notification;
+	}
+
+	@TargetApi(Build.VERSION_CODES.HONEYCOMB)
+	private Notification buildNotificationHC(int title, int text, int icon, PendingIntent contentIntent) {
+		return new Notification.Builder(this)
+				.setContentTitle(getText(title))
+				.setContentText(getText(text))
+				.setSmallIcon(icon)
+				.setContentIntent(contentIntent)
+				.getNotification();
+	}
+
+	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+	private Notification buildNotificationJB(int title, int text, int icon, PendingIntent contentIntent) {
+		return new Notification.Builder(this)
+				.setContentTitle(getText(title))
+				.setContentText(getText(text))
+				.setSmallIcon(icon)
+				.setContentIntent(contentIntent)
+				.build();
+	}
+
+	private void start() {
+		if (mThread != null)
+			return;
+		mThread = new Thread(this);
+		mThread.start();
+
+		final Intent mainIntent = new Intent(this, Settings.class);
+		mainIntent.setAction("android.intent.action.MAIN");
+		mainIntent.addCategory("android.intent.category.LAUNCHER");
+		final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
+				mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+
+		Notification notification;
+
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+			notification = buildNotificationJB(
+					R.string.notification_title_mpd_running,
+					R.string.notification_text_mpd_running,
+					R.drawable.icon,
+					contentIntent);
+		else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
+			notification = buildNotificationHC(
+					R.string.notification_title_mpd_running,
+					R.string.notification_text_mpd_running,
+					R.drawable.icon,
+					contentIntent);
+		else
+			notification = buildNotificationGB(
+					R.string.notification_title_mpd_running,
+					R.string.notification_text_mpd_running,
+					R.drawable.icon,
+					contentIntent);
+
+		startForeground(R.string.notification_title_mpd_running, notification);
+		startService(new Intent(this, Main.class));
+	}
+
+	private void stop() {
+		if (mThread != null) {
+			if (mThread.isAlive()) {
+				synchronized (this) {
+					if (mStatus == MAIN_STATUS_STARTED)
+						Bridge.shutdown();
+					else
+						mAbort = true;
+				}
+			}
+			try {
+				mThread.join();
+				mThread = null;
+				mAbort = false;
+			} catch (InterruptedException ie) {}
+		}
+		setWakelockEnabled(false);
+		stopForeground(true);
+		stopSelf();
+	}
+
+	private void setWakelockEnabled(boolean enabled) {
+		if (enabled && mWakelock == null) {
+			PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+			mWakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+			mWakelock.acquire();
+			Log.d(TAG, "Wakelock acquired");
+		} else if (!enabled && mWakelock != null) {
+			mWakelock.release();
+			mWakelock = null;
+			Log.d(TAG, "Wakelock released");
+		}
+	}
+
+	private boolean isRunning() {
+		return mThread != null && mThread.isAlive();
+	}
+
+	private void registerCallback(IMainCallback cb) {
+		if (cb != null) {
+			mCallbacks.register(cb);
+			sendMessage(MSG_SEND_STATUS, mStatus, 0, mError);
+		}
+	}
+
+	private void unregisterCallback(IMainCallback cb) {
+		if (cb != null) {
+			mCallbacks.unregister(cb);
+		}
+	}
+
+	/*
+	 * Client that bind the Main Service in order to send commands and receive callback
+	 */
+	public static class Client {
+
+		public interface Callback {
+			public void onStarted();
+			public void onStopped();
+			public void onError(String error);
+			public void onLog(int priority, String msg);
+		}
+
+		private boolean mBound = false;
+		private final Context mContext;
+		private Callback mCallback;
+		private IMain mIMain = null;
+
+		private final IMainCallback.Stub mICallback = new IMainCallback.Stub() {
+
+			@Override
+			public void onStopped() throws RemoteException {
+				mCallback.onStopped();
+			}
+
+			@Override
+			public void onStarted() throws RemoteException {
+				mCallback.onStarted();
+			}
+
+			@Override
+			public void onError(String error) throws RemoteException {
+				mCallback.onError(error);
+			}
+
+			@Override
+			public void onLog(int priority, String msg) throws RemoteException {
+				mCallback.onLog(priority, msg);
 			}
 		};
 
-	@Override protected void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
+		private final ServiceConnection mServiceConnection = new ServiceConnection() {
 
-		if (!Loader.loaded) {
-			TextView tv = new TextView(this);
-			tv.setText("Failed to load the native MPD libary.\n" +
-				   "Report this problem to us, and include the following information:\n" +
-				   "SUPPORTED_ABIS=" + String.join(", ", Build.SUPPORTED_ABIS) + "\n" +
-				   "PRODUCT=" + Build.PRODUCT + "\n" +
-				   "FINGERPRINT=" + Build.FINGERPRINT + "\n" +
-				   "error=" + Loader.error);
-			setContentView(tv);
-			return;
+			@Override
+			public void onServiceConnected(ComponentName name, IBinder service) {
+				synchronized (this) {
+					mIMain = IMain.Stub.asInterface(service);
+					try {
+						if (mCallback != null)
+							mIMain.registerCallback(mICallback);
+					} catch (RemoteException e) {
+						if (mCallback != null)
+							mCallback.onError(REMOTE_ERROR);
+					}
+				}
+			}
+
+			@Override
+			public void onServiceDisconnected(ComponentName name) {
+				if (mCallback != null)
+					mCallback.onError(REMOTE_ERROR);
+			}
+		};
+
+		public Client(Context context, Callback cb) throws IllegalArgumentException {
+			if (context == null)
+				throw new IllegalArgumentException("Context can't be null");
+			mContext = context;
+			mCallback = cb;
+			mBound = mContext.bindService(new Intent(mContext, Main.class), mServiceConnection, Context.BIND_AUTO_CREATE);
 		}
 
-		if (thread == null || !thread.isAlive()) {
-			thread = new Thread(this, "NativeMain");
-			thread.start();
+		public boolean start() {
+			synchronized (this) {
+				if (mIMain != null) {
+					try {
+						mIMain.start();
+						return true;
+					} catch (RemoteException e) {
+					}
+				}
+				return false;
+			}
 		}
 
-		textView = new TextView(this);
-		textView.setText("Music Player Daemon is running"
-				 + "\nCAUTION: this version is EXPERIMENTAL!");
-		setContentView(textView);
+		public boolean stop() {
+			synchronized (this) {
+				if (mIMain != null) {
+					try {
+						mIMain.stop();
+						return true;
+					} catch (RemoteException e) {
+					}
+				}
+				return false;
+			}
+		}
+
+		public boolean setWakelockEnabled(boolean enabled) {
+			synchronized (this) {
+				if (mIMain != null) {
+					try {
+						mIMain.setWakelockEnabled(enabled);
+						return true;
+					} catch (RemoteException e) {
+					}
+				}
+				return false;
+			}
+		}
+
+		public boolean isRunning() {
+			synchronized (this) {
+				if (mIMain != null) {
+					try {
+						return mIMain.isRunning();
+					} catch (RemoteException e) {
+					}
+				}
+				return false;
+			}
+		}
+
+		public void release() {
+			if (mBound) {
+				synchronized (this) {
+					if (mIMain != null && mICallback != null) {
+						try {
+							if (mCallback != null)
+								mIMain.unregisterCallback(mICallback);
+						} catch (RemoteException e) {
+						}
+					}
+				}
+				mBound = false;
+				mContext.unbindService(mServiceConnection);
+			}
+		}
 	}
 
-	@Override public void run() {
-		Bridge.run(this, null);
-		quitHandler.sendMessage(quitHandler.obtainMessage());
+	/*
+	 * start Main service without any callback
+	 */
+	public static void start(Context context, boolean wakelock) {
+		context.startService(new Intent(context, Main.class).putExtra("wakelock", wakelock));
 	}
 }
diff --git a/android/src/Settings.java b/android/src/Settings.java
new file mode 100644
index 000000000..89f96447d
--- /dev/null
+++ b/android/src/Settings.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2003-2018 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package org.musicpd;
+
+import android.app.Activity;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+public class Settings extends Activity {
+	private static final String TAG = "Settings";
+	private Main.Client mClient;
+	private TextView mTextView;
+	private ToggleButton mButton;
+	private LinearLayout mLayout;
+
+	private static final int MSG_ERROR = 0;
+	private static final int MSG_STOPPED = 1;
+	private static final int MSG_STARTED = 2;
+
+	private Handler mHandler = new Handler(new Handler.Callback() {
+
+		@Override
+		public boolean handleMessage(Message msg) {
+			switch (msg.what) {
+			case MSG_ERROR:
+				Log.d(TAG, "onError");
+				final String error = (String) msg.obj;
+				mTextView.setText("Failed to load the native MPD libary.\n" +
+						  "Report this problem to us, and include the following information:\n" +
+						  "SUPPORTED_ABIS=" + String.join(", ", Build.SUPPORTED_ABIS) + "\n" +
+						  "PRODUCT=" + Build.PRODUCT + "\n" +
+						  "FINGERPRINT=" + Build.FINGERPRINT + "\n" +
+						  "error=" + error);
+				mButton.setChecked(false);
+				mButton.setEnabled(false);
+				break;
+			case MSG_STOPPED:
+				Log.d(TAG, "onStopped");
+				if (mButton.isEnabled()) // don't overwrite previous error message
+					mTextView.setText("Music Player Daemon is not running");
+				mButton.setEnabled(true);
+				mButton.setChecked(false);
+				break;
+			case MSG_STARTED:
+				Log.d(TAG, "onStarted");
+				mTextView.setText("Music Player Daemon is running"
+								  + "\nCAUTION: this version is EXPERIMENTAL!");
+				mButton.setChecked(true);
+				break;
+			}
+			return true;
+		}
+	});
+
+	private OnCheckedChangeListener mOnCheckedChangeListener = new OnCheckedChangeListener() {
+
+		@Override
+		public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+			if (mClient != null) {
+				if (isChecked)
+					mClient.start();
+				else
+					mClient.stop();
+			}
+		}
+	};
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		mTextView = new TextView(this);
+		mTextView.setText("");
+
+		mButton = new ToggleButton(this);
+		mButton.setOnCheckedChangeListener(mOnCheckedChangeListener);
+
+		mLayout = new LinearLayout(this);
+		mLayout.setOrientation(LinearLayout.VERTICAL);
+		mLayout.addView(mButton);
+		mLayout.addView(mTextView);
+
+		setContentView(mLayout);
+
+		super.onCreate(savedInstanceState);
+	}
+
+	@Override
+	protected void onStart() {
+		mClient = new Main.Client(this, new Main.Client.Callback() {
+			@Override
+			public void onStopped() {
+				mHandler.removeCallbacksAndMessages(null);
+				mHandler.sendEmptyMessage(MSG_STOPPED);
+			}
+
+			@Override
+			public void onStarted() {
+				mHandler.removeCallbacksAndMessages(null);
+				mHandler.sendEmptyMessage(MSG_STARTED);
+			}
+
+			@Override
+			public void onError(String error) {
+				mHandler.removeCallbacksAndMessages(null);
+				mHandler.sendMessage(Message.obtain(mHandler, MSG_ERROR, error));
+			}
+
+			@Override
+			public void onLog(int priority, String msg) {
+			}
+		});
+		super.onStart();
+	}
+
+	@Override
+	protected void onStop() {
+		mClient.release();
+		mClient = null;
+		super.onStop();
+	}
+}