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