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; 18 19import android.util.Slog; 20 21import java.net.ServerSocket; 22import java.net.Socket; 23import java.net.InetAddress; 24import java.util.concurrent.ExecutorService; 25import java.util.concurrent.Executors; 26import java.io.IOException; 27import java.io.BufferedReader; 28import java.io.InputStreamReader; 29import java.io.OutputStream; 30import java.io.BufferedWriter; 31import java.io.OutputStreamWriter; 32 33/** 34 * The ViewServer is local socket server that can be used to communicate with the 35 * views of the opened windows. Communication with the views is ensured by the 36 * {@link com.android.server.WindowManagerService} and is a cross-process operation. 37 * 38 * {@hide} 39 */ 40class ViewServer implements Runnable { 41 /** 42 * The default port used to start view servers. 43 */ 44 public static final int VIEW_SERVER_DEFAULT_PORT = 4939; 45 46 private static final int VIEW_SERVER_MAX_CONNECTIONS = 10; 47 48 // Debug facility 49 private static final String LOG_TAG = "ViewServer"; 50 51 private static final String VALUE_PROTOCOL_VERSION = "3"; 52 private static final String VALUE_SERVER_VERSION = "4"; 53 54 // Protocol commands 55 // Returns the protocol version 56 private static final String COMMAND_PROTOCOL_VERSION = "PROTOCOL"; 57 // Returns the server version 58 private static final String COMMAND_SERVER_VERSION = "SERVER"; 59 // Lists all of the available windows in the system 60 private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST"; 61 // Keeps a connection open and notifies when the list of windows changes 62 private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST"; 63 // Returns the focused window 64 private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS"; 65 66 private ServerSocket mServer; 67 private Thread mThread; 68 69 private final WindowManagerService mWindowManager; 70 private final int mPort; 71 72 private ExecutorService mThreadPool; 73 74 /** 75 * Creates a new ViewServer associated with the specified window manager. 76 * The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server 77 * is not started by default. 78 * 79 * @param windowManager The window manager used to communicate with the views. 80 * 81 * @see #start() 82 */ 83 ViewServer(WindowManagerService windowManager) { 84 this(windowManager, VIEW_SERVER_DEFAULT_PORT); 85 } 86 87 /** 88 * Creates a new ViewServer associated with the specified window manager on the 89 * specified local port. The server is not started by default. 90 * 91 * @param windowManager The window manager used to communicate with the views. 92 * @param port The port for the server to listen to. 93 * 94 * @see #start() 95 */ 96 ViewServer(WindowManagerService windowManager, int port) { 97 mWindowManager = windowManager; 98 mPort = port; 99 } 100 101 /** 102 * Starts the server. 103 * 104 * @return True if the server was successfully created, or false if it already exists. 105 * @throws IOException If the server cannot be created. 106 * 107 * @see #stop() 108 * @see #isRunning() 109 * @see WindowManagerService#startViewServer(int) 110 */ 111 boolean start() throws IOException { 112 if (mThread != null) { 113 return false; 114 } 115 116 mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost()); 117 mThread = new Thread(this, "Remote View Server [port=" + mPort + "]"); 118 mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS); 119 mThread.start(); 120 121 return true; 122 } 123 124 /** 125 * Stops the server. 126 * 127 * @return True if the server was stopped, false if an error occured or if the 128 * server wasn't started. 129 * 130 * @see #start() 131 * @see #isRunning() 132 * @see WindowManagerService#stopViewServer() 133 */ 134 boolean stop() { 135 if (mThread != null) { 136 137 mThread.interrupt(); 138 if (mThreadPool != null) { 139 try { 140 mThreadPool.shutdownNow(); 141 } catch (SecurityException e) { 142 Slog.w(LOG_TAG, "Could not stop all view server threads"); 143 } 144 } 145 mThreadPool = null; 146 mThread = null; 147 try { 148 mServer.close(); 149 mServer = null; 150 return true; 151 } catch (IOException e) { 152 Slog.w(LOG_TAG, "Could not close the view server"); 153 } 154 } 155 return false; 156 } 157 158 /** 159 * Indicates whether the server is currently running. 160 * 161 * @return True if the server is running, false otherwise. 162 * 163 * @see #start() 164 * @see #stop() 165 * @see WindowManagerService#isViewServerRunning() 166 */ 167 boolean isRunning() { 168 return mThread != null && mThread.isAlive(); 169 } 170 171 /** 172 * Main server loop. 173 */ 174 public void run() { 175 while (Thread.currentThread() == mThread) { 176 // Any uncaught exception will crash the system process 177 try { 178 Socket client = mServer.accept(); 179 if(mThreadPool != null) { 180 mThreadPool.submit(new ViewServerWorker(client)); 181 } else { 182 try { 183 client.close(); 184 } catch (IOException e) { 185 e.printStackTrace(); 186 } 187 } 188 } catch (Exception e) { 189 Slog.w(LOG_TAG, "Connection error: ", e); 190 } 191 } 192 } 193 194 private static boolean writeValue(Socket client, String value) { 195 boolean result; 196 BufferedWriter out = null; 197 try { 198 OutputStream clientStream = client.getOutputStream(); 199 out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024); 200 out.write(value); 201 out.write("\n"); 202 out.flush(); 203 result = true; 204 } catch (Exception e) { 205 result = false; 206 } finally { 207 if (out != null) { 208 try { 209 out.close(); 210 } catch (IOException e) { 211 result = false; 212 } 213 } 214 } 215 return result; 216 } 217 218 class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener { 219 private Socket mClient; 220 private boolean mNeedWindowListUpdate; 221 private boolean mNeedFocusedWindowUpdate; 222 public ViewServerWorker(Socket client) { 223 mClient = client; 224 mNeedWindowListUpdate = false; 225 mNeedFocusedWindowUpdate = false; 226 } 227 228 public void run() { 229 230 BufferedReader in = null; 231 try { 232 in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024); 233 234 final String request = in.readLine(); 235 236 String command; 237 String parameters; 238 239 int index = request.indexOf(' '); 240 if (index == -1) { 241 command = request; 242 parameters = ""; 243 } else { 244 command = request.substring(0, index); 245 parameters = request.substring(index + 1); 246 } 247 248 boolean result; 249 if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) { 250 result = writeValue(mClient, VALUE_PROTOCOL_VERSION); 251 } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) { 252 result = writeValue(mClient, VALUE_SERVER_VERSION); 253 } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) { 254 result = mWindowManager.viewServerListWindows(mClient); 255 } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) { 256 result = mWindowManager.viewServerGetFocusedWindow(mClient); 257 } else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) { 258 result = windowManagerAutolistLoop(); 259 } else { 260 result = mWindowManager.viewServerWindowCommand(mClient, 261 command, parameters); 262 } 263 264 if (!result) { 265 Slog.w(LOG_TAG, "An error occured with the command: " + command); 266 } 267 } catch(IOException e) { 268 Slog.w(LOG_TAG, "Connection error: ", e); 269 } finally { 270 if (in != null) { 271 try { 272 in.close(); 273 274 } catch (IOException e) { 275 e.printStackTrace(); 276 } 277 } 278 if (mClient != null) { 279 try { 280 mClient.close(); 281 } catch (IOException e) { 282 e.printStackTrace(); 283 } 284 } 285 } 286 } 287 288 public void windowsChanged() { 289 synchronized(this) { 290 mNeedWindowListUpdate = true; 291 notifyAll(); 292 } 293 } 294 295 public void focusChanged() { 296 synchronized(this) { 297 mNeedFocusedWindowUpdate = true; 298 notifyAll(); 299 } 300 } 301 302 private boolean windowManagerAutolistLoop() { 303 mWindowManager.addWindowChangeListener(this); 304 BufferedWriter out = null; 305 try { 306 out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream())); 307 while (!Thread.interrupted()) { 308 boolean needWindowListUpdate = false; 309 boolean needFocusedWindowUpdate = false; 310 synchronized (this) { 311 while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) { 312 wait(); 313 } 314 if (mNeedWindowListUpdate) { 315 mNeedWindowListUpdate = false; 316 needWindowListUpdate = true; 317 } 318 if (mNeedFocusedWindowUpdate) { 319 mNeedFocusedWindowUpdate = false; 320 needFocusedWindowUpdate = true; 321 } 322 } 323 if(needWindowListUpdate) { 324 out.write("LIST UPDATE\n"); 325 out.flush(); 326 } 327 if(needFocusedWindowUpdate) { 328 out.write("FOCUS UPDATE\n"); 329 out.flush(); 330 } 331 } 332 } catch (Exception e) { 333 Slog.w(LOG_TAG, "Connection error: ", e); 334 } finally { 335 if (out != null) { 336 try { 337 out.close(); 338 } catch (IOException e) { 339 } 340 } 341 mWindowManager.removeWindowChangeListener(this); 342 } 343 return true; 344 } 345 } 346} 347