OSDN Git Service

Added in simple command scripting to monkey over a TCP socket.
[android-x86/development.git] / cmds / monkey / src / com / android / commands / monkey / MonkeySourceNetwork.java
1 /*
2  * Copyright 2009, 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 package com.android.commands.monkey;
17
18 import android.util.Log;
19 import android.view.KeyEvent;
20 import android.view.MotionEvent;
21
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.InputStreamReader;
25 import java.io.PrintWriter;
26 import java.lang.Integer;
27 import java.lang.NumberFormatException;
28 import java.net.InetAddress;
29 import java.net.ServerSocket;
30 import java.net.Socket;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.Map;
34 import java.util.List;
35 import java.util.StringTokenizer;
36
37 /**
38  * An Event source for getting Monkey Network Script commands from
39  * over the network.
40  */
41 public class MonkeySourceNetwork implements MonkeyEventSource {
42     private static final String TAG = "MonkeyStub";
43
44     private interface MonkeyCommand {
45         MonkeyEvent translateCommand(List<String> command);
46     }
47
48     /**
49      * Command to simulate closing and opening the keyboard.
50      */
51     private static class FlipCommand implements MonkeyCommand {
52         // flip open
53         // flip closed
54         public MonkeyEvent translateCommand(List<String> command) {
55             if (command.size() > 1) {
56                 String direction = command.get(1);
57                 if ("open".equals(direction)) {
58                     return new MonkeyFlipEvent(true);
59                 } else if ("close".equals(direction)) {
60                     return new MonkeyFlipEvent(false);
61                 }
62             }
63             return null;
64         }
65     }
66
67     /**
68      * Command to send touch events to the input system.
69      */
70     private static class TouchCommand implements MonkeyCommand {
71         // touch [down|up|move] [x] [y]
72         // touch down 120 120
73         // touch move 140 140
74         // touch up 140 140
75         public MonkeyEvent translateCommand(List<String> command) {
76             if (command.size() == 4) {
77                 String actionName = command.get(1);
78                 int x = 0;
79                 int y = 0;
80                 try {
81                     x = Integer.parseInt(command.get(2));
82                     y = Integer.parseInt(command.get(3));
83                 } catch (NumberFormatException e) {
84                     // Ok, it wasn't a number
85                     Log.e(TAG, "Got something that wasn't a number", e);
86                     return null;
87                 }
88
89                 // figure out the action
90                 int action = -1;
91                 if ("down".equals(actionName)) {
92                     action = MotionEvent.ACTION_DOWN;
93                 } else if ("up".equals(actionName)) {
94                     action = MotionEvent.ACTION_UP;
95                 } else if ("move".equals(actionName)) {
96                     action = MotionEvent.ACTION_MOVE;
97                 }
98                 if (action == -1) {
99                     Log.e(TAG, "Got a bad action: " + actionName);
100                     return null;
101                 }
102
103                 return new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
104                                              -1, action, x, y, 0);
105             }
106             return null;
107
108         }
109     }
110
111     /**
112      * Command to send Trackball events to the input system.
113      */
114     private static class TrackballCommand implements MonkeyCommand {
115         // trackball [dx] [dy]
116         // trackball 1 0 -- move right
117         // trackball -1 0 -- move left
118         public MonkeyEvent translateCommand(List<String> command) {
119             if (command.size() == 3) {
120                 int dx = 0;
121                 int dy = 0;
122                 try {
123                     dx = Integer.parseInt(command.get(1));
124                     dy = Integer.parseInt(command.get(2));
125                 } catch (NumberFormatException e) {
126                     // Ok, it wasn't a number
127                     Log.e(TAG, "Got something that wasn't a number", e);
128                     return null;
129                 }
130                 return new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
131                     MotionEvent.ACTION_MOVE, dx, dy, 0);
132
133             }
134             return null;
135         }
136     }
137
138     /**
139      * Command to send Key events to the input system.
140      */
141     private static class KeyCommand implements MonkeyCommand {
142         // key [down|up] [keycode]
143         // key down 82
144         // key up 82
145         public MonkeyEvent translateCommand(List<String> command) {
146             if (command.size() == 3) {
147                 int keyCode = -1;
148                 String keyName = command.get(2);
149                 try {
150                     keyCode = Integer.parseInt(keyName);
151                 } catch (NumberFormatException e) {
152                     // Ok, it wasn't a number, see if we have a
153                     // keycode name for it
154                     keyCode = MonkeySourceRandom.getKeyCode(keyName);
155                     if (keyCode == -1) {
156                         // OK, one last ditch effort to find a match.
157                         // Build the KEYCODE_STRING from the string
158                         // we've been given and see if that key
159                         // exists.  This would allow you to do "key
160                         // down menu", for example.
161                         keyCode = MonkeySourceRandom.getKeyCode("KEYCODE_" + keyName.toUpperCase());
162                         if (keyCode == -1) {
163                             // Ok, you gave us something bad.
164                             Log.e(TAG, "Can't find keyname: " + keyName);
165                             return null;
166                         }
167                     }
168                 }
169                 Log.d(TAG, "keycode: " + keyCode);
170                 int action = -1;
171                 if ("down".equals(command.get(1))) {
172                     action = KeyEvent.ACTION_DOWN;
173                 } else if ("up".equals(command.get(1))) {
174                     action = KeyEvent.ACTION_UP;
175                 }
176                 if (action == -1) {
177                     Log.e(TAG, "got unknown action.");
178                     return null;
179                 }
180                 return new MonkeyKeyEvent(action, keyCode);
181             }
182             return null;
183         }
184     }
185
186     /**
187      * Command to put the Monkey to sleep.
188      */
189     private static class SleepCommand implements MonkeyCommand {
190         // sleep 2000
191         public MonkeyEvent translateCommand(List<String> command) {
192             if (command.size() == 2) {
193                 int sleep = -1;
194                 String sleepStr = command.get(1);
195                 try {
196                     sleep = Integer.parseInt(sleepStr);
197                 } catch (NumberFormatException e) {
198                     Log.e(TAG, "Not a number: " + sleepStr, e);
199                 }
200                 return new MonkeyThrottleEvent(sleep);
201             }
202             return null;
203         }
204     }
205
206     // This maps from command names to command implementations.
207     private static final Map<String, MonkeyCommand> COMMAND_MAP = new HashMap<String, MonkeyCommand>();
208
209     static {
210         // Add in all the commands we support
211         COMMAND_MAP.put("flip", new FlipCommand());
212         COMMAND_MAP.put("touch", new TouchCommand());
213         COMMAND_MAP.put("trackball", new TrackballCommand());
214         COMMAND_MAP.put("key", new KeyCommand());
215         COMMAND_MAP.put("sleep", new SleepCommand());
216     }
217
218     // QUIT command
219     private static final String QUIT = "quit";
220
221     // command response strings
222     private static final String OK = "OK";
223     private static final String ERROR = "ERROR";
224
225
226     private final int port;
227     private BufferedReader input;
228     private PrintWriter output;
229     private boolean started = false;
230
231     public MonkeySourceNetwork(int port) {
232         this.port = port;
233     }
234
235     /**
236      * Start a network server listening on the specified port.  The
237      * network protocol is a line oriented protocol, where each line
238      * is a different command that can be run.
239      *
240      * @param port the port to listen on
241      */
242     private void startServer() throws IOException {
243         // Only bind this to local host.  This means that you can only
244         // talk to the monkey locally, or though adb port forwarding.
245         ServerSocket server = new ServerSocket(port,
246                                                0, // default backlog
247                                                InetAddress.getLocalHost());
248         Socket s = server.accept();
249         input = new BufferedReader(new InputStreamReader(s.getInputStream()));
250         // auto-flush
251         output = new PrintWriter(s.getOutputStream(), true);
252     }
253
254     /**
255      * This function splits the given line into String parts.  It obey's quoted
256      * strings and returns them as a single part.
257      *
258      * "This is a test" -> returns only one element
259      * This is a test -> returns four elements
260      *
261      * @param line the line to parse
262      * @return the List of elements
263      */
264     private static List<String> commandLineSplit(String line) {
265         ArrayList<String> result = new ArrayList<String>();
266         StringTokenizer tok = new StringTokenizer(line);
267
268         boolean insideQuote = false;
269         StringBuffer quotedWord = new StringBuffer();
270         while (tok.hasMoreTokens()) {
271             String cur = tok.nextToken();
272             if (!insideQuote && cur.startsWith("\"")) {
273                 // begin quote
274                 quotedWord.append(cur);
275                 insideQuote = true;
276             } else if (insideQuote) {
277                 // end quote
278                 if (cur.endsWith("\"")) {
279                     insideQuote = false;
280                     quotedWord.append(cur);
281                     String word = quotedWord.toString();
282
283                     // trim off the quotes
284                     result.add(word.substring(1, word.length() - 1));
285                 } else {
286                     quotedWord.append(cur);
287                 }
288             } else {
289                 result.add(cur);
290             }
291         }
292         return result;
293     }
294
295     /**
296      * Translate the given command line into a MonkeyEvent.
297      *
298      * @param commandLine the full command line given.
299      * @returns the MonkeyEvent corresponding to the command, or null
300      * if there was an issue.
301      */
302     private MonkeyEvent translateCommand(String commandLine) {
303         Log.d(TAG, "translateCommand: " + commandLine);
304         List<String> parts = commandLineSplit(commandLine);
305         if (parts.size() > 0) {
306             MonkeyCommand command = COMMAND_MAP.get(parts.get(0));
307             if (command != null) {
308                 return command.translateCommand(parts);
309             }
310             return null;
311         }
312         return null;
313     }
314
315     public MonkeyEvent getNextEvent() {
316         if (!started) {
317             try {
318                 startServer();
319             } catch (IOException e) {
320                 Log.e(TAG, "Got IOException from server", e);
321                 return null;
322             }
323             started = true;
324         }
325
326         // Now, get the next command.  This call may block, but that's OK
327         try {
328             while (true) {
329                 String command = input.readLine();
330                 if (command == null) {
331                     Log.d(TAG, "Connection dropped.");
332                     return null;
333                 }
334                 // Do quit checking here
335                 if (QUIT.equals(command)) {
336                     // then we're done
337                     Log.d(TAG, "Quit requested");
338                     // let the host know the command ran OK
339                     output.println(OK);
340                     return null;
341                 }
342
343                 // Do comment checking here.  Comments aren't a
344                 // command, so we don't echo anything back to the
345                 // user.
346                 if (command.startsWith("#")) {
347                   // keep going
348                   continue;
349                 }
350
351                 // Translate the command line
352                 MonkeyEvent event = translateCommand(command);
353                 if (event != null) {
354                     // let the host know the command ran OK
355                     output.println(OK);
356                     return event;
357                 }
358                 // keep going.  maybe the next command will make more sense
359                 Log.e(TAG, "Got unknown command! \"" + command + "\"");
360                 output.println(ERROR);
361             }
362         } catch (IOException e) {
363             Log.e(TAG, "Exception: ", e);
364             return null;
365         }
366     }
367
368     public void setVerbose(int verbose) {
369         // We're not particualy verbose
370     }
371
372     public boolean validate() {
373         // we have no pre-conditions to validate
374         return true;
375     }
376 }