OSDN Git Service

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