blob: a763e2c724c47875a1fb2252e66a2482a2fa6828 [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
Dianne Hackborna924dc0d2011-02-17 14:22:17 -080017package com.android.server.wm;
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019
Joe Onorato8a9b2202010-02-26 18:56:32 -080020import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021
22import java.net.ServerSocket;
23import java.net.Socket;
24import java.net.InetAddress;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070025import java.util.concurrent.ExecutorService;
26import java.util.concurrent.Executors;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import java.io.IOException;
28import java.io.BufferedReader;
29import java.io.InputStreamReader;
The Android Open Source Project10592532009-03-18 17:39:46 -070030import java.io.OutputStream;
31import java.io.BufferedWriter;
32import java.io.OutputStreamWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
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
Dianne Hackborna924dc0d2011-02-17 14:22:17 -080037 * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038 *
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
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070047 private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
48
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049 // Debug facility
50 private static final String LOG_TAG = "ViewServer";
51
Chet Haaseed30fd82011-04-22 16:18:45 -070052 private static final String VALUE_PROTOCOL_VERSION = "4";
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070053 private static final String VALUE_SERVER_VERSION = "4";
The Android Open Source Project10592532009-03-18 17:39:46 -070054
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 // Protocol commands
The Android Open Source Project10592532009-03-18 17:39:46 -070056 // 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";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 // Lists all of the available windows in the system
61 private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070062 // Keeps a connection open and notifies when the list of windows changes
63 private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
Konstantin Lopyrevf9624762010-07-14 17:02:37 -070064 // Returns the focused window
65 private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
67 private ServerSocket mServer;
68 private Thread mThread;
69
70 private final WindowManagerService mWindowManager;
71 private final int mPort;
72
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070073 private ExecutorService mThreadPool;
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 * Creates a new ViewServer associated with the specified window manager on the
77 * specified local port. The server is not started by default.
78 *
79 * @param windowManager The window manager used to communicate with the views.
80 * @param port The port for the server to listen to.
81 *
82 * @see #start()
83 */
84 ViewServer(WindowManagerService windowManager, int port) {
85 mWindowManager = windowManager;
86 mPort = port;
87 }
88
89 /**
90 * Starts the server.
91 *
92 * @return True if the server was successfully created, or false if it already exists.
93 * @throws IOException If the server cannot be created.
94 *
95 * @see #stop()
96 * @see #isRunning()
97 * @see WindowManagerService#startViewServer(int)
98 */
99 boolean start() throws IOException {
100 if (mThread != null) {
101 return false;
102 }
103
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700104 mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700106 mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 mThread.start();
108
109 return true;
110 }
111
112 /**
113 * Stops the server.
114 *
115 * @return True if the server was stopped, false if an error occured or if the
116 * server wasn't started.
117 *
118 * @see #start()
119 * @see #isRunning()
120 * @see WindowManagerService#stopViewServer()
121 */
122 boolean stop() {
123 if (mThread != null) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 mThread.interrupt();
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700126 if (mThreadPool != null) {
127 try {
128 mThreadPool.shutdownNow();
129 } catch (SecurityException e) {
130 Slog.w(LOG_TAG, "Could not stop all view server threads");
131 }
132 }
133 mThreadPool = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 mThread = null;
135 try {
136 mServer.close();
137 mServer = null;
138 return true;
139 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800140 Slog.w(LOG_TAG, "Could not close the view server");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 }
142 }
143 return false;
144 }
145
146 /**
147 * Indicates whether the server is currently running.
148 *
149 * @return True if the server is running, false otherwise.
150 *
151 * @see #start()
152 * @see #stop()
153 * @see WindowManagerService#isViewServerRunning()
154 */
155 boolean isRunning() {
156 return mThread != null && mThread.isAlive();
157 }
158
159 /**
160 * Main server loop.
161 */
162 public void run() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 while (Thread.currentThread() == mThread) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 // Any uncaught exception will crash the system process
165 try {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700166 Socket client = mServer.accept();
Romain Guy88b4f152011-05-19 16:15:46 -0700167 if (mThreadPool != null) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700168 mThreadPool.submit(new ViewServerWorker(client));
169 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 try {
171 client.close();
172 } catch (IOException e) {
173 e.printStackTrace();
174 }
175 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700176 } catch (Exception e) {
177 Slog.w(LOG_TAG, "Connection error: ", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 }
179 }
180 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700181
182 private static boolean writeValue(Socket client, String value) {
183 boolean result;
184 BufferedWriter out = null;
185 try {
186 OutputStream clientStream = client.getOutputStream();
187 out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
188 out.write(value);
189 out.write("\n");
190 out.flush();
191 result = true;
192 } catch (Exception e) {
193 result = false;
194 } finally {
195 if (out != null) {
196 try {
197 out.close();
198 } catch (IOException e) {
199 result = false;
200 }
201 }
202 }
203 return result;
204 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700205
206 class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
207 private Socket mClient;
208 private boolean mNeedWindowListUpdate;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700209 private boolean mNeedFocusedWindowUpdate;
Romain Guy88b4f152011-05-19 16:15:46 -0700210
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700211 public ViewServerWorker(Socket client) {
212 mClient = client;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700213 mNeedWindowListUpdate = false;
214 mNeedFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700215 }
216
217 public void run() {
218
219 BufferedReader in = null;
220 try {
221 in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
222
223 final String request = in.readLine();
224
225 String command;
226 String parameters;
227
228 int index = request.indexOf(' ');
229 if (index == -1) {
230 command = request;
231 parameters = "";
232 } else {
233 command = request.substring(0, index);
234 parameters = request.substring(index + 1);
235 }
236
237 boolean result;
238 if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
239 result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
240 } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
241 result = writeValue(mClient, VALUE_SERVER_VERSION);
242 } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
243 result = mWindowManager.viewServerListWindows(mClient);
Konstantin Lopyrevf9624762010-07-14 17:02:37 -0700244 } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
245 result = mWindowManager.viewServerGetFocusedWindow(mClient);
Romain Guy88b4f152011-05-19 16:15:46 -0700246 } else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700247 result = windowManagerAutolistLoop();
248 } else {
249 result = mWindowManager.viewServerWindowCommand(mClient,
250 command, parameters);
251 }
252
253 if (!result) {
Romain Guy88b4f152011-05-19 16:15:46 -0700254 Slog.w(LOG_TAG, "An error occurred with the command: " + command);
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700255 }
256 } catch(IOException e) {
257 Slog.w(LOG_TAG, "Connection error: ", e);
258 } finally {
259 if (in != null) {
260 try {
261 in.close();
262
263 } catch (IOException e) {
264 e.printStackTrace();
265 }
266 }
267 if (mClient != null) {
268 try {
269 mClient.close();
270 } catch (IOException e) {
271 e.printStackTrace();
272 }
273 }
274 }
275 }
276
277 public void windowsChanged() {
278 synchronized(this) {
279 mNeedWindowListUpdate = true;
280 notifyAll();
281 }
282 }
283
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700284 public void focusChanged() {
285 synchronized(this) {
286 mNeedFocusedWindowUpdate = true;
287 notifyAll();
288 }
289 }
290
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700291 private boolean windowManagerAutolistLoop() {
292 mWindowManager.addWindowChangeListener(this);
293 BufferedWriter out = null;
294 try {
295 out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
296 while (!Thread.interrupted()) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700297 boolean needWindowListUpdate = false;
298 boolean needFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700299 synchronized (this) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700300 while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700301 wait();
302 }
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700303 if (mNeedWindowListUpdate) {
304 mNeedWindowListUpdate = false;
305 needWindowListUpdate = true;
306 }
307 if (mNeedFocusedWindowUpdate) {
308 mNeedFocusedWindowUpdate = false;
309 needFocusedWindowUpdate = true;
310 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700311 }
Romain Guy88b4f152011-05-19 16:15:46 -0700312 if (needWindowListUpdate) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700313 out.write("LIST UPDATE\n");
314 out.flush();
315 }
Romain Guy88b4f152011-05-19 16:15:46 -0700316 if (needFocusedWindowUpdate) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700317 out.write("FOCUS UPDATE\n");
318 out.flush();
319 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700320 }
321 } catch (Exception e) {
Romain Guycbe67d52011-08-31 14:11:07 -0700322 // Ignore
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700323 } finally {
324 if (out != null) {
325 try {
326 out.close();
327 } catch (IOException e) {
Romain Guy88b4f152011-05-19 16:15:46 -0700328 // Ignore
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700329 }
330 }
331 mWindowManager.removeWindowChangeListener(this);
332 }
333 return true;
334 }
335 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336}