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.io.IOException;
25import java.io.BufferedReader;
26import java.io.InputStreamReader;
27import java.io.OutputStream;
28import java.io.BufferedWriter;
29import java.io.OutputStreamWriter;
30
31/**
32 * The ViewServer is local socket server that can be used to communicate with the
33 * views of the opened windows. Communication with the views is ensured by the
34 * {@link com.android.server.WindowManagerService} and is a cross-process operation.
35 *
36 * {@hide}
37 */
38class ViewServer implements Runnable {
39    /**
40     * The default port used to start view servers.
41     */
42    public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
43
44    // Debug facility
45    private static final String LOG_TAG = "ViewServer";
46
47    private static final String VALUE_PROTOCOL_VERSION = "2";
48    private static final String VALUE_SERVER_VERSION = "3";
49
50    // Protocol commands
51    // Returns the protocol version
52    private static final String COMMAND_PROTOCOL_VERSION = "PROTOCOL";
53    // Returns the server version
54    private static final String COMMAND_SERVER_VERSION = "SERVER";
55    // Lists all of the available windows in the system
56    private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
57
58    private ServerSocket mServer;
59    private Thread mThread;
60
61    private final WindowManagerService mWindowManager;
62    private final int mPort;
63
64    /**
65     * Creates a new ViewServer associated with the specified window manager.
66     * The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
67     * is not started by default.
68     *
69     * @param windowManager The window manager used to communicate with the views.
70     *
71     * @see #start()
72     */
73    ViewServer(WindowManagerService windowManager) {
74        this(windowManager, VIEW_SERVER_DEFAULT_PORT);
75    }
76
77    /**
78     * Creates a new ViewServer associated with the specified window manager on the
79     * specified local port. The server is not started by default.
80     *
81     * @param windowManager The window manager used to communicate with the views.
82     * @param port The port for the server to listen to.
83     *
84     * @see #start()
85     */
86    ViewServer(WindowManagerService windowManager, int port) {
87        mWindowManager = windowManager;
88        mPort = port;
89    }
90
91    /**
92     * Starts the server.
93     *
94     * @return True if the server was successfully created, or false if it already exists.
95     * @throws IOException If the server cannot be created.
96     *
97     * @see #stop()
98     * @see #isRunning()
99     * @see WindowManagerService#startViewServer(int)
100     */
101    boolean start() throws IOException {
102        if (mThread != null) {
103            return false;
104        }
105
106        mServer = new ServerSocket(mPort, 1, InetAddress.getLocalHost());
107        mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
108        mThread.start();
109
110        return true;
111    }
112
113    /**
114     * Stops the server.
115     *
116     * @return True if the server was stopped, false if an error occured or if the
117     *         server wasn't started.
118     *
119     * @see #start()
120     * @see #isRunning()
121     * @see WindowManagerService#stopViewServer()
122     */
123    boolean stop() {
124        if (mThread != null) {
125            mThread.interrupt();
126            mThread = null;
127            try {
128                mServer.close();
129                mServer = null;
130                return true;
131            } catch (IOException e) {
132                Slog.w(LOG_TAG, "Could not close the view server");
133            }
134        }
135        return false;
136    }
137
138    /**
139     * Indicates whether the server is currently running.
140     *
141     * @return True if the server is running, false otherwise.
142     *
143     * @see #start()
144     * @see #stop()
145     * @see WindowManagerService#isViewServerRunning()
146     */
147    boolean isRunning() {
148        return mThread != null && mThread.isAlive();
149    }
150
151    /**
152     * Main server loop.
153     */
154    public void run() {
155        final ServerSocket server = mServer;
156
157        while (Thread.currentThread() == mThread) {
158            Socket client = null;
159            // Any uncaught exception will crash the system process
160            try {
161                client = server.accept();
162
163                BufferedReader in = null;
164                try {
165                    in = new BufferedReader(new InputStreamReader(client.getInputStream()), 1024);
166
167                    final String request = in.readLine();
168
169                    String command;
170                    String parameters;
171
172                    int index = request.indexOf(' ');
173                    if (index == -1) {
174                        command = request;
175                        parameters = "";
176                    } else {
177                        command = request.substring(0, index);
178                        parameters = request.substring(index + 1);
179                    }
180
181                    boolean result;
182                    if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
183                        result = writeValue(client, VALUE_PROTOCOL_VERSION);
184                    } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
185                        result = writeValue(client, VALUE_SERVER_VERSION);
186                    } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
187                        result = mWindowManager.viewServerListWindows(client);
188                    } else {
189                        result = mWindowManager.viewServerWindowCommand(client,
190                                command, parameters);
191                    }
192
193                    if (!result) {
194                        Slog.w(LOG_TAG, "An error occured with the command: " + command);
195                    }
196                } finally {
197                    if (in != null) {
198                        in.close();
199                    }
200                }
201            } catch (Exception e) {
202                Slog.w(LOG_TAG, "Connection error: ", e);
203            } finally {
204                if (client != null) {
205                    try {
206                        client.close();
207                    } catch (IOException e) {
208                        e.printStackTrace();
209                    }
210                }
211            }
212        }
213    }
214
215    private static boolean writeValue(Socket client, String value) {
216        boolean result;
217        BufferedWriter out = null;
218        try {
219            OutputStream clientStream = client.getOutputStream();
220            out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
221            out.write(value);
222            out.write("\n");
223            out.flush();
224            result = true;
225        } catch (Exception e) {
226            result = false;
227        } finally {
228            if (out != null) {
229                try {
230                    out.close();
231                } catch (IOException e) {
232                    result = false;
233                }
234            }
235        }
236        return result;
237    }
238}
239