Roll NDK to pick std::deque patch.
[android_tools.git] / sdk / tools / apps / SdkController / src / com / android / tools / sdkcontroller / service / ControllerService.java
blob9a3408b3e59c07a3d8e3e71501555798f7a32262
1 /*
2 * Copyright (C) 2012 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
17 package com.android.tools.sdkcontroller.service;
19 import java.util.ArrayList;
20 import java.util.List;
22 import android.app.Activity;
23 import android.app.Notification;
24 import android.app.NotificationManager;
25 import android.app.PendingIntent;
26 import android.app.Service;
27 import android.content.Intent;
28 import android.os.Binder;
29 import android.os.IBinder;
30 import android.util.Log;
32 import com.android.tools.sdkcontroller.R;
33 import com.android.tools.sdkcontroller.activities.MainActivity;
34 import com.android.tools.sdkcontroller.handlers.MultiTouchChannel;
35 import com.android.tools.sdkcontroller.handlers.SensorChannel;
36 import com.android.tools.sdkcontroller.lib.Connection;
37 import com.android.tools.sdkcontroller.lib.Channel;
39 /**
40 * The background service of the SdkController.
41 * There can be only one instance of this.
42 * <p/>
43 * The service manages a number of SDK controller channels which can be seen as
44 * individual tasks that the user might want to accomplish, for example "sending
45 * sensor data to the emulator" or "sending multi-touch data and displaying an
46 * emulator screen".
47 * <p/>
48 * Each channel connects to the emulator via UNIX-domain socket that is bound to
49 * "android.sdk.controller" port. Connection class provides a socket server that
50 * listens to emulator connections on this port, and binds new connection with a
51 * channel, based on channel name.
52 * <p/>
53 * All the channels are created when the service starts, and whether the emulator
54 * connection is successful or not, and whether there's any UI to control it.
55 * It's up to the channels to deal with these specific details. <br/>
56 * For example the {@link SensorChannel} initializes its sensor list as soon as
57 * created and then tries to send data as soon as there's an emulator
58 * connection. On the other hand the {@link MultiTouchChannel} lays dormant till
59 * there's an UI interacting with it.
61 public class ControllerService extends Service {
64 * Implementation reference:
65 * http://developer.android.com/reference/android/app/Service.html#LocalServiceSample
68 /** Tag for logging messages. */
69 public static String TAG = ControllerService.class.getSimpleName();
70 /** Controls debug log. */
71 private static boolean DEBUG = true;
72 /** Identifier for the notification. */
73 private static int NOTIF_ID = 'S' << 24 + 'd' << 16 + 'k' << 8 + 'C' << 0;
75 /** Connection to the emulator. */
76 public Connection mConnection;
79 private final IBinder mBinder = new ControllerBinder();
81 private List<ControllerListener> mListeners = new ArrayList<ControllerListener>();
83 /**
84 * Whether the service is running. Set to true in onCreate, false in onDestroy.
86 private static volatile boolean gServiceIsRunning = false;
88 /** Internal error reported by the service. */
89 private String mServiceError = "";
91 /**
92 * Interface that the service uses to notify binded activities.
93 * <p/>
94 * As a design rule, implementations of this listener should be aware that most calls
95 * will NOT happen on the UI thread. Any access to the UI should be properly protected
96 * by using {@link Activity#runOnUiThread(Runnable)}.
98 public interface ControllerListener {
99 /**
100 * The error string reported by the service has changed. <br/>
101 * Note this may be called from a thread different than the UI thread.
103 void onErrorChanged();
106 * The service status has changed (emulator connected/disconnected.)
108 void onStatusChanged();
111 /** Interface that callers can use to access the service. */
112 public class ControllerBinder extends Binder {
115 * Adds a new listener that will be notified when the service state changes.
117 * @param listener A non-null listener. Ignored if already listed.
119 public void addControllerListener(ControllerListener listener) {
120 assert listener != null;
121 if (listener != null) {
122 synchronized (mListeners) {
123 if (!mListeners.contains(listener)) {
124 mListeners.add(listener);
131 * Removes a listener.
133 * @param listener A listener to remove. Can be null.
135 public void removeControllerListener(ControllerListener listener) {
136 assert listener != null;
137 synchronized (mListeners) {
138 mListeners.remove(listener);
143 * Returns the error string accumulated by the service.
144 * Typically these would relate to failures to establish the communication
145 * channel(s) with the emulator, or unexpected disconnections.
147 public String getServiceError() {
148 return mServiceError;
152 * Indicates when <em>any</all> of the SDK controller channels is connected
153 * with the emulator.
155 * @return True if any of the SDK controller channels is connected with the
156 * emulator.
158 public boolean isEmuConnected() {
159 return mConnection.isEmulatorConnected();
163 * Returns the channel instance for the given type.
165 * @param name One of the channel names. Must not be null.
166 * @return Null if the type is not found, otherwise the handler's unique instance.
168 public Channel getChannel(String name) {
169 return mConnection.getChannel(name);
174 * Whether the service is running. Set to true in onCreate, false in onDestroy.
176 public static boolean isServiceIsRunning() {
177 return gServiceIsRunning;
180 @Override
181 public void onCreate() {
182 super.onCreate();
183 if (DEBUG) Log.d(TAG, "Service onCreate");
184 gServiceIsRunning = true;
185 showNotification();
186 onServiceStarted();
189 @Override
190 public int onStartCommand(Intent intent, int flags, int startId) {
191 // We want this service to continue running until it is explicitly
192 // stopped, so return sticky.
193 if (DEBUG) Log.d(TAG, "Service onStartCommand");
194 return START_STICKY;
197 @Override
198 public IBinder onBind(Intent intent) {
199 if (DEBUG) Log.d(TAG, "Service onBind");
200 return mBinder;
203 @Override
204 public void onDestroy() {
205 if (DEBUG) Log.d(TAG, "Service onDestroy");
206 gServiceIsRunning = false;
207 removeNotification();
208 resetError();
209 onServiceStopped();
210 super.onDestroy();
213 private void disconnectAll() {
214 if (mConnection != null) {
215 mConnection.disconnect();
220 * Called when the service has been created.
222 private void onServiceStarted() {
223 try {
224 disconnectAll();
226 // Bind to SDK controller port, and start accepting emulator
227 // connections.
228 mConnection = new Connection(ControllerService.this);
229 mConnection.connect();
231 // Create and register sensors channel.
232 mConnection.registerChannel(new SensorChannel(ControllerService.this));
233 // Create and register multi-touch channel.
234 mConnection.registerChannel(new MultiTouchChannel(ControllerService.this));
235 } catch (Exception e) {
236 addError("Connection failed: " + e.toString());
241 * Called when the service is being destroyed.
243 private void onServiceStopped() {
244 disconnectAll();
247 private void notifyErrorChanged() {
248 synchronized (mListeners) {
249 for (ControllerListener listener : mListeners) {
250 listener.onErrorChanged();
255 public void notifyStatusChanged() {
256 synchronized (mListeners) {
257 for (ControllerListener listener : mListeners) {
258 listener.onStatusChanged();
264 * Resets the error string and notify listeners.
266 private void resetError() {
267 mServiceError = "";
269 notifyErrorChanged();
273 * An internal utility method to add a line to the error string and notify listeners.
274 * @param error A non-null non-empty error line. \n will be added automatically.
276 public void addError(String error) {
277 Log.e(TAG, error);
278 if (mServiceError.length() > 0) {
279 mServiceError += "\n";
281 mServiceError += error;
283 notifyErrorChanged();
287 * Displays a notification showing that the service is running.
288 * When the user touches the notification, it opens the main activity
289 * which allows the user to stop this service.
291 @SuppressWarnings("deprecated")
292 private void showNotification() {
293 NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
295 String text = getString(R.string.service_notif_title);
297 // Note: Notification is marked as deprecated -- in API 11+ there's a new Builder class
298 // but we need to have API 7 compatibility so we ignore that warning.
300 Notification n = new Notification(R.drawable.ic_launcher, text, System.currentTimeMillis());
301 n.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
302 Intent intent = new Intent(this, MainActivity.class);
303 intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
304 PendingIntent pi = PendingIntent.getActivity(
305 this, //context
306 0, //requestCode
307 intent, //intent
308 0 //pending intent flags
310 n.setLatestEventInfo(this, text, text, pi);
312 nm.notify(NOTIF_ID, n);
315 private void removeNotification() {
316 NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
317 nm.cancel(NOTIF_ID);