Merge change 21527 into donut

* changes:
  Add getvar and listvar commands to monkey to allow inspection of properties on the device.
diff --git a/cmds/monkey/README.NETWORK.txt b/cmds/monkey/README.NETWORK.txt
index 51b56c8..4e78b6c 100644
--- a/cmds/monkey/README.NETWORK.txt
+++ b/cmds/monkey/README.NETWORK.txt
@@ -27,14 +27,20 @@
 Individual commands are separated by newlines.  The Monkey will
 respond to every command with a line starting with OK for commands
 that executed without a problem, or a line starting with ERROR for
-commands that had problems being run.  The Monkey may decide to return
-more information about command execution.  That information would come
-on the same line after the OK or ERROR.  A possible example:
+commands that had problems being run.  For commands that return a
+value, that value is returned on the same line as the OK or ERROR
+response.  The value is everything after (but not include) the colon
+on that line.  For ERROR values, this could be a message indicating
+what happened.  A possible example:
 
 key down menu
 OK
 touch monkey
 ERROR: monkey not a number
+getvar sdk
+OK: donut
+getvar foo
+ERROR: no such var
 
 The complete list of commands follows:
 
@@ -92,6 +98,16 @@
 This command will simulate a user typing the given string on the
 keyboard by generating the proper KeyEvents.
 
+listvar
+
+This command lists all the vars that the monkey knows about.  They are
+returned as a whitespace separated list.
+
+getvar varname
+
+This command returns the value of the given var.  listvar can be used
+to find out what vars are supported.
+
 quit
 
 Fully quit the monkey and accept no new sessions.
diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
index 709a70c..521de16 100644
--- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java
+++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
@@ -135,6 +135,10 @@
     MonkeyEventSource mEventSource;
     private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
 
+    // information on the current activity.
+    public static Intent currentIntent;
+    public static String currentPackage;
+
     /**
      * Monitor operations happening in the system.
      */
@@ -145,6 +149,8 @@
                 System.out.println("    // " + (allow ? "Allowing" : "Rejecting")
                         + " start of " + intent + " in package " + pkg);
             }
+            currentPackage = pkg;
+            currentIntent = intent;
             return allow;
         }
 
@@ -154,9 +160,10 @@
             if (!allow) {
                 if (mVerbose > 0) {
                     System.out.println("    // " + (allow ? "Allowing" : "Rejecting")
-                            + " resume of package " + pkg);
+                                       + " resume of package " + pkg);
                 }
             }
+            currentPackage = pkg;
             return allow;
         }
 
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetwork.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetwork.java
index 7a2bccd..a9a1db4 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetwork.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetwork.java
@@ -49,8 +49,55 @@
 public class MonkeySourceNetwork implements MonkeyEventSource {
     private static final String TAG = "MonkeyStub";
 
-    private interface MonkeyCommand {
-        MonkeyEvent translateCommand(List<String> command, CommandQueue queue);
+    /**
+     * ReturnValue from the MonkeyCommand that indicates whether the
+     * command was sucessful or not.
+     */
+    public static class MonkeyCommandReturn {
+        private final boolean success;
+        private final String message;
+
+        public MonkeyCommandReturn(boolean success) {
+            this.success = success;
+            this.message = null;
+        }
+
+        public MonkeyCommandReturn(boolean success,
+                                   String message) {
+            this.success = success;
+            this.message = message;
+        }
+
+        boolean hasMessage() {
+            return message != null;
+        }
+
+        String getMessage() {
+            return message;
+        }
+
+        boolean wasSuccessful() {
+            return success;
+        }
+    }
+
+    public final static MonkeyCommandReturn OK = new MonkeyCommandReturn(true);
+    public final static MonkeyCommandReturn ERROR = new MonkeyCommandReturn(false);
+    public final static MonkeyCommandReturn EARG = new MonkeyCommandReturn(false,
+                                                                            "Invalid Argument");
+
+    /**
+     * Interface that MonkeyCommands must implement.
+     */
+    public interface MonkeyCommand {
+        /**
+         * Translate the command line into a sequence of MonkeyEvents.
+         *
+         * @param command the command line.
+         * @param queue the command queue.
+         * @returs MonkeyCommandReturn indicating what happened.
+         */
+        MonkeyCommandReturn translateCommand(List<String> command, CommandQueue queue);
     }
 
     /**
@@ -59,17 +106,19 @@
     private static class FlipCommand implements MonkeyCommand {
         // flip open
         // flip closed
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (command.size() > 1) {
                 String direction = command.get(1);
                 if ("open".equals(direction)) {
-                    return new MonkeyFlipEvent(true);
+                    queue.enqueueEvent(new MonkeyFlipEvent(true));
+                    return OK;
                 } else if ("close".equals(direction)) {
-                    return new MonkeyFlipEvent(false);
+                    queue.enqueueEvent(new MonkeyFlipEvent(false));
+                    return OK;
                 }
             }
-            return null;
+            return EARG;
         }
     }
 
@@ -81,8 +130,8 @@
         // touch down 120 120
         // touch move 140 140
         // touch up 140 140
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (command.size() == 4) {
                 String actionName = command.get(1);
                 int x = 0;
@@ -93,7 +142,7 @@
                 } catch (NumberFormatException e) {
                     // Ok, it wasn't a number
                     Log.e(TAG, "Got something that wasn't a number", e);
-                    return null;
+                    return EARG;
                 }
 
                 // figure out the action
@@ -107,14 +156,14 @@
                 }
                 if (action == -1) {
                     Log.e(TAG, "Got a bad action: " + actionName);
-                    return null;
+                    return EARG;
                 }
 
-                return new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
-                                             -1, action, x, y, 0);
+                queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
+                                                         -1, action, x, y, 0));
+                return OK;
             }
-            return null;
-
+            return EARG;
         }
     }
 
@@ -125,8 +174,8 @@
         // trackball [dx] [dy]
         // trackball 1 0 -- move right
         // trackball -1 0 -- move left
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (command.size() == 3) {
                 int dx = 0;
                 int dy = 0;
@@ -136,13 +185,14 @@
                 } catch (NumberFormatException e) {
                     // Ok, it wasn't a number
                     Log.e(TAG, "Got something that wasn't a number", e);
-                    return null;
+                    return EARG;
                 }
-                return new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
-                    MotionEvent.ACTION_MOVE, dx, dy, 0);
+                queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
+                                                         MotionEvent.ACTION_MOVE, dx, dy, 0));
+                return OK;
 
             }
-            return null;
+            return EARG;
         }
     }
 
@@ -153,14 +203,14 @@
         // key [down|up] [keycode]
         // key down 82
         // key up 82
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (command.size() == 3) {
                 int keyCode = getKeyCode(command.get(2));
                 if (keyCode < 0) {
                     // Ok, you gave us something bad.
                     Log.e(TAG, "Can't find keyname: " + command.get(2));
-                    return null;
+                    return EARG;
                 }
                 Log.d(TAG, "keycode: " + keyCode);
                 int action = -1;
@@ -171,11 +221,12 @@
                 }
                 if (action == -1) {
                     Log.e(TAG, "got unknown action.");
-                    return null;
+                    return EARG;
                 }
-                return new MonkeyKeyEvent(action, keyCode);
+                queue.enqueueEvent(new MonkeyKeyEvent(action, keyCode));
+                return OK;
             }
-            return null;
+            return EARG;
         }
     }
 
@@ -210,19 +261,21 @@
      */
     private static class SleepCommand implements MonkeyCommand {
         // sleep 2000
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (command.size() == 2) {
                 int sleep = -1;
                 String sleepStr = command.get(1);
                 try {
                     sleep = Integer.parseInt(sleepStr);
                 } catch (NumberFormatException e) {
-                  Log.e(TAG, "Not a number: " + sleepStr, e);
+                    Log.e(TAG, "Not a number: " + sleepStr, e);
+                    return EARG;
                 }
-                return new MonkeyThrottleEvent(sleep);
+                queue.enqueueEvent(new MonkeyThrottleEvent(sleep));
+                return OK;
             }
-            return null;
+            return EARG;
         }
     }
 
@@ -231,8 +284,8 @@
      */
     private static class TypeCommand implements MonkeyCommand {
         // wake
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (command.size() == 2) {
                 String str = command.get(1);
 
@@ -248,9 +301,9 @@
                 for (KeyEvent event : events) {
                     queue.enqueueEvent(new MonkeyKeyEvent(event));
                 }
-                return new MonkeyNoopEvent();
+                return OK;
             }
-            return null;
+            return EARG;
         }
     }
 
@@ -259,12 +312,12 @@
      */
     private static class WakeCommand implements MonkeyCommand {
         // wake
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (!wake()) {
-                return null;
+                return ERROR;
             }
-            return new MonkeyNoopEvent();
+            return OK;
         }
     }
 
@@ -274,8 +327,8 @@
      */
     private static class TapCommand implements MonkeyCommand {
         // tap x y
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (command.size() == 3) {
                 int x = 0;
                 int y = 0;
@@ -285,7 +338,7 @@
                 } catch (NumberFormatException e) {
                     // Ok, it wasn't a number
                     Log.e(TAG, "Got something that wasn't a number", e);
-                    return null;
+                    return EARG;
                 }
 
                 queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
@@ -294,9 +347,9 @@
                 queue.enqueueEvent(new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
                                                          -1, MotionEvent.ACTION_UP,
                                                          x, y, 0));
-                return new MonkeyNoopEvent();
+                return OK;
             }
-            return null;
+            return EARG;
         }
     }
 
@@ -305,22 +358,22 @@
      */
     private static class PressCommand implements MonkeyCommand {
         // press keycode
-        public MonkeyEvent translateCommand(List<String> command,
-                                            CommandQueue queue) {
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
             if (command.size() == 2) {
                 int keyCode = getKeyCode(command.get(1));
                 if (keyCode < 0) {
                     // Ok, you gave us something bad.
                     Log.e(TAG, "Can't find keyname: " + command.get(1));
-                    return null;
+                    return EARG;
                 }
 
                 queue.enqueueEvent(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode));
                 queue.enqueueEvent(new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode));
-                return new MonkeyNoopEvent();
+                return OK;
 
             }
-            return null;
+            return EARG;
         }
     }
 
@@ -355,6 +408,8 @@
         COMMAND_MAP.put("tap", new TapCommand());
         COMMAND_MAP.put("press", new PressCommand());
         COMMAND_MAP.put("type", new TypeCommand());
+        COMMAND_MAP.put("listvar", new MonkeySourceNetworkVars.ListVarCommand());
+        COMMAND_MAP.put("getvar", new MonkeySourceNetworkVars.GetVarCommand());
     }
 
     // QUIT command
@@ -363,10 +418,10 @@
     private static final String DONE = "done";
 
     // command response strings
-    private static final String OK = "OK";
-    private static final String ERROR = "ERROR";
+    private static final String OK_STR = "OK";
+    private static final String ERROR_STR = "ERROR";
 
-    private static interface CommandQueue {
+    public static interface CommandQueue {
         /**
          * Enqueue an event to be returned later.  This allows a
          * command to return multiple events.  Commands using the
@@ -499,21 +554,30 @@
      * Translate the given command line into a MonkeyEvent.
      *
      * @param commandLine the full command line given.
-     * @returns the MonkeyEvent corresponding to the command, or null
-     * if there was an issue.
      */
-    private MonkeyEvent translateCommand(String commandLine) {
+    private void translateCommand(String commandLine) {
         Log.d(TAG, "translateCommand: " + commandLine);
         List<String> parts = commandLineSplit(commandLine);
         if (parts.size() > 0) {
             MonkeyCommand command = COMMAND_MAP.get(parts.get(0));
             if (command != null) {
-                return command.translateCommand(parts,
-                                                commandQueue);
+                MonkeyCommandReturn ret = command.translateCommand(parts,
+                                                                   commandQueue);
+                if (ret.wasSuccessful()) {
+                    if (ret.hasMessage()) {
+                        returnOk(ret.getMessage());
+                    } else {
+                        returnOk();
+                    }
+                } else {
+                    if (ret.hasMessage()) {
+                        returnError(ret.getMessage());
+                    } else {
+                        returnError();
+                    }
+                }
             }
-            return null;
         }
-        return null;
     }
 
     public MonkeyEvent getNextEvent() {
@@ -548,16 +612,16 @@
                 }
 
                 if (DONE.equals(command)) {
-                  // stop the server so it can accept new connections
+                    // stop the server so it can accept new connections
                     try {
                         stopServer();
                     } catch (IOException e) {
                         Log.e(TAG, "Got IOException shutting down!", e);
                         return null;
                     }
-                  // return a noop event so we keep executing the main
-                  // loop
-                  return new MonkeyNoopEvent();
+                    // return a noop event so we keep executing the main
+                    // loop
+                    return new MonkeyNoopEvent();
                 }
 
                 // Do quit checking here
@@ -565,7 +629,7 @@
                     // then we're done
                     Log.d(TAG, "Quit requested");
                     // let the host know the command ran OK
-                    output.println(OK);
+                    returnOk();
                     return null;
                 }
 
@@ -573,20 +637,12 @@
                 // command, so we don't echo anything back to the
                 // user.
                 if (command.startsWith("#")) {
-                  // keep going
-                  continue;
+                    // keep going
+                    continue;
                 }
 
-                // Translate the command line
-                MonkeyEvent event = translateCommand(command);
-                if (event != null) {
-                    // let the host know the command ran OK
-                    output.println(OK);
-                    return event;
-                }
-                // keep going.  maybe the next command will make more sense
-                Log.e(TAG, "Got unknown command! \"" + command + "\"");
-                output.println(ERROR);
+                // Translate the command line.  This will handle returning error/ok to the user
+                translateCommand(command);
             }
         } catch (IOException e) {
             Log.e(TAG, "Exception: ", e);
@@ -594,6 +650,42 @@
         }
     }
 
+    /**
+     * Returns ERROR to the user.
+     */
+    private void returnError() {
+        output.println(ERROR_STR);
+    }
+
+    /**
+     * Returns ERROR to the user.
+     *
+     * @param msg the error message to include
+     */
+    private void returnError(String msg) {
+        output.print(ERROR_STR);
+        output.print(":");
+        output.println(msg);
+    }
+
+    /**
+     * Returns OK to the user.
+     */
+    private void returnOk() {
+        output.println(OK_STR);
+    }
+
+    /**
+     * Returns OK to the user.
+     *
+     * @param returnValue the value to return from this command.
+     */
+    private void returnOk(String returnValue) {
+        output.print(OK_STR);
+        output.print(":");
+        output.println(returnValue);
+    }
+
     public void setVerbose(int verbose) {
         // We're not particualy verbose
     }
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkVars.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkVars.java
new file mode 100644
index 0000000..e9890ad
--- /dev/null
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceNetworkVars.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.commands.monkey;
+
+import android.os.Build;
+import android.os.SystemClock;
+import android.view.Display;
+import android.view.WindowManagerImpl;
+import android.util.DisplayMetrics;
+
+import com.android.commands.monkey.MonkeySourceNetwork.CommandQueue;
+import com.android.commands.monkey.MonkeySourceNetwork.MonkeyCommand;
+import com.android.commands.monkey.MonkeySourceNetwork.MonkeyCommandReturn;
+
+import java.lang.Integer;
+import java.lang.Float;
+import java.lang.Long;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class MonkeySourceNetworkVars {
+    /**
+     * Interface to get the value of a var.
+     */
+    private static interface VarGetter {
+        /**
+         * Get the value of the var.
+         * @returns the value of the var.
+         */
+        public String get();
+    }
+
+    private static class StaticVarGetter implements VarGetter {
+        private final String value;
+
+        public StaticVarGetter(String value) {
+            this.value = value;
+        }
+
+        public String get() {
+            return value;
+        }
+    }
+
+    // Use a TreeMap to keep the keys sorted so they get displayed nicely in listvar
+    private static final Map<String, VarGetter> VAR_MAP = new TreeMap<String, VarGetter>();
+
+    static {
+        VAR_MAP.put("build.board", new StaticVarGetter(Build.BOARD));
+        VAR_MAP.put("build.brand", new StaticVarGetter(Build.BRAND));
+        VAR_MAP.put("build.device", new StaticVarGetter(Build.DEVICE));
+        VAR_MAP.put("build.display", new StaticVarGetter(Build.DISPLAY));
+        VAR_MAP.put("build.fingerprint", new StaticVarGetter(Build.FINGERPRINT));
+        VAR_MAP.put("build.host", new StaticVarGetter(Build.HOST));
+        VAR_MAP.put("build.id", new StaticVarGetter(Build.ID));
+        VAR_MAP.put("build.model", new StaticVarGetter(Build.MODEL));
+        VAR_MAP.put("build.product", new StaticVarGetter(Build.PRODUCT));
+        VAR_MAP.put("build.tags", new StaticVarGetter(Build.TAGS));
+        VAR_MAP.put("build.brand", new StaticVarGetter(Long.toString(Build.TIME)));
+        VAR_MAP.put("build.type", new StaticVarGetter(Build.TYPE));
+        VAR_MAP.put("build.user", new StaticVarGetter(Build.USER));
+        VAR_MAP.put("build.cpu_abi", new StaticVarGetter(Build.CPU_ABI));
+        VAR_MAP.put("build.manufacturer", new StaticVarGetter(Build.MANUFACTURER));
+        VAR_MAP.put("build.version.incremental", new StaticVarGetter(Build.VERSION.INCREMENTAL));
+        VAR_MAP.put("build.version.release", new StaticVarGetter(Build.VERSION.RELEASE));
+        VAR_MAP.put("build.version.sdk", new StaticVarGetter(Integer.toString(Build.VERSION.SDK_INT)));
+        VAR_MAP.put("build.version.codename", new StaticVarGetter(Build.VERSION.CODENAME));
+
+        // Display
+        Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
+        VAR_MAP.put("display.width", new StaticVarGetter(Integer.toString(display.getWidth())));
+        VAR_MAP.put("display.height", new StaticVarGetter(Integer.toString(display.getHeight())));
+
+        DisplayMetrics dm = new DisplayMetrics();
+        display.getMetrics(dm);
+        VAR_MAP.put("display.density", new StaticVarGetter(Float.toString(dm.density)));
+
+        // am.  note that the current activity information isn't valid
+        // until the first activity gets launched after the monkey has
+        // been started.
+        VAR_MAP.put("am.current.package", new VarGetter() {
+                public String get() {
+                    return Monkey.currentPackage;
+                }
+            });
+        VAR_MAP.put("am.current.action", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    return Monkey.currentIntent.getAction();
+                }
+            });
+        VAR_MAP.put("am.current.comp.class", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    return Monkey.currentIntent.getComponent().getClassName();
+                }
+            });
+        VAR_MAP.put("am.current.comp.package", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    return Monkey.currentIntent.getComponent().getPackageName();
+                }
+            });
+        VAR_MAP.put("am.current.data", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    return Monkey.currentIntent.getDataString();
+                }
+            });
+        VAR_MAP.put("am.current.categories", new VarGetter() {
+                public String get() {
+                    if (Monkey.currentIntent == null) {
+                        return null;
+                    }
+                    StringBuffer sb = new StringBuffer();
+                    for (String cat : Monkey.currentIntent.getCategories()) {
+                        sb.append(cat).append(" ");
+                    }
+                    return sb.toString();
+                }
+            });
+
+        // clock
+        VAR_MAP.put("clock.realtime", new VarGetter() {
+                public String get() {
+                    return Long.toString(SystemClock.elapsedRealtime());
+                }
+            });
+        VAR_MAP.put("clock.uptime", new VarGetter() {
+                public String get() {
+                    return Long.toString(SystemClock.uptimeMillis());
+                }
+            });
+        VAR_MAP.put("clock.millis", new VarGetter() {
+                public String get() {
+                    return Long.toString(System.currentTimeMillis());
+                }
+            });
+    }
+
+    /**
+     * Command to list the "vars" that the monkey knows about.
+     */
+    public static class ListVarCommand implements MonkeySourceNetwork.MonkeyCommand {
+        // listvar
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            Set<String> keys = VAR_MAP.keySet();
+            StringBuffer sb = new StringBuffer();
+            for (String key : keys) {
+                sb.append(key).append(" ");
+            }
+            return new MonkeyCommandReturn(true, sb.toString());
+        }
+    }
+
+    /**
+     * Command to get the value of a var.
+     */
+    public static class GetVarCommand implements MonkeyCommand {
+        // getvar varname
+        public MonkeyCommandReturn translateCommand(List<String> command,
+                                                    CommandQueue queue) {
+            if (command.size() == 2) {
+                VarGetter getter = VAR_MAP.get(command.get(1));
+                if (getter == null) {
+                    return new MonkeyCommandReturn(false, "unknown var");
+                }
+                return new MonkeyCommandReturn(true, getter.get());
+            }
+            return MonkeySourceNetwork.EARG;
+        }
+    }
+}