blob: 9fb35b966bfa073fbfa77ba91694954a41f4aafc [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 /**
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
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700117 mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700119 mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 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) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 mThread.interrupt();
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700139 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 mThread = null;
148 try {
149 mServer.close();
150 mServer = null;
151 return true;
152 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800153 Slog.w(LOG_TAG, "Could not close the view server");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 }
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() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 while (Thread.currentThread() == mThread) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 // Any uncaught exception will crash the system process
178 try {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700179 Socket client = mServer.accept();
180 if(mThreadPool != null) {
181 mThreadPool.submit(new ViewServerWorker(client));
182 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 try {
184 client.close();
185 } catch (IOException e) {
186 e.printStackTrace();
187 }
188 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700189 } catch (Exception e) {
190 Slog.w(LOG_TAG, "Connection error: ", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 }
192 }
193 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700194
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 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700218
219 class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
220 private Socket mClient;
221 private boolean mNeedWindowListUpdate;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700222 private boolean mNeedFocusedWindowUpdate;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700223 public ViewServerWorker(Socket client) {
224 mClient = client;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700225 mNeedWindowListUpdate = false;
226 mNeedFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700227 }
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);
Konstantin Lopyrevf9624762010-07-14 17:02:37 -0700256 } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
257 result = mWindowManager.viewServerGetFocusedWindow(mClient);
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700258 } 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
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700296 public void focusChanged() {
297 synchronized(this) {
298 mNeedFocusedWindowUpdate = true;
299 notifyAll();
300 }
301 }
302
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700303 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()) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700309 boolean needWindowListUpdate = false;
310 boolean needFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700311 synchronized (this) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700312 while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700313 wait();
314 }
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700315 if (mNeedWindowListUpdate) {
316 mNeedWindowListUpdate = false;
317 needWindowListUpdate = true;
318 }
319 if (mNeedFocusedWindowUpdate) {
320 mNeedFocusedWindowUpdate = false;
321 needFocusedWindowUpdate = true;
322 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700323 }
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700324 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 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700332 }
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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347}