• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
18 
19 import android.util.Slog;
20 
21 import java.net.ServerSocket;
22 import java.net.Socket;
23 import java.net.InetAddress;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors;
26 import java.io.IOException;
27 import java.io.BufferedReader;
28 import java.io.InputStreamReader;
29 import java.io.OutputStream;
30 import java.io.BufferedWriter;
31 import 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  */
40 class 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      */
ViewServer(WindowManagerService windowManager)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      */
ViewServer(WindowManagerService windowManager, int port)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      */
start()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      */
stop()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      */
isRunning()167     boolean isRunning() {
168         return mThread != null && mThread.isAlive();
169     }
170 
171     /**
172      * Main server loop.
173      */
run()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 
writeValue(Socket client, String value)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;
ViewServerWorker(Socket client)222         public ViewServerWorker(Socket client) {
223             mClient = client;
224             mNeedWindowListUpdate = false;
225             mNeedFocusedWindowUpdate = false;
226         }
227 
run()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 
windowsChanged()288         public void windowsChanged() {
289             synchronized(this) {
290                 mNeedWindowListUpdate = true;
291                 notifyAll();
292             }
293         }
294 
focusChanged()295         public void focusChanged() {
296             synchronized(this) {
297                 mNeedFocusedWindowUpdate = true;
298                 notifyAll();
299             }
300         }
301 
windowManagerAutolistLoop()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