1 /* 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 17 package com.android.server.wm; 18 19 20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 21 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 22 23 import android.util.Slog; 24 25 import java.net.ServerSocket; 26 import java.net.Socket; 27 import java.net.InetAddress; 28 import java.util.concurrent.ExecutorService; 29 import java.util.concurrent.Executors; 30 import java.io.IOException; 31 import java.io.BufferedReader; 32 import java.io.InputStreamReader; 33 import java.io.OutputStream; 34 import java.io.BufferedWriter; 35 import java.io.OutputStreamWriter; 36 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 40 * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation. 41 * 42 * {@hide} 43 */ 44 class 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 50 private static final int VIEW_SERVER_MAX_CONNECTIONS = 10; 51 52 // Debug facility 53 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "ViewServer" : TAG_WM; 54 55 private static final String VALUE_PROTOCOL_VERSION = "4"; 56 private static final String VALUE_SERVER_VERSION = "4"; 57 58 // Protocol commands 59 // 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"; 63 // Lists all of the available windows in the system 64 private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST"; 65 // Keeps a connection open and notifies when the list of windows changes 66 private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST"; 67 // Returns the focused window 68 private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS"; 69 70 private ServerSocket mServer; 71 private Thread mThread; 72 73 private final WindowManagerService mWindowManager; 74 private final int mPort; 75 76 private ExecutorService mThreadPool; 77 78 /** 79 * 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 107 mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost()); 108 mThread = new Thread(this, "Remote View Server [port=" + mPort + "]"); 109 mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS); 110 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) { 127 128 mThread.interrupt(); 129 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; 137 mThread = null; 138 try { 139 mServer.close(); 140 mServer = null; 141 return true; 142 } catch (IOException e) { 143 Slog.w(LOG_TAG, "Could not close the view server"); 144 } 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() 156 * @see WindowManagerService#isViewServerRunning() 157 */ 158 boolean isRunning() { 159 return mThread != null && mThread.isAlive(); 160 } 161 162 /** 163 * Main server loop. 164 */ 165 public void run() { 166 while (Thread.currentThread() == mThread) { 167 // Any uncaught exception will crash the system process 168 try { 169 Socket client = mServer.accept(); 170 if (mThreadPool != null) { 171 mThreadPool.submit(new ViewServerWorker(client)); 172 } else { 173 try { 174 client.close(); 175 } catch (IOException e) { 176 e.printStackTrace(); 177 } 178 } 179 } catch (Exception e) { 180 Slog.w(LOG_TAG, "Connection error: ", e); 181 } 182 } 183 } 184 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 } 208 209 class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener { 210 private Socket mClient; 211 private boolean mNeedWindowListUpdate; 212 private boolean mNeedFocusedWindowUpdate; 213 214 public ViewServerWorker(Socket client) { 215 mClient = client; 216 mNeedWindowListUpdate = false; 217 mNeedFocusedWindowUpdate = false; 218 } 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); 247 } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) { 248 result = mWindowManager.viewServerGetFocusedWindow(mClient); 249 } else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) { 250 result = windowManagerAutolistLoop(); 251 } else { 252 result = mWindowManager.viewServerWindowCommand(mClient, 253 command, parameters); 254 } 255 256 if (!result) { 257 Slog.w(LOG_TAG, "An error occurred with the command: " + command); 258 } 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 287 public void focusChanged() { 288 synchronized(this) { 289 mNeedFocusedWindowUpdate = true; 290 notifyAll(); 291 } 292 } 293 294 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()) { 300 boolean needWindowListUpdate = false; 301 boolean needFocusedWindowUpdate = false; 302 synchronized (this) { 303 while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) { 304 wait(); 305 } 306 if (mNeedWindowListUpdate) { 307 mNeedWindowListUpdate = false; 308 needWindowListUpdate = true; 309 } 310 if (mNeedFocusedWindowUpdate) { 311 mNeedFocusedWindowUpdate = false; 312 needFocusedWindowUpdate = true; 313 } 314 } 315 if (needWindowListUpdate) { 316 out.write("LIST UPDATE\n"); 317 out.flush(); 318 } 319 if (needFocusedWindowUpdate) { 320 out.write("ACTION_FOCUS UPDATE\n"); 321 out.flush(); 322 } 323 } 324 } catch (Exception e) { 325 // Ignore 326 } finally { 327 if (out != null) { 328 try { 329 out.close(); 330 } catch (IOException e) { 331 // Ignore 332 } 333 } 334 mWindowManager.removeWindowChangeListener(this); 335 } 336 return true; 337 } 338 } 339 } 340