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 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