ViewServer.java revision b2a4b52e8d5e499d33e2765e8c47851bf0266299
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 17package com.android.server.wm; 18 19 20import android.util.Slog; 21 22import java.net.ServerSocket; 23import java.net.Socket; 24import java.net.InetAddress; 25import java.util.concurrent.ExecutorService; 26import java.util.concurrent.Executors; 27import java.io.IOException; 28import java.io.BufferedReader; 29import java.io.InputStreamReader; 30import java.io.OutputStream; 31import java.io.BufferedWriter; 32import java.io.OutputStreamWriter; 33 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 37 * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation. 38 * 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 47 private static final int VIEW_SERVER_MAX_CONNECTIONS = 10; 48 49 // Debug facility 50 private static final String LOG_TAG = "ViewServer"; 51 52 private static final String VALUE_PROTOCOL_VERSION = "4"; 53 private static final String VALUE_SERVER_VERSION = "4"; 54 55 // Protocol commands 56 // 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"; 60 // Lists all of the available windows in the system 61 private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST"; 62 // Keeps a connection open and notifies when the list of windows changes 63 private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST"; 64 // Returns the focused window 65 private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS"; 66 67 private ServerSocket mServer; 68 private Thread mThread; 69 70 private final WindowManagerService mWindowManager; 71 private final int mPort; 72 73 private ExecutorService mThreadPool; 74 75 /** 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 117 mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost()); 118 mThread = new Thread(this, "Remote View Server [port=" + mPort + "]"); 119 mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS); 120 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) { 137 138 mThread.interrupt(); 139 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; 147 mThread = null; 148 try { 149 mServer.close(); 150 mServer = null; 151 return true; 152 } catch (IOException e) { 153 Slog.w(LOG_TAG, "Could not close the view server"); 154 } 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() { 176 while (Thread.currentThread() == mThread) { 177 // Any uncaught exception will crash the system process 178 try { 179 Socket client = mServer.accept(); 180 if(mThreadPool != null) { 181 mThreadPool.submit(new ViewServerWorker(client)); 182 } else { 183 try { 184 client.close(); 185 } catch (IOException e) { 186 e.printStackTrace(); 187 } 188 } 189 } catch (Exception e) { 190 Slog.w(LOG_TAG, "Connection error: ", e); 191 } 192 } 193 } 194 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 } 218 219 class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener { 220 private Socket mClient; 221 private boolean mNeedWindowListUpdate; 222 private boolean mNeedFocusedWindowUpdate; 223 public ViewServerWorker(Socket client) { 224 mClient = client; 225 mNeedWindowListUpdate = false; 226 mNeedFocusedWindowUpdate = false; 227 } 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); 256 } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) { 257 result = mWindowManager.viewServerGetFocusedWindow(mClient); 258 } 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 296 public void focusChanged() { 297 synchronized(this) { 298 mNeedFocusedWindowUpdate = true; 299 notifyAll(); 300 } 301 } 302 303 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()) { 309 boolean needWindowListUpdate = false; 310 boolean needFocusedWindowUpdate = false; 311 synchronized (this) { 312 while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) { 313 wait(); 314 } 315 if (mNeedWindowListUpdate) { 316 mNeedWindowListUpdate = false; 317 needWindowListUpdate = true; 318 } 319 if (mNeedFocusedWindowUpdate) { 320 mNeedFocusedWindowUpdate = false; 321 needFocusedWindowUpdate = true; 322 } 323 } 324 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 } 332 } 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 } 347} 348