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