OSDN Git Service

merge from donut
[android-x86/development.git] / cmds / monkey / src / com / android / commands / monkey / MonkeySourceRandom.java
1 /*
2  * Copyright (C) 2008 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.commands.monkey;
18
19 import android.content.ComponentName;
20 import android.os.SystemClock;
21 import android.view.Display;
22 import android.view.KeyEvent;
23 import android.view.MotionEvent;
24 import android.view.WindowManagerImpl;
25
26 import java.security.SecureRandom;
27 import java.util.ArrayList;
28 import java.util.LinkedList;
29 import java.util.Random;
30
31 /**
32  * monkey event queue
33  */
34 public class MonkeySourceRandom implements MonkeyEventSource {
35     /** Key events that move around the UI. */
36     private static final int[] NAV_KEYS = {
37         KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
38         KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
39     };
40     /**
41      * Key events that perform major navigation options (so shouldn't be sent
42      * as much).
43      */
44     private static final int[] MAJOR_NAV_KEYS = {
45         KeyEvent.KEYCODE_MENU, /*KeyEvent.KEYCODE_SOFT_RIGHT,*/
46         KeyEvent.KEYCODE_DPAD_CENTER,
47     };
48     /** Key events that perform system operations. */
49     private static final int[] SYS_KEYS = {
50         KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_BACK,
51         KeyEvent.KEYCODE_CALL, KeyEvent.KEYCODE_ENDCALL,
52         KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN,
53         KeyEvent.KEYCODE_MUTE,
54     };
55     /** Nice names for all key events. */
56     private static final String[] KEY_NAMES = {
57         "KEYCODE_UNKNOWN",
58         "KEYCODE_SOFT_LEFT",
59         "KEYCODE_SOFT_RIGHT",
60         "KEYCODE_HOME",
61         "KEYCODE_BACK",
62         "KEYCODE_CALL",
63         "KEYCODE_ENDCALL",
64         "KEYCODE_0",
65         "KEYCODE_1",
66         "KEYCODE_2",
67         "KEYCODE_3",
68         "KEYCODE_4",
69         "KEYCODE_5",
70         "KEYCODE_6",
71         "KEYCODE_7",
72         "KEYCODE_8",
73         "KEYCODE_9",
74         "KEYCODE_STAR",
75         "KEYCODE_POUND",
76         "KEYCODE_DPAD_UP",
77         "KEYCODE_DPAD_DOWN",
78         "KEYCODE_DPAD_LEFT",
79         "KEYCODE_DPAD_RIGHT",
80         "KEYCODE_DPAD_CENTER",
81         "KEYCODE_VOLUME_UP",
82         "KEYCODE_VOLUME_DOWN",
83         "KEYCODE_POWER",
84         "KEYCODE_CAMERA",
85         "KEYCODE_CLEAR",
86         "KEYCODE_A",
87         "KEYCODE_B",
88         "KEYCODE_C",
89         "KEYCODE_D",
90         "KEYCODE_E",
91         "KEYCODE_F",
92         "KEYCODE_G",
93         "KEYCODE_H",
94         "KEYCODE_I",
95         "KEYCODE_J",
96         "KEYCODE_K",
97         "KEYCODE_L",
98         "KEYCODE_M",
99         "KEYCODE_N",
100         "KEYCODE_O",
101         "KEYCODE_P",
102         "KEYCODE_Q",
103         "KEYCODE_R",
104         "KEYCODE_S",
105         "KEYCODE_T",
106         "KEYCODE_U",
107         "KEYCODE_V",
108         "KEYCODE_W",
109         "KEYCODE_X",
110         "KEYCODE_Y",
111         "KEYCODE_Z",
112         "KEYCODE_COMMA",
113         "KEYCODE_PERIOD",
114         "KEYCODE_ALT_LEFT",
115         "KEYCODE_ALT_RIGHT",
116         "KEYCODE_SHIFT_LEFT",
117         "KEYCODE_SHIFT_RIGHT",
118         "KEYCODE_TAB",
119         "KEYCODE_SPACE",
120         "KEYCODE_SYM",
121         "KEYCODE_EXPLORER",
122         "KEYCODE_ENVELOPE",
123         "KEYCODE_ENTER",
124         "KEYCODE_DEL",
125         "KEYCODE_GRAVE",
126         "KEYCODE_MINUS",
127         "KEYCODE_EQUALS",
128         "KEYCODE_LEFT_BRACKET",
129         "KEYCODE_RIGHT_BRACKET",
130         "KEYCODE_BACKSLASH",
131         "KEYCODE_SEMICOLON",
132         "KEYCODE_APOSTROPHE",
133         "KEYCODE_SLASH",
134         "KEYCODE_AT",
135         "KEYCODE_NUM",
136         "KEYCODE_HEADSETHOOK",
137         "KEYCODE_FOCUS",
138         "KEYCODE_PLUS",
139         "KEYCODE_MENU",
140         "KEYCODE_NOTIFICATION",
141         "KEYCODE_SEARCH",
142         "KEYCODE_PLAYPAUSE",
143         "KEYCODE_STOP",
144         "KEYCODE_NEXTSONG",
145         "KEYCODE_PREVIOUSSONG",
146         "KEYCODE_REWIND",
147         "KEYCODE_FORWARD",
148         "KEYCODE_MUTE",
149
150         "TAG_LAST_KEYCODE"      // EOL.  used to keep the lists in sync
151     };
152
153     public static final int FACTOR_TOUCH        = 0;
154     public static final int FACTOR_MOTION       = 1;
155     public static final int FACTOR_TRACKBALL    = 2;
156     public static final int FACTOR_NAV          = 3;
157     public static final int FACTOR_MAJORNAV     = 4;
158     public static final int FACTOR_SYSOPS       = 5;
159     public static final int FACTOR_APPSWITCH    = 6;
160     public static final int FACTOR_FLIP         = 7;
161     public static final int FACTOR_ANYTHING     = 8;
162     public static final int FACTORZ_COUNT       = 9;    // should be last+1
163
164
165     /** percentages for each type of event.  These will be remapped to working
166      * values after we read any optional values.
167      **/
168     private float[] mFactors = new float[FACTORZ_COUNT];
169     private ArrayList<ComponentName> mMainApps;
170     private int mEventCount = 0;  //total number of events generated so far
171     private MonkeyEventQueue mQ;
172     private Random mRandom;
173     private int mVerbose = 0;
174     private long mThrottle = 0;
175
176     private boolean mKeyboardOpen = false;
177
178     /**
179      * @return the last name in the key list
180      */
181     public static String getLastKeyName() {
182         return KEY_NAMES[KeyEvent.getMaxKeyCode() + 1];
183     }
184
185     public static String getKeyName(int keycode) {
186         return KEY_NAMES[keycode];
187     }
188
189     /**
190      * Looks up the keyCode from a given KEYCODE_NAME.  NOTE: This may
191      * be an expensive operation.
192      *
193      * @param keyName the name of the KEYCODE_VALUE to lookup.
194      * @returns the intenger keyCode value, or -1 if not found
195      */
196     public static int getKeyCode(String keyName) {
197         for (int x = 0; x < KEY_NAMES.length; x++) {
198             if (KEY_NAMES[x].equals(keyName)) {
199                 return x;
200             }
201         }
202         return -1;
203     }
204
205     public MonkeySourceRandom(long seed, ArrayList<ComponentName> MainApps, long throttle) {
206         // default values for random distributions
207         // note, these are straight percentages, to match user input (cmd line args)
208         // but they will be converted to 0..1 values before the main loop runs.
209         mFactors[FACTOR_TOUCH] = 15.0f;
210         mFactors[FACTOR_MOTION] = 10.0f;
211         mFactors[FACTOR_TRACKBALL] = 15.0f;
212         mFactors[FACTOR_NAV] = 25.0f;
213         mFactors[FACTOR_MAJORNAV] = 15.0f;
214         mFactors[FACTOR_SYSOPS] = 2.0f;
215         mFactors[FACTOR_APPSWITCH] = 2.0f;
216         mFactors[FACTOR_FLIP] = 1.0f;
217         mFactors[FACTOR_ANYTHING] = 15.0f;
218
219         mRandom = new SecureRandom();
220         mRandom.setSeed((seed == 0) ? -1 : seed);
221         mMainApps = MainApps;
222         mQ = new MonkeyEventQueue(throttle);
223     }
224
225     /**
226      * Adjust the percentages (after applying user values) and then normalize to a 0..1 scale.
227      */
228     private boolean adjustEventFactors() {
229         // go through all values and compute totals for user & default values
230         float userSum = 0.0f;
231         float defaultSum = 0.0f;
232         int defaultCount = 0;
233         for (int i = 0; i < FACTORZ_COUNT; ++i) {
234             if (mFactors[i] <= 0.0f) {   // user values are zero or negative
235                 userSum -= mFactors[i];
236             } else {
237                 defaultSum += mFactors[i];
238                 ++defaultCount;
239             }
240         }
241
242         // if the user request was > 100%, reject it
243         if (userSum > 100.0f) {
244             System.err.println("** Event weights > 100%");
245             return false;
246         }
247
248         // if the user specified all of the weights, then they need to be 100%
249         if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
250             System.err.println("** Event weights != 100%");
251             return false;
252         }
253
254         // compute the adjustment necessary
255         float defaultsTarget = (100.0f - userSum);
256         float defaultsAdjustment = defaultsTarget / defaultSum;
257
258         // fix all values, by adjusting defaults, or flipping user values back to >0
259         for (int i = 0; i < FACTORZ_COUNT; ++i) {
260             if (mFactors[i] <= 0.0f) {   // user values are zero or negative
261                 mFactors[i] = -mFactors[i];
262             } else {
263                 mFactors[i] *= defaultsAdjustment;
264             }
265         }
266
267         // if verbose, show factors
268
269         if (mVerbose > 0) {
270             System.out.println("// Event percentages:");
271             for (int i = 0; i < FACTORZ_COUNT; ++i) {
272                 System.out.println("//   " + i + ": " + mFactors[i] + "%");
273             }
274         }
275
276         // finally, normalize and convert to running sum
277         float sum = 0.0f;
278         for (int i = 0; i < FACTORZ_COUNT; ++i) {
279             sum += mFactors[i] / 100.0f;
280             mFactors[i] = sum;
281         }
282         return true;
283     }
284
285     /**
286      * set the factors
287      *
288      * @param factors: percentages for each type of event
289      */
290     public void setFactors(float factors[]) {
291         int c = FACTORZ_COUNT;
292         if (factors.length < c) {
293             c = factors.length;
294         }
295         for (int i = 0; i < c; i++)
296             mFactors[i] = factors[i];
297     }
298
299     public void setFactors(int index, float v) {
300         mFactors[index] = v;
301     }
302
303     /**
304      * Generates a random motion event. This method counts a down, move, and up as multiple events.
305      *
306      * TODO:  Test & fix the selectors when non-zero percentages
307      * TODO:  Longpress.
308      * TODO:  Fling.
309      * TODO:  Meta state
310      * TODO:  More useful than the random walk here would be to pick a single random direction
311      * and distance, and divvy it up into a random number of segments.  (This would serve to
312      * generate fling gestures, which are important).
313      *
314      * @param random Random number source for positioning
315      * @param motionEvent If false, touch/release.  If true, touch/move/release.
316      *
317      */
318     private void generateMotionEvent(Random random, boolean motionEvent){
319
320         Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
321
322         float x = Math.abs(random.nextInt() % display.getWidth());
323         float y = Math.abs(random.nextInt() % display.getHeight());
324         long downAt = SystemClock.uptimeMillis();
325         long eventTime = SystemClock.uptimeMillis();
326         if (downAt == -1) {
327             downAt = eventTime;
328         }
329
330         MonkeyMotionEvent e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
331                 downAt, MotionEvent.ACTION_DOWN, x, y, 0);
332         e.setIntermediateNote(false);
333         mQ.addLast(e);
334
335         // sometimes we'll move during the touch
336         if (motionEvent) {
337             int count = random.nextInt(10);
338             for (int i = 0; i < count; i++) {
339                 // generate some slop in the up event
340                 x = (x + (random.nextInt() % 10)) % display.getWidth();
341                 y = (y + (random.nextInt() % 10)) % display.getHeight();
342
343                 e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
344                         downAt, MotionEvent.ACTION_MOVE, x, y, 0);
345                 e.setIntermediateNote(true);
346                 mQ.addLast(e);
347             }
348         }
349
350         // TODO generate some slop in the up event
351         e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
352                 downAt, MotionEvent.ACTION_UP, x, y, 0);
353         e.setIntermediateNote(false);
354         mQ.addLast(e);
355     }
356
357     /**
358      * Generates a random trackball event. This consists of a sequence of small moves, followed by
359      * an optional single click.
360      *
361      * TODO:  Longpress.
362      * TODO:  Meta state
363      * TODO:  Parameterize the % clicked
364      * TODO:  More useful than the random walk here would be to pick a single random direction
365      * and distance, and divvy it up into a random number of segments.  (This would serve to
366      * generate fling gestures, which are important).
367      *
368      * @param random Random number source for positioning
369      *
370      */
371     private void generateTrackballEvent(Random random) {
372         Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
373
374         boolean drop = false;
375         int count = random.nextInt(10);
376         MonkeyMotionEvent e;
377         for (int i = 0; i < 10; ++i) {
378             // generate a small random step
379             int dX = random.nextInt(10) - 5;
380             int dY = random.nextInt(10) - 5;
381
382
383             e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
384                     MotionEvent.ACTION_MOVE, dX, dY, 0);
385             e.setIntermediateNote(i > 0);
386             mQ.addLast(e);
387         }
388
389         // 10% of trackball moves end with a click
390         if (0 == random.nextInt(10)) {
391             long downAt = SystemClock.uptimeMillis();
392
393
394             e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
395                     MotionEvent.ACTION_DOWN, 0, 0, 0);
396             e.setIntermediateNote(true);
397             mQ.addLast(e);
398
399
400             e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
401                     MotionEvent.ACTION_UP, 0, 0, 0);
402             e.setIntermediateNote(false);
403             mQ.addLast(e);
404         }
405     }
406
407     /**
408      * generate a random event based on mFactor
409      */
410     private void generateEvents() {
411         float cls = mRandom.nextFloat();
412         int lastKey = 0;
413
414         boolean touchEvent = cls < mFactors[FACTOR_TOUCH];
415         boolean motionEvent = !touchEvent && (cls < mFactors[FACTOR_MOTION]);
416         if (touchEvent || motionEvent) {
417             generateMotionEvent(mRandom, motionEvent);
418             return;
419         }
420
421         if (cls < mFactors[FACTOR_TRACKBALL]) {
422             generateTrackballEvent(mRandom);
423             return;
424         }
425
426         // The remaining event categories are injected as key events
427         if (cls < mFactors[FACTOR_NAV]) {
428             lastKey = NAV_KEYS[mRandom.nextInt(NAV_KEYS.length)];
429         } else if (cls < mFactors[FACTOR_MAJORNAV]) {
430             lastKey = MAJOR_NAV_KEYS[mRandom.nextInt(MAJOR_NAV_KEYS.length)];
431         } else if (cls < mFactors[FACTOR_SYSOPS]) {
432             lastKey = SYS_KEYS[mRandom.nextInt(SYS_KEYS.length)];
433         } else if (cls < mFactors[FACTOR_APPSWITCH]) {
434             MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(
435                     mRandom.nextInt(mMainApps.size())));
436             mQ.addLast(e);
437             return;
438         } else if (cls < mFactors[FACTOR_FLIP]) {
439             MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen);
440             mKeyboardOpen = !mKeyboardOpen;
441             mQ.addLast(e);
442             return;
443         } else {
444             lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
445         }
446
447         MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
448         mQ.addLast(e);
449
450         e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
451         mQ.addLast(e);
452     }
453
454     public boolean validate() {
455         //check factors
456         return adjustEventFactors();
457     }
458
459     public void setVerbose(int verbose) {
460         mVerbose = verbose;
461     }
462
463     /**
464      * generate an activity event
465      */
466     public void generateActivity() {
467         MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(
468                 mRandom.nextInt(mMainApps.size())));
469         mQ.addLast(e);
470     }
471
472     /**
473      * if the queue is empty, we generate events first
474      * @return the first event in the queue
475      */
476     public MonkeyEvent getNextEvent() {
477         if (mQ.isEmpty()) {
478                 generateEvents();
479         }
480         mEventCount++;
481         MonkeyEvent e = mQ.getFirst();
482         mQ.removeFirst();
483         return e;
484     }
485 }