OSDN Git Service

Merge "SDK doc change: Misc fixes." into eclair
authorDirk Dougherty <ddougherty@google.com>
Sat, 23 Jan 2010 00:45:03 +0000 (16:45 -0800)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Sat, 23 Jan 2010 00:45:03 +0000 (16:45 -0800)
14 files changed:
build/sdk.atree
build/tools/make_windows_sdk.sh
cmds/monkey/src/com/android/commands/monkey/Monkey.java
cmds/monkey/src/com/android/commands/monkey/MonkeyEventSource.java
cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandomScript.java [new file with mode: 0644]
cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
host/windows/usb/android_winusb.inf
ndk/docs/CHANGES.TXT
ndk/docs/STABLE-APIS.TXT
samples/BusinessCard/_index.html [new file with mode: 0644]
samples/source.properties [new file with mode: 0644]
tools/etc1tool/Android.mk [new file with mode: 0644]
tools/etc1tool/etc1tool.cpp [new file with mode: 0644]

index b0cd182..845ddea 100644 (file)
@@ -29,6 +29,7 @@ bin/aidl platforms/${PLATFORM_NAME}/tools/aidl
 bin/adb tools/adb
 bin/sqlite3 tools/sqlite3
 bin/dmtracedump tools/dmtracedump
+bin/etc1tool tools/etc1tool
 bin/hprof-conv tools/hprof-conv
 bin/mksdcard tools/mksdcard
 bin/zipalign tools/zipalign
@@ -72,22 +73,23 @@ sdk/files/devices.xml tools/lib/devices.xml
 sdk/files/android.el tools/lib/android.el
 
 # samples
-development/apps/GestureBuilder platforms/${PLATFORM_NAME}/samples/GestureBuilder
-development/samples/BluetoothChat platforms/${PLATFORM_NAME}/samples/BluetoothChat
-development/samples/Home platforms/${PLATFORM_NAME}/samples/Home
-development/samples/LunarLander platforms/${PLATFORM_NAME}/samples/LunarLander
-development/samples/NotePad platforms/${PLATFORM_NAME}/samples/NotePad
-development/samples/ApiDemos platforms/${PLATFORM_NAME}/samples/ApiDemos
-development/samples/SkeletonApp platforms/${PLATFORM_NAME}/samples/SkeletonApp
-development/samples/Snake platforms/${PLATFORM_NAME}/samples/Snake
-development/samples/SoftKeyboard platforms/${PLATFORM_NAME}/samples/SoftKeyboard
-development/samples/JetBoy platforms/${PLATFORM_NAME}/samples/JetBoy
-development/samples/SearchableDictionary platforms/${PLATFORM_NAME}/samples/SearchableDictionary
-development/samples/ContactManager platforms/${PLATFORM_NAME}/samples/ContactManager
-development/samples/MultiResolution platforms/${PLATFORM_NAME}/samples/MultiResolution
-development/samples/Wiktionary platforms/${PLATFORM_NAME}/samples/Wiktionary
-development/samples/WiktionarySimple platforms/${PLATFORM_NAME}/samples/WiktionarySimple
-development/samples/CubeLiveWallpaper platforms/${PLATFORM_NAME}/samples/CubeLiveWallpaper
+development/samples/source.properties     samples/${PLATFORM_NAME}/source.properties
+development/apps/GestureBuilder           samples/${PLATFORM_NAME}/GestureBuilder
+development/samples/BluetoothChat         samples/${PLATFORM_NAME}/BluetoothChat
+development/samples/Home                  samples/${PLATFORM_NAME}/Home
+development/samples/LunarLander           samples/${PLATFORM_NAME}/LunarLander
+development/samples/NotePad               samples/${PLATFORM_NAME}/NotePad
+development/samples/ApiDemos              samples/${PLATFORM_NAME}/ApiDemos
+development/samples/SkeletonApp           samples/${PLATFORM_NAME}/SkeletonApp
+development/samples/Snake                 samples/${PLATFORM_NAME}/Snake
+development/samples/SoftKeyboard          samples/${PLATFORM_NAME}/SoftKeyboard
+development/samples/JetBoy                samples/${PLATFORM_NAME}/JetBoy
+development/samples/SearchableDictionary  samples/${PLATFORM_NAME}/SearchableDictionary
+development/samples/ContactManager        samples/${PLATFORM_NAME}/ContactManager
+development/samples/MultiResolution       samples/${PLATFORM_NAME}/MultiResolution
+development/samples/Wiktionary            samples/${PLATFORM_NAME}/Wiktionary
+development/samples/WiktionarySimple      samples/${PLATFORM_NAME}/WiktionarySimple
+development/samples/CubeLiveWallpaper     samples/${PLATFORM_NAME}/CubeLiveWallpaper
 
 # dx
 bin/dx platforms/${PLATFORM_NAME}/tools/dx
@@ -176,10 +178,10 @@ sdk/emulator/skins/WVGA854   platforms/${PLATFORM_NAME}/skins/WVGA854
 
 # NOTICE files are copied by build/core/Makefile from sdk.git
 sdk/files/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/templates/NOTICE.txt
-sdk/files/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/samples/NOTICE.txt
 sdk/files/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/data/NOTICE.txt
 sdk/files/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/skins/NOTICE.txt
 sdk/files/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/tools/NOTICE.txt
+sdk/files/sdk_files_NOTICE.txt samples/${PLATFORM_NAME}/NOTICE.txt
 
 # the readme
 development/docs/SDK_RELEASE_NOTES RELEASE_NOTES.html
index c443430..6847f73 100755 (executable)
@@ -86,14 +86,21 @@ function check() {
 
 function build() {
 
+    # IMPORTANT: For Cygwin to be able to build Android targets here,
+    # you will generally need to edit build/core/main.mk and add directories
+    # where Android.mk makefiles are to be found to the SDK_ONLY==true section.
+
     echo 
     echo "Building..."
     [ -n "$MAKE_OPT" ] && echo "Make options: $MAKE_OPT"
+
     . build/envsetup.sh
-    make -j 4 emulator || die "Build failed"
+
     # Disable parallel build: it generates "permission denied" issues when
     # multiple "ar.exe" are running in parallel.
-    make aapt adb aidl \
+    make \
+        aapt adb aidl \
+        etc1tool \
         prebuilt \
         dexdump dmtracedump \
         fastboot \
@@ -102,6 +109,9 @@ function build() {
         sdklauncher sqlite3 \
         zipalign \
         || die "Build failed"
+
+    # It's worth building the emulator with -j 4 so do it separately
+    make -j 4 emulator || die "Build failed"
 }
 
 function package() {
@@ -140,7 +150,7 @@ function package() {
     # Remove obsolete stuff from tools & platform
     TOOLS="$TEMP_SDK_DIR/tools"
     LIB="$TEMP_SDK_DIR/tools/lib"
-    rm -v "$TOOLS"/{adb,android,apkbuilder,ddms,dmtracedump,draw9patch,emulator}
+    rm -v "$TOOLS"/{adb,android,apkbuilder,ddms,dmtracedump,draw9patch,emulator,etc1tool}
     rm -v "$TOOLS"/{hierarchyviewer,hprof-conv,layoutopt,mksdcard,sqlite3,traceview,zipalign}
     rm -v "$LIB"/*/swt.jar
     rm -v "$PLATFORM_TOOLS"/{aapt,aidl,dx,dexdump}
index 7109bc6..79548d4 100644 (file)
@@ -1,19 +1,18 @@
-/**
-** Copyright 2007, 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.
-*/
-
+/*
+ * Copyright 2007, 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;
 
@@ -51,19 +50,25 @@ public class Monkey {
 
     /**
      * Monkey Debugging/Dev Support
-     *
+     * <p>
      * All values should be zero when checking in.
      */
     private final static int DEBUG_ALLOW_ANY_STARTS = 0;
+
     private final static int DEBUG_ALLOW_ANY_RESTARTS = 0;
+
     private IActivityManager mAm;
+
     private IWindowManager mWm;
+
     private IPackageManager mPm;
 
     /** Command line arguments */
     private String[] mArgs;
+
     /** Current argument being parsed */
     private int mNextArg;
+
     /** Data of current argument */
     private String mCurArgData;
 
@@ -83,16 +88,28 @@ public class Monkey {
     /** Monitor /data/tombstones and stop the monkey if new files appear. */
     private boolean mMonitorNativeCrashes;
 
-    /** Send no events.  Use with long throttle-time to watch user operations */
+    /** Send no events. Use with long throttle-time to watch user operations */
     private boolean mSendNoEvents;
 
     /** This is set when we would like to abort the running of the monkey. */
     private boolean mAbort;
 
-    /** This is set by the ActivityController thread to request collection of ANR trace files */
+    /**
+     * Count each event as a cycle. Set to false for scripts so that each time
+     * through the script increments the count.
+     */
+    private boolean mCountEvents = true;
+
+    /**
+     * This is set by the ActivityController thread to request collection of ANR
+     * trace files
+     */
     private boolean mRequestAnrTraces = false;
 
-    /** This is set by the ActivityController thread to request a "dumpsys meminfo" */
+    /**
+     * This is set by the ActivityController thread to request a
+     * "dumpsys meminfo"
+     */
     private boolean mRequestDumpsysMemInfo = false;
 
     /** Kill the process after a timeout or crash. */
@@ -103,8 +120,10 @@ public class Monkey {
 
     /** Packages we are allowed to run, or empty if no restriction. */
     private HashSet<String> mValidPackages = new HashSet<String>();
+
     /** Categories we are allowed to launch **/
-    ArrayList<String> mMainCategories = new ArrayList<String>();
+    private ArrayList<String> mMainCategories = new ArrayList<String>();
+
     /** Applications we can switch to. */
     private ArrayList<ComponentName> mMainApps = new ArrayList<ComponentName>();
 
@@ -119,25 +138,35 @@ public class Monkey {
 
     /** Dropped-event statistics **/
     long mDroppedKeyEvents = 0;
+
     long mDroppedPointerEvents = 0;
+
     long mDroppedTrackballEvents = 0;
+
     long mDroppedFlipEvents = 0;
 
-    /** a filename to the script (if any) **/
-    private String mScriptFileName = null;
+    /** a filename to the setup script (if any) */
+    private String mSetupFileName = null;
+
+    /** filenames of the script (if any) */
+    private ArrayList<String> mScriptFileNames = new ArrayList<String>();
 
     /** a TCP port to listen on for remote commands. */
     private int mServerPort = -1;
 
     private static final File TOMBSTONES_PATH = new File("/data/tombstones");
+
     private HashSet<String> mTombstones = null;
 
     float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
+
     MonkeyEventSource mEventSource;
+
     private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
 
     // information on the current activity.
     public static Intent currentIntent;
+
     public static String currentPackage;
 
     /**
@@ -147,8 +176,8 @@ public class Monkey {
         public boolean activityStarting(Intent intent, String pkg) {
             boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_STARTS != 0);
             if (mVerbose > 0) {
-                System.out.println("    // " + (allow ? "Allowing" : "Rejecting")
-                        + " start of " + intent + " in package " + pkg);
+                System.out.println("    // " + (allow ? "Allowing" : "Rejecting") + " start of "
+                        + intent + " in package " + pkg);
             }
             currentPackage = pkg;
             currentIntent = intent;
@@ -161,7 +190,7 @@ public class Monkey {
             if (!allow) {
                 if (mVerbose > 0) {
                     System.out.println("    // " + (allow ? "Allowing" : "Rejecting")
-                                       + " resume of package " + pkg);
+                            + " resume of package " + pkg);
                 }
             }
             currentPackage = pkg;
@@ -172,7 +201,8 @@ public class Monkey {
             if (pkg == null) {
                 return true;
             }
-            // preflight the hash lookup to avoid the cost of hash key generation
+            // preflight the hash lookup to avoid the cost of hash key
+            // generation
             if (mValidPackages.size() == 0) {
                 return true;
             } else {
@@ -180,26 +210,22 @@ public class Monkey {
             }
         }
 
-        public boolean appCrashed(String processName, int pid, String shortMsg,
-                String longMsg, byte[] crashData) {
-            System.err.println("// CRASH: " + processName + " (pid " + pid
-                    + ")");
+        public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
+                byte[] crashData) {
+            System.err.println("// CRASH: " + processName + " (pid " + pid + ")");
             System.err.println("// Short Msg: " + shortMsg);
             System.err.println("// Long Msg: " + longMsg);
             if (crashData != null) {
                 try {
-                    CrashData cd = new CrashData(new DataInputStream(
-                            new ByteArrayInputStream(crashData)));
-                    System.err.println("// Build Label: "
-                            + cd.getBuildData().getFingerprint());
+                    CrashData cd = new CrashData(new DataInputStream(new ByteArrayInputStream(
+                            crashData)));
+                    System.err.println("// Build Label: " + cd.getBuildData().getFingerprint());
                     System.err.println("// Build Changelist: "
                             + cd.getBuildData().getIncrementalVersion());
-                    System.err.println("// Build Time: "
-                            + cd.getBuildData().getTime());
+                    System.err.println("// Build Time: " + cd.getBuildData().getTime());
                     System.err.println("// ID: " + cd.getId());
                     System.err.println("// Tag: " + cd.getActivity());
-                    System.err.println(cd.getThrowableData().toString(
-                            "// "));
+                    System.err.println(cd.getThrowableData().toString("// "));
                 } catch (IOException e) {
                     System.err.println("// BAD STACK CRAWL");
                 }
@@ -215,10 +241,8 @@ public class Monkey {
             return false;
         }
 
-        public int appNotResponding(String processName, int pid,
-                String processStats) {
-            System.err.println("// NOT RESPONDING: " + processName
-                    + " (pid " + pid + ")");
+        public int appNotResponding(String processName, int pid, String processStats) {
+            System.err.println("// NOT RESPONDING: " + processName + " (pid " + pid + ")");
             System.err.println(processStats);
             reportProcRank();
             synchronized (Monkey.this) {
@@ -236,15 +260,16 @@ public class Monkey {
     }
 
     /**
-     * Run the procrank tool to insert system status information into the debug report.
+     * Run the procrank tool to insert system status information into the debug
+     * report.
      */
     private void reportProcRank() {
-      commandLineReport("procrank", "procrank");
+        commandLineReport("procrank", "procrank");
     }
 
     /**
-     * Run "cat /data/anr/traces.txt".  Wait about 5 seconds first, to let the asynchronous
-     * report writing complete.
+     * Run "cat /data/anr/traces.txt". Wait about 5 seconds first, to let the
+     * asynchronous report writing complete.
      */
     private void reportAnrTraces() {
         try {
@@ -256,9 +281,10 @@ public class Monkey {
 
     /**
      * Run "dumpsys meminfo"
-     *
-     * NOTE:  You cannot perform a dumpsys call from the ActivityController callback, as it will
-     * deadlock.  This should only be called from the main loop of the monkey.
+     * <p>
+     * NOTE: You cannot perform a dumpsys call from the ActivityController
+     * callback, as it will deadlock. This should only be called from the main
+     * loop of the monkey.
      */
     private void reportDumpsysMemInfo() {
         commandLineReport("meminfo", "dumpsys meminfo");
@@ -266,16 +292,20 @@ public class Monkey {
 
     /**
      * Print report from a single command line.
-     * @param reportName Simple tag that will print before the report and in various annotations.
+     * <p>
+     * TODO: Use ProcessBuilder & redirectErrorStream(true) to capture both
+     * streams (might be important for some command lines)
+     *
+     * @param reportName Simple tag that will print before the report and in
+     *            various annotations.
      * @param command Command line to execute.
-     * TODO: Use ProcessBuilder & redirectErrorStream(true) to capture both streams (might be
-     * important for some command lines)
      */
     private void commandLineReport(String reportName, String command) {
         System.err.println(reportName + ":");
         Runtime rt = Runtime.getRuntime();
         try {
-            // Process must be fully qualified here because android.os.Process is used elsewhere
+            // Process must be fully qualified here because android.os.Process
+            // is used elsewhere
             java.lang.Process p = Runtime.getRuntime().exec(command);
 
             // pipe everything from process stdout -> System.err
@@ -312,7 +342,7 @@ public class Monkey {
      * Run the command!
      *
      * @param args The command-line arguments
-     * @return Returns a posix-style result code.  0 for no error.
+     * @return Returns a posix-style result code. 0 for no error.
      */
     private int run(String[] args) {
         // Super-early debugger wait
@@ -332,7 +362,7 @@ public class Monkey {
         mArgs = args;
         mNextArg = 0;
 
-        //set a positive value, indicating none of the factors is provided yet
+        // set a positive value, indicating none of the factors is provided yet
         for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
             mFactors[i] = 1.0f;
         }
@@ -375,10 +405,22 @@ public class Monkey {
             return -4;
         }
 
-        if (mScriptFileName != null) {
+        if (mScriptFileNames != null && mScriptFileNames.size() == 1) {
             // script mode, ignore other options
-            mEventSource = new MonkeySourceScript(mScriptFileName, mThrottle);
+            mEventSource = new MonkeySourceScript(mScriptFileNames.get(0), mThrottle);
             mEventSource.setVerbose(mVerbose);
+
+            mCountEvents = false;
+        } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {
+            if (mSetupFileName != null) {
+                mEventSource = new MonkeySourceRandomScript(mSetupFileName, mScriptFileNames,
+                        mThrottle, mSeed);
+                mCount++;
+            } else {
+                mEventSource = new MonkeySourceRandomScript(mScriptFileNames, mThrottle, mSeed);
+            }
+            mEventSource.setVerbose(mVerbose);
+            mCountEvents = false;
         } else if (mServerPort != -1) {
             try {
                 mEventSource = new MonkeySourceNetwork(mServerPort);
@@ -389,37 +431,29 @@ public class Monkey {
             mCount = Integer.MAX_VALUE;
         } else {
             // random source by default
-            if (mVerbose >= 2) {    // check seeding performance
+            if (mVerbose >= 2) { // check seeding performance
                 System.out.println("// Seeded: " + mSeed);
             }
             mEventSource = new MonkeySourceRandom(mSeed, mMainApps, mThrottle);
             mEventSource.setVerbose(mVerbose);
-            //set any of the factors that has been set
+            // set any of the factors that has been set
             for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
                 if (mFactors[i] <= 0.0f) {
                     ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
                 }
             }
 
-            //in random mode, we start with a random activity
+            // in random mode, we start with a random activity
             ((MonkeySourceRandom) mEventSource).generateActivity();
         }
 
-        //validate source generator
+        // validate source generator
         if (!mEventSource.validate()) {
             return -5;
         }
 
-        if (mScriptFileName != null) {
-            // in random mode, count is the number of single events
-            // while in script mode, count is the number of repetition
-            // for a sequence of events, so we need do multiply the length of
-            // that sequence
-            mCount = mCount * ((MonkeySourceScript) mEventSource)
-                .getOneRoundEventCount();
-        }
-
-        // If we're profiling, do it immediately before/after the main monkey loop
+        // If we're profiling, do it immediately before/after the main monkey
+        // loop
         if (mGenerateHprof) {
             signalPersistentProcesses();
         }
@@ -473,9 +507,9 @@ public class Monkey {
         mNetworkMonitor.dump();
 
         if (crashedAtCycle < mCount - 1) {
-            System.err.println("** System appears to have crashed at event "
-                    + crashedAtCycle + " of " + mCount + " using seed " + mSeed);
-           return crashedAtCycle;
+            System.err.println("** System appears to have crashed at event " + crashedAtCycle
+                    + " of " + mCount + " using seed " + mSeed);
+            return crashedAtCycle;
         } else {
             if (mVerbose > 0) {
                 System.out.println("// Monkey finished");
@@ -520,29 +554,29 @@ public class Monkey {
                 } else if (opt.equals("--hprof")) {
                     mGenerateHprof = true;
                 } else if (opt.equals("--pct-touch")) {
-                    mFactors[MonkeySourceRandom.FACTOR_TOUCH] =
-                        -nextOptionLong("touch events percentage");
+                    int i = MonkeySourceRandom.FACTOR_TOUCH;
+                    mFactors[i] = -nextOptionLong("touch events percentage");
                 } else if (opt.equals("--pct-motion")) {
-                    mFactors[MonkeySourceRandom.FACTOR_MOTION] =
-                        -nextOptionLong("motion events percentage");
+                    int i = MonkeySourceRandom.FACTOR_MOTION;
+                    mFactors[i] = -nextOptionLong("motion events percentage");
                 } else if (opt.equals("--pct-trackball")) {
-                    mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
-                        -nextOptionLong("trackball events percentage");
+                    int i = MonkeySourceRandom.FACTOR_TRACKBALL;
+                    mFactors[i] = -nextOptionLong("trackball events percentage");
                 } else if (opt.equals("--pct-nav")) {
-                    mFactors[MonkeySourceRandom.FACTOR_NAV] =
-                        -nextOptionLong("nav events percentage");
+                    int i = MonkeySourceRandom.FACTOR_NAV;
+                    mFactors[i] = -nextOptionLong("nav events percentage");
                 } else if (opt.equals("--pct-majornav")) {
-                    mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
-                        -nextOptionLong("major nav events percentage");
+                    int i = MonkeySourceRandom.FACTOR_MAJORNAV;
+                    mFactors[i] = -nextOptionLong("major nav events percentage");
                 } else if (opt.equals("--pct-appswitch")) {
-                    mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
-                        -nextOptionLong("app switch events percentage");
+                    int i = MonkeySourceRandom.FACTOR_APPSWITCH;
+                    mFactors[i] = -nextOptionLong("app switch events percentage");
                 } else if (opt.equals("--pct-flip")) {
-                    mFactors[MonkeySourceRandom.FACTOR_FLIP] =
-                        -nextOptionLong("keyboard flip percentage");
+                    int i = MonkeySourceRandom.FACTOR_FLIP;
+                    mFactors[i] = -nextOptionLong("keyboard flip percentage");
                 } else if (opt.equals("--pct-anyevent")) {
-                    mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
-                        -nextOptionLong("any events percentage");
+                    int i = MonkeySourceRandom.FACTOR_ANYTHING;
+                    mFactors[i] = -nextOptionLong("any events percentage");
                 } else if (opt.equals("--throttle")) {
                     mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
                 } else if (opt.equals("--wait-dbg")) {
@@ -551,8 +585,10 @@ public class Monkey {
                     mSendNoEvents = true;
                 } else if (opt.equals("--port")) {
                     mServerPort = (int) nextOptionLong("Server port to listen on for commands");
+                } else if (opt.equals("--setup")) {
+                    mSetupFileName = nextOptionData();
                 } else if (opt.equals("-f")) {
-                    mScriptFileName = nextOptionData();
+                    mScriptFileNames.add(nextOptionData());
                 } else if (opt.equals("-h")) {
                     showUsage();
                     return false;
@@ -619,19 +655,22 @@ public class Monkey {
     private boolean getSystemInterfaces() {
         mAm = ActivityManagerNative.getDefault();
         if (mAm == null) {
-            System.err.println("** Error: Unable to connect to activity manager; is the system running?");
+            System.err.println("** Error: Unable to connect to activity manager; is the system "
+                    + "running?");
             return false;
         }
 
         mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
         if (mWm == null) {
-            System.err.println("** Error: Unable to connect to window manager; is the system running?");
+            System.err.println("** Error: Unable to connect to window manager; is the system "
+                    + "running?");
             return false;
         }
 
         mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
         if (mPm == null) {
-            System.err.println("** Error: Unable to connect to package manager; is the system running?");
+            System.err.println("** Error: Unable to connect to package manager; is the system "
+                    + "running?");
             return false;
         }
 
@@ -647,15 +686,16 @@ public class Monkey {
     }
 
     /**
-     * Using the restrictions provided (categories & packages), generate a list of activities
-     * that we can actually switch to.
+     * Using the restrictions provided (categories & packages), generate a list
+     * of activities that we can actually switch to.
      *
-     * @return Returns true if it could successfully build a list of target activities
+     * @return Returns true if it could successfully build a list of target
+     *         activities
      */
     private boolean getMainApps() {
         try {
             final int N = mMainCategories.size();
-            for (int i = 0; i< N; i++) {
+            for (int i = 0; i < N; i++) {
                 Intent intent = new Intent(Intent.ACTION_MAIN);
                 String category = mMainCategories.get(i);
                 if (category.length() > 0) {
@@ -666,31 +706,26 @@ public class Monkey {
                     System.err.println("// Warning: no activities found for category " + category);
                     continue;
                 }
-                if (mVerbose >= 2) {     // very verbose
+                if (mVerbose >= 2) { // very verbose
                     System.out.println("// Selecting main activities from category " + category);
                 }
                 final int NA = mainApps.size();
                 for (int a = 0; a < NA; a++) {
                     ResolveInfo r = mainApps.get(a);
-                    if (mValidPackages.size() == 0 ||
-                            mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
-                        if (mVerbose >= 2) {     // very verbose
-                            System.out.println("//   + Using main activity "
-                                    + r.activityInfo.name
+                    if (mValidPackages.size() == 0
+                            || mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
+                        if (mVerbose >= 2) { // very verbose
+                            System.out.println("//   + Using main activity " + r.activityInfo.name
                                     + " (from package "
-                                    + r.activityInfo.applicationInfo.packageName
-                                    + ")");
+                                    + r.activityInfo.applicationInfo.packageName + ")");
                         }
-                        mMainApps.add(new ComponentName(
-                                r.activityInfo.applicationInfo.packageName,
+                        mMainApps.add(new ComponentName(r.activityInfo.applicationInfo.packageName,
                                 r.activityInfo.name));
                     } else {
-                        if (mVerbose >= 3) {     // very very verbose
+                        if (mVerbose >= 3) { // very very verbose
                             System.out.println("//   - NOT USING main activity "
-                                    + r.activityInfo.name
-                                    + " (from package "
-                                    + r.activityInfo.applicationInfo.packageName
-                                    + ")");
+                                    + r.activityInfo.name + " (from package "
+                                    + r.activityInfo.applicationInfo.packageName + ")");
                         }
                     }
                 }
@@ -710,18 +745,20 @@ public class Monkey {
 
     /**
      * Run mCount cycles and see if we hit any crashers.
-     *
+     * <p>
      * TODO: Meta state on keys
      *
-     * @return Returns the last cycle which executed. If the value == mCount, no errors detected.
+     * @return Returns the last cycle which executed. If the value == mCount, no
+     *         errors detected.
      */
     private int runMonkeyCycles() {
-        int i = 0;
-        int lastKey = 0;
+
+        int eventCounter = 0;
+        int cycleCounter = 0;
 
         boolean systemCrashed = false;
 
-        while (!systemCrashed && i < mCount) {
+        while (!systemCrashed && cycleCounter < mCount) {
             synchronized (this) {
                 if (mRequestAnrTraces) {
                     reportAnrTraces();
@@ -732,37 +769,36 @@ public class Monkey {
                     mRequestDumpsysMemInfo = false;
                 }
                 if (mMonitorNativeCrashes) {
-                    // first time through, when i == 0, just set up the watcher (ignore the error)
-                    if (checkNativeCrashes() && (i > 0)) {
+                    // first time through, when eventCounter == 0, just set up
+                    // the watcher (ignore the error)
+                    if (checkNativeCrashes() && (eventCounter > 0)) {
                         System.out.println("** New native crash detected.");
                         mAbort = mAbort || mKillProcessAfterError;
                     }
                 }
                 if (mAbort) {
                     System.out.println("** Monkey aborted due to error.");
-                    System.out.println("Events injected: " + i);
-                    return i;
+                    System.out.println("Events injected: " + eventCounter);
+                    return eventCounter;
                 }
             }
 
-            // In this debugging mode, we never send any events.  This is primarily
-            // here so you can manually test the package or category limits, while manually
-            // exercising the system.
+            // In this debugging mode, we never send any events. This is
+            // primarily here so you can manually test the package or category
+            // limits, while manually exercising the system.
             if (mSendNoEvents) {
-                i++;
+                eventCounter++;
+                cycleCounter++;
                 continue;
             }
 
-            if ((mVerbose > 0) && (i % 100) == 0 && i != 0 && lastKey == 0) {
-                System.out.println("    // Sending event #" + i);
+            if ((mVerbose > 0) && (eventCounter % 100) == 0 && eventCounter != 0) {
+                System.out.println("    // Sending event #" + eventCounter);
             }
 
             MonkeyEvent ev = mEventSource.getNextEvent();
             if (ev != null) {
-                // We don't want to count throttling as an event.
-                if (!(ev instanceof MonkeyThrottleEvent)) {
-                    i++;
-                }
+
                 int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
                 if (injectCode == MonkeyEvent.INJECT_FAIL) {
                     if (ev instanceof MonkeyKeyEvent) {
@@ -774,21 +810,39 @@ public class Monkey {
                     }
                 } else if (injectCode == MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION) {
                     systemCrashed = true;
+                    System.err.println("** Error: RemoteException while injecting event.");
                 } else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
                     systemCrashed = !mIgnoreSecurityExceptions;
+                    if (systemCrashed) {
+                        System.err.println("** Error: SecurityException while injecting event.");
+                    }
+                }
+
+                // Don't count throttling as an event.
+                if (!(ev instanceof MonkeyThrottleEvent)) {
+                    eventCounter++;
+                    if (mCountEvents) {
+                        cycleCounter++;
+                    }
                 }
             } else {
-                // Event Source has signaled that we have no more events to process
-                break;
+                if (!mCountEvents) {
+                    cycleCounter++;
+                } else {
+                    System.err.println("** Error: Event source exhausted.");
+                    break;
+                }
             }
         }
+
         // If we got this far, we succeeded!
-        return mCount;
+        System.out.println("Events injected: " + eventCounter);
+        return eventCounter;
     }
 
     /**
-     * Send SIGNAL_USR1 to all processes.  This will generate large (5mb) profiling reports
-     * in data/misc, so use with care.
+     * Send SIGNAL_USR1 to all processes. This will generate large (5mb)
+     * profiling reports in data/misc, so use with care.
      */
     private void signalPersistentProcesses() {
         try {
@@ -804,14 +858,16 @@ public class Monkey {
     }
 
     /**
-     * Watch for appearance of new tombstone files, which indicate native crashes.
+     * Watch for appearance of new tombstone files, which indicate native
+     * crashes.
      *
      * @return Returns true if new files have appeared in the list
      */
     private boolean checkNativeCrashes() {
         String[] tombstones = TOMBSTONES_PATH.list();
 
-        // shortcut path for usually empty directory, so we don't waste even more objects
+        // shortcut path for usually empty directory, so we don't waste even
+        // more objects
         if ((tombstones == null) || (tombstones.length == 0)) {
             mTombstones = null;
             return false;
@@ -832,17 +888,20 @@ public class Monkey {
     }
 
     /**
-     * Return the next command line option.  This has a number of special cases which
-     * closely, but not exactly, follow the POSIX command line options patterns:
+     * Return the next command line option. This has a number of special cases
+     * which closely, but not exactly, follow the POSIX command line options
+     * patterns:
      *
+     * <pre>
      * -- means to stop processing additional options
      * -z means option z
      * -z ARGS means option z with (non-optional) arguments ARGS
      * -zARGS means option z with (optional) arguments ARGS
      * --zz means option zz
      * --zz ARGS means option zz with (non-optional) arguments ARGS
+     * </pre>
      *
-     * Note that you cannot combine single letter options;  -abc != -a -b -c
+     * Note that you cannot combine single letter options; -abc != -a -b -c
      *
      * @return Returns the option string, or null if there are no more options.
      */
@@ -889,7 +948,8 @@ public class Monkey {
     }
 
     /**
-     * Returns a long converted from the next data argument, with error handling if not available.
+     * Returns a long converted from the next data argument, with error handling
+     * if not available.
      *
      * @param opt The name of the option.
      * @return Returns a long converted from the argument.
@@ -923,19 +983,22 @@ public class Monkey {
      * Print how to use this command.
      */
     private void showUsage() {
-      System.err.println("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]");
-      System.err.println("              [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]");
-      System.err.println("              [--ignore-crashes] [--ignore-timeouts]");
-      System.err.println("              [--ignore-security-exceptions] [--monitor-native-crashes]");
-      System.err.println("              [--kill-process-after-error] [--hprof]");
-      System.err.println("              [--pct-touch PERCENT] [--pct-motion PERCENT]");
-      System.err.println("              [--pct-trackball PERCENT] [--pct-syskeys PERCENT]");
-      System.err.println("              [--pct-nav PERCENT] [--pct-majornav PERCENT]");
-      System.err.println("              [--pct-appswitch PERCENT] [--pct-flip PERCENT]");
-      System.err.println("              [--pct-anyevent PERCENT]");
-      System.err.println("              [--wait-dbg] [--dbg-no-events] [-f scriptfile]");
-      System.err.println("              [--port port]");
-      System.err.println("              [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
-      System.err.println("              COUNT");
-  }
+        StringBuffer usage = new StringBuffer();
+        usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n");
+        usage.append("              [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n");
+        usage.append("              [--ignore-crashes] [--ignore-timeouts]\n");
+        usage.append("              [--ignore-security-exceptions] [--monitor-native-crashes]\n");
+        usage.append("              [--kill-process-after-error] [--hprof]\n");
+        usage.append("              [--pct-touch PERCENT] [--pct-motion PERCENT]\n");
+        usage.append("              [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n");
+        usage.append("              [--pct-nav PERCENT] [--pct-majornav PERCENT]\n");
+        usage.append("              [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n");
+        usage.append("              [--pct-anyevent PERCENT]\n");
+        usage.append("              [--wait-dbg] [--dbg-no-events]\n");
+        usage.append("              [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]\n");
+        usage.append("              [--port port]\n");
+        usage.append("              [-s SEED] [-v [-v] ...] [--throttle MILLISEC]\n");
+        usage.append("              COUNT");
+        System.err.println(usage.toString());
+    }
 }
index a236554..af8b4ec 100644 (file)
@@ -19,23 +19,24 @@ package com.android.commands.monkey;
 /**
  * event source interface
  */
-public interface MonkeyEventSource {    
+public interface MonkeyEventSource {
     /**
-     * 
      * @return the next monkey event from the source
      */
     public MonkeyEvent getNextEvent();
-    
+
     /**
      * set verbose to allow different level of log
+     *
      * @param verbose output mode? 1= verbose, 2=very verbose
      */
     public void setVerbose(int verbose);
-    
+
     /**
      * check whether precondition is satisfied
-     * @return false if something fails, e.g. factor failure in random source
-     * or file can not open from script source etc
+     *
+     * @return false if something fails, e.g. factor failure in random source or
+     *         file can not open from script source etc
      */
     public boolean validate();
 }
index 27c8a51..5c7fdbc 100644 (file)
@@ -285,7 +285,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
     /**
      * set the factors
      *
-     * @param factors: percentages for each type of event
+     * @param factors percentages for each type of event
      */
     public void setFactors(float factors[]) {
         int c = FACTORZ_COUNT;
@@ -475,7 +475,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
      */
     public MonkeyEvent getNextEvent() {
         if (mQ.isEmpty()) {
-                generateEvents();
+            generateEvents();
         }
         mEventCount++;
         MonkeyEvent e = mQ.getFirst();
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandomScript.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandomScript.java
new file mode 100644 (file)
index 0000000..a937398
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 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 java.security.SecureRandom;
+import java.util.ArrayList;
+
+/**
+ * Class for generating MonkeyEvents from multiple scripts.
+ */
+public class MonkeySourceRandomScript implements MonkeyEventSource {
+    /** The verbose level of the source (currently not used) */
+    private int mVerbose = 0;
+
+    /** The source for the setup script if it exists */
+    private MonkeySourceScript mSetupSource = null;
+
+    /** The list of MonkeySourceScript instances to be played in random order */
+    private ArrayList<MonkeySourceScript> mScriptSources = new ArrayList<MonkeySourceScript>();
+
+    /** The current source, set to the setup source and then a random script */
+    private MonkeySourceScript mCurrentSource = null;
+
+    /** The random number generator */
+    private SecureRandom mRandom;
+
+    /**
+     * Creates a MonkeySourceRandomScript instance with an additional setup script.
+     *
+     * @param setupFileName The name of the setup script file on the device.
+     * @param scriptFileNames An ArrayList of the names of the script files to be run randomly.
+     * @param throttle The amount of time to sleep in ms between events.
+     * @param seed The seed of the random number generator.
+     */
+    public MonkeySourceRandomScript(String setupFileName, ArrayList<String> scriptFileNames,
+            long throttle, long seed) {
+        if (setupFileName != null) {
+            mSetupSource = new MonkeySourceScript(setupFileName, throttle);
+            mCurrentSource = mSetupSource;
+        }
+
+        for (String fileName: scriptFileNames) {
+            mScriptSources.add(new MonkeySourceScript(fileName, throttle));
+        }
+
+        mRandom = new SecureRandom();
+        mRandom.setSeed((seed == 0) ? -1 : seed);
+    }
+
+    /**
+     * Creates a MonkeySourceRandomScript instance without an additional setup script.
+     *
+     * @param scriptFileNames An ArrayList of the names of the script files to be run randomly.
+     * @param throttle The amount of time to sleep in ms between events.
+     * @param seed The seed of the random number generator.
+     */
+    public MonkeySourceRandomScript(ArrayList<String> scriptFileNames, long throttle, long seed) {
+        this(null, scriptFileNames, throttle, seed);
+    }
+
+    /**
+     * Gets the next event from the current event source.  If the event source is null, a new
+     * script event source is chosen randomly from the list of script sources and the next event is
+     * chosen from that.
+     *
+     * @return The first event in the event queue or null if the end of the file
+     *         is reached or if an error is encountered reading the file.
+     */
+    public MonkeyEvent getNextEvent() {
+        if (mCurrentSource == null) {
+            int numSources = mScriptSources.size();
+            if (numSources == 1) {
+                mCurrentSource = mScriptSources.get(0);
+            } else if (numSources > 1) {
+                mCurrentSource = mScriptSources.get(mRandom.nextInt(numSources));
+            }
+        }
+
+        if (mCurrentSource != null) {
+            MonkeyEvent nextEvent = mCurrentSource.getNextEvent();
+            if (nextEvent == null) {
+                mCurrentSource = null;
+            }
+            return nextEvent;
+        }
+        return null;
+    }
+
+    /**
+     * Sets the verbosity for the source as well as all sub event sources.
+     *
+     * @param verbose The verbose level.
+     */
+    public void setVerbose(int verbose) {
+        mVerbose = verbose;
+
+        if (mSetupSource != null) {
+            mSetupSource.setVerbose(verbose);
+        }
+
+        for (MonkeySourceScript source: mScriptSources) {
+            source.setVerbose(verbose);
+        }
+    }
+
+    /**
+     * Validates that all the underlying event sources are valid
+     *
+     * @return True if all the script files are valid.
+     *
+     * @see MonkeySourceScript#validate()
+     */
+    public boolean validate() {
+        if (mSetupSource != null && !mSetupSource.validate()) {
+            return false;
+        }
+
+        for (MonkeySourceScript source: mScriptSources) {
+            if (!source.validate()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
index 783dcf6..e372385 100644 (file)
@@ -23,461 +23,504 @@ import android.view.KeyEvent;
 import java.io.BufferedReader;
 import java.io.DataInputStream;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.util.LinkedList;
-import java.util.StringTokenizer;
+import java.util.NoSuchElementException;
 
-import android.view.KeyEvent;
 /**
- * monkey event queue. It takes a script to produce events
- * 
- * sample script format:
- *      type= raw events
- *      count= 10
- *      speed= 1.0
- *      start data >>
- *      captureDispatchPointer(5109520,5109520,0,230.75429,458.1814,0.20784314,
- *          0.06666667,0,0.0,0.0,65539,0)
- *      captureDispatchKey(5113146,5113146,0,20,0,0,0,0)
- *      captureDispatchFlip(true)
- *      ...
+ * monkey event queue. It takes a script to produce events sample script format:
+ *
+ * <pre>
+ * type= raw events
+ * count= 10
+ * speed= 1.0
+ * start data &gt;&gt;
+ * captureDispatchPointer(5109520,5109520,0,230.75429,458.1814,0.20784314,0.06666667,0,0.0,0.0,65539,0)
+ * captureDispatchKey(5113146,5113146,0,20,0,0,0,0)
+ * captureDispatchFlip(true)
+ * ...
+ * </pre>
  */
-public class MonkeySourceScript implements MonkeyEventSource {    
-    private int mEventCountInScript = 0;  //total number of events in the file
+public class MonkeySourceScript implements MonkeyEventSource {
+    private int mEventCountInScript = 0; // total number of events in the file
+
     private int mVerbose = 0;
+
     private double mSpeed = 1.0;
-    private String mScriptFileName; 
+
+    private String mScriptFileName;
+
     private MonkeyEventQueue mQ;
-    
-    private static final String HEADER_TYPE = "type=";
+
     private static final String HEADER_COUNT = "count=";
+
     private static final String HEADER_SPEED = "speed=";
-    // New script type
-    private static final String USER_EVENT_TYPE = "user";
-    
-    private long mLastRecordedDownTimeKey = 0;    
+
+    private long mLastRecordedDownTimeKey = 0;
+
     private long mLastRecordedDownTimeMotion = 0;
+
     private long mLastExportDownTimeKey = 0;
+
     private long mLastExportDownTimeMotion = 0;
+
     private long mLastExportEventTime = -1;
+
     private long mLastRecordedEventTime = -1;
-    private String mScriptType = USER_EVENT_TYPE;
-    
+
     private static final boolean THIS_DEBUG = false;
-    // a parameter that compensates the difference of real elapsed time and 
+
+    // a parameter that compensates the difference of real elapsed time and
     // time in theory
-    private static final long SLEEP_COMPENSATE_DIFF = 16;    
-    
+    private static final long SLEEP_COMPENSATE_DIFF = 16;
+
     // maximum number of events that we read at one time
     private static final int MAX_ONE_TIME_READS = 100;
-    
-    // number of additional events added to the script 
-    // add HOME_KEY down and up events to make start UI consistent in each round 
-    private static final int POLICY_ADDITIONAL_EVENT_COUNT = 2;
 
-    // event key word in the capture log    
+    // event key word in the capture log
     private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
+
     private static final String EVENT_KEYWORD_TRACKBALL = "DispatchTrackball";
+
     private static final String EVENT_KEYWORD_KEY = "DispatchKey";
+
     private static final String EVENT_KEYWORD_FLIP = "DispatchFlip";
+
     private static final String EVENT_KEYWORD_KEYPRESS = "DispatchPress";
+
     private static final String EVENT_KEYWORD_ACTIVITY = "LaunchActivity";
+
     private static final String EVENT_KEYWORD_WAIT = "UserWait";
+
     private static final String EVENT_KEYWORD_LONGPRESS = "LongPress";
 
     // a line at the end of the header
-    private static final String STARTING_DATA_LINE = "start data >>";    
-    private boolean mFileOpened = false;    
-    private static int LONGPRESS_WAIT_TIME = 2000; // wait time for the long press
-    
+    private static final String STARTING_DATA_LINE = "start data >>";
+
+    private boolean mFileOpened = false;
+
+    private static int LONGPRESS_WAIT_TIME = 2000; // wait time for the long
+
+    // press
+
     FileInputStream mFStream;
+
     DataInputStream mInputStream;
-    BufferedReader mBufferReader;
-    
+
+    BufferedReader mBufferedReader;
+
+    /**
+     * Creates a MonkeySourceScript instance.
+     *
+     * @param filename The filename of the script (on the device).
+     * @param throttle The amount of time in ms to sleep between events.
+     */
     public MonkeySourceScript(String filename, long throttle) {
         mScriptFileName = filename;
         mQ = new MonkeyEventQueue(throttle);
     }
-    
+
     /**
-     * 
-     * @return the number of total events that will be generated in a round 
+     * Resets the globals used to timeshift events.
      */
-    public int getOneRoundEventCount() {
-        //plus one home key down and up event
-        return mEventCountInScript + POLICY_ADDITIONAL_EVENT_COUNT; 
-    }
-    
     private void resetValue() {
-        mLastRecordedDownTimeKey = 0;    
+        mLastRecordedDownTimeKey = 0;
         mLastRecordedDownTimeMotion = 0;
+        mLastRecordedEventTime = -1;
         mLastExportDownTimeKey = 0;
-        mLastExportDownTimeMotion = 0;    
-        mLastRecordedEventTime = -1;        
+        mLastExportDownTimeMotion = 0;
         mLastExportEventTime = -1;
     }
-    
-    private boolean readScriptHeader() {
-        mEventCountInScript = -1;
-        mFileOpened = false;        
-        try {
-            if (THIS_DEBUG) {
-                System.out.println("reading script header");
-            }
-            
-            mFStream  =  new FileInputStream(mScriptFileName);
-            mInputStream  = new DataInputStream(mFStream);
-            mBufferReader = new BufferedReader(
-                    new InputStreamReader(mInputStream));
-            String sLine;
-            while ((sLine = mBufferReader.readLine()) != null) {
-                sLine = sLine.trim();
-                if (sLine.indexOf(HEADER_TYPE) >= 0) {
-                    mScriptType = sLine.substring(HEADER_TYPE.length() + 1).trim();
-                } else if (sLine.indexOf(HEADER_COUNT) >= 0) {
-                    try {
-                        mEventCountInScript = Integer.parseInt(sLine.substring(
-                                HEADER_COUNT.length() + 1).trim());
-                    } catch (NumberFormatException e) {
-                        System.err.println(e);
-                    }
-                } else if (sLine.indexOf(HEADER_SPEED) >= 0) {
-                    try {
-                        mSpeed = Double.parseDouble(sLine.substring(
-                                HEADER_SPEED.length() + 1).trim());
-                        
-                    } catch (NumberFormatException e) {
-                        System.err.println(e);
-                    }
-                } else if (sLine.indexOf(STARTING_DATA_LINE) >= 0) {
-                    // header ends until we read the start data mark
-                    mFileOpened = true;
-                    if (THIS_DEBUG) {
-                        System.out.println("read script header success");
-                    }    
-                    return true;
+
+    /**
+     * Reads the header of the script file.
+     *
+     * @return True if the file header could be parsed, and false otherwise.
+     * @throws IOException If there was an error reading the file.
+     */
+    private boolean readHeader() throws IOException {
+        mFileOpened = true;
+
+        mFStream = new FileInputStream(mScriptFileName);
+        mInputStream = new DataInputStream(mFStream);
+        mBufferedReader = new BufferedReader(new InputStreamReader(mInputStream));
+
+        String line;
+
+        while ((line = mBufferedReader.readLine()) != null) {
+            line = line.trim();
+
+            if (line.indexOf(HEADER_COUNT) >= 0) {
+                try {
+                    String value = line.substring(HEADER_COUNT.length() + 1).trim();
+                    mEventCountInScript = Integer.parseInt(value);
+                } catch (NumberFormatException e) {
+                    System.err.println(e);
+                    return false;
                 }
-            }            
-        } catch (FileNotFoundException e) {
-            System.err.println(e);
-        } catch (IOException e) {
-            System.err.println(e);
+            } else if (line.indexOf(HEADER_SPEED) >= 0) {
+                try {
+                    String value = line.substring(HEADER_COUNT.length() + 1).trim();
+                    mSpeed = Double.parseDouble(value);
+                } catch (NumberFormatException e) {
+                    System.err.println(e);
+                    return false;
+                }
+            } else if (line.indexOf(STARTING_DATA_LINE) >= 0) {
+                return true;
+            }
         }
-        
-        if (THIS_DEBUG) {
-            System.out.println("Error in reading script header");
-        }        
-        return false;        
-    }    
-
-    private void handleRawEvent(String s, StringTokenizer st) {
-        if (s.indexOf(EVENT_KEYWORD_KEY) >= 0) {
-            // key events
+
+        return false;
+    }
+
+    /**
+     * Reads a number of lines and passes the lines to be processed.
+     *
+     * @return The number of lines read.
+     * @throws IOException If there was an error reading the file.
+     */
+    private int readLines() throws IOException {
+        String line;
+        for (int i = 0; i < MAX_ONE_TIME_READS; i++) {
+            line = mBufferedReader.readLine();
+            if (line == null) {
+                return i;
+            }
+            line.trim();
+            processLine(line);
+        }
+        return MAX_ONE_TIME_READS;
+    }
+
+    /**
+     * Creates an event and adds it to the event queue. If the parameters are
+     * not understood, they are ignored and no events are added.
+     *
+     * @param s The entire string from the script file.
+     * @param args An array of arguments extracted from the script file line.
+     */
+    private void handleEvent(String s, String[] args) {
+        // Handle key event
+        if (s.indexOf(EVENT_KEYWORD_KEY) >= 0 && args.length == 8) {
             try {
                 System.out.println(" old key\n");
-                long downTime = Long.parseLong(st.nextToken());
-                long eventTime = Long.parseLong(st.nextToken());
-                int action = Integer.parseInt(st.nextToken());
-                int code = Integer.parseInt(st.nextToken());
-                int repeat = Integer.parseInt(st.nextToken());
-                int metaState = Integer.parseInt(st.nextToken());
-                int device = Integer.parseInt(st.nextToken());
-                int scancode = Integer.parseInt(st.nextToken());
-
-                MonkeyKeyEvent e =
-                        new MonkeyKeyEvent(downTime, eventTime, action, code, repeat, metaState,
-                                device, scancode);
+                long downTime = Long.parseLong(args[0]);
+                long eventTime = Long.parseLong(args[1]);
+                int action = Integer.parseInt(args[2]);
+                int code = Integer.parseInt(args[3]);
+                int repeat = Integer.parseInt(args[4]);
+                int metaState = Integer.parseInt(args[5]);
+                int device = Integer.parseInt(args[6]);
+                int scancode = Integer.parseInt(args[7]);
+
+                MonkeyKeyEvent e = new MonkeyKeyEvent(downTime, eventTime, action, code, repeat,
+                        metaState, device, scancode);
                 System.out.println(" Key code " + code + "\n");
 
                 mQ.addLast(e);
                 System.out.println("Added key up \n");
-
             } catch (NumberFormatException e) {
-                // something wrong with this line in the script
             }
-        } else if (s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || 
-                s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0) {
-            // trackball/pointer event
+            return;
+        }
+
+        // Handle trackball or pointer events
+        if ((s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0)
+                && args.length == 12) {
             try {
-                long downTime = Long.parseLong(st.nextToken());
-                long eventTime = Long.parseLong(st.nextToken());
-                int action = Integer.parseInt(st.nextToken());
-                float x = Float.parseFloat(st.nextToken());
-                float y = Float.parseFloat(st.nextToken());
-                float pressure = Float.parseFloat(st.nextToken());
-                float size = Float.parseFloat(st.nextToken());
-                int metaState = Integer.parseInt(st.nextToken());
-                float xPrecision = Float.parseFloat(st.nextToken());
-                float yPrecision = Float.parseFloat(st.nextToken());
-                int device = Integer.parseInt(st.nextToken());
-                int edgeFlags = Integer.parseInt(st.nextToken());
+                long downTime = Long.parseLong(args[0]);
+                long eventTime = Long.parseLong(args[1]);
+                int action = Integer.parseInt(args[2]);
+                float x = Float.parseFloat(args[3]);
+                float y = Float.parseFloat(args[4]);
+                float pressure = Float.parseFloat(args[5]);
+                float size = Float.parseFloat(args[6]);
+                int metaState = Integer.parseInt(args[7]);
+                float xPrecision = Float.parseFloat(args[8]);
+                float yPrecision = Float.parseFloat(args[9]);
+                int device = Integer.parseInt(args[10]);
+                int edgeFlags = Integer.parseInt(args[11]);
 
                 int type = MonkeyEvent.EVENT_TYPE_TRACKBALL;
                 if (s.indexOf("Pointer") > 0) {
                     type = MonkeyEvent.EVENT_TYPE_POINTER;
                 }
-                MonkeyMotionEvent e =
-                        new MonkeyMotionEvent(type, downTime, eventTime, action, x, y, pressure,
-                                size, metaState, xPrecision, yPrecision, device, edgeFlags);
+                MonkeyMotionEvent e = new MonkeyMotionEvent(type, downTime, eventTime, action, x,
+                        y, pressure, size, metaState, xPrecision, yPrecision, device, edgeFlags);
                 mQ.addLast(e);
             } catch (NumberFormatException e) {
-                // we ignore this event
             }
-        } else if (s.indexOf(EVENT_KEYWORD_FLIP) >= 0) {
-            boolean keyboardOpen = Boolean.parseBoolean(st.nextToken());
+            return;
+        }
+
+        // Handle flip events
+        if (s.indexOf(EVENT_KEYWORD_FLIP) >= 0 && args.length == 1) {
+            boolean keyboardOpen = Boolean.parseBoolean(args[0]);
             MonkeyFlipEvent e = new MonkeyFlipEvent(keyboardOpen);
             mQ.addLast(e);
         }
 
-    }
-
-    private void handleUserEvent(String s, StringTokenizer st) {
-        if (s.indexOf(EVENT_KEYWORD_ACTIVITY) >= 0) {
-            String pkg_name = st.nextToken();
-            String cl_name = st.nextToken();
+        // Handle launch events
+        if (s.indexOf(EVENT_KEYWORD_ACTIVITY) >= 0 && args.length == 2) {
+            String pkg_name = args[0];
+            String cl_name = args[1];
             ComponentName mApp = new ComponentName(pkg_name, cl_name);
             MonkeyActivityEvent e = new MonkeyActivityEvent(mApp);
             mQ.addLast(e);
+            return;
+        }
 
-        } else if (s.indexOf(EVENT_KEYWORD_WAIT) >= 0) {
-            long sleeptime = Integer.parseInt(st.nextToken());
-            MonkeyWaitEvent e = new MonkeyWaitEvent(sleeptime);
-            mQ.addLast(e);
+        // Handle wait events
+        if (s.indexOf(EVENT_KEYWORD_WAIT) >= 0 && args.length == 1) {
+            try {
+                long sleeptime = Integer.parseInt(args[0]);
+                MonkeyWaitEvent e = new MonkeyWaitEvent(sleeptime);
+                mQ.addLast(e);
+            } catch (NumberFormatException e) {
+            }
+            return;
+        }
 
-        } else if (s.indexOf(EVENT_KEYWORD_KEYPRESS) >= 0) {
-            String key_name = st.nextToken();
+        // Handle keypress events
+        if (s.indexOf(EVENT_KEYWORD_KEYPRESS) >= 0 && args.length == 1) {
+            String key_name = args[0];
             int keyCode = MonkeySourceRandom.getKeyCode(key_name);
             MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode);
             mQ.addLast(e);
             e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode);
             mQ.addLast(e);
-        } else if (s.indexOf(EVENT_KEYWORD_LONGPRESS) >= 0) {
-            // handle the long press
-            MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN,
-                    KeyEvent.KEYCODE_DPAD_CENTER);
+            return;
+        }
+
+        // Handle longpress events
+        if (s.indexOf(EVENT_KEYWORD_LONGPRESS) >= 0) {
+            MonkeyKeyEvent e;
+            e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
             mQ.addLast(e);
             MonkeyWaitEvent we = new MonkeyWaitEvent(LONGPRESS_WAIT_TIME);
             mQ.addLast(we);
-            e = new MonkeyKeyEvent(KeyEvent.ACTION_UP,
-                    KeyEvent.KEYCODE_DPAD_CENTER);
+            e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER);
             mQ.addLast(e);
         }
     }
 
-    private void processLine(String s) {
-        int index1 = s.indexOf('(');
-        int index2 = s.indexOf(')');
+    /**
+     * Extracts an event and a list of arguments from a line. If the line does
+     * not match the format required, it is ignored.
+     *
+     * @param line A string in the form {@code cmd(arg1,arg2,arg3)}.
+     */
+    private void processLine(String line) {
+        int index1 = line.indexOf('(');
+        int index2 = line.indexOf(')');
 
         if (index1 < 0 || index2 < 0) {
             return;
         }
 
-        StringTokenizer st = new StringTokenizer(
-                s.substring(index1 + 1, index2), ",");
-        if (mScriptType.compareTo(USER_EVENT_TYPE) == 0) {
-            // User event type
-            handleUserEvent(s, st);
-        } else {
-            // Raw type
-            handleRawEvent(s,st);
+        String[] args = line.substring(index1 + 1, index2).split(",");
+
+        for (int i = 0; i < args.length; i++) {
+            args[i] = args[i].trim();
         }
+
+        handleEvent(line, args);
     }
 
-    private void closeFile() {
-        mFileOpened = false;        
-        if (THIS_DEBUG) {
-            System.out.println("closing script file");
-        }
-        
+    /**
+     * Closes the script file.
+     *
+     * @throws IOException If there was an error closing the file.
+     */
+    private void closeFile() throws IOException {
+        mFileOpened = false;
+
         try {
             mFStream.close();
             mInputStream.close();
-        } catch (IOException e) {
-            System.out.println(e);
+        } catch (NullPointerException e) {
+            // File was never opened so it can't be closed.
         }
     }
-    
-    /**
-     * add home key press/release event to the queue
-     */
-    private void addHomeKeyEvent() {        
-        MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, 
-                KeyEvent.KEYCODE_HOME);
-        mQ.addLast(e);        
-        e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HOME);
-        mQ.addLast(e);
-    }
-    
+
     /**
-      * read next batch of events from the provided script file
-     * @return true if success
+     * Read next batch of events from the script file into the event queue.
+     * Checks if the script is open and then reads the next MAX_ONE_TIME_READS
+     * events or reads until the end of the file. If no events are read, then
+     * the script is closed.
+     *
+     * @throws IOException If there was an error reading the file.
      */
-    private boolean readNextBatch() {
-        /*
-         * The script should restore the original state when it run multiple
-         * times.
-         */
-        String sLine = null;
-        int readCount = 0;
+    private void readNextBatch() throws IOException {
+        int linesRead = 0;
 
         if (THIS_DEBUG) {
             System.out.println("readNextBatch(): reading next batch of events");
         }
 
         if (!mFileOpened) {
-            if (!readScriptHeader()) {
-                closeFile();
-                return false;
-            }
             resetValue();
+            readHeader();
         }
-        
-        try {            
-            while (readCount++ < MAX_ONE_TIME_READS &&                    
-                   (sLine = mBufferReader.readLine()) != null) {
-                sLine = sLine.trim();                        
-                processLine(sLine);
-            }
-        } catch (IOException e) {
-            System.err.println(e);
-            return false;
-        }
-        
-        if (sLine == null) {
-            // to the end of the file
-            if (THIS_DEBUG) {
-                System.out.println("readNextBatch(): to the end of file");
-            }
+
+        linesRead = readLines();
+
+        if (linesRead == 0) {
             closeFile();
-        }        
-        return true;
+        }
     }
-    
+
     /**
-     * sleep for a period of given time, introducing latency among events
-     * @param time to sleep in millisecond
+     * Sleep for a period of given time. Used to introduce latency between
+     * events.
+     *
+     * @param time The amount of time to sleep in ms
      */
-    private void needSleep(long time) {        
+    private void needSleep(long time) {
         if (time < 1) {
-            return;    
-        }        
+            return;
+        }
         try {
             Thread.sleep(time);
-        } catch (InterruptedException e) {            
-        }        
-    }    
-    
+        } catch (InterruptedException e) {
+        }
+    }
+
     /**
-     * check whether we can successfully read the header of the script file
+     * Checks if the file can be opened and if the header is valid.
+     *
+     * @return True if the file exists and the header is valid, false otherwise.
      */
     public boolean validate() {
-        boolean b = readNextBatch(); 
+        boolean validHeader;
+        try {
+            validHeader = readHeader();
+            closeFile();
+        } catch (IOException e) {
+            return false;
+        }
+
         if (mVerbose > 0) {
-            System.out.println("Replaying " + mEventCountInScript + 
-                    " events with speed " + mSpeed);
+            System.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed);
         }
-        return b;        
+        return validHeader;
     }
-    
+
     public void setVerbose(int verbose) {
         mVerbose = verbose;
     }
-    
+
     /**
-     * adjust key downtime and eventtime according to both  
-     * recorded values and current system time 
-     * @param e KeyEvent
+     * Adjust key downtime and eventtime according to both recorded values and
+     * current system time.
+     *
+     * @param e A KeyEvent
      */
     private void adjustKeyEventTime(MonkeyKeyEvent e) {
         if (e.getEventTime() < 0) {
             return;
-        }      
+        }
         long thisDownTime = 0;
         long thisEventTime = 0;
         long expectedDelay = 0;
-        
+
         if (mLastRecordedEventTime <= 0) {
-            // first time event            
+            // first time event
             thisDownTime = SystemClock.uptimeMillis();
             thisEventTime = thisDownTime;
-        } else {            
+        } else {
             if (e.getDownTime() != mLastRecordedDownTimeKey) {
                 thisDownTime = e.getDownTime();
             } else {
                 thisDownTime = mLastExportDownTimeKey;
-            }            
-            expectedDelay = (long) ((e.getEventTime() - 
-                    mLastRecordedEventTime) * mSpeed);            
+            }
+            expectedDelay = (long) ((e.getEventTime() - mLastRecordedEventTime) * mSpeed);
             thisEventTime = mLastExportEventTime + expectedDelay;
             // add sleep to simulate everything in recording
             needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF);
-        }        
+        }
         mLastRecordedDownTimeKey = e.getDownTime();
-        mLastRecordedEventTime = e.getEventTime();         
+        mLastRecordedEventTime = e.getEventTime();
         e.setDownTime(thisDownTime);
-        e.setEventTime(thisEventTime);        
+        e.setEventTime(thisEventTime);
         mLastExportDownTimeKey = thisDownTime;
-        mLastExportEventTime =  thisEventTime;
+        mLastExportEventTime = thisEventTime;
     }
-    
+
     /**
-     * adjust motion downtime and eventtime according to both  
-     * recorded values and current system time 
-     * @param e KeyEvent
+     * Adjust motion downtime and eventtime according to both recorded values
+     * and current system time.
+     *
+     * @param e A KeyEvent
      */
     private void adjustMotionEventTime(MonkeyMotionEvent e) {
         if (e.getEventTime() < 0) {
             return;
-        }      
+        }
         long thisDownTime = 0;
         long thisEventTime = 0;
         long expectedDelay = 0;
-        
+
         if (mLastRecordedEventTime <= 0) {
-            // first time event            
+            // first time event
             thisDownTime = SystemClock.uptimeMillis();
             thisEventTime = thisDownTime;
-        } else {            
+        } else {
             if (e.getDownTime() != mLastRecordedDownTimeMotion) {
                 thisDownTime = e.getDownTime();
             } else {
                 thisDownTime = mLastExportDownTimeMotion;
-            }            
-            expectedDelay = (long) ((e.getEventTime() - 
-                    mLastRecordedEventTime) * mSpeed);            
+            }
+            expectedDelay = (long) ((e.getEventTime() - mLastRecordedEventTime) * mSpeed);
             thisEventTime = mLastExportEventTime + expectedDelay;
             // add sleep to simulate everything in recording
             needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF);
         }
-        
+
         mLastRecordedDownTimeMotion = e.getDownTime();
-        mLastRecordedEventTime = e.getEventTime();         
+        mLastRecordedEventTime = e.getEventTime();
         e.setDownTime(thisDownTime);
-        e.setEventTime(thisEventTime);        
+        e.setEventTime(thisEventTime);
         mLastExportDownTimeMotion = thisDownTime;
-        mLastExportEventTime =  thisEventTime;
-    }    
-    
+        mLastExportEventTime = thisEventTime;
+    }
+
     /**
-     * if the queue is empty, we generate events first
-     * @return the first event in the queue, if null, indicating the system crashes 
+     * Gets the next event to be injected from the script. If the event queue is
+     * empty, reads the next n events from the script into the queue, where n is
+     * the lesser of the number of remaining events and the value specified by
+     * MAX_ONE_TIME_READS. If the end of the file is reached, no events are
+     * added to the queue and null is returned.
+     *
+     * @return The first event in the event queue or null if the end of the file
+     *         is reached or if an error is encountered reading the file.
      */
     public MonkeyEvent getNextEvent() {
         long recordedEventTime = -1;
-        
+        MonkeyEvent ev;
+
         if (mQ.isEmpty()) {
-            readNextBatch();
+            try {
+                readNextBatch();
+            } catch (IOException e) {
+                return null;
+            }
         }
-        MonkeyEvent e = mQ.getFirst();
-        mQ.removeFirst();
-        if (e.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
-            adjustKeyEventTime((MonkeyKeyEvent) e);        
-        } else if (e.getEventType() == MonkeyEvent.EVENT_TYPE_POINTER || 
-                e.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
-            adjustMotionEventTime((MonkeyMotionEvent) e);
+
+        try {
+            ev = mQ.getFirst();
+            mQ.removeFirst();
+        } catch (NoSuchElementException e) {
+            return null;
+        }
+
+        if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
+            adjustKeyEventTime((MonkeyKeyEvent) ev);
+        } else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_POINTER
+                || ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
+            adjustMotionEventTime((MonkeyMotionEvent) ev);
         }
-        return e;
+        return ev;
     }
 }
index 54f7aa2..c1ef3ef 100755 (executable)
@@ -42,6 +42,9 @@ HKR,,Icon,,-1
 %CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_0D02&MI_01\r
 %SingleAdbInterface%        = USB_Install, USB\VID_18D1&PID_4E11
 %CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_4E12&MI_01
+;
+; Dell's Mini5
+%CompositeAdbInterface%     = USB_Install, USB\VID_413C&PID_B007&MI_01
 \r
 [Google.NTamd64]\r
 ; HTC Dream\r
@@ -60,6 +63,9 @@ HKR,,Icon,,-1
 %CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_0D02&MI_01
 %SingleAdbInterface%        = USB_Install, USB\VID_18D1&PID_4E11\r
 %CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_4E12&MI_01
+;
+; Dell's Mini5
+%CompositeAdbInterface%     = USB_Install, USB\VID_413C&PID_B007&MI_01
 \r
 [USB_Install]\r
 Include = winusb.inf\r
index c6db521..e36a82f 100644 (file)
@@ -1,7 +1,7 @@
 Android NDK ChangeLog:
 
 -------------------------------------------------------------------------------
-current version
+android-ndk-r3
 
 IMPORTANT BUG FIXES:
 
@@ -70,6 +70,11 @@ IMPORTANT CHANGES:
     folder corresponding to the old ABI.
 
 
+- Updated the STABLE-APIS.TXT document to clarify the OpenGL ES 1.0/1.1/2.0
+  issues regarding specific devices (i.e. 1.0 supported everywhere, 1.1 and
+  2.0 on specific devices only, need for <uses-feature> tag in manifest).
+
+
 OTHER FIXES & CHANGES:
 
 - Actually use the awk version detected by host-setup.sh during the build.
index 48e6b5a..0ffeb25 100644 (file)
@@ -138,6 +138,29 @@ as in:
 
   LOCAL_LDLIBS := -lGLESv1_CM.so
 
+
+The '1.x' here refers to both versions 1.0 and 1.1 of the OpenGL ES APIs.
+Please note that:
+
+  - OpenGL ES 1.0 is supported on *all* Android-based devices.
+  - OpenGL ES 1.1 is fully supported only on specific devices that
+    have the corresponding GPU.
+
+This is because Android comes with a 1.0-capable software renderer that can
+be used on GPU-less devices.
+
+Developers should query the OpenGL ES version string and extension string
+to know if the current device supports the features they need. See the
+description of glGetString() in the specification to see how to do that:
+
+    http://www.khronos.org/opengles/sdk/1.1/docs/man/glGetString.xml
+
+Additionally, developers must put a <uses-feature> tag in their manifest
+file to indicate which version of OpenGL ES their application requires. See
+the documentation linked below for details:
+
+ http://developer.android.com/guide/topics/manifest/uses-feature-element.html
+
 Please note that, at the moment, native headers and libraries for the EGL APIs
 are *not* available. EGL is used to perform surface creation and flipping
 (instead of rendering). The corresponding operations must be performed in your
@@ -173,6 +196,10 @@ as in:
 
   LOCAL_LDLIBS := -lGLESv2.so
 
+Not all devices support OpenGL ES 2.0, developers should thus query the
+implementation's version and extension strings, and put a <uses-feature>
+tag in their Android manifest. See Section III above for details.
+
 Please note that, at the moment, native headers and libraries for the EGL APIs
 are *not* available. EGL is used to perform surface creation and flipping
 (instead of rendering). The corresponding operations must be performed in your
diff --git a/samples/BusinessCard/_index.html b/samples/BusinessCard/_index.html
new file mode 100644 (file)
index 0000000..580b850
--- /dev/null
@@ -0,0 +1,7 @@
+<p>A sample application that demonstrates how to launch the built-in contact
+picker from within an activity. This sample also uses reflection to ensure
+that the correct version of the contacts API is used, depending on which API
+level the application is running under.</p>
+
+<img alt="Screenshot #1 of the Business Card application" src="../images/BusinessCard1.png" />
+<img alt="Screenshot #2 of the Business Card application" src="../images/BusinessCard2.png" />
diff --git a/samples/source.properties b/samples/source.properties
new file mode 100644 (file)
index 0000000..73c0d76
--- /dev/null
@@ -0,0 +1,5 @@
+Pkg.UserSrc=false
+Platform.Version=2.1
+Pkg.Revision=1
+AndroidVersion.ApiLevel=7
+
diff --git a/tools/etc1tool/Android.mk b/tools/etc1tool/Android.mk
new file mode 100644 (file)
index 0000000..5536158
--- /dev/null
@@ -0,0 +1,40 @@
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Android.mk for etc1tool 
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := etc1tool.cpp
+
+LOCAL_C_INCLUDES += external/libpng
+LOCAL_C_INCLUDES += external/zlib
+LOCAL_C_INCLUDES += build/libs/host/include
+LOCAL_C_INCLUDES += frameworks/base/opengl/include
+
+#LOCAL_WHOLE_STATIC_LIBRARIES := 
+LOCAL_STATIC_LIBRARIES := \
+       libhost \
+       libutils \
+       libcutils \
+       libexpat \
+       libpng \
+       libETC1
+
+LOCAL_LDLIBS := -lz
+
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lrt
+endif
+
+ifeq ($(HOST_OS),windows)
+ifeq ($(strip $(USE_CYGWIN),),)
+LOCAL_LDLIBS += -lws2_32
+endif
+endif
+
+LOCAL_MODULE := etc1tool
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/etc1tool/etc1tool.cpp b/tools/etc1tool/etc1tool.cpp
new file mode 100644 (file)
index 0000000..ecf96ec
--- /dev/null
@@ -0,0 +1,583 @@
+// Copyright 2009 Google Inc.
+//
+// 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.
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <png.h>
+#include <ETC1/etc1.h>
+
+
+int writePNGFile(const char* pOutput, png_uint_32 width, png_uint_32 height,
+        const png_bytep pImageData, png_uint_32 imageStride);
+
+const char* gpExeName;
+
+static
+void usage(char* message, ...) {
+    if (message) {
+        va_list ap;
+        va_start(ap, message);
+        vfprintf(stderr, message, ap);
+        va_end(ap);
+        fprintf(stderr, "\n\n");
+        fprintf(stderr, "usage:\n");
+    }
+    fprintf(
+            stderr,
+            "%s infile [--help | --encode | --encodeNoHeader | --decode] [--showDifference difffile] [-o outfile]\n",
+            gpExeName);
+    fprintf(stderr, "\tDefault is --encode\n");
+    fprintf(stderr, "\t\t--help           print this usage information.\n");
+    fprintf(stderr,
+            "\t\t--encode         create an ETC1 file from a PNG file.\n");
+    fprintf(
+            stderr,
+            "\t\t--encodeNoHeader create a raw ETC1 data file (without a header) from a PNG file.\n");
+    fprintf(stderr,
+            "\t\t--decode         create a PNG file from an ETC1 file.\n");
+    fprintf(stderr,
+            "\t\t--showDifference difffile    Write difference between original and encoded\n");
+    fprintf(stderr,
+            "\t\t                             image to difffile. (Only valid when encoding).\n");
+    fprintf(stderr,
+            "\tIf outfile is not specified, an outfile path is constructed from infile,\n");
+    fprintf(stderr, "\twith the apropriate suffix (.pkm or .png).\n");
+    exit(1);
+}
+
+// Returns non-zero if an error occured
+
+static
+int changeExtension(char* pPath, size_t pathCapacity, const char* pExtension) {
+    size_t pathLen = strlen(pPath);
+    size_t extensionLen = strlen(pExtension);
+    if (pathLen + extensionLen + 1 > pathCapacity) {
+        return -1;
+    }
+
+    // Check for '.' and '..'
+    if ((pathLen == 1 && pPath[0] == '.') || (pathLen == 2 && pPath[0] == '.'
+            && pPath[1] == '.') || (pathLen >= 2 && pPath[pathLen - 2] == '/'
+            && pPath[pathLen - 1] == '.') || (pathLen >= 3
+            && pPath[pathLen - 3] == '/' && pPath[pathLen - 2] == '.'
+            && pPath[pathLen - 1] == '.')) {
+        return -2;
+    }
+
+    int index;
+    for (index = pathLen - 1; index > 0; index--) {
+        char c = pPath[index];
+        if (c == '/') {
+            // No extension found. Append our extension.
+            strcpy(pPath + pathLen, pExtension);
+            return 0;
+        } else if (c == '.') {
+            strcpy(pPath + index, pExtension);
+            return 0;
+        }
+    }
+
+    // No extension or directory found. Append our extension
+    strcpy(pPath + pathLen, pExtension);
+    return 0;
+}
+
+void PNGAPI user_error_fn(png_structp png_ptr, png_const_charp message) {
+    fprintf(stderr, "PNG error: %s\n", message);
+}
+
+void PNGAPI user_warning_fn(png_structp png_ptr, png_const_charp message) {
+    fprintf(stderr, "PNG warning: %s\n", message);
+}
+
+// Return non-zero on error
+int fwrite_big_endian_uint16(png_uint_32 data, FILE* pOut) {
+    if (fputc(0xff & (data >> 8), pOut) == EOF) {
+        return -1;
+    }
+    if (fputc(0xff & data, pOut) == EOF) {
+        return -1;
+    }
+    return 0;
+}
+
+// Return non-zero on error
+int fread_big_endian_uint16(png_uint_32* data, FILE* pIn) {
+    int a, b;
+    if ((a = fgetc(pIn)) == EOF) {
+        return -1;
+    }
+    if ((b = fgetc(pIn)) == EOF) {
+        return -1;
+    }
+    *data = ((0xff & a) << 8) | (0xff & b);
+    return 0;
+}
+
+// Read a PNG file into a contiguous buffer.
+// Returns non-zero if an error occurred.
+// caller has to delete[] *ppImageData when done with the image.
+
+int read_PNG_File(const char* pInput, etc1_byte** ppImageData,
+        etc1_uint32* pWidth, etc1_uint32* pHeight) {
+    FILE* pIn = NULL;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    png_infop end_info = NULL;
+    png_bytep* row_pointers = NULL; // Does not need to be deallocated.
+    png_uint_32 width = 0;
+    png_uint_32 height = 0;
+    int result = -1;
+    etc1_byte* pSourceImage = 0;
+    png_uint_32 stride = 0;
+
+    if ((pIn = fopen(pInput, "rb")) == NULL) {
+        fprintf(stderr, "Could not open input file %s for reading: %d\n",
+                pInput, errno);
+        goto exit;
+    }
+
+    static const size_t PNG_HEADER_SIZE = 8;
+    png_byte pngHeader[PNG_HEADER_SIZE];
+    if (fread(pngHeader, 1, PNG_HEADER_SIZE, pIn) != PNG_HEADER_SIZE) {
+        fprintf(stderr, "Could not read PNG header from %s: %d\n", pInput,
+                errno);
+        goto exit;
+    }
+
+    if (png_sig_cmp(pngHeader, 0, PNG_HEADER_SIZE)) {
+        fprintf(stderr, "%s is not a PNG file.\n", pInput);
+        goto exit;
+    }
+
+    if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+            (png_voidp) NULL, user_error_fn, user_warning_fn))) {
+        fprintf(stderr, "Could not initialize png read struct.\n");
+        goto exit;
+    }
+
+    if (!(info_ptr = png_create_info_struct(png_ptr))) {
+        fprintf(stderr, "Could not create info struct.\n");
+        goto exit;
+    }
+    if (!(end_info = png_create_info_struct(png_ptr))) {
+        fprintf(stderr, "Could not create end_info struct.\n");
+        goto exit;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        goto exit;
+    }
+
+    png_init_io(png_ptr, pIn);
+    png_set_sig_bytes(png_ptr, PNG_HEADER_SIZE);
+    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY
+            | PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_STRIP_ALPHA
+            | PNG_TRANSFORM_PACKING, NULL);
+
+    row_pointers = png_get_rows(png_ptr, info_ptr);
+    {
+        int bit_depth, color_type;
+        png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
+                &color_type, NULL, NULL, NULL);
+    }
+
+    stride = 3 * width;
+
+    pSourceImage = new etc1_byte[stride * height];
+    if (! pSourceImage) {
+        fprintf(stderr, "Out of memory.\n");
+        goto exit;
+    }
+
+    for (etc1_uint32 y = 0; y < height; y++) {
+        memcpy(pSourceImage + y * stride, row_pointers[y], stride);
+    }
+
+    *pWidth = width;
+    *pHeight = height;
+    *ppImageData = pSourceImage;
+
+    result = 0;
+    exit:
+    if (result) {
+        delete[] pSourceImage;
+    }
+    if (png_ptr) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+    }
+    if (pIn) {
+        fclose(pIn);
+    }
+
+    return result;
+}
+
+// Read a PNG file into a contiguous buffer.
+// Returns non-zero if an error occurred.
+// caller has to delete[] *ppImageData when done with the image.
+int readPKMFile(const char* pInput, etc1_byte** ppImageData,
+        etc1_uint32* pWidth, etc1_uint32* pHeight) {
+    int result = -1;
+    FILE* pIn = NULL;
+    etc1_byte header[ETC_PKM_HEADER_SIZE];
+    png_bytep pEncodedData = NULL;
+    png_bytep pImageData = NULL;
+
+    png_uint_32 width = 0;
+    png_uint_32 height = 0;
+    png_uint_32 stride = 0;
+    png_uint_32 encodedSize = 0;
+
+    if ((pIn = fopen(pInput, "rb")) == NULL) {
+        fprintf(stderr, "Could not open input file %s for reading: %d\n",
+                pInput, errno);
+        goto exit;
+    }
+
+    if (fread(header, sizeof(header), 1, pIn) != 1) {
+        fprintf(stderr, "Could not read header from input file %s: %d\n",
+                pInput, errno);
+        goto exit;
+    }
+
+    if (! etc1_pkm_is_valid(header)) {
+        fprintf(stderr, "Bad header PKM header for input file %s\n", pInput);
+        goto exit;
+    }
+
+    width = etc1_pkm_get_width(header);
+    height = etc1_pkm_get_height(header);
+    encodedSize = etc1_get_encoded_data_size(width, height);
+
+    pEncodedData = new png_byte[encodedSize];
+    if (!pEncodedData) {
+        fprintf(stderr, "Out of memory.\n");
+        goto exit;
+    }
+
+    if (fread(pEncodedData, encodedSize, 1, pIn) != 1) {
+        fprintf(stderr, "Could not read encoded data from input file %s: %d\n",
+                pInput, errno);
+        goto exit;
+    }
+
+    fclose(pIn);
+    pIn = NULL;
+
+    stride = width * 3;
+    pImageData = new png_byte[stride * height];
+    if (!pImageData) {
+        fprintf(stderr, "Out of memory.\n");
+        goto exit;
+    }
+
+    etc1_decode_image(pEncodedData, pImageData, width, height, 3, stride);
+
+    // Success
+    result = 0;
+    *ppImageData = pImageData;
+    pImageData = 0;
+    *pWidth = width;
+    *pHeight = height;
+
+    exit:
+    delete[] pEncodedData;
+    delete[] pImageData;
+    if (pIn) {
+        fclose(pIn);
+    }
+
+    return result;
+}
+
+
+// Encode the file.
+// Returns non-zero if an error occurred.
+
+int encode(const char* pInput, const char* pOutput, bool bEmitHeader, const char* pDiffFile) {
+    FILE* pOut = NULL;
+    etc1_uint32 width = 0;
+    etc1_uint32 height = 0;
+    etc1_uint32 encodedSize = 0;
+    int result = -1;
+    etc1_byte* pSourceImage = 0;
+    etc1_byte* pEncodedData = 0;
+    etc1_byte* pDiffImage = 0; // Used for differencing
+
+    if (read_PNG_File(pInput, &pSourceImage, &width, &height)) {
+        goto exit;
+    }
+
+    encodedSize = etc1_get_encoded_data_size(width, height);
+    pEncodedData = new etc1_byte[encodedSize];
+    if (!pEncodedData) {
+        fprintf(stderr, "Out of memory.\n");
+        goto exit;
+    }
+
+    etc1_encode_image(pSourceImage,
+            width, height, 3, width * 3, pEncodedData);
+
+    if ((pOut = fopen(pOutput, "wb")) == NULL) {
+        fprintf(stderr, "Could not open output file %s: %d\n", pOutput, errno);
+        goto exit;
+    }
+
+    if (bEmitHeader) {
+        etc1_byte header[ETC_PKM_HEADER_SIZE];
+        etc1_pkm_format_header(header, width, height);
+        if (fwrite(header, sizeof(header), 1, pOut) != 1) {
+            fprintf(stderr,
+                    "Could not write header output file %s: %d\n",
+                    pOutput, errno);
+            goto exit;
+        }
+    }
+
+    if (fwrite(pEncodedData, encodedSize, 1, pOut) != 1) {
+        fprintf(stderr,
+                "Could not write encoded data to output file %s: %d\n",
+                pOutput, errno);
+        goto exit;
+    }
+
+    fclose(pOut);
+    pOut = NULL;
+
+    if (pDiffFile) {
+        etc1_uint32 outWidth;
+        etc1_uint32 outHeight;
+        if (readPKMFile(pOutput, &pDiffImage, &outWidth, &outHeight)) {
+            goto exit;
+        }
+        if (outWidth != width || outHeight != height) {
+            fprintf(stderr, "Output file has incorrect bounds: %u, %u != %u, %u\n",
+                    outWidth, outHeight, width, height);
+            goto exit;
+        }
+        const etc1_byte* pSrc = pSourceImage;
+        etc1_byte* pDest = pDiffImage;
+        etc1_uint32 size = width * height * 3;
+        for (etc1_uint32 i = 0; i < size; i++) {
+            int diff = *pSrc++ - *pDest;
+            diff *= diff;
+            diff <<= 3;
+            if (diff < 0) {
+                diff = 0;
+            } else if (diff > 255) {
+                diff = 255;
+            }
+            *pDest++ = (png_byte) diff;
+        }
+        writePNGFile(pDiffFile, outWidth, outHeight, pDiffImage, 3 * outWidth);
+    }
+
+    // Success
+    result = 0;
+
+    exit:
+    delete[] pSourceImage;
+    delete[] pEncodedData;
+    delete[] pDiffImage;
+
+    if (pOut) {
+        fclose(pOut);
+    }
+    return result;
+}
+
+int writePNGFile(const char* pOutput, png_uint_32 width, png_uint_32 height,
+        const png_bytep pImageData, png_uint_32 imageStride) {
+    int result = -1;
+    FILE* pOut = NULL;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+
+    if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+            (png_voidp) NULL, user_error_fn, user_warning_fn)) || !(info_ptr
+            = png_create_info_struct(png_ptr))) {
+        fprintf(stderr, "Could not initialize PNG library for writing.\n");
+        goto exit;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        goto exit;
+    }
+
+    if ((pOut = fopen(pOutput, "wb")) == NULL) {
+        fprintf(stderr, "Could not open output file %s: %d\n", pOutput, errno);
+        goto exit;
+    }
+
+    png_init_io(png_ptr, pOut);
+
+    png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
+            PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+            PNG_FILTER_TYPE_DEFAULT);
+
+    png_write_info(png_ptr, info_ptr);
+
+    for (png_uint_32 y = 0; y < height; y++) {
+        png_write_row(png_ptr, pImageData + y * imageStride);
+    }
+    png_write_end(png_ptr, info_ptr);
+
+    result = 0;
+
+    exit: if (png_ptr) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+    }
+
+    if (pOut) {
+        fclose(pOut);
+    }
+    return result;
+}
+
+int decode(const char* pInput, const char* pOutput) {
+    int result = -1;
+    png_bytep pImageData = NULL;
+    etc1_uint32 width = 0;
+    etc1_uint32 height = 0;
+
+    if (readPKMFile(pInput, &pImageData, &width, &height)) {
+        goto exit;
+    }
+
+    if (writePNGFile(pOutput, width, height, pImageData, width * 3)) {
+        goto exit;
+    }
+
+    // Success
+    result = 0;
+
+    exit: delete[] pImageData;
+
+    return result;
+}
+
+void multipleEncodeDecodeCheck(bool* pbEncodeDecodeSeen) {
+    if (*pbEncodeDecodeSeen) {
+        usage("At most one occurrence of --encode --encodeNoHeader or --decode is allowed.\n");
+    }
+    *pbEncodeDecodeSeen = true;
+}
+
+int main(int argc, char** argv) {
+    gpExeName = argv[0];
+    const char* pInput = NULL;
+    const char* pOutput = NULL;
+    const char* pDiffFile = NULL;
+    char* pOutputFileBuff = NULL;
+
+    bool bEncodeDecodeSeen = false;
+    bool bEncode = false;
+    bool bEncodeHeader = false;
+    bool bDecode = false;
+    bool bShowDifference = false;
+
+    for (int i = 1; i < argc; i++) {
+        const char* pArg = argv[i];
+        if (pArg[0] == '-') {
+            char c = pArg[1];
+            switch (c) {
+            case 'o':
+                if (pOutput != NULL) {
+                    usage("Only one -o flag allowed.");
+                }
+                if (i + 1 >= argc) {
+                    usage("Expected outfile after -o");
+                }
+                pOutput = argv[++i];
+                break;
+            case '-':
+                if (strcmp(pArg, "--encode") == 0) {
+                    multipleEncodeDecodeCheck(&bEncodeDecodeSeen);
+                    bEncode = true;
+                    bEncodeHeader = true;
+                } else if (strcmp(pArg, "--encodeNoHeader") == 0) {
+                    multipleEncodeDecodeCheck(&bEncodeDecodeSeen);
+                    bEncode = true;
+                    bEncodeHeader = false;
+                } else if (strcmp(pArg, "--decode") == 0) {
+                    multipleEncodeDecodeCheck(&bEncodeDecodeSeen);
+                    bDecode = true;
+                } else if (strcmp(pArg, "--showDifference") == 0) {
+                    if (bShowDifference) {
+                        usage("Only one --showDifference option allowed.\n");
+                    }
+                    bShowDifference = true;
+                    if (i + 1 >= argc) {
+                        usage("Expected difffile after --showDifference");
+                    }
+                    pDiffFile = argv[++i];
+                } else if (strcmp(pArg, "--help") == 0) {
+                    usage( NULL);
+                } else {
+                    usage("Unknown flag %s", pArg);
+                }
+
+                break;
+            default:
+                usage("Unknown flag %s", pArg);
+                break;
+            }
+        } else {
+            if (pInput != NULL) {
+                usage(
+                        "Only one input file allowed. Already have %s, now see %s",
+                        pInput, pArg);
+            }
+            pInput = pArg;
+        }
+    }
+
+    if (!bEncodeDecodeSeen) {
+        bEncode = true;
+        bEncodeHeader = true;
+    }
+    if ((! bEncode) && bShowDifference) {
+        usage("--showDifference is only valid when encoding.");
+    }
+
+    if (!pInput) {
+        usage("Expected an input file.");
+    }
+
+    if (!pOutput) {
+        const char* kDefaultExtension = bEncode ? ".pkm" : ".png";
+        size_t buffSize = strlen(pInput) + strlen(kDefaultExtension) + 1;
+        pOutputFileBuff = new char[buffSize];
+        strcpy(pOutputFileBuff, pInput);
+        if (changeExtension(pOutputFileBuff, buffSize, kDefaultExtension)) {
+            usage("Could not change extension of input file name: %s\n", pInput);
+        }
+        pOutput = pOutputFileBuff;
+    }
+
+    if (bEncode) {
+        encode(pInput, pOutput, bEncodeHeader, pDiffFile);
+    } else {
+        decode(pInput, pOutput);
+    }
+
+    delete[] pOutputFileBuff;
+
+    return 0;
+}