blob: b369f716c7c550f357cc94f2a6e81ce2542a69eb [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";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
64 private ServerSocket mServer;
65 private Thread mThread;
66
67 private final WindowManagerService mWindowManager;
68 private final int mPort;
69
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070070 private ExecutorService mThreadPool;
71
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 /**
73 * Creates a new ViewServer associated with the specified window manager.
74 * The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
75 * is not started by default.
76 *
77 * @param windowManager The window manager used to communicate with the views.
78 *
79 * @see #start()
80 */
81 ViewServer(WindowManagerService windowManager) {
82 this(windowManager, VIEW_SERVER_DEFAULT_PORT);
83 }
84
85 /**
86 * Creates a new ViewServer associated with the specified window manager on the
87 * specified local port. The server is not started by default.
88 *
89 * @param windowManager The window manager used to communicate with the views.
90 * @param port The port for the server to listen to.
91 *
92 * @see #start()
93 */
94 ViewServer(WindowManagerService windowManager, int port) {
95 mWindowManager = windowManager;
96 mPort = port;
97 }
98
99 /**
100 * Starts the server.
101 *
102 * @return True if the server was successfully created, or false if it already exists.
103 * @throws IOException If the server cannot be created.
104 *
105 * @see #stop()
106 * @see #isRunning()
107 * @see WindowManagerService#startViewServer(int)
108 */
109 boolean start() throws IOException {
110 if (mThread != null) {
111 return false;
112 }
113
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700114 mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700116 mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 mThread.start();
118
119 return true;
120 }
121
122 /**
123 * Stops the server.
124 *
125 * @return True if the server was stopped, false if an error occured or if the
126 * server wasn't started.
127 *
128 * @see #start()
129 * @see #isRunning()
130 * @see WindowManagerService#stopViewServer()
131 */
132 boolean stop() {
133 if (mThread != null) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 mThread.interrupt();
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700136 if (mThreadPool != null) {
137 try {
138 mThreadPool.shutdownNow();
139 } catch (SecurityException e) {
140 Slog.w(LOG_TAG, "Could not stop all view server threads");
141 }
142 }
143 mThreadPool = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 mThread = null;
145 try {
146 mServer.close();
147 mServer = null;
148 return true;
149 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800150 Slog.w(LOG_TAG, "Could not close the view server");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 }
152 }
153 return false;
154 }
155
156 /**
157 * Indicates whether the server is currently running.
158 *
159 * @return True if the server is running, false otherwise.
160 *
161 * @see #start()
162 * @see #stop()
163 * @see WindowManagerService#isViewServerRunning()
164 */
165 boolean isRunning() {
166 return mThread != null && mThread.isAlive();
167 }
168
169 /**
170 * Main server loop.
171 */
172 public void run() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 while (Thread.currentThread() == mThread) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 // Any uncaught exception will crash the system process
175 try {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700176 Socket client = mServer.accept();
177 if(mThreadPool != null) {
178 mThreadPool.submit(new ViewServerWorker(client));
179 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 try {
181 client.close();
182 } catch (IOException e) {
183 e.printStackTrace();
184 }
185 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700186 } catch (Exception e) {
187 Slog.w(LOG_TAG, "Connection error: ", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 }
189 }
190 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700191
192 private static boolean writeValue(Socket client, String value) {
193 boolean result;
194 BufferedWriter out = null;
195 try {
196 OutputStream clientStream = client.getOutputStream();
197 out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
198 out.write(value);
199 out.write("\n");
200 out.flush();
201 result = true;
202 } catch (Exception e) {
203 result = false;
204 } finally {
205 if (out != null) {
206 try {
207 out.close();
208 } catch (IOException e) {
209 result = false;
210 }
211 }
212 }
213 return result;
214 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700215
216 class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
217 private Socket mClient;
218 private boolean mNeedWindowListUpdate;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700219 private boolean mNeedFocusedWindowUpdate;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700220 public ViewServerWorker(Socket client) {
221 mClient = client;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700222 mNeedWindowListUpdate = false;
223 mNeedFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700224 }
225
226 public void run() {
227
228 BufferedReader in = null;
229 try {
230 in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
231
232 final String request = in.readLine();
233
234 String command;
235 String parameters;
236
237 int index = request.indexOf(' ');
238 if (index == -1) {
239 command = request;
240 parameters = "";
241 } else {
242 command = request.substring(0, index);
243 parameters = request.substring(index + 1);
244 }
245
246 boolean result;
247 if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
248 result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
249 } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
250 result = writeValue(mClient, VALUE_SERVER_VERSION);
251 } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
252 result = mWindowManager.viewServerListWindows(mClient);
253 } else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
254 result = windowManagerAutolistLoop();
255 } else {
256 result = mWindowManager.viewServerWindowCommand(mClient,
257 command, parameters);
258 }
259
260 if (!result) {
261 Slog.w(LOG_TAG, "An error occured with the command: " + command);
262 }
263 } catch(IOException e) {
264 Slog.w(LOG_TAG, "Connection error: ", e);
265 } finally {
266 if (in != null) {
267 try {
268 in.close();
269
270 } catch (IOException e) {
271 e.printStackTrace();
272 }
273 }
274 if (mClient != null) {
275 try {
276 mClient.close();
277 } catch (IOException e) {
278 e.printStackTrace();
279 }
280 }
281 }
282 }
283
284 public void windowsChanged() {
285 synchronized(this) {
286 mNeedWindowListUpdate = true;
287 notifyAll();
288 }
289 }
290
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700291 public void focusChanged() {
292 synchronized(this) {
293 mNeedFocusedWindowUpdate = true;
294 notifyAll();
295 }
296 }
297
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700298 private boolean windowManagerAutolistLoop() {
299 mWindowManager.addWindowChangeListener(this);
300 BufferedWriter out = null;
301 try {
302 out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
303 while (!Thread.interrupted()) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700304 boolean needWindowListUpdate = false;
305 boolean needFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700306 synchronized (this) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700307 while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700308 wait();
309 }
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700310 if (mNeedWindowListUpdate) {
311 mNeedWindowListUpdate = false;
312 needWindowListUpdate = true;
313 }
314 if (mNeedFocusedWindowUpdate) {
315 mNeedFocusedWindowUpdate = false;
316 needFocusedWindowUpdate = true;
317 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700318 }
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700319 if(needWindowListUpdate) {
320 out.write("LIST UPDATE\n");
321 out.flush();
322 }
323 if(needFocusedWindowUpdate) {
324 out.write("FOCUS UPDATE\n");
325 out.flush();
326 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700327 }
328 } catch (Exception e) {
329 Slog.w(LOG_TAG, "Connection error: ", e);
330 } finally {
331 if (out != null) {
332 try {
333 out.close();
334 } catch (IOException e) {
335 }
336 }
337 mWindowManager.removeWindowChangeListener(this);
338 }
339 return true;
340 }
341 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342}