• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package com.android.tools.sdkcontroller.handlers;
18 
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.BlockingQueue;
22 import java.util.concurrent.LinkedBlockingQueue;
23 import java.util.concurrent.atomic.AtomicInteger;
24 
25 import android.content.Context;
26 import android.os.Message;
27 import android.util.Log;
28 
29 import com.android.tools.sdkcontroller.lib.EmulatorConnection;
30 import com.android.tools.sdkcontroller.lib.EmulatorListener;
31 import com.android.tools.sdkcontroller.service.ControllerService;
32 
33 
34 /**
35  * An abstract base class for all "action handlers".
36  * <p/>
37  * The {@link ControllerService} can deal with several handlers, each have a specific
38  * purpose as described by {@link HandlerType}.
39  * <p/>
40  * The {@link BaseHandler} class adds support for activities to connect by providing
41  * an {@link android.os.Handler} (which we'll call a "UI Handler" to differentiate it
42  * from our "Service Handler"). The service handler will provide events via this
43  * UI handler directly on the activity's UI thread.
44  * <p/>
45  * The {@link BaseHandler} keeps track of the current {@link EmulatorConnection} given
46  * via {@link #onStart(EmulatorConnection, Context)}.
47  * <p/>
48  * The {@link BaseHandler} provides a simple way for activities to send event messages
49  * back to the emulator by using {@link #sendEventToEmulator(String)}. This method
50  * is safe to call from any thread, even the UI thread.
51  */
52 public abstract class BaseHandler {
53 
54     protected static final boolean DEBUG = false;
55     protected static final String TAG = null;
56 
57     private EmulatorConnection mConnection;
58 
59     private final AtomicInteger mEventCount = new AtomicInteger(0);
60     private volatile boolean mRunEventQueue = true;
61     private final BlockingQueue<String> mEventQueue = new LinkedBlockingQueue<String>();
62     private static String EVENT_QUEUE_END = "@end@";
63     private final List<android.os.Handler> mUiHandlers = new ArrayList<android.os.Handler>();
64     private final HandlerType mHandlerType;
65     private final Thread mEventThread;
66     private int mPort;
67 
68     /**
69      * The type of action that this handler manages.
70      */
71     public enum HandlerType {
72         /** A handler to send multitouch events from the device to the emulator and display
73          *  the emulator screen on the device. */
74         MultiTouch,
75         /** A handler to send sensor events from the device to the emulaotr. */
76         Sensor
77     }
78 
79     /**
80      * Initializes a new base handler.
81      *
82      * @param type A non-null {@link HandlerType} value.
83      * @param port A non-null communication port number.
84      */
BaseHandler(HandlerType type, int port)85     protected BaseHandler(HandlerType type, int port) {
86         mHandlerType = type;
87         mPort = port;
88 
89         final String name = type.toString();
90         mEventThread = new Thread(new Runnable() {
91             @Override
92             public void run() {
93                 if (DEBUG) Log.d(TAG, "EventThread.started-" + name);
94                 while(mRunEventQueue) {
95                     try {
96                         String msg = mEventQueue.take();
97                         if (msg != null && mConnection != null && !msg.equals(EVENT_QUEUE_END)) {
98                             mConnection.sendNotification(msg);
99                             mEventCount.incrementAndGet();
100                         }
101                     } catch (InterruptedException e) {
102                         Log.e(TAG, "EventThread-" + name, e);
103                     }
104                 }
105                 if (DEBUG) Log.d(TAG, "EventThread.terminate-" + name);
106             }
107         }, "EventThread-" + name);
108     }
109 
110     /**
111      * Returns the type of this handler, as given to the constructor.
112      *
113      * @return One of the {@link HandlerType} values.
114      */
getType()115     public HandlerType getType() {
116         return mHandlerType;
117     }
118 
119     /**
120      * Returns he communication port used by this handler to communicate with the emulator,
121      * as given to the constructor.
122      * <p/>
123      * Note that right now we have 2 handlers that each use their own port. The goal is
124      * to move to a single-connection mechanism where all the handlers' data will be
125      * multiplexed on top of a single {@link EmulatorConnection}.
126      *
127      * @return A non-null port value.
128      */
getPort()129     public int getPort() {
130         return mPort;
131     }
132 
133     /**
134      * Returns the last {@link EmulatorConnection} passed to
135      * {@link #onStart(EmulatorConnection, Context)}.
136      * It becomes null when {@link #onStop()} is called.
137      *
138      * @return The current {@link EmulatorConnection}.
139      */
getConnection()140     public EmulatorConnection getConnection() {
141         return mConnection;
142     }
143 
144     /**
145      * Called once the {@link EmulatorConnection} has been successfully initialized.
146      * <p/>
147      * Note that this will <em>not</em> be called if the {@link EmulatorConnection}
148      * fails to bind to the underlying socket.
149      * <p/>
150      * This base implementation keeps tracks of the connection.
151      *
152      * @param connection The connection that has just been created.
153      *   A handler might want to use this to send data to the emulator via
154      *   {@link EmulatorConnection#sendNotification(String)}. However handlers
155      *   need to be particularly careful in <em>not</em> sending network data
156      *   from the main UI thread.
157      * @param context The controller service' context.
158      * @see #getConnection()
159      */
onStart(EmulatorConnection connection, Context context)160     public void onStart(EmulatorConnection connection, Context context) {
161         assert connection != null;
162         mConnection = connection;
163         mRunEventQueue = true;
164         mEventThread.start();
165     }
166 
167     /**
168      * Called once the {@link EmulatorConnection} is being disconnected.
169      * This nullifies the connection returned by {@link #getConnection()}.
170      */
onStop()171     public void onStop() {
172         // Stop the message queue
173         mConnection = null;
174         if (mRunEventQueue) {
175             mRunEventQueue = false;
176             mEventQueue.offer(EVENT_QUEUE_END);
177         }
178     }
179 
getEventSentCount()180     public int getEventSentCount() {
181         return mEventCount.get();
182     }
183 
184     /**
185      * Utility for handlers or activities to sends a string event to the emulator.
186      * This method is safe for the activity to call from any thread, including the UI thread.
187      *
188      * @param msg Event message. Must not be null.
189      */
sendEventToEmulator(String msg)190     public void sendEventToEmulator(String msg) {
191         try {
192             mEventQueue.put(msg);
193         } catch (InterruptedException e) {
194             Log.e(TAG, "EventQueue.put", e);
195         }
196     }
197 
198     // ------------
199     // Interaction from the emulator connection towards the handler
200 
201     /**
202      * Emulator query being forwarded to the handler.
203      *
204      * @see EmulatorListener#onEmulatorQuery(String, String)
205      */
onEmulatorQuery(String query, String param)206     public abstract String onEmulatorQuery(String query, String param);
207 
208     /**
209      * Emulator blob query being forwarded to the handler.
210      *
211      * @see EmulatorListener#onEmulatorBlobQuery(byte[])
212      */
onEmulatorBlobQuery(byte[] array)213     public abstract String onEmulatorBlobQuery(byte[] array);
214 
215     // ------------
216     // Interaction from handler towards listening UI
217 
218     /**
219      * Indicates any UI handler is currently registered with the handler.
220      * If no UI is displaying the handler's state, maybe the handler can skip UI related tasks.
221      *
222      * @return True if there's at least one UI handler registered.
223      */
hasUiHandler()224     public boolean hasUiHandler() {
225         return !mUiHandlers.isEmpty();
226     }
227 
228     /**
229      * Registers a new UI handler.
230      *
231      * @param uiHandler A non-null UI handler to register.
232      *   Ignored if the UI handler is null or already registered.
233      */
addUiHandler(android.os.Handler uiHandler)234     public void addUiHandler(android.os.Handler uiHandler) {
235         assert uiHandler != null;
236         if (uiHandler != null) {
237             if (!mUiHandlers.contains(uiHandler)) {
238                 mUiHandlers.add(uiHandler);
239             }
240         }
241     }
242 
243     /**
244      * Unregisters an UI handler.
245      *
246      * @param uiHandler A non-null UI listener to unregister.
247      *   Ignored if the listener is null or already registered.
248      */
removeUiHandler(android.os.Handler uiHandler)249     public void removeUiHandler(android.os.Handler uiHandler) {
250         assert uiHandler != null;
251         mUiHandlers.remove(uiHandler);
252     }
253 
254     /**
255      * Protected method to be used by handlers to send an event to all UI handlers.
256      *
257      * @param event An integer event code with no specific parameters.
258      *   To be defined by the handler itself.
259      */
notifyUiHandlers(int event)260     protected void notifyUiHandlers(int event) {
261         for (android.os.Handler uiHandler : mUiHandlers) {
262             uiHandler.sendEmptyMessage(event);
263         }
264     }
265 
266     /**
267      * Protected method to be used by handlers to send an event to all UI handlers.
268      *
269      * @param msg An event with parameters. To be defined by the handler itself.
270      */
notifyUiHandlers(Message msg)271     protected void notifyUiHandlers(Message msg) {
272         for (android.os.Handler uiHandler : mUiHandlers) {
273             uiHandler.sendMessage(msg);
274         }
275     }
276 
277 }
278