blob: ecf5652a1e6914d71456df7348e08ac1cd2ff78a [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
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080020import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
22
Joe Onorato8a9b2202010-02-26 18:56:32 -080023import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024
25import java.net.ServerSocket;
26import java.net.Socket;
27import java.net.InetAddress;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070028import java.util.concurrent.ExecutorService;
29import java.util.concurrent.Executors;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import java.io.IOException;
31import java.io.BufferedReader;
32import java.io.InputStreamReader;
The Android Open Source Project10592532009-03-18 17:39:46 -070033import java.io.OutputStream;
34import java.io.BufferedWriter;
35import java.io.OutputStreamWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036
37/**
38 * The ViewServer is local socket server that can be used to communicate with the
39 * views of the opened windows. Communication with the views is ensured by the
Dianne Hackborna924dc0d2011-02-17 14:22:17 -080040 * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 *
42 * {@hide}
43 */
44class ViewServer implements Runnable {
45 /**
46 * The default port used to start view servers.
47 */
48 public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
49
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070050 private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
51
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 // Debug facility
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080053 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "ViewServer" : TAG_WM;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Chet Haaseed30fd82011-04-22 16:18:45 -070055 private static final String VALUE_PROTOCOL_VERSION = "4";
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070056 private static final String VALUE_SERVER_VERSION = "4";
The Android Open Source Project10592532009-03-18 17:39:46 -070057
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 // Protocol commands
The Android Open Source Project10592532009-03-18 17:39:46 -070059 // Returns the protocol version
60 private static final String COMMAND_PROTOCOL_VERSION = "PROTOCOL";
61 // Returns the server version
62 private static final String COMMAND_SERVER_VERSION = "SERVER";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 // Lists all of the available windows in the system
64 private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070065 // Keeps a connection open and notifies when the list of windows changes
66 private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
Konstantin Lopyrevf9624762010-07-14 17:02:37 -070067 // Returns the focused window
68 private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
70 private ServerSocket mServer;
71 private Thread mThread;
72
73 private final WindowManagerService mWindowManager;
74 private final int mPort;
75
Konstantin Lopyrevdc301012010-07-08 17:55:51 -070076 private ExecutorService mThreadPool;
77
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 * Creates a new ViewServer associated with the specified window manager on the
80 * specified local port. The server is not started by default.
81 *
82 * @param windowManager The window manager used to communicate with the views.
83 * @param port The port for the server to listen to.
84 *
85 * @see #start()
86 */
87 ViewServer(WindowManagerService windowManager, int port) {
88 mWindowManager = windowManager;
89 mPort = port;
90 }
91
92 /**
93 * Starts the server.
94 *
95 * @return True if the server was successfully created, or false if it already exists.
96 * @throws IOException If the server cannot be created.
97 *
98 * @see #stop()
99 * @see #isRunning()
100 * @see WindowManagerService#startViewServer(int)
101 */
102 boolean start() throws IOException {
103 if (mThread != null) {
104 return false;
105 }
106
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700107 mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700109 mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 mThread.start();
111
112 return true;
113 }
114
115 /**
116 * Stops the server.
117 *
118 * @return True if the server was stopped, false if an error occured or if the
119 * server wasn't started.
120 *
121 * @see #start()
122 * @see #isRunning()
123 * @see WindowManagerService#stopViewServer()
124 */
125 boolean stop() {
126 if (mThread != null) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 mThread.interrupt();
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700129 if (mThreadPool != null) {
130 try {
131 mThreadPool.shutdownNow();
132 } catch (SecurityException e) {
133 Slog.w(LOG_TAG, "Could not stop all view server threads");
134 }
135 }
136 mThreadPool = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 mThread = null;
138 try {
139 mServer.close();
140 mServer = null;
141 return true;
142 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800143 Slog.w(LOG_TAG, "Could not close the view server");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 }
145 }
146 return false;
147 }
148
149 /**
150 * Indicates whether the server is currently running.
151 *
152 * @return True if the server is running, false otherwise.
153 *
154 * @see #start()
155 * @see #stop()
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800156 * @see WindowManagerService#isViewServerRunning()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 */
158 boolean isRunning() {
159 return mThread != null && mThread.isAlive();
160 }
161
162 /**
163 * Main server loop.
164 */
165 public void run() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 while (Thread.currentThread() == mThread) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 // Any uncaught exception will crash the system process
168 try {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700169 Socket client = mServer.accept();
Romain Guy88b4f152011-05-19 16:15:46 -0700170 if (mThreadPool != null) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700171 mThreadPool.submit(new ViewServerWorker(client));
172 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 try {
174 client.close();
175 } catch (IOException e) {
176 e.printStackTrace();
177 }
178 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700179 } catch (Exception e) {
180 Slog.w(LOG_TAG, "Connection error: ", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 }
182 }
183 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700184
185 private static boolean writeValue(Socket client, String value) {
186 boolean result;
187 BufferedWriter out = null;
188 try {
189 OutputStream clientStream = client.getOutputStream();
190 out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
191 out.write(value);
192 out.write("\n");
193 out.flush();
194 result = true;
195 } catch (Exception e) {
196 result = false;
197 } finally {
198 if (out != null) {
199 try {
200 out.close();
201 } catch (IOException e) {
202 result = false;
203 }
204 }
205 }
206 return result;
207 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700208
209 class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
210 private Socket mClient;
211 private boolean mNeedWindowListUpdate;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700212 private boolean mNeedFocusedWindowUpdate;
Romain Guy88b4f152011-05-19 16:15:46 -0700213
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700214 public ViewServerWorker(Socket client) {
215 mClient = client;
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700216 mNeedWindowListUpdate = false;
217 mNeedFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700218 }
219
220 public void run() {
221
222 BufferedReader in = null;
223 try {
224 in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
225
226 final String request = in.readLine();
227
228 String command;
229 String parameters;
230
231 int index = request.indexOf(' ');
232 if (index == -1) {
233 command = request;
234 parameters = "";
235 } else {
236 command = request.substring(0, index);
237 parameters = request.substring(index + 1);
238 }
239
240 boolean result;
241 if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
242 result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
243 } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
244 result = writeValue(mClient, VALUE_SERVER_VERSION);
245 } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
246 result = mWindowManager.viewServerListWindows(mClient);
Konstantin Lopyrevf9624762010-07-14 17:02:37 -0700247 } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
248 result = mWindowManager.viewServerGetFocusedWindow(mClient);
Romain Guy88b4f152011-05-19 16:15:46 -0700249 } else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700250 result = windowManagerAutolistLoop();
251 } else {
252 result = mWindowManager.viewServerWindowCommand(mClient,
253 command, parameters);
254 }
255
256 if (!result) {
Romain Guy88b4f152011-05-19 16:15:46 -0700257 Slog.w(LOG_TAG, "An error occurred with the command: " + command);
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700258 }
259 } catch(IOException e) {
260 Slog.w(LOG_TAG, "Connection error: ", e);
261 } finally {
262 if (in != null) {
263 try {
264 in.close();
265
266 } catch (IOException e) {
267 e.printStackTrace();
268 }
269 }
270 if (mClient != null) {
271 try {
272 mClient.close();
273 } catch (IOException e) {
274 e.printStackTrace();
275 }
276 }
277 }
278 }
279
280 public void windowsChanged() {
281 synchronized(this) {
282 mNeedWindowListUpdate = true;
283 notifyAll();
284 }
285 }
286
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700287 public void focusChanged() {
288 synchronized(this) {
289 mNeedFocusedWindowUpdate = true;
290 notifyAll();
291 }
292 }
293
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700294 private boolean windowManagerAutolistLoop() {
295 mWindowManager.addWindowChangeListener(this);
296 BufferedWriter out = null;
297 try {
298 out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
299 while (!Thread.interrupted()) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700300 boolean needWindowListUpdate = false;
301 boolean needFocusedWindowUpdate = false;
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700302 synchronized (this) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700303 while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700304 wait();
305 }
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700306 if (mNeedWindowListUpdate) {
307 mNeedWindowListUpdate = false;
308 needWindowListUpdate = true;
309 }
310 if (mNeedFocusedWindowUpdate) {
311 mNeedFocusedWindowUpdate = false;
312 needFocusedWindowUpdate = true;
313 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700314 }
Romain Guy88b4f152011-05-19 16:15:46 -0700315 if (needWindowListUpdate) {
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700316 out.write("LIST UPDATE\n");
317 out.flush();
318 }
Romain Guy88b4f152011-05-19 16:15:46 -0700319 if (needFocusedWindowUpdate) {
Kristian Monsen74bc1942014-04-29 11:00:17 -0700320 out.write("ACTION_FOCUS UPDATE\n");
Konstantin Lopyrev6e0f65f2010-07-14 14:55:33 -0700321 out.flush();
322 }
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700323 }
324 } catch (Exception e) {
Romain Guycbe67d52011-08-31 14:11:07 -0700325 // Ignore
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700326 } finally {
327 if (out != null) {
328 try {
329 out.close();
330 } catch (IOException e) {
Romain Guy88b4f152011-05-19 16:15:46 -0700331 // Ignore
Konstantin Lopyrevdc301012010-07-08 17:55:51 -0700332 }
333 }
334 mWindowManager.removeWindowChangeListener(this);
335 }
336 return true;
337 }
338 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339}