ViewServer.java revision a17de9b493123f0d6e6d0b842150bf29322b7a88
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
19
20import android.util.Slog;
21
22import java.net.ServerSocket;
23import java.net.Socket;
24import java.net.InetAddress;
25import java.util.concurrent.ExecutorService;
26import java.util.concurrent.Executors;
27import java.io.IOException;
28import java.io.BufferedReader;
29import java.io.InputStreamReader;
30import java.io.OutputStream;
31import java.io.BufferedWriter;
32import java.io.OutputStreamWriter;
33
34/**
35 * The ViewServer is local socket server that can be used to communicate with the
36 * views of the opened windows. Communication with the views is ensured by the
37 * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation.
38 *
39 * {@hide}
40 */
41class ViewServer implements Runnable {
42    /**
43     * The default port used to start view servers.
44     */
45    public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
46
47    private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
48
49    // Debug facility
50    private static final String LOG_TAG = "ViewServer";
51
52    private static final String VALUE_PROTOCOL_VERSION = "3";
53    private static final String VALUE_SERVER_VERSION = "4";
54
55    // Protocol commands
56    // Returns the protocol version
57    private static final String COMMAND_PROTOCOL_VERSION = "PROTOCOL";
58    // Returns the server version
59    private static final String COMMAND_SERVER_VERSION = "SERVER";
60    // Lists all of the available windows in the system
61    private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
62    // Keeps a connection open and notifies when the list of windows changes
63    private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
64    // Returns the focused window
65    private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
66
67    private ServerSocket mServer;
68    private Thread mThread;
69
70    private final WindowManagerService mWindowManager;
71    private final int mPort;
72
73    private ExecutorService mThreadPool;
74
75    /**
76     * Creates a new ViewServer associated with the specified window manager.
77     * The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
78     * is not started by default.
79     *
80     * @param windowManager The window manager used to communicate with the views.
81     *
82     * @see #start()
83     */
84    ViewServer(WindowManagerService windowManager) {
85        this(windowManager, VIEW_SERVER_DEFAULT_PORT);
86    }
87
88    /**
89     * Creates a new ViewServer associated with the specified window manager on the
90     * specified local port. The server is not started by default.
91     *
92     * @param windowManager The window manager used to communicate with the views.
93     * @param port The port for the server to listen to.
94     *
95     * @see #start()
96     */
97    ViewServer(WindowManagerService windowManager, int port) {
98        mWindowManager = windowManager;
99        mPort = port;
100    }
101
102    /**
103     * Starts the server.
104     *
105     * @return True if the server was successfully created, or false if it already exists.
106     * @throws IOException If the server cannot be created.
107     *
108     * @see #stop()
109     * @see #isRunning()
110     * @see WindowManagerService#startViewServer(int)
111     */
112    boolean start() throws IOException {
113        if (mThread != null) {
114            return false;
115        }
116
117        mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
118        mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
119        mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
120        mThread.start();
121
122        return true;
123    }
124
125    /**
126     * Stops the server.
127     *
128     * @return True if the server was stopped, false if an error occured or if the
129     *         server wasn't started.
130     *
131     * @see #start()
132     * @see #isRunning()
133     * @see WindowManagerService#stopViewServer()
134     */
135    boolean stop() {
136        if (mThread != null) {
137
138            mThread.interrupt();
139            if (mThreadPool != null) {
140                try {
141                    mThreadPool.shutdownNow();
142                } catch (SecurityException e) {
143                    Slog.w(LOG_TAG, "Could not stop all view server threads");
144                }
145            }
146            mThreadPool = null;
147            mThread = null;
148            try {
149                mServer.close();
150                mServer = null;
151                return true;
152            } catch (IOException e) {
153                Slog.w(LOG_TAG, "Could not close the view server");
154            }
155        }
156        return false;
157    }
158
159    /**
160     * Indicates whether the server is currently running.
161     *
162     * @return True if the server is running, false otherwise.
163     *
164     * @see #start()
165     * @see #stop()
166     * @see WindowManagerService#isViewServerRunning()
167     */
168    boolean isRunning() {
169        return mThread != null && mThread.isAlive();
170    }
171
172    /**
173     * Main server loop.
174     */
175    public void run() {
176        while (Thread.currentThread() == mThread) {
177            // Any uncaught exception will crash the system process
178            try {
179                Socket client = mServer.accept();
180                if(mThreadPool != null) {
181                    mThreadPool.submit(new ViewServerWorker(client));
182                } else {
183                    try {
184                        client.close();
185                    } catch (IOException e) {
186                        e.printStackTrace();
187                    }
188                }
189            } catch (Exception e) {
190                Slog.w(LOG_TAG, "Connection error: ", e);
191            }
192        }
193    }
194
195    private static boolean writeValue(Socket client, String value) {
196        boolean result;
197        BufferedWriter out = null;
198        try {
199            OutputStream clientStream = client.getOutputStream();
200            out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
201            out.write(value);
202            out.write("\n");
203            out.flush();
204            result = true;
205        } catch (Exception e) {
206            result = false;
207        } finally {
208            if (out != null) {
209                try {
210                    out.close();
211                } catch (IOException e) {
212                    result = false;
213                }
214            }
215        }
216        return result;
217    }
218
219    class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
220        private Socket mClient;
221        private boolean mNeedWindowListUpdate;
222        private boolean mNeedFocusedWindowUpdate;
223        public ViewServerWorker(Socket client) {
224            mClient = client;
225            mNeedWindowListUpdate = false;
226            mNeedFocusedWindowUpdate = false;
227        }
228
229        public void run() {
230
231            BufferedReader in = null;
232            try {
233                in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
234
235                final String request = in.readLine();
236
237                String command;
238                String parameters;
239
240                int index = request.indexOf(' ');
241                if (index == -1) {
242                    command = request;
243                    parameters = "";
244                } else {
245                    command = request.substring(0, index);
246                    parameters = request.substring(index + 1);
247                }
248
249                boolean result;
250                if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
251                    result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
252                } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
253                    result = writeValue(mClient, VALUE_SERVER_VERSION);
254                } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
255                    result = mWindowManager.viewServerListWindows(mClient);
256                } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
257                    result = mWindowManager.viewServerGetFocusedWindow(mClient);
258                } else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
259                    result = windowManagerAutolistLoop();
260                } else {
261                    result = mWindowManager.viewServerWindowCommand(mClient,
262                            command, parameters);
263                }
264
265                if (!result) {
266                    Slog.w(LOG_TAG, "An error occured with the command: " + command);
267                }
268            } catch(IOException e) {
269                Slog.w(LOG_TAG, "Connection error: ", e);
270            } finally {
271                if (in != null) {
272                    try {
273                        in.close();
274
275                    } catch (IOException e) {
276                        e.printStackTrace();
277                    }
278                }
279                if (mClient != null) {
280                    try {
281                        mClient.close();
282                    } catch (IOException e) {
283                        e.printStackTrace();
284                    }
285                }
286            }
287        }
288
289        public void windowsChanged() {
290            synchronized(this) {
291                mNeedWindowListUpdate = true;
292                notifyAll();
293            }
294        }
295
296        public void focusChanged() {
297            synchronized(this) {
298                mNeedFocusedWindowUpdate = true;
299                notifyAll();
300            }
301        }
302
303        private boolean windowManagerAutolistLoop() {
304            mWindowManager.addWindowChangeListener(this);
305            BufferedWriter out = null;
306            try {
307                out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
308                while (!Thread.interrupted()) {
309                    boolean needWindowListUpdate = false;
310                    boolean needFocusedWindowUpdate = false;
311                    synchronized (this) {
312                        while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
313                            wait();
314                        }
315                        if (mNeedWindowListUpdate) {
316                            mNeedWindowListUpdate = false;
317                            needWindowListUpdate = true;
318                        }
319                        if (mNeedFocusedWindowUpdate) {
320                            mNeedFocusedWindowUpdate = false;
321                            needFocusedWindowUpdate = true;
322                        }
323                    }
324                    if(needWindowListUpdate) {
325                        out.write("LIST UPDATE\n");
326                        out.flush();
327                    }
328                    if(needFocusedWindowUpdate) {
329                        out.write("FOCUS UPDATE\n");
330                        out.flush();
331                    }
332                }
333            } catch (Exception e) {
334                Slog.w(LOG_TAG, "Connection error: ", e);
335            } finally {
336                if (out != null) {
337                    try {
338                        out.close();
339                    } catch (IOException e) {
340                    }
341                }
342                mWindowManager.removeWindowChangeListener(this);
343            }
344            return true;
345        }
346    }
347}
348