347 lines
12 KiB
Java
347 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2007 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.server;
|
|
|
|
import android.util.Slog;
|
|
|
|
import java.net.ServerSocket;
|
|
import java.net.Socket;
|
|
import java.net.InetAddress;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.io.IOException;
|
|
import java.io.BufferedReader;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStream;
|
|
import java.io.BufferedWriter;
|
|
import java.io.OutputStreamWriter;
|
|
|
|
/**
|
|
* The ViewServer is local socket server that can be used to communicate with the
|
|
* views of the opened windows. Communication with the views is ensured by the
|
|
* {@link com.android.server.WindowManagerService} and is a cross-process operation.
|
|
*
|
|
* {@hide}
|
|
*/
|
|
class ViewServer implements Runnable {
|
|
/**
|
|
* The default port used to start view servers.
|
|
*/
|
|
public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
|
|
|
|
private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
|
|
|
|
// Debug facility
|
|
private static final String LOG_TAG = "ViewServer";
|
|
|
|
private static final String VALUE_PROTOCOL_VERSION = "3";
|
|
private static final String VALUE_SERVER_VERSION = "4";
|
|
|
|
// Protocol commands
|
|
// Returns the protocol version
|
|
private static final String COMMAND_PROTOCOL_VERSION = "PROTOCOL";
|
|
// Returns the server version
|
|
private static final String COMMAND_SERVER_VERSION = "SERVER";
|
|
// Lists all of the available windows in the system
|
|
private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
|
|
// Keeps a connection open and notifies when the list of windows changes
|
|
private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
|
|
// Returns the focused window
|
|
private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
|
|
|
|
private ServerSocket mServer;
|
|
private Thread mThread;
|
|
|
|
private final WindowManagerService mWindowManager;
|
|
private final int mPort;
|
|
|
|
private ExecutorService mThreadPool;
|
|
|
|
/**
|
|
* Creates a new ViewServer associated with the specified window manager.
|
|
* The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
|
|
* is not started by default.
|
|
*
|
|
* @param windowManager The window manager used to communicate with the views.
|
|
*
|
|
* @see #start()
|
|
*/
|
|
ViewServer(WindowManagerService windowManager) {
|
|
this(windowManager, VIEW_SERVER_DEFAULT_PORT);
|
|
}
|
|
|
|
/**
|
|
* Creates a new ViewServer associated with the specified window manager on the
|
|
* specified local port. The server is not started by default.
|
|
*
|
|
* @param windowManager The window manager used to communicate with the views.
|
|
* @param port The port for the server to listen to.
|
|
*
|
|
* @see #start()
|
|
*/
|
|
ViewServer(WindowManagerService windowManager, int port) {
|
|
mWindowManager = windowManager;
|
|
mPort = port;
|
|
}
|
|
|
|
/**
|
|
* Starts the server.
|
|
*
|
|
* @return True if the server was successfully created, or false if it already exists.
|
|
* @throws IOException If the server cannot be created.
|
|
*
|
|
* @see #stop()
|
|
* @see #isRunning()
|
|
* @see WindowManagerService#startViewServer(int)
|
|
*/
|
|
boolean start() throws IOException {
|
|
if (mThread != null) {
|
|
return false;
|
|
}
|
|
|
|
mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
|
|
mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
|
|
mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
|
|
mThread.start();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Stops the server.
|
|
*
|
|
* @return True if the server was stopped, false if an error occured or if the
|
|
* server wasn't started.
|
|
*
|
|
* @see #start()
|
|
* @see #isRunning()
|
|
* @see WindowManagerService#stopViewServer()
|
|
*/
|
|
boolean stop() {
|
|
if (mThread != null) {
|
|
|
|
mThread.interrupt();
|
|
if (mThreadPool != null) {
|
|
try {
|
|
mThreadPool.shutdownNow();
|
|
} catch (SecurityException e) {
|
|
Slog.w(LOG_TAG, "Could not stop all view server threads");
|
|
}
|
|
}
|
|
mThreadPool = null;
|
|
mThread = null;
|
|
try {
|
|
mServer.close();
|
|
mServer = null;
|
|
return true;
|
|
} catch (IOException e) {
|
|
Slog.w(LOG_TAG, "Could not close the view server");
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Indicates whether the server is currently running.
|
|
*
|
|
* @return True if the server is running, false otherwise.
|
|
*
|
|
* @see #start()
|
|
* @see #stop()
|
|
* @see WindowManagerService#isViewServerRunning()
|
|
*/
|
|
boolean isRunning() {
|
|
return mThread != null && mThread.isAlive();
|
|
}
|
|
|
|
/**
|
|
* Main server loop.
|
|
*/
|
|
public void run() {
|
|
while (Thread.currentThread() == mThread) {
|
|
// Any uncaught exception will crash the system process
|
|
try {
|
|
Socket client = mServer.accept();
|
|
if(mThreadPool != null) {
|
|
mThreadPool.submit(new ViewServerWorker(client));
|
|
} else {
|
|
try {
|
|
client.close();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
Slog.w(LOG_TAG, "Connection error: ", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static boolean writeValue(Socket client, String value) {
|
|
boolean result;
|
|
BufferedWriter out = null;
|
|
try {
|
|
OutputStream clientStream = client.getOutputStream();
|
|
out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
|
|
out.write(value);
|
|
out.write("\n");
|
|
out.flush();
|
|
result = true;
|
|
} catch (Exception e) {
|
|
result = false;
|
|
} finally {
|
|
if (out != null) {
|
|
try {
|
|
out.close();
|
|
} catch (IOException e) {
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
|
|
private Socket mClient;
|
|
private boolean mNeedWindowListUpdate;
|
|
private boolean mNeedFocusedWindowUpdate;
|
|
public ViewServerWorker(Socket client) {
|
|
mClient = client;
|
|
mNeedWindowListUpdate = false;
|
|
mNeedFocusedWindowUpdate = false;
|
|
}
|
|
|
|
public void run() {
|
|
|
|
BufferedReader in = null;
|
|
try {
|
|
in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
|
|
|
|
final String request = in.readLine();
|
|
|
|
String command;
|
|
String parameters;
|
|
|
|
int index = request.indexOf(' ');
|
|
if (index == -1) {
|
|
command = request;
|
|
parameters = "";
|
|
} else {
|
|
command = request.substring(0, index);
|
|
parameters = request.substring(index + 1);
|
|
}
|
|
|
|
boolean result;
|
|
if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
|
|
result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
|
|
} else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
|
|
result = writeValue(mClient, VALUE_SERVER_VERSION);
|
|
} else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
|
|
result = mWindowManager.viewServerListWindows(mClient);
|
|
} else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
|
|
result = mWindowManager.viewServerGetFocusedWindow(mClient);
|
|
} else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
|
|
result = windowManagerAutolistLoop();
|
|
} else {
|
|
result = mWindowManager.viewServerWindowCommand(mClient,
|
|
command, parameters);
|
|
}
|
|
|
|
if (!result) {
|
|
Slog.w(LOG_TAG, "An error occured with the command: " + command);
|
|
}
|
|
} catch(IOException e) {
|
|
Slog.w(LOG_TAG, "Connection error: ", e);
|
|
} finally {
|
|
if (in != null) {
|
|
try {
|
|
in.close();
|
|
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
if (mClient != null) {
|
|
try {
|
|
mClient.close();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void windowsChanged() {
|
|
synchronized(this) {
|
|
mNeedWindowListUpdate = true;
|
|
notifyAll();
|
|
}
|
|
}
|
|
|
|
public void focusChanged() {
|
|
synchronized(this) {
|
|
mNeedFocusedWindowUpdate = true;
|
|
notifyAll();
|
|
}
|
|
}
|
|
|
|
private boolean windowManagerAutolistLoop() {
|
|
mWindowManager.addWindowChangeListener(this);
|
|
BufferedWriter out = null;
|
|
try {
|
|
out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
|
|
while (!Thread.interrupted()) {
|
|
boolean needWindowListUpdate = false;
|
|
boolean needFocusedWindowUpdate = false;
|
|
synchronized (this) {
|
|
while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
|
|
wait();
|
|
}
|
|
if (mNeedWindowListUpdate) {
|
|
mNeedWindowListUpdate = false;
|
|
needWindowListUpdate = true;
|
|
}
|
|
if (mNeedFocusedWindowUpdate) {
|
|
mNeedFocusedWindowUpdate = false;
|
|
needFocusedWindowUpdate = true;
|
|
}
|
|
}
|
|
if(needWindowListUpdate) {
|
|
out.write("LIST UPDATE\n");
|
|
out.flush();
|
|
}
|
|
if(needFocusedWindowUpdate) {
|
|
out.write("FOCUS UPDATE\n");
|
|
out.flush();
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
Slog.w(LOG_TAG, "Connection error: ", e);
|
|
} finally {
|
|
if (out != null) {
|
|
try {
|
|
out.close();
|
|
} catch (IOException e) {
|
|
}
|
|
}
|
|
mWindowManager.removeWindowChangeListener(this);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|