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