Merge change 556 into donut

* changes:
  Fixed a bug in XML parser handler where some words were being split in two. Fixes #1812655.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7d49c4a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,48 @@
+*~
+tools/eclipse/plugins/com.android.ide.eclipse.adt/androidprefs.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/jarutils.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/kxml2-2.3.0.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/layoutlib_api.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/layoutlib_utils.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/ninepatch.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/sdklib.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/sdkstats.jar
+tools/eclipse/plugins/com.android.ide.eclipse.adt/sdkuilib.jar
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/add.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/backward.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/clear.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/d.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/debug-attach.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/debug-error.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/debug-wait.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/delete.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/device.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/down.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/e.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/edit.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/empty.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/emulator.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/forward.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/gc.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/halt.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/heap.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/i.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/importBug.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/load.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/pause.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/play.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/pull.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/push.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/save.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/thread.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/up.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/v.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/w.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/warning.png
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/jcommon-1.0.12.jar
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/jfreechart-1.0.9-swt.jar
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/jfreechart-1.0.9.jar
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ddmlib
+tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ddmuilib
+tools/eclipse/plugins/com.android.ide.eclipse.tests/kxml2-2.3.0.jar
+tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ddmlib
diff --git a/apps/Development/AndroidManifest.xml b/apps/Development/AndroidManifest.xml
index 879e7d6..316cbf5 100644
--- a/apps/Development/AndroidManifest.xml
+++ b/apps/Development/AndroidManifest.xml
@@ -36,7 +36,6 @@
             android:icon="@drawable/ic_launcher_devtools">
 
         <uses-library android:name="android.test.runner" />
-        <uses-library android:name="com.google.android.maps" />
 
         <activity android:name="Development" android:label="Dev Tools"
             android:icon="@drawable/ic_launcher_devtools">
diff --git a/apps/Development/res/layout/development_settings.xml b/apps/Development/res/layout/development_settings.xml
index 0486748..4f185aa 100644
--- a/apps/Development/res/layout/development_settings.xml
+++ b/apps/Development/res/layout/development_settings.xml
@@ -142,13 +142,6 @@
             android:layout_below="@id/font_hinting"
             android:layout_alignParentLeft="true"
             android:text="@string/development_settings_show_xmpp_text" />
-
-        <CheckBox android:id="@+id/show_maps_compass"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/show_xmpp"
-            android:layout_alignParentLeft="true"
-            android:text="@string/development_settings_show_maps_compass_text" />
             
     </RelativeLayout>
 
diff --git a/apps/Development/res/values/strings.xml b/apps/Development/res/values/strings.xml
index 8d343ae..74f411d 100644
--- a/apps/Development/res/values/strings.xml
+++ b/apps/Development/res/values/strings.xml
@@ -84,7 +84,6 @@
     <string name="development_settings_show_xmpp_text">Show GTalk service connection status</string>
     <string name="development_settings_debug_app_label_text">Debug App:</string>
     <string name="development_settings_show_sleep_text">Show sleep state on LED</string>
-    <string name="development_settings_show_maps_compass_text">Show compass in Maps</string>
     <string name="development_settings_keep_screen_on_text">Keep screen on while plugged in</string>
 
     <string name="monkey_screen_initialActivity_text"></string>
diff --git a/apps/Development/src/com/android/development/DevelopmentSettings.java b/apps/Development/src/com/android/development/DevelopmentSettings.java
index cdc1e0a..3b4848a 100644
--- a/apps/Development/src/com/android/development/DevelopmentSettings.java
+++ b/apps/Development/src/com/android/development/DevelopmentSettings.java
@@ -57,7 +57,6 @@
     private CheckBox mShowUpdatesCB;
     private CheckBox mShowBackgroundCB;
     private CheckBox mShowSleepCB;
-    private CheckBox mShowMapsCompassCB;
     private CheckBox mShowXmppCB;
     private Spinner mMaxProcsSpinner;
     private Spinner mWindowAnimationScaleSpinner;
@@ -69,7 +68,6 @@
     private boolean mAlwaysFinish;
     private int mProcessLimit;
     private boolean mShowSleep;
-    private boolean mShowMapsCompass;
     private boolean mShowXmpp;
     private AnimationScaleSelectedListener mWindowAnimationScale
             = new AnimationScaleSelectedListener(0);
@@ -106,8 +104,6 @@
         mShowBackgroundCB.setOnCheckedChangeListener(new SurfaceFlingerClicker(1003));
         mShowSleepCB = (CheckBox)findViewById(R.id.show_sleep);
         mShowSleepCB.setOnClickListener(mShowSleepClicked);
-        mShowMapsCompassCB = (CheckBox)findViewById(R.id.show_maps_compass);
-        mShowMapsCompassCB.setOnClickListener(mShowMapsCompassClicked);
         mShowXmppCB = (CheckBox)findViewById(R.id.show_xmpp);
         mShowXmppCB.setOnClickListener(mShowXmppClicked);
         mMaxProcsSpinner = (Spinner)findViewById(R.id.max_procs);
@@ -172,7 +168,6 @@
         updateSharedOptions();
         updateFlingerOptions();
         updateSleepOptions();
-        updateMapsCompassOptions();
         updateXmppOptions();        
 
         try {
@@ -293,31 +288,6 @@
         mShowSleepCB.setChecked(mShowSleep);
     }
 
-    private void writeMapsCompassOptions() {
-        try {
-            Context c = createPackageContext("com.google.android.apps.maps", 0);
-            c.getSharedPreferences("extra-features", MODE_WORLD_WRITEABLE)
-                .edit()
-                .putBoolean("compass", mShowMapsCompass)
-                .commit();
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Failed setting maps compass");
-            e.printStackTrace();
-        }
-    }
-
-    private void updateMapsCompassOptions() {
-        try {
-            Context c = createPackageContext("com.google.android.apps.maps", 0);
-            mShowMapsCompass = c.getSharedPreferences("extra-features", MODE_WORLD_READABLE)
-                .getBoolean("compass", false);
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Failed reading maps compass");
-            e.printStackTrace();
-        }
-        mShowMapsCompassCB.setChecked(mShowMapsCompass);
-    }
-
     private void writeXmppOptions() {
         Settings.System.setShowGTalkServiceStatus(getContentResolver(), mShowXmpp);
     }
@@ -410,16 +380,6 @@
         }
     };
 
-    private View.OnClickListener mShowMapsCompassClicked =
-        new View.OnClickListener() {
-        public void onClick(View v) {
-            mShowMapsCompass = ((CheckBox)v).isChecked();
-            writeMapsCompassOptions();
-            updateMapsCompassOptions();
-        }
-    };
-
-    
     private View.OnClickListener mShowXmppClicked = new View.OnClickListener() {
         public void onClick(View v) {
             mShowXmpp = ((CheckBox)v).isChecked();
diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
index fe0de99..5e9f07c 100644
--- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java
+++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
@@ -130,7 +130,8 @@
     
     float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];    
     MonkeyEventSource mEventSource;
-
+    private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
+    
     /**
      * Monitor operations happening in the system.
      */
@@ -222,14 +223,14 @@
             return 1;
         }
     }
-    
+  
     /**
      * Run the procrank tool to insert system status information into the debug report.
      */
     private void reportProcRank() {
       commandLineReport("procrank", "procrank");
     }
-    
+  
     /**
      * Run "cat /data/anr/traces.txt".  Wait about 5 seconds first, to let the asynchronous
      * report writing complete.
@@ -401,7 +402,9 @@
             signalPersistentProcesses();
         }
         
+        mNetworkMonitor.start();
         int crashedAtCycle = runMonkeyCycles();
+        mNetworkMonitor.stop();
 
         synchronized (this) {
             if (mRequestAnrTraces) {
@@ -423,6 +426,7 @@
         
         try {
             mAm.setActivityWatcher(null);
+            mNetworkMonitor.unregister(mAm);
         } catch (RemoteException e) {
             // just in case this was latent (after mCount cycles), make sure
             // we report it
@@ -442,6 +446,9 @@
             System.out.print(" flips=");
             System.out.println(mDroppedFlipEvents);
         }
+        
+        // report network stats
+        mNetworkMonitor.dump();
 
         if (crashedAtCycle < mCount - 1) {
             System.err.println("** System appears to have crashed at event "
@@ -602,6 +609,7 @@
 
         try {
             mAm.setActivityWatcher(new ActivityWatcher());
+            mNetworkMonitor.register(mAm);
         } catch (RemoteException e) {
             System.err.println("** Failed talking with activity manager!");
             return false;
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java
new file mode 100644
index 0000000..e8d9812
--- /dev/null
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java
@@ -0,0 +1,105 @@
+/**
+** 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;
+
+import android.app.IActivityManager;
+import android.app.IIntentReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+/**
+ * Class for monitoring network connectivity during monkey runs.
+ */
+public class MonkeyNetworkMonitor extends IIntentReceiver.Stub {
+    private static final boolean LDEBUG = false;
+    private final IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+    private long mCollectionStartTime; // time we started collecting data
+    private long mEventTime; // time of last event (connect, disconnect, etc.)
+    private int mLastNetworkType = -1; // unknown
+    private long mWifiElapsedTime = 0;  // accumulated time spent on wifi since start()
+    private long mMobileElapsedTime = 0; // accumulated time spent on mobile since start()
+    private long mElapsedTime = 0; // amount of time spent between start() and stop()
+    
+    public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
+            boolean ordered) throws RemoteException {
+        NetworkInfo ni = (NetworkInfo) intent.getParcelableExtra(
+                ConnectivityManager.EXTRA_NETWORK_INFO);
+        if (LDEBUG) System.out.println("Network state changed: " 
+                + "type=" + ni.getType() + ", state="  + ni.getState());
+        updateNetworkStats();
+        if (NetworkInfo.State.CONNECTED == ni.getState()) {
+            if (LDEBUG) System.out.println("Network connected");
+            mLastNetworkType = ni.getType();
+        } else if (NetworkInfo.State.DISCONNECTED == ni.getState()) {
+            if (LDEBUG) System.out.println("Network not connected");
+            mLastNetworkType = -1; // unknown since we're disconnected
+        }
+        mEventTime = SystemClock.elapsedRealtime();
+    }
+
+    private void updateNetworkStats() {
+        long timeNow = SystemClock.elapsedRealtime();
+        long delta = timeNow - mEventTime;
+        switch (mLastNetworkType) {
+            case ConnectivityManager.TYPE_MOBILE:
+                if (LDEBUG) System.out.println("Adding to mobile: " + delta);
+                mMobileElapsedTime += delta;
+                break;
+            case ConnectivityManager.TYPE_WIFI:
+                if (LDEBUG) System.out.println("Adding to wifi: " + delta);
+                mWifiElapsedTime += delta;
+                break;
+            default:
+                if (LDEBUG) System.out.println("Unaccounted for: " + delta);
+                break;
+        }
+        mElapsedTime = timeNow - mCollectionStartTime;
+    }
+
+    public void start() {
+        mWifiElapsedTime = 0;
+        mMobileElapsedTime = 0;
+        mElapsedTime = 0;
+        mEventTime = mCollectionStartTime = SystemClock.elapsedRealtime();
+    }
+
+    public void register(IActivityManager am) throws RemoteException {
+        if (LDEBUG) System.out.println("registering Receiver");
+        am.registerReceiver(null, this, filter, null); 
+    }
+    
+    public void unregister(IActivityManager am) throws RemoteException {
+        if (LDEBUG) System.out.println("unregistering Receiver");
+        am.unregisterReceiver(this);
+    }
+    
+    public void stop() {
+        updateNetworkStats();
+    }
+    
+    public void dump() {
+        System.out.println("## Network stats: elapsed time=" + mElapsedTime + "ms (" 
+                + mMobileElapsedTime + "ms mobile, "
+                + mWifiElapsedTime + "ms wifi, "
+                + (mElapsedTime - mMobileElapsedTime - mWifiElapsedTime) + "ms not connected)");
+    }
+ }
\ No newline at end of file
diff --git a/docs/copyright-templates/asm.txt b/docs/copyright-templates/asm.txt
new file mode 100644
index 0000000..95a9022
--- /dev/null
+++ b/docs/copyright-templates/asm.txt
@@ -0,0 +1,13 @@
+; Copyright (C) 2009 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
diff --git a/docs/copyright-templates/bash.txt b/docs/copyright-templates/bash.txt
new file mode 100644
index 0000000..ecf510e
--- /dev/null
+++ b/docs/copyright-templates/bash.txt
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/docs/copyright-templates/bsd/c.txt b/docs/copyright-templates/bsd/c.txt
new file mode 100644
index 0000000..c574b4a
--- /dev/null
+++ b/docs/copyright-templates/bsd/c.txt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/docs/copyright-templates/c.txt b/docs/copyright-templates/c.txt
new file mode 100644
index 0000000..7cd7083
--- /dev/null
+++ b/docs/copyright-templates/c.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/docs/copyright-templates/java.txt b/docs/copyright-templates/java.txt
new file mode 100644
index 0000000..7cd7083
--- /dev/null
+++ b/docs/copyright-templates/java.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/docs/copyright-templates/make.txt b/docs/copyright-templates/make.txt
new file mode 100644
index 0000000..e4b376e
--- /dev/null
+++ b/docs/copyright-templates/make.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/docs/copyright-templates/plain.txt b/docs/copyright-templates/plain.txt
new file mode 100644
index 0000000..4549ecb
--- /dev/null
+++ b/docs/copyright-templates/plain.txt
@@ -0,0 +1,13 @@
+Copyright (C) 2009 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/docs/copyright-templates/sh.txt b/docs/copyright-templates/sh.txt
new file mode 100644
index 0000000..e53137d
--- /dev/null
+++ b/docs/copyright-templates/sh.txt
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/docs/copyright-templates/xml.txt b/docs/copyright-templates/xml.txt
new file mode 100644
index 0000000..2a9d2b6
--- /dev/null
+++ b/docs/copyright-templates/xml.txt
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
diff --git a/pdk/Pdk.mk b/pdk/Pdk.mk
index 584eccf..29a08a3 100644
--- a/pdk/Pdk.mk
+++ b/pdk/Pdk.mk
@@ -101,7 +101,7 @@
 # copy-one-file defines the actual rule.
 $(foreach template,$(pdk_templates), \
   $(eval _chFrom := $(template)) \
-  $(eval _chTo :=  $(pdk_docs_dest_dir)/$(notdir $(template))) \
+  $(eval _chTo :=  $(pdk_docs_dest_dir)/$(patsubst $(pdk_templates_dir)/%,%,$(template))) \
   $(eval $(call copy-one-header,$(_chFrom),$(_chTo))) \
   $(eval all_copied_pdk_templates: $(_chTo)) \
  )
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java b/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
index 1a4b5e4..3094afe 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
@@ -20,6 +20,8 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.speech.RecognizerIntent;
 import android.view.View;
@@ -29,6 +31,7 @@
 import android.widget.ListView;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Sample code that invokes the speech recognition intent API.
@@ -54,8 +57,16 @@
         
         mList = (ListView) findViewById(R.id.list);
 
-        // Attach actions to buttons
-        speakButton.setOnClickListener(this);
+        // Check to see if a recognition activity is present
+        PackageManager pm = getPackageManager();
+        List<ResolveInfo> activities = pm.queryIntentActivities(
+                new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
+        if (activities.size() != 0) {
+            speakButton.setOnClickListener(this);
+        } else {
+            speakButton.setEnabled(false);
+            speakButton.setText("Recognizer not present");
+        }
     }
 
     /**
@@ -71,7 +82,6 @@
      * Fire an intent to start the speech recognition activity.
      */
     private void startVoiceRecognitionActivity() {
-        //TODO Get these values from constants
         Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
         intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                 RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
@@ -85,6 +95,7 @@
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == RESULT_OK) {
+            // Fill the list view with the strings the recognizer thought it could have heard
             ArrayList<String> matches = data.getStringArrayListExtra(
                     RecognizerIntent.EXTRA_RESULTS);
             mList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/StaticTriangleRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/StaticTriangleRenderer.java
new file mode 100644
index 0000000..c492e3f
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/StaticTriangleRenderer.java
@@ -0,0 +1,252 @@
+/*
+ * 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.example.android.apis.graphics;
+
+import static android.opengl.GLES10.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLU;
+import android.opengl.GLUtils;
+import android.os.SystemClock;
+
+import com.example.android.apis.R;
+
+/**
+ * A GLSurfaceView.Renderer that uses the Android-specific
+ * android.opengl.GLESXXX static OpenGL ES APIs. The static APIs
+ * expose more of the OpenGL ES features than the
+ * javax.microedition.khronos.opengles APIs, and also
+ * provide a programming model that is closer to the C OpenGL ES APIs, which
+ * may make it easier to reuse code and documentation written for the
+ * C OpenGL ES APIs.
+ *
+ */
+public class StaticTriangleRenderer implements GLSurfaceView.Renderer{
+
+    public StaticTriangleRenderer(Context context) {
+        mContext = context;
+        mTriangle = new Triangle();
+    }
+
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        /*
+         * By default, OpenGL enables features that improve quality
+         * but reduce performance. One might want to tweak that
+         * especially on software renderer.
+         */
+        glDisable(GL_DITHER);
+
+        /*
+         * Some one-time OpenGL initialization can be made here
+         * probably based on features of this particular context
+         */
+        glHint(GL_PERSPECTIVE_CORRECTION_HINT,
+                GL_FASTEST);
+
+        glClearColor(.5f, .5f, .5f, 1);
+        glShadeModel(GL_SMOOTH);
+        glEnable(GL_DEPTH_TEST);
+        glEnable(GL_TEXTURE_2D);
+
+        /*
+         * Create our texture. This has to be done each time the
+         * surface is created.
+         */
+
+        int[] textures = new int[1];
+        glGenTextures(1, textures, 0);
+
+        mTextureID = textures[0];
+        glBindTexture(GL_TEXTURE_2D, mTextureID);
+
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                GL_NEAREST);
+        glTexParameterf(GL_TEXTURE_2D,
+                GL_TEXTURE_MAG_FILTER,
+                GL_LINEAR);
+
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+                GL_CLAMP_TO_EDGE);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+                GL_CLAMP_TO_EDGE);
+
+        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
+                GL_REPLACE);
+
+        InputStream is = mContext.getResources()
+                .openRawResource(R.drawable.robot);
+        Bitmap bitmap;
+        try {
+            bitmap = BitmapFactory.decodeStream(is);
+        } finally {
+            try {
+                is.close();
+            } catch(IOException e) {
+                // Ignore.
+            }
+        }
+
+        GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
+        bitmap.recycle();
+    }
+
+    public void onDrawFrame(GL10 gl) {
+        /*
+         * By default, OpenGL enables features that improve quality
+         * but reduce performance. One might want to tweak that
+         * especially on software renderer.
+         */
+        glDisable(GL_DITHER);
+
+        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
+                GL_MODULATE);
+
+        /*
+         * Usually, the first thing one might want to do is to clear
+         * the screen. The most efficient way of doing this is to use
+         * glClear().
+         */
+
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+        /*
+         * Now we're ready to draw some 3D objects
+         */
+
+        glMatrixMode(GL_MODELVIEW);
+        glLoadIdentity();
+
+        GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+
+        glEnableClientState(GL_VERTEX_ARRAY);
+        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, mTextureID);
+        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+                GL_REPEAT);
+        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+                GL_REPEAT);
+
+        long time = SystemClock.uptimeMillis() % 4000L;
+        float angle = 0.090f * ((int) time);
+
+        glRotatef(angle, 0, 0, 1.0f);
+
+        mTriangle.draw(gl);
+    }
+
+    public void onSurfaceChanged(GL10 gl, int w, int h) {
+        glViewport(0, 0, w, h);
+
+        /*
+        * Set our projection matrix. This doesn't have to be done
+        * each time we draw, but usually a new projection needs to
+        * be set when the viewport is resized.
+        */
+
+        float ratio = (float) w / h;
+        glMatrixMode(GL_PROJECTION);
+        glLoadIdentity();
+        glFrustumf(-ratio, ratio, -1, 1, 3, 7);
+    }
+
+    private Context mContext;
+    private Triangle mTriangle;
+    private int mTextureID;
+
+    static class Triangle {
+        public Triangle() {
+
+            // Buffers to be passed to gl*Pointer() functions
+            // must be direct, i.e., they must be placed on the
+            // native heap where the garbage collector cannot
+            // move them.
+            //
+            // Buffers with multi-byte datatypes (e.g., short, int, float)
+            // must have their byte order set to native order
+
+            ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
+            vbb.order(ByteOrder.nativeOrder());
+            mFVertexBuffer = vbb.asFloatBuffer();
+
+            ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
+            tbb.order(ByteOrder.nativeOrder());
+            mTexBuffer = tbb.asFloatBuffer();
+
+            ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
+            ibb.order(ByteOrder.nativeOrder());
+            mIndexBuffer = ibb.asShortBuffer();
+
+            // A unit-sided equilateral triangle centered on the origin.
+            float[] coords = {
+                    // X, Y, Z
+                    -0.5f, -0.25f, 0,
+                     0.5f, -0.25f, 0,
+                     0.0f,  0.559016994f, 0
+            };
+
+            for (int i = 0; i < VERTS; i++) {
+                for(int j = 0; j < 3; j++) {
+                    mFVertexBuffer.put(coords[i*3+j] * 2.0f);
+                }
+            }
+
+            for (int i = 0; i < VERTS; i++) {
+                for(int j = 0; j < 2; j++) {
+                    mTexBuffer.put(coords[i*3+j] * 2.0f + 0.5f);
+                }
+            }
+
+            for(int i = 0; i < VERTS; i++) {
+                mIndexBuffer.put((short) i);
+            }
+
+            mFVertexBuffer.position(0);
+            mTexBuffer.position(0);
+            mIndexBuffer.position(0);
+        }
+
+        public void draw(GL10 gl) {
+            glFrontFace(GL_CCW);
+            glVertexPointer(3, GL_FLOAT, 0, mFVertexBuffer);
+            glEnable(GL_TEXTURE_2D);
+            glTexCoordPointer(2, GL_FLOAT, 0, mTexBuffer);
+            glDrawElements(GL_TRIANGLE_STRIP, VERTS,
+                    GL_UNSIGNED_SHORT, mIndexBuffer);
+        }
+
+        private final static int VERTS = 3;
+
+        private FloatBuffer mFVertexBuffer;
+        private FloatBuffer mTexBuffer;
+        private ShortBuffer mIndexBuffer;
+    }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
index e5b06f4..7d7cd4a 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
@@ -27,7 +27,7 @@
         super.onCreate(savedInstanceState);
         mGLView = new GLSurfaceView(this);
         mGLView.setEGLConfigChooser(false);
-        mGLView.setRenderer(new TriangleRenderer(this));
+        mGLView.setRenderer(new StaticTriangleRenderer(this));
         setContentView(mGLView);
     }
 
diff --git a/samples/ApiDemos/tests/src/com/example/android/apis/ApiDemosTest.java b/samples/ApiDemos/tests/src/com/example/android/apis/ApiDemosTest.java
index 0103da8..bc174ea 100644
--- a/samples/ApiDemos/tests/src/com/example/android/apis/ApiDemosTest.java
+++ b/samples/ApiDemos/tests/src/com/example/android/apis/ApiDemosTest.java
@@ -24,6 +24,12 @@
  */
 public class ApiDemosTest extends ActivityInstrumentationTestCase<ApiDemos> {
 
+    /**
+     * The first constructor parameter must refer to the package identifier of the
+     * package hosting the activity to be launched, which is specified in the AndroidManifest.xml
+     * file.  This is not necessarily the same as the java package name of the class - in fact, in
+     * some cases it may not match at all.
+     */
     public ApiDemosTest() {
         super("com.example.android.apis", ApiDemos.class);
     }
diff --git a/samples/ApiDemos/tests/src/com/example/android/apis/view/Focus2ActivityTest.java b/samples/ApiDemos/tests/src/com/example/android/apis/view/Focus2ActivityTest.java
index 91f712f..5938209 100644
--- a/samples/ApiDemos/tests/src/com/example/android/apis/view/Focus2ActivityTest.java
+++ b/samples/ApiDemos/tests/src/com/example/android/apis/view/Focus2ActivityTest.java
@@ -48,7 +48,12 @@
     private Button mCenterButton;
     private Button mRightButton;
 
-
+    /**
+     * The first constructor parameter must refer to the package identifier of the
+     * package hosting the activity to be launched, which is specified in the AndroidManifest.xml
+     * file.  This is not necessarily the same as the java package name of the class - in fact, in
+     * some cases it may not match at all.
+     */
     public Focus2ActivityTest() {
         super("com.example.android.apis", Focus2.class);
     }
diff --git a/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java b/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java
index 3a49a87..1b17925 100755
--- a/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java
+++ b/samples/JetBoy/src/com/example/android/jetboy/JetBoyView.java
@@ -416,14 +416,8 @@
          */
         private void initializeJetPlayer() {
 
-            // JET info: this is what we do to undesirable instances: we release them!
-            if (mJet != null)
-            {
-                mJet.release();
-                mJet = null;
-            }
-
-            // JET info: let's create our JetPlayer instance using the factory
+            // JET info: let's create our JetPlayer instance using the factory.
+            // JET info: if we already had one, the same singleton is returned.
             mJet = JetPlayer.getJetPlayer();
 
             mJetPlaying = false;
diff --git a/testrunner/run_command.py b/testrunner/run_command.py
index 6b72b77..5336f33 100755
--- a/testrunner/run_command.py
+++ b/testrunner/run_command.py
@@ -3,37 +3,37 @@
 #
 # 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 
+# 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 
+#     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 
+# 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.
 
 # System imports
 import os
 import signal
 import subprocess
-import time
 import threading
+import time
 
 # local imports
-import logger
 import errors
+import logger
 
 _abort_on_error = False
 
 def SetAbortOnError(abort=True):
-  """Sets behavior of RunCommand to throw AbortError if command process returns 
+  """Sets behavior of RunCommand to throw AbortError if command process returns
   a negative error code"""
   global _abort_on_error
   _abort_on_error = abort
-  
+
 def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True):
   """Spawns a subprocess to run the given shell command, and checks for
   timeout_time. If return_output is True, the output of the command is returned
@@ -42,7 +42,7 @@
   result = None
   while True:
     try:
-      result = RunOnce(cmd, timeout_time=timeout_time, 
+      result = RunOnce(cmd, timeout_time=timeout_time,
                        return_output=return_output)
     except errors.WaitForResponseTimedOutError:
       if retry_count == 0:
@@ -59,13 +59,13 @@
   pid = []
   global _abort_on_error
   error_occurred = False
-  
+
   def Run():
     if return_output:
       output_dest = subprocess.PIPE
     else:
       # None means direct to stdout
-      output_dest = None  
+      output_dest = None
     pipe = subprocess.Popen(
         cmd,
         executable='/bin/bash',
@@ -83,9 +83,9 @@
       so.append("ERROR")
       error_occurred = True
     if pipe.returncode < 0:
-      logger.SilentLog("Error: %s was terminated by signal %d" %(cmd, 
+      logger.SilentLog("Error: %s was terminated by signal %d" %(cmd,
           pipe.returncode))
-      error_occurred = True  
+      error_occurred = True
 
   t = threading.Thread(target=Run)
   t.start()
@@ -113,5 +113,26 @@
 
   if _abort_on_error and error_occurred:
     raise errors.AbortError
-  
+
   return "".join(so)
+
+
+def RunHostCommand(binary, valgrind=False):
+  """Run a command on the host (opt using valgrind).
+
+  Runs the host binary. Does not capture any output but it
+  returns the exit code. The command can be run under valgrind.
+
+  Args:
+    binary: basename of the file to be run. It is expected to be under
+            out/host/linux-x86/bin.
+    valgrind: If True the command will be run under valgrind.
+
+  Returns:
+    The command exit code (int)
+  """
+  full_path = os.path.join("out", "host", "linux-x86", "bin", binary)
+  if not valgrind:
+    return subprocess.call(full_path)
+  else:
+    return subprocess.call(["/usr/bin/valgrind", "-q", full_path])
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index 6d6c6df..f87d451 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -44,7 +44,7 @@
 
   # file path to android core platform tests, relative to android build root
   # TODO move these test data files to another directory
-  _CORE_TEST_PATH = os.path.join("development", "testrunner", 
+  _CORE_TEST_PATH = os.path.join("development", "testrunner",
                                  _TEST_FILE_NAME)
 
   # vendor glob file path patterns to tests, relative to android
@@ -60,7 +60,7 @@
   def __init__(self):
     # disable logging of timestamp
     self._root_path = android_build.GetTop()
-    logger.SetTimestampLogging(False)  
+    logger.SetTimestampLogging(False)
 
   def _ProcessOptions(self):
     """Processes command-line options."""
@@ -290,12 +290,27 @@
     # find all test files, convert unicode names to ascii, take the basename
     # and drop the .cc/.cpp  extension.
     file_pattern = os.path.join(test_suite.GetBuildPath(), "test_*")
+    logger.SilentLog("Scanning %s" % test_suite.GetBuildPath())
     file_list = []
     for f in map(str, glob.glob(file_pattern)):
       f = os.path.basename(f)
       f = re.split(".[cp]+$", f)[0]
+      logger.SilentLog("Found %s" % f)
       file_list.append(f)
 
+    # Run on the host
+    logger.Log("\nRunning on host")
+    for f in file_list:
+      if run_command.RunHostCommand(f) != 0:
+        logger.Log("%s... failed" % f)
+      else:
+        if run_command.RunHostCommand(f, valgrind=True) == 0:
+          logger.Log("%s... ok\t\t[valgrind: ok]" % f)
+        else:
+          logger.Log("%s... ok\t\t[valgrind: failed]" % f)
+
+    # Run on the device
+    logger.Log("\nRunning on target")
     for f in file_list:
       full_path = "/system/bin/%s" % f
 
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
index 72fe060..36bd148 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
@@ -17,10 +17,12 @@
 package com.android.ide.eclipse.editors.ui.tree;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.editors.AndroidEditor;
 import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
 import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor;
 import com.android.ide.eclipse.editors.uimodel.UiElementNode;
 
+import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.viewers.ILabelProvider;
@@ -31,10 +33,16 @@
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
 import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.part.FileEditorInput;
 
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Map.Entry;
 
 /**
  * A selection dialog to select the type of the new element node to
@@ -50,15 +58,25 @@
     private UiElementNode mChosenRootNode;
     private UiElementNode mLocalRootNode;
     /** The descriptor of the elements to be displayed as root in this tree view. All elements
-     *  of the same type in the root will be displayed. */
+     *  of the same type in the root will be displayed. Can be null. */
     private ElementDescriptor[] mDescriptorFilters;
+    /** The key for the {@link #setLastUsedXmlName(Object[])}. It corresponds to the full
+     * workspace path of the currently edited file, if this can be computed. This is computed
+     * by {@link #getLastUsedXmlName(UiElementNode)}, called from the constructor. */
+    private String mLastUsedKey;
+    /** A static map of known XML Names used for a given file. The map has full workspace
+     * paths as key and XML names as values. */
+    private static final Map<String, String> sLastUsedXmlName = new HashMap<String, String>();
+    /** The potential XML Name to initially select in the selection dialog. This is computed
+     * in the constructor and set by {@link #setInitialSelection(UiElementNode)}. */
+    private String mInitialXmlName;
 
     /**
      * Creates the new item selection dialog.
      * 
      * @param shell The parent shell for the list.
      * @param labelProvider ILabelProvider for the list.
-     * @param descriptorFilters The element allows at the root of the tree
+     * @param descriptorFilters The element allows at the root of the tree. Can be null.
      * @param ui_node The selected node, or null if none is selected.
      * @param root_node The root of the Ui Tree, either the UiDocumentNode or a sub-node.
      */
@@ -109,6 +127,114 @@
                 }
             }
         });
+        
+        // Determine the initial selection using a couple heuristics.
+        
+        // First check if we can get the last used node type for this file.
+        // The heuristic is that generally one keeps adding the same kind of items to the
+        // same file, so reusing the last used item type makes most sense.
+        String xmlName = getLastUsedXmlName(root_node);
+        if (xmlName == null) {
+            // Another heuristic is to find the most used item and default to that.
+            xmlName = getMostUsedXmlName(root_node);
+        }
+        if (xmlName == null) {
+            // Finally the last heuristic is to see if there's an item with a name
+            // similar to the edited file name.
+            xmlName = getLeafFileName(root_node);
+        }
+        // Set the potential name. Selecting the right item is done later by setInitialSelection().
+        mInitialXmlName = xmlName;
+    }
+
+    /**
+     * Returns a potential XML name based on the file name.
+     * The item name is marked with an asterisk to identify it as a partial match.
+     */
+    private String getLeafFileName(UiElementNode ui_node) {
+        if (ui_node != null) {
+            AndroidEditor editor = ui_node.getEditor();
+            if (editor != null) {
+                IEditorInput editorInput = editor.getEditorInput();
+                if (editorInput instanceof FileEditorInput) {
+                    IFile f = ((FileEditorInput) editorInput).getFile();
+                    if (f != null) {
+                        String leafName = f.getFullPath().removeFileExtension().lastSegment();
+                        return "*" + leafName; //$NON-NLS-1$
+                    }
+                }
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     * Given a potential non-null root node, this method looks for the currently edited
+     * file path and uses it as a key to retrieve the last used item for this file by this
+     * selection dialog. Returns null if nothing can be found, otherwise returns the string
+     * name of the item.
+     */
+    private String getLastUsedXmlName(UiElementNode ui_node) {
+        if (ui_node != null) {
+            AndroidEditor editor = ui_node.getEditor();
+            if (editor != null) {
+                IEditorInput editorInput = editor.getEditorInput();
+                if (editorInput instanceof FileEditorInput) {
+                    IFile f = ((FileEditorInput) editorInput).getFile();
+                    if (f != null) {
+                        mLastUsedKey = f.getFullPath().toPortableString();
+    
+                        return sLastUsedXmlName.get(mLastUsedKey);
+                    }
+                }
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     * Sets the last used item for this selection dialog for this file.
+     * @param objects The currently selected items. Only the first one is used if it is an
+     *                {@link ElementDescriptor}.
+     */
+    private void setLastUsedXmlName(Object[] objects) {
+        if (mLastUsedKey != null &&
+                objects != null &&
+                objects.length > 0 &&
+                objects[0] instanceof ElementDescriptor) {
+            ElementDescriptor desc = (ElementDescriptor) objects[0];
+            sLastUsedXmlName.put(mLastUsedKey, desc.getXmlName());
+        }
+    }
+
+    /**
+     * Returns the most used sub-element name, if any, or null.
+     */
+    private String getMostUsedXmlName(UiElementNode ui_node) {
+        if (ui_node != null) {
+            TreeMap<String, Integer> counts = new TreeMap<String, Integer>();
+            int max = -1;
+            
+            for (UiElementNode child : ui_node.getUiChildren()) {
+                String name = child.getDescriptor().getXmlName();
+                Integer i = counts.get(name);
+                int count = i == null ? 1 : i.intValue() + 1;
+                counts.put(name, count);
+                max = Math.max(max, count);
+            }
+
+            if (max > 0) {
+                // Find first key with this max and return it
+                for (Entry<String, Integer> entry : counts.entrySet()) {
+                    if (entry.getValue().intValue() == max) {
+                        return entry.getKey();
+                    }
+                }
+            }
+        }
+        return null;
     }
 
     /**
@@ -126,6 +252,7 @@
     @Override
     protected void computeResult() {
         setResult(Arrays.asList(getSelectedElements()));
+        setLastUsedXmlName(getSelectedElements());
     }
 
     /**
@@ -152,11 +279,47 @@
         // Initialize the list state.
         // This must be done after the filtered list as been created.
         chooseNode(mChosenRootNode);
-        setSelection(getInitialElementSelections().toArray());
+        
+        // Set the initial selection
+        setInitialSelection(mChosenRootNode);
         return contents;
     }
     
     /**
+     * Tries to set the initial selection based on the {@link #mInitialXmlName} computed
+     * in the constructor. The selection is only set if there's an element descriptor
+     * that matches the same exact XML name. When {@link #mInitialXmlName} starts with an
+     * asterisk, it means to do a partial case-insensitive match on the start of the
+     * strings.
+     */
+    private void setInitialSelection(UiElementNode rootNode) {
+        ElementDescriptor initialElement = null;
+
+        if (mInitialXmlName != null && mInitialXmlName.length() > 0) {
+            String name = mInitialXmlName;
+            boolean partial = name.startsWith("*");   //$NON-NLS-1$
+            if (partial) {
+                name = name.substring(1).toLowerCase();
+            }
+            
+            for (ElementDescriptor desc : getAllowedDescriptors(rootNode)) {
+                if (!partial && desc.getXmlName().equals(name)) {
+                    initialElement = desc;
+                    break;
+                } else if (partial) {
+                    String name2 = desc.getXmlLocalName().toLowerCase();
+                    if (name.startsWith(name2) || name2.startsWith(name)) {
+                        initialElement = desc;
+                        break;
+                    }
+                }
+            }
+        }
+        
+        setSelection(initialElement == null ? null : new ElementDescriptor[] { initialElement });
+    }
+
+    /**
      * Creates the message text widget and sets layout data.
      * @param content the parent composite of the message area.
      */
@@ -217,13 +380,23 @@
      */
     private void chooseNode(UiElementNode ui_node) {
         mChosenRootNode = ui_node;
+        setListElements(getAllowedDescriptors(ui_node));
+    }
 
+    /**
+     * Returns the list of {@link ElementDescriptor}s that can be added to the given
+     * UI node.
+     * 
+     * @param ui_node The UI node to which element should be added. Cannot be null.
+     * @return A non-null array of {@link ElementDescriptor}. The array might be empty.
+     */
+    private ElementDescriptor[] getAllowedDescriptors(UiElementNode ui_node) {
         if (ui_node == mLocalRootNode && 
                 mDescriptorFilters != null &&
                 mDescriptorFilters.length != 0) {
-            setListElements(mDescriptorFilters);
+            return mDescriptorFilters;
         } else {
-            setListElements(ui_node.getDescriptor().getChildren());
+            return ui_node.getDescriptor().getChildren();
         }
     }
 }
diff --git a/tools/mkstubs/README.txt b/tools/mkstubs/README.txt
new file mode 100644
index 0000000..8f9abbc
--- /dev/null
+++ b/tools/mkstubs/README.txt
@@ -0,0 +1,90 @@
+2009/04/20.
+
+-------
+1- Goal
+-------
+
+MkStub is small tool that takes a given JAR and filters all the private stuff we don't want to
+expose, e.g.:
+- remove all private members.
+- only include a subset of classes.
+- exclude specific classes, fields or methods.
+
+Each method body is replaced by the bytecode for 'throw new RuntimeException("stub");'.
+
+
+--------
+2- Usage
+--------
+
+To control it, you give it patterns like this:
+
+  +foo  => accepts all items which signature is exactly "foo"
+  +foo* => accepts all items which signature starts by "foo"
+  -bar  => rejects all items which signature is exactly "bar"
+  -bar* => rejects all items which signature starts by "bar"
+
+Signatures are defined by:
+- a package name, e.g. com.android.blah
+- a dot followed by a class name
+- a # followed by a field or method name
+- an internal "Java method signature" that define parameters types and return value. 
+
+Examples of signatures:
+ com.android.blah
+ com.android.blah.MyClass
+ com.android.blah.MyClass$MyInnerClass
+ com.android.blah.MyClass#mPrivateField
+ com.android.blah.MyClass#getInternalStuff
+ com.android.blah.MyClass#getInternalStuff(Ljava/lang/String;I)V
+
+An example of configuration file:
+ +com.android.blah
+ -com.android.blah.MyClass$MyInnerClass
+ -com.android.blah.MyClass#mPrivateField
+ -com.android.blah.MyClass#getInternalStuff(Ljava/lang/String;I)V
+
+This would include only the indicated package yet would totally exclude the inner class
+and the specific field and the method with the exact given signature.
+
+
+
+To invoke MkStub, the syntax is:
+
+  $ java -jar mkstubs input.jar output.jar [@configfile -pattern +pattern ...]
+    
+
+
+--------------------
+3- Known Limitations
+--------------------
+
+Most of the following limitations exist solely because of the short development time and
+because the tool was designed to solve one task and not just to be super generic. That means
+any limitation here can be easily lifted.
+
+- The generated constructors are not proper. They do not invoke the matching super()
+  before the generated throw exception. Any attempt to load such a class should trigger
+  an error from the byte code verifier or the class loader.
+
+- We do not currently check whether a class or method uses only included types.
+  Suggestion: if type x.y.z is excluded, then any field, annotation, generic type,
+  method parameter or return value that uses that type should generate a fatal error.
+
+- We do not filter out private classes. Their .class will still be present in the
+  output (and stubbed), unless they are explicitly excluded.
+  This is not orthogonal to the fact that private fields and methods are automatically
+  excluded.
+
+- Private fields and methods are automatically excluded. There is no command line
+  switch to prevent that.
+
+- The stubbed source is always generated. For example if the output jar name is
+  given as ~/somedir/myfinal.jar, there will be a directory created at
+  ~/somedir/myfinal.jar_sources that will contain the equivalent Java sources.
+  There is not command line switch to prevent that.
+
+- There is no attempt to match features or behavior with DroidDoc.
+
+-- 
+end
diff --git a/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java b/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
index 0a37f29..5e64ae6 100644
--- a/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
@@ -28,7 +28,10 @@
 import java.util.zip.ZipFile;
 
 /**
- * 
+ * Analyzes an input Jar to get all the relevant classes according to the given filter.
+ * <p/>
+ * This is mostly a helper extracted for convenience. Callers will want to use
+ * {@link #parseInputJar(String)} followed by {@link #filter(Map, Filter)}.
  */
 class AsmAnalyzer {
 
@@ -66,12 +69,22 @@
         }
     }
 
-    public void filter(Map<String, ClassReader> classes, Filter filter) {
+    /**
+     * Filters the set of classes. Removes all classes that should not be included in the
+     * filter or that should be excluded. This modifies the map in-place.
+     * 
+     * @param classes The in-out map of classes to examine and filter. The map is filtered
+     *                in-place.
+     * @param filter  A filter describing which classes to include and which ones to exclude.
+     */
+    void filter(Map<String, ClassReader> classes, Filter filter) {
 
         Set<String> keys = classes.keySet();
         for(Iterator<String> it = keys.iterator(); it.hasNext(); ) {
             String key = it.next();
 
+            // TODO: We *could* filter out all private classes here: classes.get(key).getAccess().
+            
             // remove if we don't keep it
             if (!filter.accept(key)) {
                 System.out.println("- Remove class " + key);
diff --git a/tools/mkstubs/src/com/android/mkstubs/Filter.java b/tools/mkstubs/src/com/android/mkstubs/Filter.java
index c566c6b..0dcd8da 100644
--- a/tools/mkstubs/src/com/android/mkstubs/Filter.java
+++ b/tools/mkstubs/src/com/android/mkstubs/Filter.java
@@ -19,7 +19,15 @@
 import java.util.TreeSet;
 
 /**
- * 
+ * A "filter" holds the various patterns that MkStubs should accept (include)
+ * or reject (exclude). Patterns can be of two kind:
+ * <ul>
+ * <li>Full patterns are simple string matches, similar to a "^pattern$" regex.
+ * <li>Prefix patterns are partial string matches, similar to a "^pattern.*" regex.
+ * </ul>
+ * <p/>
+ * The {@link #accept(String)} method examines a given string against the known
+ * pattern to decide if it should be included.
  */
 class Filter {
     private TreeSet<String> mIncludePrefix = new TreeSet<String>();
@@ -27,22 +35,44 @@
     private TreeSet<String> mExcludePrefix = new TreeSet<String>();
     private TreeSet<String> mExcludeFull   = new TreeSet<String>();
 
+    /**
+     * Returns the set of all full patterns to be included.
+     */
     public TreeSet<String> getIncludeFull() {
         return mIncludeFull;
     }
-    
+
+    /**
+     * Returns the set of all prefix patterns to be included.
+     */
     public TreeSet<String> getIncludePrefix() {
         return mIncludePrefix;
     }
     
+    /**
+     * Returns the set of all full patterns to be excluded.
+     */
     public TreeSet<String> getExcludeFull() {
         return mExcludeFull;
     }
     
+    /**
+     * Returns the set of all prefix patterns to be excluded.
+     */
     public TreeSet<String> getExcludePrefix() {
         return mExcludePrefix;
     }
-    
+
+    /**
+     * Checks if the given string passes the various include/exclude rules.
+     * The matching is done as follows:
+     * <ul>
+     * <li> The string must match either a full include or a prefix include.
+     * <li> The string must not match any full exclude nor any prefix exclude.
+     * </ul>
+     * @param s The string to accept or reject.
+     * @return True if the string can be accepted, false if it must be rejected.
+     */
     public boolean accept(String s) {
         
         // Check if it can be included.
diff --git a/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java b/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
index c3585a8..d3b06f7 100644
--- a/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
+++ b/tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
@@ -25,7 +25,8 @@
 import org.objectweb.asm.Opcodes;
 
 /**
- * A class visitor that filters out all the referenced exclusions
+ * A class visitor that filters out all members (fields, methods and inner classes) that are
+ * either private or rejected by the {@link Filter}.
  */
 class FilterClassAdapter extends ClassAdapter {
 
diff --git a/tools/mkstubs/src/com/android/mkstubs/Main.java b/tools/mkstubs/src/com/android/mkstubs/Main.java
index fd8fa4d..01cda71 100644
--- a/tools/mkstubs/src/com/android/mkstubs/Main.java
+++ b/tools/mkstubs/src/com/android/mkstubs/Main.java
@@ -26,10 +26,15 @@
 
 
 /**
- * 
+ * Main entry point of the MkStubs app.
+ * <p/>
+ * For workflow details, see {@link #process(Params)}.
  */
 public class Main {
     
+    /**
+     * A struct-like class to hold the various input values (e.g. command-line args)
+     */
     static class Params {
         private String mInputJarPath;
         private String mOutputJarPath;
@@ -40,22 +45,25 @@
             mOutputJarPath = outputJarPath;
             mFilter = new Filter();
         }
-        
+
+        /** Returns the name of the input jar, where to read classes from. */
         public String getInputJarPath() {
             return mInputJarPath;
         }
 
+        /** Returns the name of the output jar, where to write classes to. */
         public String getOutputJarPath() {
             return mOutputJarPath;
         }
-        
+
+        /** Returns the current instance of the filter, the include/exclude patterns. */
         public Filter getFilter() {
             return mFilter;
         }
     }
     
     /**
-     * @param args
+     * Main entry point. Processes arguments then performs the "real" work.
      */
     public static void main(String[] args) {
 
@@ -68,6 +76,17 @@
         }
     }
 
+    /**
+     * Grabs command-line arguments.
+     * The expected arguments are:
+     * <ul>
+     * <li> The filename of the input Jar.
+     * <li> The filename of the output Jar.
+     * <li> One or more include/exclude patterns or files containing these patterns.
+     *      See {@link #addString(Params, String)} for syntax.
+     * </ul>
+     * @throws IOException on failure to read a pattern file.
+     */
     private Params processArgs(String[] args) throws IOException {
         
         if (args.length < 2) {
@@ -83,7 +102,26 @@
         return p;
     }
 
+    /**
+     * Adds one pattern string to the current filter.
+     * The syntax must be:
+     * <ul>
+     * <li> +full_include or +prefix_include*
+     * <li> -full_exclude or -prefix_exclude*
+     * <li> @filename
+     * </ul>
+     * The input string is trimmed so any space around the first letter (-/+/@) or
+     * at the end is removed. Empty strings are ignored.
+     * 
+     * @param p The params which filters to edit.
+     * @param s The string to examine.
+     * @throws IOException
+     */
     private void addString(Params p, String s) throws IOException {
+        if (s == null) {
+            return;
+        }
+
         s = s.trim();
 
         if (s.length() < 2) {
@@ -114,11 +152,20 @@
         }
     }
 
-    private void addStringsFromFile(Params p, String inputFile)
+    /**
+     * Adds all the filter strings from the given file.
+     * 
+     * @param p The params which filter to edit.
+     * @param osFilePath The OS path to the file containing the patterns.
+     * @throws IOException
+     * 
+     * @see #addString(Params, String)
+     */
+    private void addStringsFromFile(Params p, String osFilePath)
             throws IOException {
         BufferedReader br = null;
         try {
-            br = new BufferedReader(new FileReader(inputFile));
+            br = new BufferedReader(new FileReader(osFilePath));
             String line;
             while ((line = br.readLine()) != null) {
                 addString(p, line);
@@ -128,6 +175,9 @@
         }
     }
 
+    /**
+     * Prints some help to stdout.
+     */
     private void usage() {
         System.out.println("Usage: mkstub input.jar output.jar [excluded-class @excluded-classes-file ...]");
 
@@ -144,6 +194,17 @@
         System.exit(1);
     }
 
+    /**
+     * Performs the main workflow of this app:
+     * <ul>
+     * <li> Read the input Jar to get all its classes.
+     * <li> Filter out all classes that should not be included or that should be excluded.
+     * <li> Goes thru the classes, filters methods/fields and generate their source
+     *      in a directory called "&lt;outpath_jar_path&gt;_sources"
+     * <li> Does the same filtering on the classes but this time generates the real stubbed
+     *      output jar.
+     * </ul>
+     */
     private void process(Params p) throws IOException {
         AsmAnalyzer aa = new AsmAnalyzer();
         Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath());
diff --git a/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java b/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
index 461a25f..f5a339d 100644
--- a/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
+++ b/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java
@@ -30,7 +30,11 @@
 import java.util.Map.Entry;
 
 /**
- * 
+ * Given a set of already filtered classes, this filters out all private members and then
+ * generates the Java source for the remaining classes.
+ * <p/>
+ * This is an helper extracted for convenience. Callers just need to use
+ * {@link #generateSource(File, Map, Filter)}.
  */
 class SourceGenerator {
 
diff --git a/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java b/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java
index 0321dc3..6126e17 100644
--- a/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java
+++ b/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java
@@ -32,7 +32,11 @@
 import java.util.jar.JarOutputStream;
 
 /**
- * 
+ * Given a set of already filtered classes, this filters out all private members,
+ * stubs the remaining classes and then generates a Jar out of them.
+ * <p/>
+ * This is an helper extracted for convenience. Callers just need to use
+ * {@link #generateStubbedJar(File, Map, Filter)}.
  */
 class StubGenerator {
 
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
index 757dcea..3b14ea7 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
@@ -19,7 +19,11 @@
 import org.objectweb.asm.Opcodes;
 
 /**
- * 
+ * Source generator for the access fields of methods, fields and classes.
+ * <p/>
+ * Given an integer access field and a type ({@link #IS_CLASS}, {@link #IS_FIELD} or
+ * {@link #IS_METHOD}), the {@link #write(int, int)} method can generate a string
+ * desribing the access modifiers for a Java source. 
  */
 class AccessSourcer {
 
@@ -81,6 +85,9 @@
 
     /**
      * Generates a list of access keywords, e.g. "public final".
+     * <p/>
+     * It is up to the caller to filter extra keywords that should not be generated,
+     * e.g. {@link Flag#ACC_SYNTHETIC}.
      *  
      * @param access The access mode, e.g. 33 or 18
      * @param filter One of {@link #IS_CLASS}, {@link #IS_FIELD} or {@link #IS_METHOD}, which
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java
index bbf1cb2..d2843a8 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/AnnotationSourcer.java
@@ -19,7 +19,7 @@
 import org.objectweb.asm.AnnotationVisitor;
 
 /**
- * 
+ * An annotation visitor that generates Java source for an annotation.
  */
 class AnnotationSourcer implements AnnotationVisitor {
 
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
index 189e1a0..3d95039 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java
@@ -25,7 +25,7 @@
 import org.objectweb.asm.signature.SignatureReader;
 
 /**
- * A class visitor that rewrites a java source
+ * A class visitor that writes a java source.
  */
 public class ClassSourcer implements ClassVisitor {
 
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java
index 4f9c229..7f30a24 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/FieldSourcer.java
@@ -19,10 +19,11 @@
 import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.Attribute;
 import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Type;
 import org.objectweb.asm.signature.SignatureReader;
 
 /**
- * 
+ * A field visitor that generates Java source defining a field. 
  */
 class FieldSourcer implements FieldVisitor {
 
@@ -56,7 +57,7 @@
         as.write(mAccess, AccessSourcer.IS_FIELD);
         
         if (mSignature == null) {
-            mOutput.write(" %s", mOutput.decodeDesc(mDesc));
+            mOutput.write(" %s", Type.getType(mDesc).getClassName());
         } else {
             mOutput.write(" ");
             SignatureReader sigReader = new SignatureReader(mSignature);
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
index d474e4b..f58de32 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
@@ -26,7 +26,7 @@
 import java.util.ArrayList;
 
 /**
- * 
+ * A method visitor that generates the Java source for a whole method. 
  */
 class MethodSourcer implements MethodVisitor {
 
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
index 837cf95..bc2218f 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
@@ -16,22 +16,38 @@
 
 package com.android.mkstubs.sourcer;
 
-import org.objectweb.asm.Type;
-
 import java.io.IOException;
 import java.io.Writer;
 
 /**
- * 
+ * An {@link Output} objects is an helper to write to a character stream {@link Writer}.
+ * <p/>
+ * It provide some helper methods to the various "sourcer" classes from this package
+ * to help them write to the underlying stream.
  */
 public class Output {
     
     private final Writer mWriter;
 
+    /**
+     * Creates a new {@link Output} object that wraps the given {@link Writer}.
+     * <p/>
+     * The caller is responsible of opening and closing the {@link Writer}.
+     * 
+     * @param writer The writer to write to. Could be a file, a string, etc.
+     */
     public Output(Writer writer) {
         mWriter = writer;
     }
 
+    /**
+     * Writes a formatted string to the writer.
+     * 
+     * @param format The format string.
+     * @param args The arguments for the format string.
+     * 
+     * @see String#format(String, Object...)
+     */
     public void write(String format, Object... args) {
         try {
             mWriter.write(String.format(format, args));
@@ -40,18 +56,21 @@
         }
     }
     
+    /**
+     * Writes a single character to the writer.
+     * 
+     * @param c The character to write.
+     */
     public void write(char c) {
         write(Character.toString(c));
     }
     
+    /**
+     * Writes a {@link StringBuilder} to the writer.
+     * 
+     * @param sb The {@link StringBuilder#toString()} method is used to ge the string to write.
+     */
     public void write(StringBuilder sb) {
         write(sb.toString());
     }
-
-    
-    public String decodeDesc(String desc) {
-        return Type.getType(desc).getClassName();
-    }
-
-
 }
diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java
index 6202528..7805d7d 100644
--- a/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java
+++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/SignatureSourcer.java
@@ -24,14 +24,22 @@
 import java.util.ArrayList;
 
 /**
- * Note: most of the implementation is a duplicate of
- * ASM's SignatureWriter with some slight variations.
+ * A signature visitor that can be used to generate Java source corresponding to
+ * various types of signatures.
  * <p/>
- * Note: When processing a method's signature, the signature order is the
- * reverse of the source order, e.g. it is (parameters)return-type where
- * we want to generate "return-type method-name (parameters)".
- * So in this case the return-type and parameters are not output directly
- * but are instead accumulated in internal variables.
+ * Terminology: a "signature" is a type descriptor for generics. There are different types
+ * of signatures depending on the context where they are used, e.g. method declarations,
+ * method parameters, class declarations, etc..
+ * <p/>
+ * Note: most of the implementation is a duplicate of ASM's SignatureWriter with some
+ * slight variations.
+ * <p/>
+ * Note: When processing a method's signature, the signature order is the reverse of the source
+ * order, e.g. the signature is written as "(parameters)return-type" where we want to generate
+ * "return-type method-name (parameters)". To hanlde this case, the return-type and parameters
+ * are <em>not</em> output directly but are instead accumulated in internal variables that you can
+ * get later using {@link #getReturnType()}, {@link #getParameters()}, {@link #getSuperClass()}
+ * and {@link #formalsToString()}.
  */
 class SignatureSourcer implements SignatureVisitor {
 
@@ -58,10 +66,22 @@
      */
     private int mArgumentStack;
 
+    /**
+     * {@link SignatureSourcer} generated when parsing the return type of <em>this</em>
+     * signature. Initially null.
+     */
     private SignatureSourcer mReturnType;
 
+    /**
+     * {@link SignatureSourcer} generated when parsing the super class of <em>this</em>
+     * signature. Initially null.
+     */
     private SignatureSourcer mSuperClass;
 
+    /**
+     * {@link SignatureSourcer}s for each parameters generated when parsing the method parameters
+     * of <em>this</em> signature. Initially empty but not null.
+     */
     private ArrayList<SignatureSourcer> mParameters = new ArrayList<SignatureSourcer>();
 
 
diff --git a/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java b/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java
index dea0a52..8f9ae11 100644
--- a/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java
+++ b/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java
@@ -24,7 +24,8 @@
 import org.objectweb.asm.MethodVisitor;
 
 /**
- * 
+ * A class visitor that generates stubs for all methods of the visited class.
+ * Everything else is passed as-is.
  */
 public class ClassStubber extends ClassAdapter {
 
diff --git a/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java b/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java
index 3e200cd..1617809 100644
--- a/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java
+++ b/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java
@@ -24,7 +24,13 @@
 import org.objectweb.asm.Opcodes;
 
 /**
- * 
+ * A method visitor that generates a code stub for the visited method.
+ * <p/>
+ * Annotations and parameters are passed as-is.
+ * All other code is replaced by the following:
+ * <pre>throw new RuntimeException("stub");</pre>
+ * Note that constructors rewritten this way will probably fail with the runtime bytecode
+ * verifier since no call to <code>super</code> is generated.
  */
 public class MethodStubber extends MethodAdapter {
 
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
index 003021c..236327c 100644
--- a/tools/scripts/android_rules.xml
+++ b/tools/scripts/android_rules.xml
@@ -64,7 +64,7 @@
     <property name="out-debug-package" value="${out-folder}/${ant.project.name}-debug.apk"/>
 
     <!-- Tools -->
-    <condition property="exe" value="exe" else=""><os family="windows"/></condition>
+    <condition property="exe" value=".exe" else=""><os family="windows"/></condition>
     <property name="adb" value="${android-tools}/adb${exe}"/>
 
     <!-- rules -->
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
index 9f3fb99..c20bfa4 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
@@ -482,22 +482,29 @@
             if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) {
                 
                 String value = "";
-                if (arg.getDefaultValue() instanceof String[]) {
-                    for (String v : (String[]) arg.getDefaultValue()) {
-                        if (value.length() > 0) {
-                            value += ", ";
+                String required = "";
+                if (arg.isMandatory()) {
+                    required = " [required]";
+                    
+                } else {
+                    if (arg.getDefaultValue() instanceof String[]) {
+                        for (String v : (String[]) arg.getDefaultValue()) {
+                            if (value.length() > 0) {
+                                value += ", ";
+                            }
+                            value += v;
                         }
-                        value += v;
+                    } else if (arg.getDefaultValue() != null) {
+                        Object v = arg.getDefaultValue();
+                        if (arg.getMode() != MODE.BOOLEAN || v.equals(Boolean.TRUE)) {
+                            value = v.toString();
+                        }
                     }
-                } else if (arg.getDefaultValue() != null) {
-                    value = arg.getDefaultValue().toString();
-                }
-                if (value.length() > 0) {
-                    value = " (" + value + ")";
+                    if (value.length() > 0) {
+                        value = " [Default: " + value + "]";
+                    }
                 }
                 
-                String required = arg.isMandatory() ? " [required]" : "";
-
                 stdout("  -%1$s %2$-10s %3$s%4$s%5$s",
                         arg.getShortArg(),
                         "--" + arg.getLongArg(),
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
index 66c2419..36172e9 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
@@ -138,9 +138,13 @@
 
         // --- create project ---
 
+        /* Disabled for ADT 0.9 / Cupcake SDK 1.5_r1 release. [bug #1795718].
+           This currently does not work, the alias build rules need to be fixed.
+           
         define(MODE.ENUM, true, 
                 VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE,
                 "Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS });
+        */
         define(MODE.STRING, true, 
                 VERB_CREATE, OBJECT_PROJECT,
                 "p", KEY_PATH,
diff --git a/tools/yuv420sp2rgb/Android.mk b/tools/yuv420sp2rgb/Android.mk
new file mode 100644
index 0000000..b7e7802
--- /dev/null
+++ b/tools/yuv420sp2rgb/Android.mk
@@ -0,0 +1,16 @@
+# Copyright 2005 Google Inc. All Rights Reserved.
+#
+# Android.mk for yuv420sp2rgb 
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifeq ($(TARGET_ARCH),arm)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := yuv420sp2rgb.c cmdline.c debug.c
+
+LOCAL_MODULE := yuv420sp2rgb
+
+include $(BUILD_HOST_EXECUTABLE)
+endif
diff --git a/tools/yuv420sp2rgb/cmdline.c b/tools/yuv420sp2rgb/cmdline.c
new file mode 100644
index 0000000..eb870b5
--- /dev/null
+++ b/tools/yuv420sp2rgb/cmdline.c
@@ -0,0 +1,147 @@
+#include <debug.h>
+#include <cmdline.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static struct option long_options[] = {
+    {"output",  required_argument, 0, 'o'},
+    {"height",  required_argument, 0, 'h'},
+    {"width",   required_argument, 0, 'w'},
+    {"gray",    no_argument,       0, 'g'},
+    {"type",    required_argument, 0, 't'},
+    {"rotate",  required_argument, 0, 'r'},
+    {"verbose", no_argument,       0, 'V'},
+    {"help",    no_argument,       0, 1},
+    {0, 0, 0, 0},
+};
+
+/* This array must parallel long_options[] */
+static const char *descriptions[] = {
+    "output file",
+    "image height in pixels",
+    "image width in pixels",
+    "process the luma plane only",
+    "encode as one of { 'ppm', 'rgb', or 'argb' }",
+    "rotate (90, -90, 180 degrees)",
+    "print verbose output",
+    "print this help screen",
+};
+
+void print_help(const char *name) {
+    fprintf(stdout,
+            "Converts yuv 4:2:0 to rgb24 and generates a PPM file.\n"
+            "invokation:\n"
+            "\t%s infile --height <height> --width <width> --output <outfile> -t <ppm|grb|argb> [ --gray ] [ --rotate <degrees> ] [ --verbose ]\n"
+            "\t%s infile --help\n",
+            name, name);
+    fprintf(stdout, "options:\n");
+    struct option *opt = long_options;
+    const char **desc = descriptions;
+    while (opt->name) {
+        fprintf(stdout, "\t-%c/--%s%s: %s\n",
+                isprint(opt->val) ? opt->val : ' ',
+                opt->name,
+                (opt->has_arg ? " (argument)" : ""),
+                *desc);
+        opt++;
+        desc++;
+    }
+}
+
+int get_options(int argc, char **argv,
+                char **outfile,
+                int *height,
+                int *width,
+                int *gray,
+                char **type,
+                int *rotate,
+                int *verbose) {
+    int c;
+
+    ASSERT(outfile); *outfile = NULL;
+    ASSERT(height); *height = -1;
+    ASSERT(width); *width = -1;
+    ASSERT(gray); *gray = 0;
+    ASSERT(rotate); *rotate = 0;
+    ASSERT(verbose); *verbose = 0;
+    ASSERT(type); *type = NULL;
+
+    while (1) {
+        /* getopt_long stores the option index here. */
+        int option_index = 0;
+
+        c = getopt_long (argc, argv,
+                         "Vgo:h:w:r:t:",
+                         long_options,
+                         &option_index);
+        /* Detect the end of the options. */
+        if (c == -1) break;
+
+        if (isgraph(c)) {
+            INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)"));
+        }
+
+#define SET_STRING_OPTION(name) do {                                   \
+    ASSERT(optarg);                                                    \
+    (*name) = strdup(optarg);                                          \
+} while(0)
+
+#define SET_INT_OPTION(val) do {                                       \
+    ASSERT(optarg);                                                    \
+	if (strlen(optarg) >= 2 && optarg[0] == '0' && optarg[1] == 'x') { \
+			FAILIF(1 != sscanf(optarg+2, "%x", val),                   \
+				   "Expecting a hexadecimal argument!\n");             \
+	} else {                                                           \
+		FAILIF(1 != sscanf(optarg, "%d", val),                         \
+			   "Expecting a decimal argument!\n");                     \
+	}                                                                  \
+} while(0)
+
+        switch (c) {
+        case 0:
+            /* If this option set a flag, do nothing else now. */
+            if (long_options[option_index].flag != 0)
+                break;
+            INFO ("option %s", long_options[option_index].name);
+            if (optarg)
+                INFO (" with arg %s", optarg);
+            INFO ("\n");
+            break;
+        case 1: print_help(argv[0]); exit(1); break;
+		case 'o':
+			SET_STRING_OPTION(outfile);
+			break;
+		case 't':
+			SET_STRING_OPTION(type);
+			break;
+        case 'h':
+            SET_INT_OPTION(height);
+            break;
+        case 'w':
+            SET_INT_OPTION(width);
+            break;
+        case 'r':
+            SET_INT_OPTION(rotate);
+            break;
+        case 'g': *gray = 1; break;
+        case 'V': *verbose = 1; break;
+        case '?':
+            /* getopt_long already printed an error message. */
+            break;
+
+#undef SET_STRING_OPTION
+#undef SET_INT_OPTION
+
+        default:
+            FAILIF(1, "Unknown option");
+        }
+    }
+
+    return optind;
+}
diff --git a/tools/yuv420sp2rgb/cmdline.h b/tools/yuv420sp2rgb/cmdline.h
new file mode 100644
index 0000000..49760ad
--- /dev/null
+++ b/tools/yuv420sp2rgb/cmdline.h
@@ -0,0 +1,15 @@
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+void print_help(const char *executable_name);
+
+extern int get_options(int argc, char **argv,
+                       char **outfile,
+                       int *height,
+                       int *width,
+                       int *gray,
+                       char **type,
+                       int *rotate,
+                       int *verbose);
+
+#endif/*CMDLINE_H*/
diff --git a/tools/yuv420sp2rgb/debug.c b/tools/yuv420sp2rgb/debug.c
new file mode 100644
index 0000000..263e09f
--- /dev/null
+++ b/tools/yuv420sp2rgb/debug.c
@@ -0,0 +1,38 @@
+#include <debug.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define NUM_COLS  (32)
+
+int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize) {
+    int num_nonprintable = 0;
+    int i, last;
+    char *pchr = (char *)b;
+    fputc('\n', s);
+    fprintf(s, "%p: ", b);
+    for (i = last = 0; i < len; i++) {
+        if (!elsize) {
+            if (i && !(i % 4)) fprintf(s, " ");
+            if (i && !(i % 8)) fprintf(s, " ");
+        } else {
+            if (i && !(i % elsize)) fprintf(s, " ");
+        }
+
+        if (i && !(i % NUM_COLS)) {
+            while (last < i) {
+                if (isprint(pchr[last]))
+                    fputc(pchr[last], s);
+                else {
+                    fputc('.', s); 
+                    num_nonprintable++;
+                }
+                last++;
+            }
+            fprintf(s, " (%d)\n%p: ", i, b);
+        }
+        fprintf(s, "%02x", (unsigned char)pchr[i]);
+    }
+    if (i && (i % NUM_COLS)) fputs("\n", s);
+    return num_nonprintable;
+}
+
diff --git a/tools/yuv420sp2rgb/debug.h b/tools/yuv420sp2rgb/debug.h
new file mode 100644
index 0000000..9bbf47f
--- /dev/null
+++ b/tools/yuv420sp2rgb/debug.h
@@ -0,0 +1,90 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr)   __builtin_expect (expr, 1)
+
+#ifdef DEBUG
+
+    #define FAILIF(cond, msg...) do {                        \
+	if (unlikely(cond)) {                                \
+        fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+		fprintf(stderr, ##msg);                          \
+		exit(1);                                         \
+	}                                                    \
+} while(0)
+
+/* Debug enabled */
+    #define ASSERT(x) do {                                \
+	if (unlikely(!(x))) {                             \
+		fprintf(stderr,                               \
+				"ASSERTION FAILURE %s:%d: [%s]\n",    \
+				__FILE__, __LINE__, #x);              \
+		exit(1);                                      \
+	}                                                 \
+} while(0)
+
+#else
+
+    #define FAILIF(cond, msg...) do { \
+	if (unlikely(cond)) {         \
+		fprintf(stderr, ##msg);   \
+		exit(1);                  \
+	}                             \
+} while(0)
+
+/* No debug */
+    #define ASSERT(x)   do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+    FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size) {
+    void *m = malloc(size);
+    FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+    return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) {
+    void *m = calloc(num_entries, entry_size);
+    FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+    return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size) {
+    void *m = realloc(ptr, size);
+    FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+    return m;
+}
+
+static inline void FREE(void *ptr) {
+    free(ptr);
+}
+
+static inline void FREEIF(void *ptr) {
+    if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...)  do {                             \
+    extern int quiet_flag;                            \
+    if(likely(!quiet_flag))                           \
+        fprintf(stdout, ##x);                         \
+} while(0)
+
+#define ERROR PRINT
+
+#define INFO(x...)  do {                              \
+    extern int verbose_flag;                          \
+    if(unlikely(verbose_flag))                        \
+        fprintf(stdout, ##x);                         \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/tools/yuv420sp2rgb/yuv420sp2rgb.c b/tools/yuv420sp2rgb/yuv420sp2rgb.c
new file mode 100644
index 0000000..6fe82e5
--- /dev/null
+++ b/tools/yuv420sp2rgb/yuv420sp2rgb.c
@@ -0,0 +1,339 @@
+#include <stdio.h>
+#include <debug.h>
+#include <cmdline.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#ifndef max
+#define max(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; })
+#define min(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; })
+#endif
+
+#define CONVERT_TYPE_PPM 0
+#define CONVERT_TYPE_RGB 1
+#define CONVERT_TYPE_ARGB 2
+
+/*
+   YUV 4:2:0 image with a plane of 8 bit Y samples followed by an interleaved
+   U/V plane containing 8 bit 2x2 subsampled chroma samples.
+   except the interleave order of U and V is reversed.
+
+                        H V
+   Y Sample Period      1 1
+   U (Cb) Sample Period 2 2
+   V (Cr) Sample Period 2 2
+ */
+
+typedef struct rgb_context {
+    unsigned char *buffer;
+    int width;
+    int height;
+    int rotate;
+    int i;
+    int j;
+    int size; /* for debugging */
+} rgb_context;
+
+typedef void (*rgb_cb)(
+    unsigned char r,
+    unsigned char g,
+    unsigned char b,
+    rgb_context *ctx);
+
+const int bytes_per_pixel = 2;
+
+static void color_convert_common(
+    unsigned char *pY, unsigned char *pUV,
+    int width, int height,
+    unsigned char *buffer,
+    int size, /* buffer size in bytes */
+    int gray,
+    int rotate,
+    rgb_cb cb) 
+{
+	int i, j;
+	int nR, nG, nB;
+	int nY, nU, nV;
+    rgb_context ctx;
+
+    ctx.buffer = buffer;
+    ctx.size = size; /* debug */
+    ctx.width = width;
+    ctx.height = height;
+    ctx.rotate = rotate;
+
+    if (gray) {
+        for (i = 0; i < height; i++) {
+            for (j = 0; j < width; j++) {
+                nB = *(pY + i * width + j);
+                ctx.i = i;
+                ctx.j = j;
+                cb(nB, nB, nB, &ctx);
+            }
+        }	
+    } else {
+        // YUV 4:2:0
+        for (i = 0; i < height; i++) {
+            for (j = 0; j < width; j++) {
+                nY = *(pY + i * width + j);
+                nV = *(pUV + (i/2) * width + bytes_per_pixel * (j/2));
+                nU = *(pUV + (i/2) * width + bytes_per_pixel * (j/2) + 1);
+            
+                // Yuv Convert
+                nY -= 16;
+                nU -= 128;
+                nV -= 128;
+            
+                if (nY < 0)
+                    nY = 0;
+            
+                // nR = (int)(1.164 * nY + 2.018 * nU);
+                // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
+                // nB = (int)(1.164 * nY + 1.596 * nV);
+            
+                nB = (int)(1192 * nY + 2066 * nU);
+                nG = (int)(1192 * nY - 833 * nV - 400 * nU);
+                nR = (int)(1192 * nY + 1634 * nV);
+            
+                nR = min(262143, max(0, nR));
+                nG = min(262143, max(0, nG));
+                nB = min(262143, max(0, nB));
+            
+                nR >>= 10; nR &= 0xff;
+                nG >>= 10; nG &= 0xff;
+                nB >>= 10; nB &= 0xff;
+
+                ctx.i = i;
+                ctx.j = j;
+                cb(nR, nG, nB, &ctx);
+            }
+        }
+    }
+}   
+
+static void rgb16_cb(
+    unsigned char r,
+    unsigned char g,
+    unsigned char b,
+    rgb_context *ctx)
+{
+    unsigned short *rgb16 = (unsigned short *)ctx->buffer;
+    *(rgb16 + ctx->i * ctx->width + ctx->j) = b | (g << 5) | (r << 11);
+}
+
+static void common_rgb_cb(
+    unsigned char r,
+    unsigned char g,
+    unsigned char b,
+    rgb_context *ctx,
+    int alpha)
+{
+    unsigned char *out = ctx->buffer;
+    int offset = 0;
+    int bpp;
+    int i = 0;
+    switch(ctx->rotate) {
+    case 0: /* no rotation */
+        offset = ctx->i * ctx->width + ctx->j;
+        break;
+    case 1: /* 90 degrees */
+        offset = ctx->height * (ctx->j + 1) - ctx->i;
+        break;
+    case 2: /* 180 degrees */
+        offset = (ctx->height - 1 - ctx->i) * ctx->width + ctx->j;
+        break;
+    case 3: /* 270 degrees */
+        offset = (ctx->width - 1 - ctx->j) * ctx->height + ctx->i;
+        break;
+    default:
+        FAILIF(1, "Unexpected roation value %d!\n", ctx->rotate);
+    }
+
+    bpp = 3 + !!alpha;
+    offset *= bpp;
+    FAILIF(offset < 0, "point (%d, %d) generates a negative offset.\n", ctx->i, ctx->j);
+    FAILIF(offset + bpp > ctx->size, "point (%d, %d) at offset %d exceeds the size %d of the buffer.\n",
+           ctx->i, ctx->j,
+           offset,
+           ctx->size);
+
+    out += offset;
+
+    if (alpha) out[i++] = 0xff;
+    out[i++] = r;
+    out[i++] = g;
+    out[i] = b;
+}
+
+static void rgb24_cb(
+    unsigned char r,
+    unsigned char g,
+    unsigned char b,
+    rgb_context *ctx)
+{
+    return common_rgb_cb(r,g,b,ctx,0);
+}
+
+static void argb_cb(
+    unsigned char r,
+    unsigned char g,
+    unsigned char b,
+    rgb_context *ctx)
+{
+    return common_rgb_cb(r,g,b,ctx,1);
+}
+
+static void convert(const char *infile,
+                    const char *outfile,
+                    int height,
+                    int width,
+                    int gray,
+                    int type,
+                    int rotate)
+{
+    void *in, *out;
+    int ifd, ofd, rc;
+    int psz = getpagesize();
+    static char header[1024];
+    int header_size;
+    size_t outsize;
+
+    int bpp = 3;
+    switch (type) {
+    case CONVERT_TYPE_PPM:
+        PRINT("encoding PPM\n");
+        if (rotate & 1)
+            header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", height, width);
+        else
+            header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", width, height);
+	break;
+    case CONVERT_TYPE_RGB:
+        PRINT("encoding raw RGB24\n");
+        header_size = 0;
+        break;
+    case CONVERT_TYPE_ARGB:
+        PRINT("encoding raw ARGB\n");
+        header_size = 0;
+        bpp = 4;
+        break;
+    }
+        
+    outsize = header_size + width * height * bpp;
+    outsize = (outsize + psz - 1) & ~(psz - 1);
+
+    INFO("Opening input file %s\n", infile);
+    ifd = open(infile, O_RDONLY);
+    FAILIF(ifd < 0, "open(%s) failed: %s (%d)\n",
+           infile, strerror(errno), errno);
+
+    INFO("Opening output file %s\n", outfile);
+    ofd = open(outfile, O_RDWR | O_CREAT, 0664);
+    FAILIF(ofd < 0, "open(%s) failed: %s (%d)\n",
+           outfile, strerror(errno), errno);
+    
+    INFO("Memory-mapping input file %s\n", infile);
+    in = mmap(0, width * height * 3 / 2, PROT_READ, MAP_PRIVATE, ifd, 0);
+    FAILIF(in == MAP_FAILED, "could not mmap input file: %s (%d)\n",
+           strerror(errno), errno);
+
+    INFO("Truncating output file %s to %d bytes\n", outfile, outsize);
+    FAILIF(ftruncate(ofd, outsize) < 0,
+           "Could not truncate output file to required size: %s (%d)\n",
+           strerror(errno), errno);
+
+    INFO("Memory mapping output file %s\n", outfile);
+    out = mmap(0, outsize, PROT_WRITE, MAP_SHARED, ofd, 0);
+    FAILIF(out == MAP_FAILED, "could not mmap output file: %s (%d)\n",
+           strerror(errno), errno);
+
+    INFO("PPM header (%d) bytes:\n%s\n", header_size, header);
+    FAILIF(write(ofd, header, header_size) != header_size,
+           "Error wrinting PPM header: %s (%d)\n",
+           strerror(errno), errno);
+
+    INFO("Converting %dx%d YUV 4:2:0 to RGB24...\n", width, height);
+    color_convert_common(in, in + width * height,
+                         width, height, 
+                         out + header_size, outsize - header_size,
+                         gray, rotate,
+                         type == CONVERT_TYPE_ARGB ? argb_cb : rgb24_cb);
+}
+
+int verbose_flag;
+int quiet_flag;
+
+int main(int argc, char **argv) {
+
+    char *infile, *outfile, *type;
+    int height, width, gray, rotate;
+    int cmdline_error = 0;
+
+    /* Parse command-line arguments. */
+    
+    int first = get_options(argc, argv,
+                            &outfile,
+                            &height,
+                            &width,
+                            &gray,
+                            &type,
+                            &rotate,
+                            &verbose_flag);
+
+    if (first == argc) {
+        ERROR("You must specify an input file!\n");
+        cmdline_error++;
+    }
+    if (!outfile) {
+        ERROR("You must specify an output file!\n");
+        cmdline_error++;
+    }
+    if (height < 0 || width < 0) {
+        ERROR("You must specify both image height and width!\n");
+        cmdline_error++;
+    }
+
+    FAILIF(rotate % 90, "Rotation angle must be a multiple of 90 degrees!\n");
+
+    rotate /= 90;
+    rotate %= 4;
+    if (rotate < 0) rotate += 4;
+
+    if (cmdline_error) {
+        print_help(argv[0]);
+        exit(1);
+    }
+
+    infile = argv[first];
+
+    INFO("input file: [%s]\n", infile);
+    INFO("output file: [%s]\n", outfile);
+    INFO("height: %d\n", height);
+    INFO("width: %d\n", width);
+    INFO("gray only: %d\n", gray);
+    INFO("encode as: %s\n", type);
+    INFO("rotation: %d\n", rotate);
+    
+    /* Convert the image */
+
+    int conv_type;
+    if (!type || !strcmp(type, "ppm"))
+        conv_type = CONVERT_TYPE_PPM;
+    else if (!strcmp(type, "rgb"))
+        conv_type = CONVERT_TYPE_RGB;
+    else if (!strcmp(type, "argb"))
+        conv_type = CONVERT_TYPE_ARGB;
+    else FAILIF(1, "Unknown encoding type %s.\n", type);
+    
+    convert(infile, outfile,
+            height, width, gray,
+            conv_type,
+            rotate);
+        
+    free(outfile);
+    return 0;
+}