blob: 7b5d18ac8b2c1fb130cb3b427f4efe6ef0b4a3b5 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
18
Joe Onorato8a9b2202010-02-26 18:56:32 -080019import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020
21import java.net.ServerSocket;
22import java.net.Socket;
23import java.net.InetAddress;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070024import java.util.concurrent.ExecutorService;
25import java.util.concurrent.Executors;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import java.io.IOException;
27import java.io.BufferedReader;
28import java.io.InputStreamReader;
The Android Open Source Project10592532009-03-18 17:39:46 -070029import java.io.OutputStream;
30import java.io.BufferedWriter;
31import java.io.OutputStreamWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
33/**
34 * The ViewServer is local socket server that can be used to communicate with the
35 * views of the opened windows. Communication with the views is ensured by the
36 * {@link com.android.server.WindowManagerService} and is a cross-process operation.
37 *
38 * {@hide}
39 */
40class ViewServer implements Runnable {
41 /**
42 * The default port used to start view servers.
43 */
44 public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
45
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070046 private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
47
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 // Debug facility
49 private static final String LOG_TAG = "ViewServer";
50
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070051 private static final String VALUE_PROTOCOL_VERSION = "3";
52 private static final String VALUE_SERVER_VERSION = "4";
The Android Open Source Project10592532009-03-18 17:39:46 -070053
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 // Protocol commands
The Android Open Source Project10592532009-03-18 17:39:46 -070055 // Returns the protocol version
56 private static final String COMMAND_PROTOCOL_VERSION = "PROTOCOL";
57 // Returns the server version
58 private static final String COMMAND_SERVER_VERSION = "SERVER";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 // Lists all of the available windows in the system
60 private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070061 // Keeps a connection open and notifies when the list of windows changes
62 private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
Konstantin Lopyrevf9624762010-07-14 17:02:37 -070063 // Returns the focused window
64 private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
66 private ServerSocket mServer;
67 private Thread mThread;
68
69 private final WindowManagerService mWindowManager;
70 private final int mPort;
71
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070072 private ExecutorService mThreadPool;
73
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 /**
75 * Creates a new ViewServer associated with the specified window manager.
76 * The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
77 * is not started by default.
78 *
79 * @param windowManager The window manager used to communicate with the views.
80 *
81 * @see #start()
82 */
83 ViewServer(WindowManagerService windowManager) {
84 this(windowManager, VIEW_SERVER_DEFAULT_PORT);
85 }
86
87 /**
88 * Creates a new ViewServer associated with the specified window manager on the
89 * specified local port. The server is not started by default.
90 *
91 * @param windowManager The window manager used to communicate with the views.
92 * @param port The port for the server to listen to.
93 *
94 * @see #start()
95 */
96 ViewServer(WindowManagerService windowManager, int port) {
97 mWindowManager = windowManager;
98 mPort = port;
99 }
100
101 /**
102 * Starts the server.
103 *
104 * @return True if the server was successfully created, or false if it already exists.
105 * @throws IOException If the server cannot be created.
106 *
107 * @see #stop()
108 * @see #isRunning()
109 * @see WindowManagerService#startViewServer(int)
110 */
111 boolean start() throws IOException {
112 if (mThread != null) {
113 return false;
114 }
115
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700116 mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700118 mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 mThread.start();
120
121 return true;
122 }
123
124 /**
125 * Stops the server.
126 *
127 * @return True if the server was stopped, false if an error occured or if the
128 * server wasn't started.
129 *
130 * @see #start()
131 * @see #isRunning()
132 * @see WindowManagerService#stopViewServer()
133 */
134 boolean stop() {
135 if (mThread != null) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 mThread.interrupt();
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700138 if (mThreadPool != null) {
139 try {
140 mThreadPool.shutdownNow();
141 } catch (SecurityException e) {
142 Slog.w(LOG_TAG, "Could not stop all view server threads");
143 }
144 }
145 mThreadPool = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 mThread = null;
147 try {
148 mServer.close();
149 mServer = null;
150 return true;
151 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800152 Slog.w(LOG_TAG, "Could not close the view server");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 }
154 }
155 return false;
156 }
157
158 /**
159 * Indicates whether the server is currently running.
160 *
161 * @return True if the server is running, false otherwise.
162 *
163 * @see #start()
164 * @see #stop()
165 * @see WindowManagerService#isViewServerRunning()
166 */
167 boolean isRunning() {
168 return mThread != null && mThread.isAlive();
169 }
170
171 /**
172 * Main server loop.
173 */
174 public void run() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 while (Thread.currentThread() == mThread) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 // Any uncaught exception will crash the system process
177 try {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700178 Socket client = mServer.accept();
179 if(mThreadPool != null) {
180 mThreadPool.submit(new ViewServerWorker(client));
181 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 try {
183 client.close();
184 } catch (IOException e) {
185 e.printStackTrace();
186 }
187 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700188 } catch (Exception e) {
189 Slog.w(LOG_TAG, "Connection error: ", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 }
191 }
192 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700193
194 private static boolean writeValue(Socket client, String value) {
195 boolean result;
196 BufferedWriter out = null;
197 try {
198 OutputStream clientStream = client.getOutputStream();
199 out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
200 out.write(value);
201 out.write("\n");
202 out.flush();
203 result = true;
204 } catch (Exception e) {
205 result = false;
206 } finally {
207 if (out != null) {
208 try {
209 out.close();
210 } catch (IOException e) {
211 result = false;
212 }
213 }
214 }
215 return result;
216 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700217
218 class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
219 private Socket mClient;
220 private boolean mNeedWindowListUpdate;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700221 private boolean mNeedFocusedWindowUpdate;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700222 public ViewServerWorker(Socket client) {
223 mClient = client;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700224 mNeedWindowListUpdate = false;
225 mNeedFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700226 }
227
228 public void run() {
229
230 BufferedReader in = null;
231 try {
232 in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
233
234 final String request = in.readLine();
235
236 String command;
237 String parameters;
238
239 int index = request.indexOf(' ');
240 if (index == -1) {
241 command = request;
242 parameters = "";
243 } else {
244 command = request.substring(0, index);
245 parameters = request.substring(index + 1);
246 }
247
248 boolean result;
249 if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
250 result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
251 } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
252 result = writeValue(mClient, VALUE_SERVER_VERSION);
253 } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
254 result = mWindowManager.viewServerListWindows(mClient);
Konstantin Lopyrevf9624762010-07-14 17:02:37 -0700255 } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
256 result = mWindowManager.viewServerGetFocusedWindow(mClient);
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700257 } else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
258 result = windowManagerAutolistLoop();
259 } else {
260 result = mWindowManager.viewServerWindowCommand(mClient,
261 command, parameters);
262 }
263
264 if (!result) {
265 Slog.w(LOG_TAG, "An error occured with the command: " + command);
266 }
267 } catch(IOException e) {
268 Slog.w(LOG_TAG, "Connection error: ", e);
269 } finally {
270 if (in != null) {
271 try {
272 in.close();
273
274 } catch (IOException e) {
275 e.printStackTrace();
276 }
277 }
278 if (mClient != null) {
279 try {
280 mClient.close();
281 } catch (IOException e) {
282 e.printStackTrace();
283 }
284 }
285 }
286 }
287
288 public void windowsChanged() {
289 synchronized(this) {
290 mNeedWindowListUpdate = true;
291 notifyAll();
292 }
293 }
294
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700295 public void focusChanged() {
296 synchronized(this) {
297 mNeedFocusedWindowUpdate = true;
298 notifyAll();
299 }
300 }
301
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700302 private boolean windowManagerAutolistLoop() {
303 mWindowManager.addWindowChangeListener(this);
304 BufferedWriter out = null;
305 try {
306 out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
307 while (!Thread.interrupted()) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700308 boolean needWindowListUpdate = false;
309 boolean needFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700310 synchronized (this) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700311 while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700312 wait();
313 }
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700314 if (mNeedWindowListUpdate) {
315 mNeedWindowListUpdate = false;
316 needWindowListUpdate = true;
317 }
318 if (mNeedFocusedWindowUpdate) {
319 mNeedFocusedWindowUpdate = false;
320 needFocusedWindowUpdate = true;
321 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700322 }
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700323 if(needWindowListUpdate) {
324 out.write("LIST UPDATE\n");
325 out.flush();
326 }
327 if(needFocusedWindowUpdate) {
328 out.write("FOCUS UPDATE\n");
329 out.flush();
330 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700331 }
332 } catch (Exception e) {
333 Slog.w(LOG_TAG, "Connection error: ", e);
334 } finally {
335 if (out != null) {
336 try {
337 out.close();
338 } catch (IOException e) {
339 }
340 }
341 mWindowManager.removeWindowChangeListener(this);
342 }
343 return true;
344 }
345 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346}