aboutsummaryrefslogtreecommitdiffstats
path: root/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/BaseHandler.java
blob: 0e30c4d039c1bfe67be302c4829a2a04378e1ca4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.android.tools.sdkcontroller.handlers;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import android.content.Context;
import android.os.Message;
import android.util.Log;

import com.android.tools.sdkcontroller.lib.EmulatorConnection;
import com.android.tools.sdkcontroller.lib.EmulatorListener;
import com.android.tools.sdkcontroller.service.ControllerService;


/**
 * An abstract base class for all "action handlers".
 * <p/>
 * The {@link ControllerService} can deal with several handlers, each have a specific
 * purpose as described by {@link HandlerType}.
 * <p/>
 * The {@link BaseHandler} class adds support for activities to connect by providing
 * an {@link android.os.Handler} (which we'll call a "UI Handler" to differentiate it
 * from our "Service Handler"). The service handler will provide events via this
 * UI handler directly on the activity's UI thread.
 * <p/>
 * The {@link BaseHandler} keeps track of the current {@link EmulatorConnection} given
 * via {@link #onStart(EmulatorConnection, Context)}.
 * <p/>
 * The {@link BaseHandler} provides a simple way for activities to send event messages
 * back to the emulator by using {@link #sendEventToEmulator(String)}. This method
 * is safe to call from any thread, even the UI thread.
 */
public abstract class BaseHandler {

    protected static final boolean DEBUG = false;
    protected static final String TAG = null;

    private EmulatorConnection mConnection;

    private final AtomicInteger mEventCount = new AtomicInteger(0);
    private volatile boolean mRunEventQueue = true;
    private final BlockingQueue<String> mEventQueue = new LinkedBlockingQueue<String>();
    private static String EVENT_QUEUE_END = "@end@";
    private final List<android.os.Handler> mUiHandlers = new ArrayList<android.os.Handler>();
    private final HandlerType mHandlerType;
    private final Thread mEventThread;
    private int mPort;

    /**
     * The type of action that this handler manages.
     */
    public enum HandlerType {
        /** A handler to send multitouch events from the device to the emulator and display
         *  the emulator screen on the device. */
        MultiTouch,
        /** A handler to send sensor events from the device to the emulaotr. */
        Sensor
    }

    /**
     * Initializes a new base handler.
     *
     * @param type A non-null {@link HandlerType} value.
     * @param port A non-null communication port number.
     */
    protected BaseHandler(HandlerType type, int port) {
        mHandlerType = type;
        mPort = port;

        final String name = type.toString();
        mEventThread = new Thread(new Runnable() {
            @Override
            public void run() {
                if (DEBUG) Log.d(TAG, "EventThread.started-" + name);
                while(mRunEventQueue) {
                    try {
                        String msg = mEventQueue.take();
                        if (msg != null && mConnection != null && !msg.equals(EVENT_QUEUE_END)) {
                            mConnection.sendNotification(msg);
                            mEventCount.incrementAndGet();
                        }
                    } catch (InterruptedException e) {
                        Log.e(TAG, "EventThread-" + name, e);
                    }
                }
                if (DEBUG) Log.d(TAG, "EventThread.terminate-" + name);
            }
        }, "EventThread-" + name);
    }

    /**
     * Returns the type of this handler, as given to the constructor.
     *
     * @return One of the {@link HandlerType} values.
     */
    public HandlerType getType() {
        return mHandlerType;
    }

    /**
     * Returns he communication port used by this handler to communicate with the emulator,
     * as given to the constructor.
     * <p/>
     * Note that right now we have 2 handlers that each use their own port. The goal is
     * to move to a single-connection mechanism where all the handlers' data will be
     * multiplexed on top of a single {@link EmulatorConnection}.
     *
     * @return A non-null port value.
     */
    public int getPort() {
        return mPort;
    }

    /**
     * Returns the last {@link EmulatorConnection} passed to
     * {@link #onStart(EmulatorConnection, Context)}.
     * It becomes null when {@link #onStop()} is called.
     *
     * @return The current {@link EmulatorConnection}.
     */
    public EmulatorConnection getConnection() {
        return mConnection;
    }

    /**
     * Called once the {@link EmulatorConnection} has been successfully initialized.
     * <p/>
     * Note that this will <em>not</em> be called if the {@link EmulatorConnection}
     * fails to bind to the underlying socket.
     * <p/>
     * This base implementation keeps tracks of the connection.
     *
     * @param connection The connection that has just been created.
     *   A handler might want to use this to send data to the emulator via
     *   {@link EmulatorConnection#sendNotification(String)}. However handlers
     *   need to be particularly careful in <em>not</em> sending network data
     *   from the main UI thread.
     * @param context The controller service' context.
     * @see #getConnection()
     */
    public void onStart(EmulatorConnection connection, Context context) {
        assert connection != null;
        mConnection = connection;
        mRunEventQueue = true;
        mEventThread.start();
    }

    /**
     * Called once the {@link EmulatorConnection} is being disconnected.
     * This nullifies the connection returned by {@link #getConnection()}.
     */
    public void onStop() {
        // Stop the message queue
        mConnection = null;
        if (mRunEventQueue) {
            mRunEventQueue = false;
            mEventQueue.offer(EVENT_QUEUE_END);
        }
    }

    public int getEventSentCount() {
        return mEventCount.get();
    }

    /**
     * Utility for handlers or activities to sends a string event to the emulator.
     * This method is safe for the activity to call from any thread, including the UI thread.
     *
     * @param msg Event message. Must not be null.
     */
    public void sendEventToEmulator(String msg) {
        try {
            mEventQueue.put(msg);
        } catch (InterruptedException e) {
            Log.e(TAG, "EventQueue.put", e);
        }
    }

    // ------------
    // Interaction from the emulator connection towards the handler

    /**
     * Emulator query being forwarded to the handler.
     *
     * @see EmulatorListener#onEmulatorQuery(String, String)
     */
    public abstract String onEmulatorQuery(String query, String param);

    /**
     * Emulator blob query being forwarded to the handler.
     *
     * @see EmulatorListener#onEmulatorBlobQuery(byte[])
     */
    public abstract String onEmulatorBlobQuery(byte[] array);

    // ------------
    // Interaction from handler towards listening UI

    /**
     * Indicates whether the given UI handler is already registered with the handler.
     *
     * @param uiHandler The UI handler.
     * @return True if the UI handler is not null and already registered.
     */
    public boolean hasUiHandler(android.os.Handler uiHandler) {
        return uiHandler != null && mUiHandlers.contains(uiHandler);
    }

    /**
     * Registers a new UI handler.
     *
     * @param uiHandler A non-null UI handler to register.
     *   Ignored if the UI handler is null or already registered.
     */
    public void addUiHandler(android.os.Handler uiHandler) {
        assert uiHandler != null;
        if (uiHandler != null) {
            if (!mUiHandlers.contains(uiHandler)) {
                mUiHandlers.add(uiHandler);
            }
        }
    }

    /**
     * Unregisters an UI handler.
     *
     * @param uiHandler A non-null UI listener to unregister.
     *   Ignored if the listener is null or already registered.
     */
    public void removeUiHandler(android.os.Handler uiHandler) {
        assert uiHandler != null;
        mUiHandlers.remove(uiHandler);
    }

    /**
     * Protected method to be used by handlers to send an event to all UI handlers.
     *
     * @param event An integer event code with no specific parameters.
     *   To be defined by the handler itself.
     */
    protected void notifyUiHandlers(int event) {
        for (android.os.Handler uiHandler : mUiHandlers) {
            uiHandler.sendEmptyMessage(event);
        }
    }

    /**
     * Protected method to be used by handlers to send an event to all UI handlers.
     *
     * @param msg An event with parameters. To be defined by the handler itself.
     */
    protected void notifyUiHandlers(Message msg) {
        for (android.os.Handler uiHandler : mUiHandlers) {
            uiHandler.sendMessage(msg);
        }
    }

}