am 15223314: am d6ff1cc8: Merge "emugen: porperly handle nullAllowed API calls"

* commit '15223314375a8a0c945f916a4024abf59e80682b':
  emugen: porperly handle nullAllowed API calls
diff --git a/apps/Development/src/com/android/development/AccountsTester.java b/apps/Development/src/com/android/development/AccountsTester.java
index e2a0789..b4155e7 100644
--- a/apps/Development/src/com/android/development/AccountsTester.java
+++ b/apps/Development/src/com/android/development/AccountsTester.java
@@ -21,6 +21,7 @@
 import android.app.AlertDialog;
 import android.content.*;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.accounts.*;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -32,6 +33,8 @@
 import android.text.TextUtils;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 public class AccountsTester extends Activity implements OnAccountsUpdateListener {
     private static final String TAG = "AccountsTester";
@@ -142,7 +145,7 @@
 
     private void initializeAuthenticatorsSpinner() {
         mAuthenticatorDescs = mAccountManager.getAuthenticatorTypes();
-        String[] names = new String[mAuthenticatorDescs.length];
+        List<String> names = new ArrayList(mAuthenticatorDescs.length);
         for (int i = 0; i < mAuthenticatorDescs.length; i++) {
             Context authContext;
             try {
@@ -150,12 +153,17 @@
             } catch (PackageManager.NameNotFoundException e) {
                 continue;
             }
-            names[i] = authContext.getString(mAuthenticatorDescs[i].labelId);
+            try  {
+                names.add(authContext.getString(mAuthenticatorDescs[i].labelId));
+            } catch (Resources.NotFoundException e) {
+                continue;
+            }
         }
 
+        String[] namesArray = names.toArray(new String[names.size()]);
         ArrayAdapter<String> adapter =
                 new ArrayAdapter<String>(AccountsTester.this,
-                android.R.layout.simple_spinner_item, names);
+                android.R.layout.simple_spinner_item, namesArray);
         mAccountTypesSpinner.setAdapter(adapter);
     }
 
diff --git a/apps/SpareParts/Android.mk b/apps/SpareParts/Android.mk
deleted file mode 100644
index 1a1ea0a..0000000
--- a/apps/SpareParts/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := eng
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := SpareParts
-
-include $(BUILD_PACKAGE)
diff --git a/apps/SpareParts/AndroidManifest.xml b/apps/SpareParts/AndroidManifest.xml
deleted file mode 100644
index 137f907..0000000
--- a/apps/SpareParts/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.spare_parts">
-    <uses-permission android:name="android.permission.SET_ANIMATION_SCALE" />
-    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
-    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
-    
-    <application android:label="@string/app_label"
-            android:icon="@mipmap/app_icon">
-
-        <activity android:name="SpareParts">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-
-    </application>
-</manifest>
diff --git a/apps/SpareParts/NOTICE b/apps/SpareParts/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/apps/SpareParts/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2005-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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/apps/SpareParts/res/layout/spare_parts.xml b/apps/SpareParts/res/layout/spare_parts.xml
deleted file mode 100644
index c1cd3c8..0000000
--- a/apps/SpareParts/res/layout/spare_parts.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/Settings/assets/res/any/layout/keyboard_version.xml
-**
-** Copyright 2006, 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.
-*/
--->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <RelativeLayout 
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-        <Spinner android:id="@+id/window_animation_scale"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentLeft="true">
-        </Spinner>
-
-        <Spinner android:id="@+id/transition_animation_scale"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/window_animation_scale"
-            android:layout_alignParentLeft="true">
-        </Spinner>
-
-    </RelativeLayout>
-
-</ScrollView>
-
diff --git a/apps/SpareParts/res/mipmap-hdpi/app_icon.png b/apps/SpareParts/res/mipmap-hdpi/app_icon.png
deleted file mode 100755
index 60fbdf5..0000000
--- a/apps/SpareParts/res/mipmap-hdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/apps/SpareParts/res/mipmap-mdpi/app_icon.png b/apps/SpareParts/res/mipmap-mdpi/app_icon.png
deleted file mode 100644
index cb40a19..0000000
--- a/apps/SpareParts/res/mipmap-mdpi/app_icon.png
+++ /dev/null
Binary files differ
diff --git a/apps/SpareParts/res/values/arrays.xml b/apps/SpareParts/res/values/arrays.xml
deleted file mode 100644
index e6026da..0000000
--- a/apps/SpareParts/res/values/arrays.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 
- * Copyright (C) 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.
- -->
-
-<resources>
-    <string-array name="entries_animations">
-        <item>Off</item>
-        <item>Fast</item>
-        <item>Normal</item>
-        <item>Slow</item>
-        <item>Very Slow</item>
-    </string-array>
-
-    <string-array name="entryvalues_animations">
-        <item>0.0</item>
-        <item>0.5</item>
-        <item>1.0</item>
-        <item>1.5</item>
-        <item>2.0</item>
-    </string-array>
-    
-    <string-array name="entries_font_size">
-        <item>Extremely Small</item>
-        <item>Extra Small</item>
-        <item>Small</item>
-        <item>Normal</item>
-        <item>Large</item>
-        <item>Extra Large</item>
-        <item>Extremely Large</item>
-    </string-array>
-
-    <string-array name="entryvalues_font_size">
-        <item>0.70</item>
-        <item>0.85</item>
-        <item>0.95</item>
-        <item>1.0</item>
-        <item>1.05</item>
-        <item>1.15</item>
-        <item>1.30</item>
-    </string-array>
-    
-    <string-array name="entries_end_button">
-        <item>Nothing</item>
-        <item>Go to home</item>
-        <item>Go to sleep</item>
-        <item>Home, then sleep</item>
-    </string-array>
-    
-    <string-array name="entryvalues_end_button">
-        <item>0</item>
-        <item>1</item>
-        <item>2</item>
-        <item>3</item>
-    </string-array>
-</resources>
diff --git a/apps/SpareParts/res/values/strings.xml b/apps/SpareParts/res/values/strings.xml
deleted file mode 100644
index 9e78be0..0000000
--- a/apps/SpareParts/res/values/strings.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 2006, 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.
-*/
--->
-<resources>
-    <string name="app_label">Spare Parts</string>
-
-    <string name="device_info_title">Device info</string>
-    
-    <string name="title_battery_history">Battery history</string>
-    <string name="summary_battery_history">Summary of how battery has been used</string>
-    
-    <string name="title_battery_information">Battery information</string>
-    <string name="summary_battery_information">Current battery status information</string>
-    
-    <string name="title_usage_statistics">Usage statistics</string>
-    <string name="summary_usage_statistics">Summary of application usage</string>
-    
-    <string name="general_title">General</string>
-    
-    <string name="title_window_animations">Window animations</string>
-    <string name="summary_window_animations">Speed of animations in individual windows</string>
-    <string name="dialog_title_window_animations">Select window speed</string>
-    
-    <string name="title_transition_animations">Transition animations</string>
-    <string name="summary_transition_animations">Speed of animations moving between screens</string>
-    <string name="dialog_title_transition_animations">Select transition speed</string>
-    
-    <string name="title_fancy_ime_animations">Fancy input animations</string>
-    <string name="summary_on_fancy_ime_animations">Use fancier animations for input method windows</string>
-    <string name="summary_off_fancy_ime_animations">Use normal animations for input method windows</string>
-    
-    <string name="title_haptic_feedback">Haptic feedback</string>
-    <string name="summary_on_haptic_feedback">Use haptic feedback with user interaction</string>
-    <string name="summary_off_haptic_feedback">Use haptic feedback with user interaction</string>
-    
-    <string name="title_font_size">Font size</string>
-    <string name="summary_font_size">Overall size of fonts</string>
-    <string name="dialog_title_font_size">Select font size</string>
-    
-    <string name="title_end_button">End button behavior</string>
-    <string name="summary_end_button">Select End (red) button action</string>
-    <string name="dialog_title_end_button">Select End button</string>
-    
-    <string name="applications_title">Applications</string>
-    
-    <!-- Sound & display settings screen, compatibility mode check box label -->
-    <string name="compatibility_mode_title">Compatibility Mode</string>
-    <!-- Sound & display settings screen, compatibility mode option summary text when check box is selected -->
-    <string name="compatibility_mode_summary_on">Run older apps in Compatibility mode. This requires rebooting. </string>
-    <!-- Sound & display settings screen, compatibility mode option summary text when check box is clear -->
-    <string name="compatibility_mode_summary_off">Run older apps in Compatibility mode. This requires rebooting. </string>
-</resources>
diff --git a/apps/SpareParts/res/xml/spare_parts.xml b/apps/SpareParts/res/xml/spare_parts.xml
deleted file mode 100644
index 0d06e26..0000000
--- a/apps/SpareParts/res/xml/spare_parts.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
- * Copyright 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.
- */
--->
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <PreferenceCategory
-        android:title="@string/device_info_title">
-
-        <PreferenceScreen android:key="battery_history_settings"
-                android:title="@string/title_battery_history" 
-                android:summary="@string/summary_battery_history">
-            <intent android:action="android.intent.action.MAIN"
-                    android:targetPackage="com.android.settings"
-                    android:targetClass="com.android.settings.fuelgauge.PowerUsageSummary" />
-        </PreferenceScreen>
-        
-        <PreferenceScreen android:key="battery_information_settings"
-                android:title="@string/title_battery_information" 
-                android:summary="@string/summary_battery_information">
-            <intent android:action="android.intent.action.MAIN"
-                    android:targetPackage="com.android.settings"
-                    android:targetClass="com.android.settings.BatteryInfo" />
-        </PreferenceScreen>
-        
-        <PreferenceScreen android:key="usage_statistics_settings"
-                android:title="@string/title_usage_statistics" 
-                android:summary="@string/summary_usage_statistics">
-            <intent android:action="android.intent.action.MAIN"
-                    android:targetPackage="com.android.settings"
-                    android:targetClass="com.android.settings.UsageStats" />
-        </PreferenceScreen>
-        
-    </PreferenceCategory>
-            
-    <PreferenceCategory
-        android:title="@string/general_title">
-        
-        <ListPreference
-                android:key="window_animations"
-                android:title="@string/title_window_animations"
-                android:summary="@string/summary_window_animations"
-                android:entries="@array/entries_animations"
-                android:entryValues="@array/entryvalues_animations"
-                android:dialogTitle="@string/dialog_title_window_animations" />
-                
-        <ListPreference
-                android:key="transition_animations"
-                android:title="@string/title_transition_animations"
-                android:summary="@string/summary_transition_animations"
-                android:entries="@array/entries_animations"
-                android:entryValues="@array/entryvalues_animations"
-                android:dialogTitle="@string/dialog_title_transition_animations" />
-        
-        <CheckBoxPreference 
-            android:key="fancy_ime_animations" 
-            android:title="@string/title_fancy_ime_animations" 
-            android:summaryOn="@string/summary_on_fancy_ime_animations"
-            android:summaryOff="@string/summary_off_fancy_ime_animations"/>
-        
-        <ListPreference
-                android:key="font_size"
-                android:title="@string/title_font_size"
-                android:summary="@string/summary_font_size"
-                android:entries="@array/entries_font_size"
-                android:entryValues="@array/entryvalues_font_size"
-                android:dialogTitle="@string/dialog_title_font_size" />
-        
-        <ListPreference
-                android:key="end_button"
-                android:title="@string/title_end_button"
-                android:summary="@string/summary_end_button"
-                android:entries="@array/entries_end_button"
-                android:entryValues="@array/entryvalues_end_button"
-                android:dialogTitle="@string/dialog_title_end_button" />
-        
-        <CheckBoxPreference 
-            android:key="haptic_feedback" 
-            android:title="@string/title_haptic_feedback" 
-            android:summaryOn="@string/summary_on_haptic_feedback"
-            android:summaryOff="@string/summary_off_haptic_feedback"/>
-        
-
-        <CheckBoxPreference
-                android:key="compatibility_mode"
-                android:title="@string/compatibility_mode_title"
-                android:summaryOn="@string/compatibility_mode_summary_on"
-                android:summaryOff="@string/compatibility_mode_summary_off" />
-    </PreferenceCategory>
-
-</PreferenceScreen>
diff --git a/apps/SpareParts/src/com/android/spare_parts/SpareParts.java b/apps/SpareParts/src/com/android/spare_parts/SpareParts.java
deleted file mode 100644
index 099f27a..0000000
--- a/apps/SpareParts/src/com/android/spare_parts/SpareParts.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/* //device/apps/Settings/src/com/android/settings/Keyguard.java
-**
-** Copyright 2006, 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.spare_parts;
-
-import android.app.ActivityManagerNative;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
-import android.preference.PreferenceGroup;
-import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.IWindowManager;
-
-import java.util.List;
-
-public class SpareParts extends PreferenceActivity
-        implements Preference.OnPreferenceChangeListener,
-        SharedPreferences.OnSharedPreferenceChangeListener {
-    private static final String TAG = "SpareParts";
-
-    private static final String BATTERY_HISTORY_PREF = "battery_history_settings";
-    private static final String BATTERY_INFORMATION_PREF = "battery_information_settings";
-    private static final String USAGE_STATISTICS_PREF = "usage_statistics_settings";
-    
-    private static final String WINDOW_ANIMATIONS_PREF = "window_animations";
-    private static final String TRANSITION_ANIMATIONS_PREF = "transition_animations";
-    private static final String FANCY_IME_ANIMATIONS_PREF = "fancy_ime_animations";
-    private static final String HAPTIC_FEEDBACK_PREF = "haptic_feedback";
-    private static final String FONT_SIZE_PREF = "font_size";
-    private static final String END_BUTTON_PREF = "end_button";
-    private static final String KEY_COMPATIBILITY_MODE = "compatibility_mode";
-
-    private final Configuration mCurConfig = new Configuration();
-    
-    private ListPreference mWindowAnimationsPref;
-    private ListPreference mTransitionAnimationsPref;
-    private CheckBoxPreference mFancyImeAnimationsPref;
-    private CheckBoxPreference mHapticFeedbackPref;
-    private ListPreference mFontSizePref;
-    private ListPreference mEndButtonPref;
-    private CheckBoxPreference mCompatibilityMode;
-
-    private IWindowManager mWindowManager;
-
-    public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
-            PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
-        
-        Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
-        if (preference == null) {
-            return false;
-        }
-        
-        Intent intent = preference.getIntent();
-        if (intent != null) {
-            // Find the activity that is in the system image
-            PackageManager pm = context.getPackageManager();
-            List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
-            int listSize = list.size();
-            for (int i = 0; i < listSize; i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
-                        != 0) {
-                    
-                    // Replace the intent with this specific activity
-                    preference.setIntent(new Intent().setClassName(
-                            resolveInfo.activityInfo.packageName,
-                            resolveInfo.activityInfo.name));
-                    
-                    return true;
-                }
-            }
-        }
-
-        // Did not find a matching activity, so remove the preference
-        parentPreferenceGroup.removePreference(preference);
-        
-        return true;
-    }
-    
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        addPreferencesFromResource(R.xml.spare_parts);
-
-        PreferenceScreen prefSet = getPreferenceScreen();
-        
-        mWindowAnimationsPref = (ListPreference) prefSet.findPreference(WINDOW_ANIMATIONS_PREF);
-        mWindowAnimationsPref.setOnPreferenceChangeListener(this);
-        mTransitionAnimationsPref = (ListPreference) prefSet.findPreference(TRANSITION_ANIMATIONS_PREF);
-        mTransitionAnimationsPref.setOnPreferenceChangeListener(this);
-        mFancyImeAnimationsPref = (CheckBoxPreference) prefSet.findPreference(FANCY_IME_ANIMATIONS_PREF);
-        mHapticFeedbackPref = (CheckBoxPreference) prefSet.findPreference(HAPTIC_FEEDBACK_PREF);
-        mFontSizePref = (ListPreference) prefSet.findPreference(FONT_SIZE_PREF);
-        mFontSizePref.setOnPreferenceChangeListener(this);
-        mEndButtonPref = (ListPreference) prefSet.findPreference(END_BUTTON_PREF);
-        mEndButtonPref.setOnPreferenceChangeListener(this);
-        mCompatibilityMode = (CheckBoxPreference) findPreference(KEY_COMPATIBILITY_MODE);
-        mCompatibilityMode.setPersistent(false);
-        mCompatibilityMode.setChecked(Settings.System.getInt(getContentResolver(),
-                Settings.System.COMPATIBILITY_MODE, 1) != 0);
-
-        mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
-        
-        final PreferenceGroup parentPreference = getPreferenceScreen();
-        updatePreferenceToSpecificActivityOrRemove(this, parentPreference,
-                BATTERY_HISTORY_PREF, 0);
-        updatePreferenceToSpecificActivityOrRemove(this, parentPreference,
-                BATTERY_INFORMATION_PREF, 0);
-        updatePreferenceToSpecificActivityOrRemove(this, parentPreference,
-                USAGE_STATISTICS_PREF, 0);
-        
-        parentPreference.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
-    }
-
-    private void updateToggles() {
-        mFancyImeAnimationsPref.setChecked(Settings.System.getInt(
-                getContentResolver(), 
-                Settings.System.FANCY_IME_ANIMATIONS, 0) != 0);
-        mHapticFeedbackPref.setChecked(Settings.System.getInt(
-                getContentResolver(), 
-                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) != 0);
-    }
-    
-    public boolean onPreferenceChange(Preference preference, Object objValue) {
-        if (preference == mWindowAnimationsPref) {
-            writeAnimationPreference(0, objValue);
-        } else if (preference == mTransitionAnimationsPref) {
-            writeAnimationPreference(1, objValue);
-        } else if (preference == mFontSizePref) {
-            writeFontSizePreference(objValue);
-        } else if (preference == mEndButtonPref) {
-            writeEndButtonPreference(objValue);
-        }
-        // always let the preference setting proceed.
-        return true;
-    }
-
-    @Override
-    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
-        if (preference == mCompatibilityMode) {
-            Settings.System.putInt(getContentResolver(),
-                    Settings.System.COMPATIBILITY_MODE,
-                    mCompatibilityMode.isChecked() ? 1 : 0);
-            return true;
-        }
-        return false;
-    }
-
-    public void writeAnimationPreference(int which, Object objValue) {
-        try {
-            float val = Float.parseFloat(objValue.toString());
-            mWindowManager.setAnimationScale(which, val);
-        } catch (NumberFormatException e) {
-        } catch (RemoteException e) {
-        }
-    }
-    
-    public void writeFontSizePreference(Object objValue) {
-        try {
-            mCurConfig.fontScale = Float.parseFloat(objValue.toString());
-            ActivityManagerNative.getDefault().updateConfiguration(mCurConfig);
-        } catch (RemoteException e) {
-        }
-    }
-    
-    public void writeEndButtonPreference(Object objValue) {
-        try {
-            int val = Integer.parseInt(objValue.toString());
-            Settings.System.putInt(getContentResolver(),
-                    Settings.System.END_BUTTON_BEHAVIOR, val);
-        } catch (NumberFormatException e) {
-        }
-    }
-    
-    int floatToIndex(float val, int resid) {
-        String[] indices = getResources().getStringArray(resid);
-        float lastVal = Float.parseFloat(indices[0]);
-        for (int i=1; i<indices.length; i++) {
-            float thisVal = Float.parseFloat(indices[i]);
-            if (val < (lastVal + (thisVal-lastVal)*.5f)) {
-                return i-1;
-            }
-            lastVal = thisVal;
-        }
-        return indices.length-1;
-    }
-    
-    public void readAnimationPreference(int which, ListPreference pref) {
-        try {
-            float scale = mWindowManager.getAnimationScale(which);
-            pref.setValueIndex(floatToIndex(scale,
-                    R.array.entryvalues_animations));
-        } catch (RemoteException e) {
-        }
-    }
-    
-    public void readFontSizePreference(ListPreference pref) {
-        try {
-            mCurConfig.updateFrom(
-                ActivityManagerNative.getDefault().getConfiguration());
-        } catch (RemoteException e) {
-        }
-        pref.setValueIndex(floatToIndex(mCurConfig.fontScale,
-                R.array.entryvalues_font_size));
-    }
-    
-    public void readEndButtonPreference(ListPreference pref) {
-        try {
-            pref.setValueIndex(Settings.System.getInt(getContentResolver(),
-                    Settings.System.END_BUTTON_BEHAVIOR));
-        } catch (SettingNotFoundException e) {
-        }
-    }
-    
-    public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
-        if (FANCY_IME_ANIMATIONS_PREF.equals(key)) {
-            Settings.System.putInt(getContentResolver(),
-                    Settings.System.FANCY_IME_ANIMATIONS,
-                    mFancyImeAnimationsPref.isChecked() ? 1 : 0);
-        } else if (HAPTIC_FEEDBACK_PREF.equals(key)) {
-            Settings.System.putInt(getContentResolver(),
-                    Settings.System.HAPTIC_FEEDBACK_ENABLED,
-                    mHapticFeedbackPref.isChecked() ? 1 : 0);
-        }
-    }
-    
-    @Override
-    public void onResume() {
-        super.onResume();
-        readAnimationPreference(0, mWindowAnimationsPref);
-        readAnimationPreference(1, mTransitionAnimationsPref);
-        readFontSizePreference(mFontSizePref);
-        readEndButtonPreference(mEndButtonPref);
-        updateToggles();
-    }
-}
diff --git a/build/tools/mk_sdk_repo_xml.sh b/build/tools/mk_sdk_repo_xml.sh
index f7bbec1..56f426b 100755
--- a/build/tools/mk_sdk_repo_xml.sh
+++ b/build/tools/mk_sdk_repo_xml.sh
@@ -45,7 +45,7 @@
 shift
 
 # Get XML:NS for SDK from the schema
-XMLNS=$(sed -n '/xmlns:.*schemas.android.com\/sdk\/android\//s/.*"\(.*\)".*/\1/p' "$SCHEMA")
+XMLNS=$(sed -n '/xmlns:sdk="/s/.*"\(.*\)".*/\1/p' "$SCHEMA")
 [[ -z "$XMLNS" ]] && error "Failed to find xmlns:sdk in $SCHEMA."
 echo "## Using xmlns:sdk=$XMLNS"
 
diff --git a/ide/eclipse/.classpath b/ide/eclipse/.classpath
index 1323287..47d94bb 100644
--- a/ide/eclipse/.classpath
+++ b/ide/eclipse/.classpath
@@ -11,7 +11,7 @@
 	<classpathentry kind="src" path="packages/apps/Email/src"/>
 	<classpathentry kind="src" path="packages/apps/Email/emailcommon/src"/>
 	<classpathentry kind="src" path="packages/apps/Exchange/src"/>
-	<classpathentry kind="src" path="packages/apps/Gallery3D/src"/>
+	<classpathentry kind="src" path="packages/apps/Gallery3D/new3d/src"/>
 	<classpathentry kind="src" path="packages/apps/HTMLViewer/src"/>
 	<classpathentry kind="src" path="packages/apps/Launcher2/src"/>
 	<classpathentry kind="src" path="packages/apps/Mms/src"/>
diff --git a/ndk/platforms/android-9/samples/native-activity/Android.mk b/ndk/platforms/android-9/samples/native-activity/Android.mk
index ea961ca..73b3d87 100644
--- a/ndk/platforms/android-9/samples/native-activity/Android.mk
+++ b/ndk/platforms/android-9/samples/native-activity/Android.mk
@@ -44,7 +44,7 @@
 
 LOCAL_SHARED_LIBRARIES := liblog libandroid libEGL libGLESv1_CM
 
-LOCAL_PRELINK_MODULE := false
+
 
 LOCAL_MODULE := libnative-activity
 
diff --git a/pdk/docs/compatibility/downloads.jd b/pdk/docs/compatibility/downloads.jd
index 27a2109..79ad81b 100644
--- a/pdk/docs/compatibility/downloads.jd
+++ b/pdk/docs/compatibility/downloads.jd
@@ -11,7 +11,7 @@
 </p>
 <ul>
   <li><a href="{@docRoot}compatibility/android-2.3.3-cdd.pdf">Android 2.3.3 Compatibility Definition Document (CDD)</a></li>
-  <li><a href="http://dl.google.com/dl/android/cts/android-cts-2.3_r1-x86.zip">Android 2.3 R1 Compatibility Test Suite (CTS)</a></li>
+  <li><a href="http://dl.google.com/dl/android/cts/android-cts-2.3_r2-x86.zip">Android 2.3 R2 Compatibility Test Suite (CTS)</a></li>
 </ul>
 
 <h2>Android 2.2</h2>
@@ -21,7 +21,7 @@
 </p>
 <ul>
   <li><a href="{@docRoot}compatibility/android-2.2-cdd.pdf">Android 2.2 Compatibility Definition Document (CDD)</a></li>
-  <li><a href="http://dl.google.com/dl/android/cts/android-cts-2.2_r4-x86.zip">Android 2.2 R4 Compatibility Test Suite (CTS)</a></li>
+  <li><a href="http://dl.google.com/dl/android/cts/android-cts-2.2_r5-x86.zip">Android 2.2 R5 Compatibility Test Suite (CTS)</a></li>
 </ul>
 
 <h2>Android 2.1</h2>
@@ -41,6 +41,7 @@
 in the 'donut' branch in the open-source tree.
 <ul>
   <li><a href="{@docRoot}compatibility/android-1.6-cdd.pdf">Android 1.6 Compatibility Definition Document (CDD)</a></li>
+  <li><a href="http://dl.google.com/dl/android/cts/android-cts-1.6_r1-x86.zip">Android 1.6 R1 Compatibility Test Suite (CTS)</a></li>
 </ul>
 
 <h2>Compatibility Test Suite Manual</h2>
diff --git a/pdk/docs/porting/bluetooth.jd b/pdk/docs/porting/bluetooth.jd
index c792bd2..03b2d94 100755
--- a/pdk/docs/porting/bluetooth.jd
+++ b/pdk/docs/porting/bluetooth.jd
@@ -17,12 +17,15 @@
 </div>
 </div>
 
-<p>Android's Bluetooth stack uses BlueZ for GAP, SDP, and RFCOMM profiles, and
-is a SIG-qualified Bluetooth stack. </p>
+<p>Android's Bluetooth stack uses BlueZ as the host stack.</p>
 
 <p>Bluez is GPL licensed, so the Android framework interacts with userspace bluez code through D-BUS IPC to avoid proprietary code.</p>
 
-<p>Headset and Handsfree (v1.5) profiles are implemented in the Android framework and are both tightly coupled with the Phone App. These profiles are also SIG qualified.</p>
+<p>The qualification notes mentioned below are example qualifications of the particular device in question. Each company has to re-qualify their product with Bluetooth SIG even if no changes are made to the Bluetooth stack.</p>
+
+<p>Headset and Handsfree (v1.5) profiles are implemented in the Android framework and are both tightly coupled with the Phone App. Profiles like OPP and PBAP are based on java obex. These profiles open a rfcomm socket connection into Bluez kernel bypassing the Bluez userspace stack.</p>
+
+<p> Profiles like A2DP, AVRCP, HID, PAN and other bluetooth functionality like pairing and scanning use the Bluez userspace stack.</p>
 
 <p>The diagram below offers a library-oriented view of the Bluetooth stack. Click <a href="bluetooth/bluetooth_process.html">Bluetooth Process Diagram</a> for a process-oriented view.</p>
 
@@ -79,7 +82,7 @@
 </pre>
 
 <p><strong>Deamon Logs</strong></p>
-<p>Deamon logs for <code>hcid</code> (<code>STDOUT</code>) and <code>hciattach</code> (<code>STDERR</code>) are sent to <code>/dev/null</code> by default. Edit <code>init.rc</code></span> and <code>init.PLATFORM.rc</code></span> to run these daemons under <code>logwrapper</code>, which redirects output to <code>logcat</code>.</p>
+<p>Deamon logs for <code>bluetoothd</code> (<code>STDOUT</code>) and <code>hciattach</code> (<code>STDERR</code>) are sent to <code>/dev/null</code> by default. Edit <code>init.rc</code></span> and <code>init.PLATFORM.rc</code></span> to run these daemons under <code>logwrapper</code>, which redirects output to <code>logcat</code>.</p>
 <p><strong>hciconfig -a and hcitool</strong></p>
 <p>If you compile your own system.img for Android, and <code>hciconfig -a</code> works but <code>hcitool</code> scan doesn't, try installing the firmware for the Bluetooth chipset. This firmware isn't yet available in the open source codebase, but you can <code>adb pull</code> and then <code>adb push</code>it from a stock T-Mobile G1 (located in <code>/etc/firmware/brf6300.bin</code>).<br />
   <a name="androidBluetoothTools"></a></p>
@@ -112,7 +115,7 @@
     </li>
   </ul>
 </ul>
-<h5>Qualifications</h5>
+<h5>Qualifications for HTC G1 product. Each company reusing this software version has to re-qualify with Bluetooth SIG</h5>
 <ul>
   <li>QDID B014524: Host stack (SDP, L2CAP, GAP, RFCOMM, SPP)</li>
   <li>QDID B014624: EPL for HTC Dream (HSP, HFP)</li>
@@ -154,7 +157,7 @@
     <li>play/pause/stop/prev/next</li>
   </ul>
 </ul>
-<h4>Qualifications</h4>
+<h5>Qualifications for HTC Sapphire product. Each company reusing this software version has to re-qualify with Bluetooth SIG</h5>
 <ul>
   <li>QDID B015261: Host stack (SDP, L2CAP, GAP, RFCOMM, SPP, AVCTP, AVRCP, GAVDP, AVDTP, A2DP)</li>
   <li>QDID B015262: EPL for HTC Sapphire (HSP, HFP)</li>
@@ -224,6 +227,22 @@
   <li>Improved compatibility with headsets and car kits. </li>
 </ul>
 
+<h4>Android 2.3  release (Gingerbread)</h4>
+<h4>Platform features</h4>
+<ul>
+  <li>Based on Bluez 4.69 with Linux Kernel 2.6.35</li>
+  <li>No new profiles added.</li>
+  <li>Improved compatibility with headsets and car kits. </li>
+</ul>
+
+<h4>Android 3.0 release (Honeycomb)</h4>
+<h4>Platform features</h4>
+<ul>
+  <li>Based on Bluez 4.69 with Linux Kernel 2.6.36</li>
+  <li>HID and PAN (NAP and PANU role) are the new profiles added.
+  <li>Improved compatibility with headsets and car kits. </li>
+</ul>
+
 <h5>&nbsp;</h5>
 <h4>Future releases</h4>
 <p>This section offers a rough guide of which features the team is developing for the next release. This feature list may change without notice. It isn't possible to post scheduling advice to the mailing lists.</p>
@@ -234,20 +253,3 @@
   <li>Bluetooth Low Energy </li>
 </ul>
 
-<p><strong>Development Notes</strong></p>
-<ul>
-  <li><strong>HID Support <br />
-  </strong>Cupcake features some early work&#151;Bluez has an HID plugin, <code>external/bluez/utils/input/Android.mk</code>, which gets compiled. <br />
-    <br />
-You can interact directly with this plugin using <code>dbus-send</code></span> and <code>dbus-monitor</code>. While not officially supported, you should be able to connect and use a HID keyboard and mouse using the Bluez HID plugin API. Next steps include plumbing the plugin API in the Android Java framework and offering better support for HID input methods (new keymaps and mouse support).<br />
-  <br />
-  </li>
-  <li>  <strong>Tethering - DUN and PAN Support</strong><br />
-    Cupcake features some early work&#151;Bluez has has DUN and PAN daemons which get compiled and <code>external/bluez/utils/dun/Android.mk
-  external/bluez/utils/pan/Android.mk
-BNEP</code> support is compiled into the kernel with cupcake. <br />
-<br />
-While not officially supported, you should be able to run <code>dund</code> or <code>pand</code> daemons and, using <code>pppd</code> or <code>iptables</code>, test tethering support. Next steps include plubming the DBUS APIs to these daemons up into the Android Java framework and adding code to setup the network paths via <code>pppd</code> and / or <code>iptables</code>.<br />
-  <br />
-  </li>
-</ul>
diff --git a/pdk/docs/porting/images/androidBluetooth.gif b/pdk/docs/porting/images/androidBluetooth.gif
index e62f5a8..1c1aa3f 100755
--- a/pdk/docs/porting/images/androidBluetooth.gif
+++ b/pdk/docs/porting/images/androidBluetooth.gif
Binary files differ
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 915b56f..d591e68 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -312,15 +312,6 @@
         <activity android:name=".app.FragmentLayout$DetailsActivity"
                 android:enabled="@bool/atLeastHoneycomb" />
 
-        <activity android:name=".app.FragmentListCursorLoader"
-                android:label="@string/fragment_list_cursor_loader"
-                android:enabled="@bool/atLeastHoneycomb">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-
         <activity android:name=".app.FragmentListArray"
                 android:label="@string/fragment_list_array"
                 android:enabled="@bool/atLeastHoneycomb">
@@ -425,14 +416,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".support.app.FragmentListCursorLoaderSupport"
-                android:label="@string/fragment_list_cursor_loader_support">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-
         <activity android:name=".support.app.FragmentListArraySupport"
                 android:label="@string/fragment_list_array_support">
             <intent-filter>
@@ -475,8 +458,34 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".support.app.FragmentPagerSupport"
+                android:label="@string/fragment_pager_support">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <!-- Loader Samples -->
 
+        <activity android:name=".app.LoaderCursor"
+                android:label="@string/loader_cursor"
+                android:enabled="@bool/atLeastHoneycomb">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".app.LoaderCustom"
+                android:label="@string/loader_custom"
+                android:enabled="@bool/atLeastHoneycomb">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
 <!-- BEGIN_INCLUDE(loader_throttle) -->
         <activity android:name=".app.LoaderThrottle"
                 android:label="@string/loader_throttle"
@@ -493,6 +502,14 @@
 
         <!-- Fragment Support Samples -->
 
+        <activity android:name=".support.app.LoaderCursorSupport"
+                android:label="@string/loader_cursor_support">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".support.app.LoaderThrottleSupport"
                 android:label="@string/loader_throttle_support">
             <intent-filter>
@@ -522,7 +539,7 @@
 
         <!-- Service Samples -->
 
-        <service android:name=".app.LocalService" />
+        <service android:name=".app.LocalService" android:stopWithTask="true" />
 
         <activity android:name=".app.LocalServiceActivities$Controller"
                 android:label="@string/activity_local_service_controller"
@@ -1010,6 +1027,16 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".content.ResourcesWidthAndHeight"
+                android:label="@string/activity_resources_width_and_height"
+                android:enabled="@bool/atLeastIceCreamSandwich">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+                <category android:name="android.intent.category.EMBED" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".content.ReadAsset" android:label="@string/activity_read_asset">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/samples/ApiDemos/res/layout-h420dp/resources_height.xml b/samples/ApiDemos/res/layout-h420dp/resources_height.xml
new file mode 100644
index 0000000..115a731
--- /dev/null
+++ b/samples/ApiDemos/res/layout-h420dp/resources_height.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Part of resources_width_and_height that varies based on height. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
+            android:orientation="vertical">
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#8000ff00">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#80ff0000">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#8000ff00">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#80ff0000">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+    </LinearLayout>
+</merge>
diff --git a/samples/ApiDemos/res/layout-h670dp/resources_height.xml b/samples/ApiDemos/res/layout-h670dp/resources_height.xml
new file mode 100644
index 0000000..3130a91
--- /dev/null
+++ b/samples/ApiDemos/res/layout-h670dp/resources_height.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Part of resources_width_and_height that varies based on height. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
+            android:orientation="vertical">
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#8000ff00">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#80ff0000">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#8000ff00">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#80ff0000">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#8000ff00">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#80ff0000">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#8000ff00">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#80ff0000">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+    </LinearLayout>
+</merge>
diff --git a/samples/ApiDemos/res/layout-w420dp/resources_width.xml b/samples/ApiDemos/res/layout-w420dp/resources_width.xml
new file mode 100644
index 0000000..1edffe3
--- /dev/null
+++ b/samples/ApiDemos/res/layout-w420dp/resources_width.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Part of resources_width_and_height that varies based on width. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
+            android:orientation="horizontal">
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w420dp Width\n#1">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w420dp Width\n#2">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w420dp Width\n#3">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w420dp Width\n#4">
+        </TextView>
+    </LinearLayout>
+</merge>
diff --git a/samples/ApiDemos/res/layout-w720dp/resources_width.xml b/samples/ApiDemos/res/layout-w720dp/resources_width.xml
new file mode 100644
index 0000000..8625c82
--- /dev/null
+++ b/samples/ApiDemos/res/layout-w720dp/resources_width.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Part of resources_width_and_height that varies based on width. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
+            android:orientation="horizontal">
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w720dp Width\n#1">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w720dp Width\n#2">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w720dp Width\n#3">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w720dp Width\n#4">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w720dp Width\n#5">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w720dp Width\n#6">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w720dp Width\n#7">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="w720dp Width\n#8">
+        </TextView>
+    </LinearLayout>
+</merge>
diff --git a/samples/ApiDemos/res/layout/fragment_pager.xml b/samples/ApiDemos/res/layout/fragment_pager.xml
new file mode 100644
index 0000000..3867f46
--- /dev/null
+++ b/samples/ApiDemos/res/layout/fragment_pager.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- Top-level content view for the simple fragment sample. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical" android:padding="4dip"
+        android:gravity="center_horizontal"
+        android:layout_width="match_parent" android:layout_height="match_parent">
+
+    <android.support.v4.app.FragmentPager
+            android:id="@+id/pager"
+            android:layout_width="match_parent"
+            android:layout_height="0px"
+            android:layout_weight="1">
+    </android.support.v4.app.FragmentPager>
+
+    <Button android:id="@+id/new_fragment"
+        android:layout_width="wrap_content" android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/new_fragment">
+        <requestFocus />
+    </Button>
+</LinearLayout>
diff --git a/samples/ApiDemos/res/layout/fragment_pager_list.xml b/samples/ApiDemos/res/layout/fragment_pager_list.xml
new file mode 100644
index 0000000..bbe7b1d
--- /dev/null
+++ b/samples/ApiDemos/res/layout/fragment_pager_list.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:drawable/gallery_thumb">
+
+    <TextView android:id="@+id/text"
+        android:layout_width="match_parent" android:layout_height="wrap_content"
+        android:gravity="center_vertical|center_horizontal"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@string/hello_world"/>
+
+    <!-- The frame layout is here since we will be showing either
+    the empty view or the list view.  -->
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1" >
+        <!-- Here is the list. Since we are using a ListActivity, we
+             have to call it "@android:id/list" so ListActivity will
+             find it -->
+        <ListView android:id="@android:id/list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:drawSelectorOnTop="false"/>
+
+        <!-- Here is the view to show if the list is emtpy -->
+        <TextView android:id="@android:id/empty"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="No items."/>
+
+    </FrameLayout>
+
+</LinearLayout>
diff --git a/samples/ApiDemos/res/layout/resources_height.xml b/samples/ApiDemos/res/layout/resources_height.xml
new file mode 100644
index 0000000..1e219bf
--- /dev/null
+++ b/samples/ApiDemos/res/layout/resources_height.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Part of resources_width_and_height that varies based on height. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
+            android:orientation="vertical">
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#8000ff00">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+        <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+                android:layout_weight="1" android:padding="4dp"
+                android:background="#80ff0000">
+            <include layout="@layout/resources_width" />
+        </FrameLayout>
+    </LinearLayout>
+</merge>
diff --git a/samples/ApiDemos/res/layout/resources_width.xml b/samples/ApiDemos/res/layout/resources_width.xml
new file mode 100644
index 0000000..a05a6e8
--- /dev/null
+++ b/samples/ApiDemos/res/layout/resources_width.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Part of resources_width_and_height that varies based on width. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
+            android:orientation="horizontal">
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="Default Width\n#1">
+        </TextView>
+        <TextView android:layout_width="0px" android:layout_height="match_parent"
+                android:layout_weight="1" android:gravity="center_horizontal"
+                android:layout_marginLeft="4dp" android:layout_marginRight="4dp"
+                android:background="#800000ff" android:text="Default Width\n#2">
+        </TextView>
+    </LinearLayout>
+</merge>
diff --git a/samples/ApiDemos/res/layout/resources_width_and_height.xml b/samples/ApiDemos/res/layout/resources_width_and_height.xml
new file mode 100644
index 0000000..8649dbe
--- /dev/null
+++ b/samples/ApiDemos/res/layout/resources_width_and_height.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Demonstrates using -wNNNdp and -hNNNdp resource configs. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent" android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView android:layout_width="match_parent" android:layout_height="wrap_content"
+        android:layout_weight="0" android:gravity="center_horizontal"
+        android:paddingTop="8dp" android:paddingBottom="8dp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@string/resources_width_and_height_description"/>
+
+    <FrameLayout android:layout_width="match_parent" android:layout_height="0px"
+        android:layout_weight="1"
+        android:background="@android:drawable/gallery_thumb">
+        <include layout="@layout/resources_height" />
+    </FrameLayout>
+
+</LinearLayout>
diff --git a/samples/ApiDemos/res/layout/save_restore_state.xml b/samples/ApiDemos/res/layout/save_restore_state.xml
index 4b489ed..0871dbb 100644
--- a/samples/ApiDemos/res/layout/save_restore_state.xml
+++ b/samples/ApiDemos/res/layout/save_restore_state.xml
@@ -17,42 +17,42 @@
 <!-- Demonstrates saving and restoring activity state.
      See corresponding Java code com.android.sdk.app.SaveRestoreState.java. -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent" android:layout_height="match_parent">
+    <LinearLayout android:orientation="vertical" android:padding="4dip"
+        android:layout_width="match_parent" android:layout_height="wrap_content">
 
-    <TextView android:id="@+id/msg"
-        android:layout_width="match_parent" android:layout_height="wrap_content"
-        android:layout_weight="0"
-        android:paddingBottom="4dip" />
+        <TextView android:id="@+id/msg"
+            android:layout_width="match_parent" android:layout_height="wrap_content"
+            android:layout_weight="0" android:textAppearance="?android:attr/textAppearanceMedium"
+            android:paddingBottom="4dip" />
 
-    <TextView
-        android:layout_width="match_parent" android:layout_height="wrap_content"
-        android:layout_weight="0"
-        android:paddingBottom="4dip"
-        android:text="@string/saves_state"/>
+        <TextView
+            android:layout_width="match_parent" android:layout_height="wrap_content"
+            android:layout_weight="0" android:paddingBottom="4dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/saves_state"/>
 
-    <EditText android:id="@+id/saved"
-        android:layout_width="match_parent" android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:background="@drawable/green"
-        android:text="@string/initial_text"
-        android:freezesText="true">
-        <requestFocus />
-    </EditText>
+        <EditText android:id="@+id/saved"
+            android:layout_width="match_parent" android:layout_height="wrap_content"
+            android:layout_weight="1" android:background="@drawable/green"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/initial_text"
+            android:freezesText="true">
+        </EditText>
 
-    <TextView
-        android:layout_width="match_parent" android:layout_height="wrap_content"
-        android:layout_weight="0"
-        android:paddingTop="8dip"
-        android:paddingBottom="4dip"
-        android:text="@string/no_saves_state"/>
+        <TextView
+            android:layout_width="match_parent" android:layout_height="wrap_content"
+            android:layout_weight="0" android:paddingTop="8dip" android:paddingBottom="4dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/no_saves_state"/>
 
-    <EditText 
-        android:layout_width="match_parent" android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:background="@drawable/red"
-        android:text="@string/initial_text">
-    </EditText>
+        <EditText
+            android:layout_width="match_parent" android:layout_height="wrap_content"
+            android:layout_weight="1" android:background="@drawable/red"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/initial_text">
+        </EditText>
 
-</LinearLayout>
-
+    </LinearLayout>
+</ScrollView>
diff --git a/samples/ApiDemos/res/values-v13/bools.xml b/samples/ApiDemos/res/values-v13/bools.xml
new file mode 100644
index 0000000..1b8909f
--- /dev/null
+++ b/samples/ApiDemos/res/values-v13/bools.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<resources>
+    <!-- True if running under Ice Cream Sandwich or later. -->
+    <bool name="atLeastIceCreamSandwich">true</bool>
+</resources>
diff --git a/samples/ApiDemos/res/values/bools.xml b/samples/ApiDemos/res/values/bools.xml
index 0faffb3..cf85197 100644
--- a/samples/ApiDemos/res/values/bools.xml
+++ b/samples/ApiDemos/res/values/bools.xml
@@ -19,4 +19,8 @@
          API level.  The default value is false; an alternative value
          for Honeycomb is true. -->
     <bool name="atLeastHoneycomb">false</bool>
+    <!-- This resource is true if running under at least Ice Cream Sandwich's
+         API level.  The default value is false; an alternative value
+         for Ice Cream Sandwich is true. -->
+    <bool name="atLeastIceCreamSandwich">false</bool>
 </resources>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index d86b194..d7858f0 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -134,8 +134,6 @@
 
     <string name="fragment_list_array">App/Fragment/List Array</string>
 
-    <string name="fragment_list_cursor_loader">App/Fragment/List Cursor Loader</string>
-
     <string name="fragment_menu">App/Fragment/Menu</string>
     <string name="fragment_menu_msg">Build menus from two fragments, allowing
         you to hide them to remove them..</string>
@@ -168,8 +166,6 @@
 
     <string name="fragment_list_array_support">Support/App/Fragment/List Array</string>
 
-    <string name="fragment_list_cursor_loader_support">Support/App/Fragment/List Cursor Loader</string>
-
     <string name="fragment_menu_support">Support/App/Fragment/Menu</string>
 
     <string name="fragment_retain_instance_support">Support/App/Fragment/Retain Instance</string>
@@ -178,7 +174,15 @@
 
     <string name="fragment_stack_support">Support/App/Fragment/Stack</string>
 
+    <string name="fragment_pager_support">Support/App/Fragment/Pager</string>
+
+    <string name="loader_cursor">App/Loader/Cursor</string>
+
+    <string name="loader_custom">App/Loader/Custom</string>
+
     <string name="loader_throttle">App/Loader/Throttle</string>
+
+    <string name="loader_cursor_support">Support/App/Loader/Cursor</string>
     
     <string name="loader_throttle_support">Support/Loader/Throttle</string>
     
@@ -348,13 +352,17 @@
     <string name="styled_text">Plain, <b>bold</b>, <i>italic</i>, <b><i>bold-italic</i></b></string>
     <string name="styled_text_prog">Assigned programmatically:</string>
 
+    <string name="activity_resources_width_and_height">Content/Resources/Width and Height</string>
+    <string name="resources_width_and_height_description">The layouts below use -wNNNdp and
+        -hNNNdp to select between different versions based on the size of the screen.</string>
+
     <string name="activity_read_asset">Content/Assets/Read Asset</string>
 
     <string name="activity_themes">Content/Resources/Themes</string>
     <string name="activity_resources">Content/Resources/Resources</string>
 
     <string name="activity_pick_contact">Content/Provider/Pick Contact</string>
-    <string name="pick_contact_msg">Invoke Contacts to pick varius kinds of
+    <string name="pick_contact_msg">Invoke Contacts to pick various kinds of
         contact data.  None of these require that the caller hold the
         READ_CONTACTS permission.</string>
     <string name="pick_contact">Pick a Contact</string>
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java b/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
index 9f84be1..123e369 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
@@ -66,7 +66,7 @@
     
     void invokeMethod(Method method, Object[] args) {
         try {
-            mStartForeground.invoke(this, mStartForegroundArgs);
+            method.invoke(this, args);
         } catch (InvocationTargetException e) {
             // Should not happen.
             Log.w("ApiDemos", "Unable to invoke method", e);
@@ -103,15 +103,7 @@
         // If we have the new stopForeground API, then use it.
         if (mStopForeground != null) {
             mStopForegroundArgs[0] = Boolean.TRUE;
-            try {
-                mStopForeground.invoke(this, mStopForegroundArgs);
-            } catch (InvocationTargetException e) {
-                // Should not happen.
-                Log.w("ApiDemos", "Unable to invoke stopForeground", e);
-            } catch (IllegalAccessException e) {
-                // Should not happen.
-                Log.w("ApiDemos", "Unable to invoke stopForeground", e);
-            }
+            invokeMethod(mStopForeground, mStopForegroundArgs);
             return;
         }
         
@@ -130,10 +122,10 @@
                     mStartForegroundSignature);
             mStopForeground = getClass().getMethod("stopForeground",
                     mStopForegroundSignature);
+            return;
         } catch (NoSuchMethodException e) {
             // Running on an older platform.
             mStartForeground = mStopForeground = null;
-            return;
         }
         try {
             mSetForeground = getClass().getMethod("setForeground",
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.java b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
similarity index 95%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.java
rename to samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
index 57604f0..a8ac0d4 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
@@ -38,17 +38,17 @@
 import android.widget.SearchView.OnQueryTextListener;
 
 /**
- * Demonstration of more complex use if a ListFragment, including showing
- * an empty view and loading progress.
+ * Demonstration of the use of a CursorLoader to load and display contacts
+ * data in a fragment.
  */
-public class FragmentListCursorLoader extends Activity {
+public class LoaderCursor extends Activity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         FragmentManager fm = getFragmentManager();
-        
+
         // Create the list fragment and add it as our sole content.
         if (fm.findFragmentById(android.R.id.content) == null) {
             CursorLoaderListFragment list = new CursorLoaderListFragment();
@@ -82,7 +82,10 @@
                     new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
                     new int[] { android.R.id.text1, android.R.id.text2 }, 0);
             setListAdapter(mAdapter);
-            
+
+            // Start out with a progress indicator.
+            setListShown(false);
+
             // Prepare the loader.  Either re-connect with an existing one,
             // or start a new one.
             getLoaderManager().initLoader(0, null, this);
@@ -111,7 +114,7 @@
             // Don't care about this.
             return true;
         }
-        
+
         @Override public void onListItemClick(ListView l, View v, int position, long id) {
             // Insert desired behavior here.
             Log.i("FragmentComplexList", "Item clicked: " + id);
@@ -154,6 +157,9 @@
             // Swap the new cursor in.  (The framework will take care of closing the
             // old cursor once we return.)
             mAdapter.swapCursor(data);
+
+            // The list should now be shown.
+            setListShown(true);
         }
 
         public void onLoaderReset(Loader<Cursor> loader) {
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
new file mode 100644
index 0000000..883ab14
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2010 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.app;
+
+import com.example.android.apis.R;
+
+import java.io.File;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.app.ListFragment;
+import android.app.LoaderManager;
+import android.content.AsyncTaskLoader;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.Loader;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.SearchView;
+import android.widget.TextView;
+import android.widget.SearchView.OnQueryTextListener;
+
+/**
+ * Demonstration of the implementation of a custom Loader.
+ */
+public class LoaderCustom extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        FragmentManager fm = getFragmentManager();
+
+        // Create the list fragment and add it as our sole content.
+        if (fm.findFragmentById(android.R.id.content) == null) {
+            AppListFragment list = new AppListFragment();
+            fm.beginTransaction().add(android.R.id.content, list).commit();
+        }
+    }
+
+//BEGIN_INCLUDE(loader)
+    /**
+     * This class holds the per-item data in our Loader.
+     */
+    public static class AppEntry {
+        public AppEntry(AppListLoader loader, ApplicationInfo info) {
+            mLoader = loader;
+            mInfo = info;
+            mApkFile = new File(info.sourceDir);
+        }
+
+        public ApplicationInfo getApplicationInfo() {
+            return mInfo;
+        }
+
+        public String getLabel() {
+            return mLabel;
+        }
+
+        public Drawable getIcon() {
+            if (mIcon == null) {
+                if (mApkFile.exists()) {
+                    mIcon = mInfo.loadIcon(mLoader.mPm);
+                    return mIcon;
+                } else {
+                    mMounted = false;
+                }
+            } else if (!mMounted) {
+                // If the app wasn't mounted but is now mounted, reload
+                // its icon.
+                if (mApkFile.exists()) {
+                    mMounted = true;
+                    mIcon = mInfo.loadIcon(mLoader.mPm);
+                    return mIcon;
+                }
+            } else {
+                return mIcon;
+            }
+
+            return mLoader.getContext().getResources().getDrawable(
+                    android.R.drawable.sym_def_app_icon);
+        }
+
+        @Override public String toString() {
+            return mLabel;
+        }
+
+        void loadLabel(Context context) {
+            if (mLabel == null || !mMounted) {
+                if (!mApkFile.exists()) {
+                    mMounted = false;
+                    mLabel = mInfo.packageName;
+                } else {
+                    mMounted = true;
+                    CharSequence label = mInfo.loadLabel(context.getPackageManager());
+                    mLabel = label != null ? label.toString() : mInfo.packageName;
+                }
+            }
+        }
+
+        private final AppListLoader mLoader;
+        private final ApplicationInfo mInfo;
+        private final File mApkFile;
+        private String mLabel;
+        private Drawable mIcon;
+        private boolean mMounted;
+    }
+
+    /**
+     * Perform alphabetical comparison of application entry objects.
+     */
+    public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
+        private final Collator sCollator = Collator.getInstance();
+        @Override
+        public int compare(AppEntry object1, AppEntry object2) {
+            return sCollator.compare(object1.getLabel(), object2.getLabel());
+        }
+    };
+
+    /**
+     * Helper for determining if the configuration has changed in an interesting
+     * way so we need to rebuild the app list.
+     */
+    public static class InterestingConfigChanges {
+        final Configuration mLastConfiguration = new Configuration();
+        int mLastDensity;
+
+        boolean applyNewConfig(Resources res) {
+            int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
+            boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
+            if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
+                    |ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
+                mLastDensity = res.getDisplayMetrics().densityDpi;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Helper class to look for interesting changes to the installed apps
+     * so that the loader can be updated.
+     */
+    public static class PackageIntentReceiver extends BroadcastReceiver {
+        final AppListLoader mLoader;
+
+        public PackageIntentReceiver(AppListLoader loader) {
+            mLoader = loader;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+            filter.addDataScheme("package");
+            mLoader.getContext().registerReceiver(this, filter);
+            // Register for events related to sdcard installation.
+            IntentFilter sdFilter = new IntentFilter();
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            mLoader.getContext().registerReceiver(this, sdFilter);
+        }
+
+        @Override public void onReceive(Context context, Intent intent) {
+            // Tell the loader about the change.
+            mLoader.onContentChanged();
+        }
+    }
+
+    /**
+     * A custom Loader that loads all of the installed applications.
+     */
+    public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {
+        final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
+        final PackageManager mPm;
+
+        List<AppEntry> mApps;
+        PackageIntentReceiver mPackageObserver;
+
+        public AppListLoader(Context context) {
+            super(context);
+
+            // Retrieve the package manager for later use; note we don't
+            // use 'context' directly but instead the save global application
+            // context returned by getContext().
+            mPm = getContext().getPackageManager();
+        }
+
+        /**
+         * This is where the bulk of our work is done.  This function is
+         * called in a background thread and should generate a new set of
+         * data to be published by the loader.
+         */
+        @Override public List<AppEntry> loadInBackground() {
+            // Retrieve all known applications.
+            List<ApplicationInfo> apps = mPm.getInstalledApplications(
+                    PackageManager.GET_UNINSTALLED_PACKAGES |
+                    PackageManager.GET_DISABLED_COMPONENTS);
+            if (apps == null) {
+                apps = new ArrayList<ApplicationInfo>();
+            }
+
+            final Context context = getContext();
+
+            // Create corresponding array of entries and load their labels.
+            List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
+            for (int i=0; i<apps.size(); i++) {
+                AppEntry entry = new AppEntry(this, apps.get(i));
+                entry.loadLabel(context);
+                entries.add(entry);
+            }
+
+            // Sort the list.
+            Collections.sort(entries, ALPHA_COMPARATOR);
+
+            // Done!
+            return entries;
+        }
+
+        /**
+         * Called when there is new data to deliver to the client.  The
+         * super class will take care of delivering it; the implementation
+         * here just adds a little more logic.
+         */
+        @Override public void deliverResult(List<AppEntry> apps) {
+            if (isReset()) {
+                // An async query came in while the loader is stopped.  We
+                // don't need the result.
+                if (apps != null) {
+                    onReleaseResources(apps);
+                }
+            }
+            List<AppEntry> oldApps = apps;
+            mApps = apps;
+
+            if (isStarted()) {
+                // If the Loader is currently started, we can immediately
+                // deliver its results.
+                super.deliverResult(apps);
+            }
+
+            // At this point we can release the resources associated with
+            // 'oldApps' if needed; now that the new result is delivered we
+            // know that it is no longer in use.
+            if (oldApps != null) {
+                onReleaseResources(oldApps);
+            }
+        }
+
+        /**
+         * Handles a request to start the Loader.
+         */
+        @Override protected void onStartLoading() {
+            if (mApps != null) {
+                // If we currently have a result available, deliver it
+                // immediately.
+                deliverResult(mApps);
+            }
+
+            // Start watching for changes in the app data.
+            if (mPackageObserver == null) {
+                mPackageObserver = new PackageIntentReceiver(this);
+            }
+
+            // Has something interesting in the configuration changed since we
+            // last built the app list?
+            boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());
+
+            if (takeContentChanged() || mApps == null || configChange) {
+                // If the data has changed since the last time it was loaded
+                // or is not currently available, start a load.
+                forceLoad();
+            }
+        }
+
+        /**
+         * Handles a request to stop the Loader.
+         */
+        @Override protected void onStopLoading() {
+            // Attempt to cancel the current load task if possible.
+            cancelLoad();
+        }
+
+        /**
+         * Handles a request to cancel a load.
+         */
+        @Override public void onCanceled(List<AppEntry> apps) {
+            super.onCanceled(apps);
+
+            // At this point we can release the resources associated with 'apps'
+            // if needed.
+            onReleaseResources(apps);
+        }
+
+        /**
+         * Handles a request to completely reset the Loader.
+         */
+        @Override protected void onReset() {
+            super.onReset();
+
+            // Ensure the loader is stopped
+            onStopLoading();
+
+            // At this point we can release the resources associated with 'apps'
+            // if needed.
+            if (mApps != null) {
+                onReleaseResources(mApps);
+                mApps = null;
+            }
+
+            // Stop monitoring for changes.
+            if (mPackageObserver != null) {
+                getContext().unregisterReceiver(mPackageObserver);
+                mPackageObserver = null;
+            }
+        }
+
+        /**
+         * Helper function to take care of releasing resources associated
+         * with an actively loaded data set.
+         */
+        protected void onReleaseResources(List<AppEntry> apps) {
+            // For a simple List<> there is nothing to do.  For something
+            // like a Cursor, we would close it here.
+        }
+    }
+//END_INCLUDE(loader)
+
+//BEGIN_INCLUDE(fragment)
+    public static class AppListAdapter extends ArrayAdapter<AppEntry> {
+        private final LayoutInflater mInflater;
+
+        public AppListAdapter(Context context) {
+            super(context, android.R.layout.simple_list_item_2);
+            mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        }
+
+        public void setData(List<AppEntry> data) {
+            clear();
+            if (data != null) {
+                addAll(data);
+            }
+        }
+
+        /**
+         * Populate new items in the list.
+         */
+        @Override public View getView(int position, View convertView, ViewGroup parent) {
+            View view;
+
+            if (convertView == null) {
+                view = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
+            } else {
+                view = convertView;
+            }
+
+            AppEntry item = getItem(position);
+            ((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());
+            ((TextView)view.findViewById(R.id.text)).setText(item.getLabel());
+
+            return view;
+        }
+    }
+
+    public static class AppListFragment extends ListFragment
+            implements OnQueryTextListener, LoaderManager.LoaderCallbacks<List<AppEntry>> {
+
+        // This is the Adapter being used to display the list's data.
+        AppListAdapter mAdapter;
+
+        // If non-null, this is the current filter the user has provided.
+        String mCurFilter;
+
+        @Override public void onActivityCreated(Bundle savedInstanceState) {
+            super.onActivityCreated(savedInstanceState);
+
+            // Give some text to display if there is no data.  In a real
+            // application this would come from a resource.
+            setEmptyText("No applications");
+
+            // We have a menu item to show in action bar.
+            setHasOptionsMenu(true);
+
+            // Create an empty adapter we will use to display the loaded data.
+            mAdapter = new AppListAdapter(getActivity());
+            setListAdapter(mAdapter);
+
+            // Start out with a progress indicator.
+            setListShown(false);
+
+            // Prepare the loader.  Either re-connect with an existing one,
+            // or start a new one.
+            getLoaderManager().initLoader(0, null, this);
+        }
+
+        @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+            // Place an action bar item for searching.
+            MenuItem item = menu.add("Search");
+            item.setIcon(android.R.drawable.ic_menu_search);
+            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            SearchView sv = new SearchView(getActivity());
+            sv.setOnQueryTextListener(this);
+            item.setActionView(sv);
+        }
+
+        @Override public boolean onQueryTextChange(String newText) {
+            // Called when the action bar search text has changed.  Since this
+            // is a simple array adapter, we can just have it do the filtering.
+            mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
+            mAdapter.getFilter().filter(mCurFilter);
+            return true;
+        }
+
+        @Override public boolean onQueryTextSubmit(String query) {
+            // Don't care about this.
+            return true;
+        }
+
+        @Override public void onListItemClick(ListView l, View v, int position, long id) {
+            // Insert desired behavior here.
+            Log.i("LoaderCustom", "Item clicked: " + id);
+        }
+
+        @Override public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
+            // This is called when a new Loader needs to be created.  This
+            // sample only has one Loader with no arguments, so it is simple.
+            return new AppListLoader(getActivity());
+        }
+
+        @Override public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {
+            // Set the new data in the adapter.
+            mAdapter.setData(data);
+
+            // The list should now be shown.
+            setListShown(true);
+        }
+
+        @Override public void onLoaderReset(Loader<List<AppEntry>> loader) {
+            // Clear the data in the adapter.
+            mAdapter.setData(null);
+        }
+    }
+//END_INCLUDE(fragment)
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java b/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
index a62a524..aade659 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
@@ -135,8 +135,13 @@
     };
 // END_INCLUDE(exposing_a_service)
     
-    private static final int REPORT_MSG = 1;
+    @Override
+    public void onTaskRemoved(Intent rootIntent) {
+        Toast.makeText(this, "Task removed: " + rootIntent, Toast.LENGTH_LONG).show();
+    }
     
+    private static final int REPORT_MSG = 1;
+
     /**
      * Our Handler used to execute operations on the main thread.  This is used
      * to schedule increments of our value.
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/_index.html b/samples/ApiDemos/src/com/example/android/apis/app/_index.html
index fa69dee..71ccb54 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/_index.html
+++ b/samples/ApiDemos/src/com/example/android/apis/app/_index.html
@@ -119,10 +119,6 @@
   <dt><a href="FragmentListArray.html">Fragment List Array</a></dt>
   <dd>Demonstrates use of ListFragment to show the contents of a simple ArrayAdapter.</dd>
   
-  <dt><a href="FragmentListCursorLoader.html">Fragment List Cursor Loader</a></dt>
-  <dd>Demonstrates use of LoaderManager to perform a query for a Cursor that
-  populates a ListFragment.</dd>
-  
   <dt><a href="FragmentMenu.html">Fragment Menu</a></dt>
   <dd>Demonstrates populating custom menu items from a Fragment.</dd>
   
@@ -162,6 +158,10 @@
 
 <h3 id="LoaderManager">LoaderManager</h3>
 <dl>
+  <dt><a href="LoaderCursor.html">Loader Cursor</a></dt>
+  <dd>Demonstrates use of LoaderManager to perform a query for a Cursor that
+  populates a ListFragment.</dd>
+
   <dt><a href="LoaderThrottle.html">Loader Throttle</a></dt>
   <dd>Complete end-to-end demonstration of a simple content provider that
   populates data in a list through a cursor loader.  The UI allows the list
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/ResourcesWidthAndHeight.java b/samples/ApiDemos/src/com/example/android/apis/content/ResourcesWidthAndHeight.java
new file mode 100644
index 0000000..6c64e95
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/content/ResourcesWidthAndHeight.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 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.content;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class ResourcesWidthAndHeight extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // This layout uses different configurations to adjust
+        // what is shown based on the current screen width and height.
+        setContentView(R.layout.resources_width_and_height);
+    }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/Compass.java b/samples/ApiDemos/src/com/example/android/apis/graphics/Compass.java
index 85471d9..241d98f 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/Compass.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/Compass.java
@@ -23,7 +23,6 @@
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Bundle;
-import android.util.Config;
 import android.util.Log;
 import android.view.View;
 
@@ -38,7 +37,7 @@
 
     private final SensorEventListener mListener = new SensorEventListener() {
         public void onSensorChanged(SensorEvent event) {
-            if (Config.DEBUG) Log.d(TAG,
+            if (false) Log.d(TAG,
                     "sensorChanged (" + event.values[0] + ", " + event.values[1] + ", " + event.values[2] + ")");
             mValues = event.values;
             if (mView != null) {
@@ -62,7 +61,7 @@
     @Override
     protected void onResume()
     {
-        if (Config.DEBUG) Log.d(TAG, "onResume");
+        if (false) Log.d(TAG, "onResume");
         super.onResume();
 
         mSensorManager.registerListener(mListener, mSensor,
@@ -72,7 +71,7 @@
     @Override
     protected void onStop()
     {
-        if (Config.DEBUG) Log.d(TAG, "onStop");
+        if (false) Log.d(TAG, "onStop");
         mSensorManager.unregisterListener(mListener);
         super.onStop();
     }
@@ -117,14 +116,14 @@
         @Override
         protected void onAttachedToWindow() {
             mAnimate = true;
-            if (Config.DEBUG) Log.d(TAG, "onAttachedToWindow. mAnimate=" + mAnimate);
+            if (false) Log.d(TAG, "onAttachedToWindow. mAnimate=" + mAnimate);
             super.onAttachedToWindow();
         }
 
         @Override
         protected void onDetachedFromWindow() {
             mAnimate = false;
-            if (Config.DEBUG) Log.d(TAG, "onDetachedFromWindow. mAnimate=" + mAnimate);
+            if (false) Log.d(TAG, "onDetachedFromWindow. mAnimate=" + mAnimate);
             super.onDetachedFromWindow();
         }
     }
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/SensorTest.java b/samples/ApiDemos/src/com/example/android/apis/graphics/SensorTest.java
index dc07a27..7273cae 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/SensorTest.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/SensorTest.java
@@ -23,7 +23,6 @@
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Bundle;
-import android.util.Config;
 import android.util.Log;
 import android.view.View;
 
@@ -142,21 +141,21 @@
         mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
         mView = new SampleView(this);
         setContentView(mView);
-        if (Config.DEBUG) Log.d(TAG, "create " + mSensorManager);
+        if (false) Log.d(TAG, "create " + mSensorManager);
     }
 
     @Override
     protected void onResume() {
         super.onResume();
         mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_FASTEST);
-        if (Config.DEBUG) Log.d(TAG, "resume " + mSensorManager);
+        if (false) Log.d(TAG, "resume " + mSensorManager);
     }
 
     @Override
     protected void onStop() {
         mSensorManager.unregisterListener(mListener);
         super.onStop();
-        if (Config.DEBUG) Log.d(TAG, "stop " + mSensorManager);
+        if (false) Log.d(TAG, "stop " + mSensorManager);
     }
 
     private class SampleView extends View {
@@ -200,14 +199,14 @@
         @Override
         protected void onAttachedToWindow() {
             mAnimate = true;
-            if (Config.DEBUG) Log.d(TAG, "onAttachedToWindow. mAnimate="+mAnimate);
+            if (false) Log.d(TAG, "onAttachedToWindow. mAnimate="+mAnimate);
             super.onAttachedToWindow();
         }
 
         @Override
         protected void onDetachedFromWindow() {
             mAnimate = false;
-            if (Config.DEBUG) Log.d(TAG, "onAttachedToWindow. mAnimate="+mAnimate);
+            if (false) Log.d(TAG, "onAttachedToWindow. mAnimate="+mAnimate);
             super.onDetachedFromWindow();
         }
     }
diff --git a/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentPagerSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentPagerSupport.java
new file mode 100644
index 0000000..46d29ac
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentPagerSupport.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2011 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.support.app;
+
+import com.example.android.apis.R;
+import com.example.android.apis.Shakespeare;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentPager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.ListFragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class FragmentPagerSupport extends FragmentActivity
+        implements FragmentPager.Adapter {
+    static final int NUM_ITEMS = 10;
+
+    FragmentPager mPager;
+    int mCurPos;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.fragment_pager);
+
+        mPager = (FragmentPager)findViewById(R.id.pager);
+        mPager.setAdapter(this);
+
+        // Watch for button clicks.
+        Button button = (Button)findViewById(R.id.new_fragment);
+        button.setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                mCurPos++;
+                if (mCurPos < NUM_ITEMS) {
+                    mPager.setCurrentItem(mCurPos);
+                } else {
+                    mCurPos--;
+                }
+            }
+        });
+    }
+
+    @Override
+    public int getCount() {
+        return NUM_ITEMS;
+    }
+
+    @Override
+    public Fragment getItem(int position) {
+        return ArrayListFragment.newInstance(position);
+    }
+
+    public static class ArrayListFragment extends ListFragment {
+        int mNum;
+
+        /**
+         * Create a new instance of CountingFragment, providing "num"
+         * as an argument.
+         */
+        static ArrayListFragment newInstance(int num) {
+            ArrayListFragment f = new ArrayListFragment();
+
+            // Supply num input as an argument.
+            Bundle args = new Bundle();
+            args.putInt("num", num);
+            f.setArguments(args);
+
+            return f;
+        }
+
+        /**
+         * When creating, retrieve this instance's number from its arguments.
+         */
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            mNum = getArguments() != null ? getArguments().getInt("num") : 1;
+        }
+
+        /**
+         * The Fragment's UI is just a simple text view showing its
+         * instance number.
+         */
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            View v = inflater.inflate(R.layout.fragment_pager_list, container, false);
+            View tv = v.findViewById(R.id.text);
+            ((TextView)tv).setText("Fragment #" + mNum);
+            return v;
+        }
+
+        @Override
+        public void onActivityCreated(Bundle savedInstanceState) {
+            super.onActivityCreated(savedInstanceState);
+            setListAdapter(new ArrayAdapter<String>(getActivity(),
+                    android.R.layout.simple_list_item_1, Shakespeare.TITLES));
+        }
+
+        @Override
+        public void onListItemClick(ListView l, View v, int position, long id) {
+            Log.i("FragmentList", "Item clicked: " + id);
+        }
+    }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListCursorLoaderSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/LoaderCursorSupport.java
similarity index 96%
rename from samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListCursorLoaderSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/LoaderCursorSupport.java
index 98aa47f..491ae0e 100644
--- a/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListCursorLoaderSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/LoaderCursorSupport.java
@@ -37,10 +37,10 @@
 import android.widget.ListView;
 
 /**
- * Demonstration of more complex use if a ListFragment, including showing
- * an empty view and loading progress.
+ * Demonstration of the use of a CursorLoader to load and display contacts
+ * data in a fragment.
  */
-public class FragmentListCursorLoaderSupport extends FragmentActivity {
+public class LoaderCursorSupport extends FragmentActivity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/samples/BrowserPlugin/jni/Android.mk b/samples/BrowserPlugin/jni/Android.mk
index b153e37..f8cea4d 100644
--- a/samples/BrowserPlugin/jni/Android.mk
+++ b/samples/BrowserPlugin/jni/Android.mk
@@ -66,7 +66,7 @@
 	libskia
 
 LOCAL_CFLAGS += -fvisibility=hidden 
-LOCAL_PRELINK_MODULE:=false
+
 
 LOCAL_MODULE:= libsampleplugin
 
diff --git a/samples/RenderScript/Balls/src/com/example/android/rs/balls/Balls.java b/samples/RenderScript/Balls/src/com/example/android/rs/balls/Balls.java
index d3b900a..2a7436a 100644
--- a/samples/RenderScript/Balls/src/com/example/android/rs/balls/Balls.java
+++ b/samples/RenderScript/Balls/src/com/example/android/rs/balls/Balls.java
@@ -26,7 +26,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings.System;
-import android.util.Config;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -51,7 +50,7 @@
 
     private static final String LOG_TAG = "libRS_jni";
     private static final boolean DEBUG  = false;
-    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+    private static final boolean LOG_ENABLED = false;
 
     private BallsView mView;
     private SensorManager mSensorManager;
diff --git a/samples/RenderScript/Fountain/src/com/example/android/rs/fountain/Fountain.java b/samples/RenderScript/Fountain/src/com/example/android/rs/fountain/Fountain.java
index 53b4f26..311455a 100644
--- a/samples/RenderScript/Fountain/src/com/example/android/rs/fountain/Fountain.java
+++ b/samples/RenderScript/Fountain/src/com/example/android/rs/fountain/Fountain.java
@@ -26,7 +26,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings.System;
-import android.util.Config;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -42,7 +41,7 @@
 
     private static final String LOG_TAG = "libRS_jni";
     private static final boolean DEBUG  = false;
-    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+    private static final boolean LOG_ENABLED = false;
 
     private FountainView mView;
 
diff --git a/samples/SimpleJNI/jni/Android.mk b/samples/SimpleJNI/jni/Android.mk
index 528196b..a704fcf 100644
--- a/samples/SimpleJNI/jni/Android.mk
+++ b/samples/SimpleJNI/jni/Android.mk
@@ -44,11 +44,4 @@
 # No special compiler flags.
 LOCAL_CFLAGS +=
 
-# Don't prelink this library.  For more efficient code, you may want
-# to add this library to the prelink map and set this to true. However,
-# it's difficult to do this for applications that are not supplied as
-# part of a system image.
-
-LOCAL_PRELINK_MODULE := false
-
 include $(BUILD_SHARED_LIBRARY)
diff --git a/tools/emulator/system/gps/Android.mk b/tools/emulator/system/gps/Android.mk
index 41bdc64..d41c5a7 100644
--- a/tools/emulator/system/gps/Android.mk
+++ b/tools/emulator/system/gps/Android.mk
@@ -23,15 +23,19 @@
 LOCAL_PATH := $(call my-dir)
 
 ifneq ($(TARGET_PRODUCT),sim)
-# HAL module implemenation, not prelinked and stored in
+# HAL module implemenation stored in
 # hw/<GPS_HARDWARE_MODULE_ID>.<ro.hardware>.so
 include $(CLEAR_VARS)
-LOCAL_PRELINK_MODULE := false
+
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
 LOCAL_CFLAGS += -DQEMU_HARDWARE
 LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
 LOCAL_SRC_FILES := gps_qemu.c
+ifeq ($(TARGET_PRODUCT),vbox_x86)
+LOCAL_MODULE := gps.vbox_x86
+else
 LOCAL_MODULE := gps.goldfish
+endif
 LOCAL_MODULE_TAGS := debug
 include $(BUILD_SHARED_LIBRARY)
 endif
diff --git a/tools/emulator/system/sensors/Android.mk b/tools/emulator/system/sensors/Android.mk
index 9b0e83d..4ae048b 100644
--- a/tools/emulator/system/sensors/Android.mk
+++ b/tools/emulator/system/sensors/Android.mk
@@ -23,14 +23,18 @@
 LOCAL_PATH := $(call my-dir)
 
 ifneq ($(TARGET_PRODUCT),sim)
-# HAL module implemenation, not prelinked and stored in
+# HAL module implemenation stored in
 # hw/<SENSORS_HARDWARE_MODULE_ID>.<ro.hardware>.so
 include $(CLEAR_VARS)
-LOCAL_PRELINK_MODULE := false
+
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
 LOCAL_SHARED_LIBRARIES := liblog libcutils
 LOCAL_SRC_FILES := sensors_qemu.c
+ifeq ($(TARGET_PRODUCT),vbox_x86)
+LOCAL_MODULE := sensors.vbox_x86
+else
 LOCAL_MODULE := sensors.goldfish
+endif
 LOCAL_MODULE_TAGS := debug
 include $(BUILD_SHARED_LIBRARY)
 endif
diff --git a/tools/glesv2debugger/.classpath b/tools/glesv2debugger/.classpath
new file mode 100755
index 0000000..4ba3585
--- /dev/null
+++ b/tools/glesv2debugger/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="lib" path="lib/sdklib.jar"/>
+	<classpathentry kind="lib" path="lib/liblzf.jar"/>
+	<classpathentry kind="lib" path="lib/host-libprotobuf-java-2.3.0-lite.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="test"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/glesv2debugger/.gitignore b/tools/glesv2debugger/.gitignore
new file mode 100644
index 0000000..574bfc7
--- /dev/null
+++ b/tools/glesv2debugger/.gitignore
@@ -0,0 +1,4 @@
+lib/*.jar
+bin/*
+.settings/*
+
diff --git a/tools/glesv2debugger/.project b/tools/glesv2debugger/.project
new file mode 100755
index 0000000..0c974ca
--- /dev/null
+++ b/tools/glesv2debugger/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>GLESv2DebuggerClient</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/glesv2debugger/META-INF/MANIFEST.MF b/tools/glesv2debugger/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..cf42bdb
--- /dev/null
+++ b/tools/glesv2debugger/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: GLESv2DebuggerClient
+Bundle-SymbolicName: GLESv2DebuggerClient; singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: com.android.glesv2debugger.Activator
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.junit
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ClassPath: lib/host-libprotobuf-java-2.3.0-lite.jar,
+ lib/liblzf.jar,
+ lib/sdklib.jar,
+ .
diff --git a/tools/glesv2debugger/README.android b/tools/glesv2debugger/README.android
new file mode 100644
index 0000000..ae95463
--- /dev/null
+++ b/tools/glesv2debugger/README.android
@@ -0,0 +1,20 @@
+The following is taken from slide 3 & 4 of https://docs.google.com/a/google.com/present/edit?id=0AcZLV3icFYi0ZGZxa3NqZndfMGRqa2tiOXB4&authkey=CMfb8ukI&hl=en
+The spec doc is at https://docs.google.com/a/google.com/document/d/1dsASXCF9Suq8KOGcxwB2mAwgdRlrFj4QhMxkfaRJlA0/edit?hl=en&authkey=CPj4tKkO#
+
+
+Building and Running
+
+Debugger server is linked into EGL, code is in framework/base/opengl/libs/GLES2_dbg and already included in latest master builds, no action needed.
+Use development/tools/glesv2debugger/setup.sh to build and copy the jars: libprotobuf-java-2.3.0-lite, liblzf, sdklib into development/tools/glesv2debugger/lib
+Install Eclipse SDK for Eclipse: Eclipse->Help->Install New Software. Select "All Available Sites" in the "Work with:" drop down, then find "Eclipse SDK". (If Eclipse reports dependency conflicts, try install updates first)
+Debugger client is an Eclipse plug-in, code is at development/tools/glesv2debugger, built in Eclipse
+Optional: build glsl_compiler and copy to plug-in working directory; this is used for shader syntax check
+
+
+"Attaching" to a Process
+
+adb shell setprop debug.egl.debug_proc <process name> before running process. ie: com.example.android.apis
+EGL checks /proc/<proc_id>/cmdline for match during init and sets debug functions in eglMakeCurrent
+EGL will bind to socket and wait for incoming connection, so need to adb forward tcp:5039 tcp:5039. Port can be overridden by adb shell setprop debug.egl.debug_port <port>
+If create socket failed, EGL will try to open /data/local/tmp/dump.gles2dbg for write, and exit when 8MB is written. The relevant properties are ...debug_forceUseFile, ...debug_maxFileSize, and ...debug_filePath
+Now manually start the process on device; on host, open development/tools/glesv2debugger/.project and run/debug as Eclipse application, then Window->Show View->Other->Debug->OpenGL ES 2.0 Debugger, then Connect or Open File
diff --git a/tools/glesv2debugger/build.properties b/tools/glesv2debugger/build.properties
new file mode 100644
index 0000000..39d82d2
--- /dev/null
+++ b/tools/glesv2debugger/build.properties
@@ -0,0 +1,11 @@
+source.. = src/,\

+           test/

+output.. = bin/

+bin.includes = plugin.xml,\

+               META-INF/,\

+               .,\

+               icons/,\
+               contexts.xml,\
+               lib/host-libprotobuf-java-2.3.0-lite.jar,\
+               lib/liblzf.jar,\
+               lib/sdklib.jar
diff --git a/tools/glesv2debugger/contexts.xml b/tools/glesv2debugger/contexts.xml
new file mode 100644
index 0000000..02e26e4
--- /dev/null
+++ b/tools/glesv2debugger/contexts.xml
@@ -0,0 +1,12 @@
+<contexts>
+	<context id="viewer" title="Sample View">
+		<description>This is the context help for the sample view with a table viewer. It was generated by a PDE template.</description>
+		<topic href="/PLUGINS_ROOT/org.eclipse.platform.doc.isv/guide/ua_help_context.htm" label="Context-sensitive help">
+			<enablement>
+				<with variable="platform">
+	            	<test property="org.eclipse.core.runtime.isBundleInstalled" args="org.eclipse.platform.doc.isv"/>
+	     		</with>
+			</enablement>
+		</topic>
+	</context>
+</contexts>
diff --git a/tools/glesv2debugger/generate_GLEnum_java.py b/tools/glesv2debugger/generate_GLEnum_java.py
new file mode 100755
index 0000000..cf543c8
--- /dev/null
+++ b/tools/glesv2debugger/generate_GLEnum_java.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+#
+# Copyright 2011, 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.
+#
+
+if __name__ == "__main__":
+    externs = []
+    lines = open("../../../frameworks/base/opengl/libs/enums.in").readlines()
+    output = open("src/com/android/glesv2debugger/GLEnum.java", "w")
+    i = 0
+    output.write(
+"""/*
+ ** Copyright 2011, 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.
+ */
+
+// auto generated by generate_GLEnum_java.py"
+
+package com.android.glesv2debugger;
+
+public enum GLEnum {
+""")
+    
+    index = 0
+    for line in lines:
+        value = line[line.find("(") + 1: line.find(",")]
+        name = line[line.find(",") + 1: line.find(")")]    
+        output.write("    %s(%s),\n" % (name, value))
+
+    output.write("""    ;
+
+    public final int value;
+    GLEnum(final int value) {
+        this.value = value;
+    }
+
+    private static final java.util.HashMap<Integer, GLEnum> reverseMap = new java.util.HashMap<Integer, GLEnum>();
+    static {
+        for (GLEnum e : GLEnum.values())
+        reverseMap.put(e.value, e);
+    }
+
+    public static GLEnum valueOf(final int value) {
+        return reverseMap.get(value);
+    }
+}""")
+
+
diff --git a/tools/glesv2debugger/generate_MessageFormatter_java.py b/tools/glesv2debugger/generate_MessageFormatter_java.py
new file mode 100755
index 0000000..dfbf2ea
--- /dev/null
+++ b/tools/glesv2debugger/generate_MessageFormatter_java.py
@@ -0,0 +1,281 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+#
+# Copyright 2011, 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.
+#
+
+import os
+import sys
+
+def RemoveAnnotation(line):
+    if line.find(":") >= 0:
+        annotation = line[line.find(":"): line.find(" ", line.find(":"))]
+        return line.replace(annotation, "*")
+    else:
+        return line
+        
+if __name__ == "__main__":
+    externs = []
+    lines = open("../../../frameworks/base/opengl/libs/GLES2_dbg/gl2_api_annotated.in").readlines()
+    output = open("src/com/android/glesv2debugger/MessageFormatter.java", "w")
+    
+    i = 0
+    output.write(
+"""/*
+ ** Copyright 2011, 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.
+ */
+
+// auto generated by generate_MessageFormatter_java.py"
+
+package com.android.glesv2debugger;
+
+import java.nio.ByteBuffer;
+
+public class MessageFormatter {
+
+    static String formatFloats(int count, final ByteBuffer data) {
+        if (data.remaining() == 0)
+            return "{}";
+        data.order(SampleView.targetByteOrder);
+        String ret = "{";
+        for (int i = 0; i < count; i++) {
+            ret += Float.intBitsToFloat(data.getInt());
+            if (i < count - 1)
+                ret += ", ";
+        }
+        return ret + "}";
+    }
+
+    static String formatInts(int count, final ByteBuffer data) {
+        if (data.remaining() == 0)
+            return "{}";
+        data.order(SampleView.targetByteOrder);
+        String ret = "{";
+        for (int i = 0; i < count; i++) {
+            ret += data.getInt();
+            if (i < count - 1)
+                ret += ", ";
+        }
+        return ret + "}";
+    }
+
+    static String formatUInts(int count, final ByteBuffer data) {
+        if (data.remaining() == 0)
+            return "{}";
+        data.order(SampleView.targetByteOrder);
+        String ret = "{";
+        for (int i = 0; i < count; i++) {
+            long bits = data.getInt() & 0xffffffff;
+            ret += bits;
+            if (i < count - 1)
+                ret += ", ";
+        }
+        return ret + "}";
+    }
+
+    static String formatMatrix(int columns, int count, final ByteBuffer data) {
+        if (data.remaining() == 0)
+            return "{}";
+        data.order(SampleView.targetByteOrder);
+        String ret = "{";
+        for (int i = 0; i < count; i++) {
+            ret += Float.intBitsToFloat(data.getInt());
+            if (i < count - 1)
+                ret += ", ";
+            if (i % columns == columns - 1)
+                ret += "\\n                                             ";
+        }
+        return ret + "}";
+    }
+
+    public static String format(final DebuggerMessage.Message msg,
+                                final boolean code) {
+        String str;
+        switch (msg.getFunction()) {
+""")
+    #in source code these turn into program_%d etc.
+    nameReplaces = ["program", "shader", "texture", "buffer", "framebuffer", "renderbuffer"]
+    for line in lines:
+        if line.find("API_ENTRY(") >= 0: # a function prototype
+            returnType = line[0: line.find(" API_ENTRY(")].replace("const ", "")
+            functionName = line[line.find("(") + 1: line.find(")")] #extract GL function name
+            parameterList = line[line.find(")(") + 2: line.find(") {")]
+
+            parameters = parameterList.split(',')
+            paramIndex = 0
+
+            formatString = "%s"
+            formatArgs = ""
+            if returnType != "void":
+                if returnType == "GLenum":
+                    formatArgs += '\
+                    (code ? "%s" : GLEnum.valueOf(msg.getRet()))\n' % (functionName)
+                elif returnType.find("*") >= 0:
+                    formatArgs += '\
+                    (code ? "%s" : "0x" + Integer.toHexString(msg.getRet()))\n' % (functionName)
+                else:
+                    formatArgs += '\
+                    (code ? "%s" : msg.getRet())\n' % (functionName)
+            else:
+                formatArgs += '\
+                    (code ? "%s" : "void")\n' % (functionName)
+
+            formatString += "("
+
+            if parameterList == "void":
+                parameters = []
+            inout = ""
+
+            paramNames = []
+
+            for parameter in parameters:
+                parameter = parameter.replace("const","")
+                parameter = parameter.strip()
+                paramType = parameter.split(' ')[0]
+                paramName = parameter.split(' ')[1]
+                annotation = ""
+
+                formatString += "%s%s"
+                formatArgs += '\
+                    , (code ? "/*%s*/ " : "%s=")\n' % (paramName, paramName)
+                if parameter.find(":") >= 0:
+                    assert inout == "" # only one parameter should be annotated
+                    inout = paramType.split(":")[2]
+                    annotation = paramType.split(":")[1]
+                    paramType = paramType.split(":")[0]
+                    count = 1
+                    countArg = ""
+                    if annotation.find("*") >= 0: # [1,n] * param
+                        count = int(annotation.split("*")[0])
+                        countArg = annotation.split("*")[1]
+                        assert countArg in paramNames
+                    elif annotation in paramNames:
+                        count = 1
+                        countArg = annotation
+                    elif annotation == "GLstring":
+                        annotation = annotation
+                    else:
+                        count = int(annotation)
+                    dataFormatter = ""
+                    if paramType == "GLfloat":
+                        dataFormatter = "formatFloats"
+                    elif paramType == "GLint":
+                        dataFormatter = "formatInts"
+                    elif paramType == "GLuint":
+                        dataFormatter = "formatUInts"
+                    elif annotation == "GLstring":
+                        assert paramType == "GLchar"
+                    elif paramType.find("void") >= 0:
+                        assert 1
+                    else:
+                        assert 0
+                    if functionName.find("Matrix") >= 0:
+                        columns = int(functionName[functionName.find("fv") - 1: functionName.find("fv")])
+                        assert columns * columns == count
+                        assert countArg != ""
+                        assert paramType == "GLfloat"
+                        formatArgs += '\
+                    , (code ? "(GLfloat [])" : "") + formatMatrix(%d, %d * msg.getArg%d(), msg.getData().asReadOnlyByteBuffer())' % (
+                        columns, count, paramNames.index(countArg))
+                    elif annotation == "GLstring":
+                        formatArgs += '\
+                    , (code ? "\\"" : "") + msg.getData().toStringUtf8() + (code ? "\\"" : "")'
+                    elif paramType.find("void") >= 0:
+                        formatArgs += '\
+                    , (code ? "arg%d" : "0x" + Integer.toHexString(msg.getArg%d()))' % (paramIndex, paramIndex)
+                    elif countArg == "":
+                        formatArgs += '\
+                    , (code ? "(%s [])" : "") + %s(%d, msg.getData().asReadOnlyByteBuffer())' % (
+                        paramType, dataFormatter, count)
+                    else:
+                        formatArgs += '\
+                    , (code ? "(%s [])" : "") +  %s(%d * msg.getArg%d(), msg.getData().asReadOnlyByteBuffer())' % (
+                        paramType, dataFormatter, count, paramNames.index(countArg))
+                else:
+                    if paramType == "GLfloat" or paramType == "GLclampf":
+                        formatArgs += "\
+                    , Float.intBitsToFloat(msg.getArg%d())" % (paramIndex)
+                    elif paramType == "GLenum": 
+                        formatArgs += "\
+                    , GLEnum.valueOf(msg.getArg%d())" % (paramIndex)
+                    elif paramType.find("*") >= 0:
+                        formatArgs += '\
+                    , (code ? "arg%d" : "0x" + Integer.toHexString(msg.getArg%d()))' % (paramIndex, paramIndex)
+                    elif paramName in nameReplaces:
+                        formatArgs += '\
+                    , (code ? "%s_" : "") + msg.getArg%d()' % (paramName, paramIndex)
+                    else:
+                        formatArgs += "\
+                    , msg.getArg%d()" % (paramIndex)
+                if paramIndex < len(parameters) - 1:
+                    formatString += ", "
+                    formatArgs += '\n'
+                paramNames.append(paramName)
+                paramIndex += 1  
+
+                
+            formatString += ")"
+             
+            output.write("            case %s:\n" % (functionName))
+            if line.find("*") >= 0 and (line.find("*") < line.find(":") or line.find("*") > line.rfind(":")):
+                sys.stderr.write(line)
+                output.write("                // FIXME: this function uses pointers, debugger may send data in msg.data\n")
+            output.write('\
+                str = String.format("%s",\n%s);\n\
+                break;\n' % (formatString, formatArgs))
+
+
+    output.write("""            default:
+                str = msg.toString();
+        }
+        return str;
+    }
+}""")
+
+'''    print """/*
+package GLESv2Debugger;
+
+public class MessageFormatterCustom {
+
+    public static String format(final DebuggerMessage.Message msg) {
+        String str;
+        switch (msg.getFunction()) {"""
+
+    for extern in externs:
+        print "        case %s" % (extern)
+        print "            // TODO:"
+
+print """        default:
+            str = msg.toString();
+        }
+        return str;
+    }
+}
+*/"""    '''
+        
+        
diff --git a/tools/glesv2debugger/generate_MessageParser_java.py b/tools/glesv2debugger/generate_MessageParser_java.py
new file mode 100755
index 0000000..b6e8282
--- /dev/null
+++ b/tools/glesv2debugger/generate_MessageParser_java.py
@@ -0,0 +1,304 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+#
+# Copyright 2011, 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.
+#
+
+import os
+import sys
+
+def RemoveAnnotation(line):
+    if line.find(":") >= 0:
+        annotation = line[line.find(":"): line.find(" ", line.find(":"))]
+        return line.replace(annotation, "*")
+    else:
+        return line
+
+if __name__ == "__main__":
+    externs = []
+    lines = open("../../../frameworks/base/opengl/libs/GLES2_dbg/gl2_api_annotated.in").readlines()
+    output = open("src/com/android/glesv2debugger/MessageParser.java", "w")
+
+    i = 0
+    output.write("""\
+/*
+ ** Copyright 2011, 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.
+ */
+
+// auto generated by generate_MessageParser_java.py,
+//  which also prints skeleton code for MessageParserEx.java
+
+package com.android.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.google.protobuf.ByteString;
+
+import java.nio.ByteBuffer;
+
+public abstract class MessageParser {
+
+    String args;
+
+    String[] getList()
+    {
+        String arg = args;
+        args = args.substring(args.lastIndexOf('}') + 1);
+        final int comma = args.indexOf(',');
+        if (comma >= 0)
+            args = args.substring(comma + 1).trim();
+        else
+            args = null;
+
+        final int comment = arg.indexOf('=');
+        if (comment >= 0)
+            arg = arg.substring(comment + 1);
+        arg = arg.trim();
+        assert arg.charAt(0) == '{';
+        arg = arg.substring(1, arg.lastIndexOf('}')).trim();
+        return arg.split("\\s*,\\s*");
+    }
+
+    ByteString parseFloats(int count) {
+        ByteBuffer buffer = ByteBuffer.allocate(count * 4);
+        buffer.order(SampleView.targetByteOrder);
+        String [] arg = getList();
+        for (int i = 0; i < count; i++)
+            buffer.putFloat(Float.parseFloat(arg[i].trim()));
+        buffer.rewind();
+        return ByteString.copyFrom(buffer);
+    }
+
+    ByteString parseInts(int count) {
+        ByteBuffer buffer = ByteBuffer.allocate(count * 4);
+        buffer.order(SampleView.targetByteOrder);
+        String [] arg = getList();
+        for (int i = 0; i < count; i++)
+            buffer.putInt(Integer.parseInt(arg[i].trim()));
+        buffer.rewind();
+        return ByteString.copyFrom(buffer);
+    }
+
+    ByteString parseUInts(int count) {
+        ByteBuffer buffer = ByteBuffer.allocate(count * 4);
+        buffer.order(SampleView.targetByteOrder);
+        String [] arg = getList();
+        for (int i = 0; i < count; i++)
+            buffer.putInt((int)(Long.parseLong(arg[i].trim()) & 0xffffffff));
+        buffer.rewind();
+        return ByteString.copyFrom(buffer);
+    }
+
+    ByteString parseMatrix(int columns, int count) {
+        return parseFloats(columns * columns * count);
+    }
+
+    ByteString parseString() {
+        // TODO: escape sequence and proper string literal
+        String arg = args.substring(args.indexOf('"') + 1, args.lastIndexOf('"'));
+        args = args.substring(args.lastIndexOf('"'));
+        int comma = args.indexOf(',');
+        if (comma >= 0)
+            args = args.substring(comma + 1).trim();
+        else
+            args = null;
+        return ByteString.copyFromUtf8(arg);
+    }
+
+    String getArgument()
+    {
+        final int comma = args.indexOf(',');
+        String arg = null;
+        if (comma >= 0)
+        {
+            arg = args.substring(0, comma);
+            args = args.substring(comma + 1);
+        }
+        else
+        {
+            arg = args;
+            args = null;
+        }
+        final int comment = arg.indexOf('=');
+        if (comment >= 0)
+            arg = arg.substring(comment + 1);
+        return arg.trim();
+    }
+
+    int parseArgument()
+    {
+        String arg = getArgument();
+        if (arg.startsWith("GL_"))
+            return GLEnum.valueOf(arg).value;
+        else if (arg.toLowerCase().startsWith("0x"))
+            return Integer.parseInt(arg.substring(2), 16);
+        else
+            return Integer.parseInt(arg);
+    }
+
+    int parseFloat()
+    {
+        String arg = getArgument();
+        return Float.floatToRawIntBits(Float.parseFloat(arg));
+    }
+
+    public void parse(final Message.Builder builder, String string) {
+        int lparen = string.indexOf("("), rparen = string.lastIndexOf(")");
+        String s = string.substring(0, lparen).trim();
+        args = string.substring(lparen + 1, rparen);
+        String[] t = s.split(" ");
+        Function function = Function.valueOf(t[t.length - 1]);
+        builder.setFunction(function);
+        switch (function) {
+""")
+
+    abstractParsers = ""
+
+    for line in lines:
+        if line.find("API_ENTRY(") >= 0: # a function prototype
+            returnType = line[0: line.find(" API_ENTRY(")].replace("const ", "")
+            functionName = line[line.find("(") + 1: line.find(")")] #extract GL function name
+            parameterList = line[line.find(")(") + 2: line.find(") {")]
+
+            parameters = parameterList.split(',')
+            paramIndex = 0
+
+            #if returnType != "void":
+            #else:
+
+            if parameterList == "void":
+                parameters = []
+            inout = ""
+
+            paramNames = []
+            abstract = False
+            argumentSetters = ""
+            output.write("\
+            case %s:\n" % (functionName))
+
+            for parameter in parameters:
+                parameter = parameter.replace("const","")
+                parameter = parameter.strip()
+                paramType = parameter.split(' ')[0]
+                paramName = parameter.split(' ')[1]
+                annotation = ""
+
+                argumentParser = ""
+
+                if parameter.find(":") >= 0:
+                    dataSetter = ""
+                    assert inout == "" # only one parameter should be annotated
+                    inout = paramType.split(":")[2]
+                    annotation = paramType.split(":")[1]
+                    paramType = paramType.split(":")[0]
+                    count = 1
+                    countArg = ""
+                    if annotation.find("*") >= 0: # [1,n] * param
+                        count = int(annotation.split("*")[0])
+                        countArg = annotation.split("*")[1]
+                        assert countArg in paramNames
+                    elif annotation in paramNames:
+                        count = 1
+                        countArg = annotation
+                    elif annotation == "GLstring":
+                        annotation = annotation
+                    else:
+                        count = int(annotation)
+
+                    if paramType == "GLfloat":
+                        argumentParser = "parseFloats"
+                    elif paramType == "GLint":
+                        argumentParser = "parseInts"
+                    elif paramType == "GLuint":
+                        argumentParser = "parseUInts"
+                    elif annotation == "GLstring":
+                        assert paramType == 'GLchar'
+                    elif paramType.find("void") >= 0:
+                        assert 1
+                    else:
+                        assert 0
+
+                    if functionName.find('Matrix') >= 0:
+                        columns = int(functionName[functionName.find("fv") - 1: functionName.find("fv")])
+                        assert columns * columns == count
+                        assert countArg != ""
+                        assert paramType == "GLfloat"
+                        dataSetter = "builder.setData(parseMatrix(%d, builder.getArg%d()));" % (
+                            columns, paramNames.index(countArg))
+                    elif annotation == "GLstring":
+                        dataSetter = "builder.setData(parseString());"
+                    elif paramType.find("void") >= 0:
+                        dataSetter = "// TODO"
+                        abstract = True
+                    elif countArg == "":
+                        dataSetter = "builder.setData(%s(%d));" % (argumentParser, count)
+                    else:
+                        dataSetter = "builder.setData(%s(%d * builder.getArg%d()));" % (
+                            argumentParser, count, paramNames.index(countArg))
+                    argumentSetters += "\
+                %s // %s %s\n" % (dataSetter, paramType, paramName)
+                else:
+                    if paramType == "GLfloat" or paramType == "GLclampf":
+                        argumentSetters += "\
+                builder.setArg%d(parseFloat()); // %s %s\n" % (
+                    paramIndex, paramType, paramName)
+                    elif paramType.find("*") >= 0:
+                        argumentSetters += "\
+                // TODO: %s %s\n" % (paramType, paramName)
+                        abstract = True
+                    else:
+                        argumentSetters += "\
+                builder.setArg%d(parseArgument()); // %s %s\n" % (
+                    paramIndex, paramType, paramName)
+                paramNames.append(paramName)
+                paramIndex += 1
+
+            if not abstract:
+                output.write("%s" % argumentSetters)
+            else:
+                output.write("\
+                parse_%s(builder);\n" % functionName)
+                abstractParsers += "\
+    abstract void parse_%s(Message.Builder builder);\n" % functionName
+                print """\
+    @Override
+    void parse_%s(Message.Builder builder) {
+%s    }
+""" % (functionName, argumentSetters) # print skeleton code for MessageParserEx
+
+            output.write("\
+                break;\n")
+    output.write("""\
+            default:
+                assert false;
+        }
+    }
+""")
+    output.write(abstractParsers)
+    output.write("\
+}""")
diff --git a/tools/glesv2debugger/icons/sample.gif b/tools/glesv2debugger/icons/sample.gif
new file mode 100644
index 0000000..34fb3c9
--- /dev/null
+++ b/tools/glesv2debugger/icons/sample.gif
Binary files differ
diff --git a/tools/glesv2debugger/plugin.xml b/tools/glesv2debugger/plugin.xml
new file mode 100644
index 0000000..f1512a5
--- /dev/null
+++ b/tools/glesv2debugger/plugin.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<?eclipse version="3.4"?>

+<plugin>

+

+   <extension

+         point="org.eclipse.ui.views">

+      <view

+            name="OpenGL ES 2.0 Debugger"

+            icon="icons/sample.gif"

+            category="org.eclipse.debug.ui"

+            class="com.android.glesv2debugger.SampleView"

+            id="glesv2debuggerclient.views.SampleView">

+      </view>

+   </extension>

+   <extension

+         point="org.eclipse.ui.perspectiveExtensions">

+      <perspectiveExtension

+            targetID="org.eclipse.jdt.ui.JavaPerspective">

+         <view

+               ratio="0.5"

+               relative="org.eclipse.ui.views.TaskList"

+               relationship="right"

+               id="glesv2debuggerclient.views.SampleView">

+         </view>

+      </perspectiveExtension>

+   </extension>

+   <extension

+         point="org.eclipse.help.contexts">

+      <contexts

+            file="contexts.xml">

+      </contexts>

+   </extension>

+

+</plugin>

diff --git a/tools/glesv2debugger/setup.sh b/tools/glesv2debugger/setup.sh
new file mode 100755
index 0000000..839019d
--- /dev/null
+++ b/tools/glesv2debugger/setup.sh
@@ -0,0 +1,35 @@
+source ../../../build/envsetup.sh
+pushd ../../../
+
+# need lunch before building jars
+if [ -z "$TARGET_PRODUCT" ]; then
+    lunch
+fi
+
+pushd external/liblzf/
+mm
+popd
+
+pushd external/protobuf/
+mm
+popd
+
+pushd sdk/sdkmanager/libs/sdklib
+mm
+popd
+
+# glsl_compiler is optional
+# make glsl_compiler -j3
+
+popd
+
+mkdir -p lib
+cp "$ANDROID_HOST_OUT/framework/host-libprotobuf-java-2.3.0-lite.jar" lib/
+cp "$ANDROID_HOST_OUT/framework/liblzf.jar" lib/
+cp "$ANDROID_HOST_OUT/framework/sdklib.jar" lib/
+
+# optional; usually for linux
+#cp "$ANDROID_HOST_OUT/bin/glsl_compiler" ~/
+
+# optional; usually for mac, need to replace eclipse.app with actual path
+#cp "$ANDROID_HOST_OUT/bin/glsl_compiler" eclipse.app/Contents/MacOS
diff --git a/tools/glesv2debugger/src/META-INF/MANIFEST.MF b/tools/glesv2debugger/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..212b27a
--- /dev/null
+++ b/tools/glesv2debugger/src/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0

+Created-By: 1.6.0_22 (Sun Microsystems Inc.)

+

diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/Activator.java b/tools/glesv2debugger/src/com/android/glesv2debugger/Activator.java
new file mode 100644
index 0000000..6083c0f
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/Activator.java
@@ -0,0 +1,83 @@
+/*

+ ** Copyright 2011, 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.glesv2debugger;

+

+import org.eclipse.jface.resource.ImageDescriptor;

+import org.eclipse.ui.plugin.AbstractUIPlugin;

+import org.osgi.framework.BundleContext;

+

+/**

+ * The activator class controls the plug-in life cycle

+ */

+public class Activator extends AbstractUIPlugin {

+

+    // The plug-in ID

+    public static final String PLUGIN_ID = "GLESv2DebuggerClient"; //$NON-NLS-1$

+

+    // The shared instance

+    private static Activator plugin;

+

+    /**

+     * The constructor

+     */

+    public Activator() {

+    }

+

+    /*

+     * (non-Javadoc)

+     * @see

+     * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext

+     * )

+     */

+    @Override

+    public void start(BundleContext context) throws Exception {

+        super.start(context);

+        plugin = this;

+    }

+

+    /*

+     * (non-Javadoc)

+     * @see

+     * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext

+     * )

+     */

+    @Override

+    public void stop(BundleContext context) throws Exception {

+        plugin = null;

+        super.stop(context);

+    }

+

+    /**

+     * Returns the shared instance

+     * 

+     * @return the shared instance

+     */

+    public static Activator getDefault() {

+        return plugin;

+    }

+

+    /**

+     * Returns an image descriptor for the image file at the given plug-in

+     * relative path

+     * 

+     * @param path the path

+     * @return the image descriptor

+     */

+    public static ImageDescriptor getImageDescriptor(String path) {

+        return imageDescriptorFromPlugin(PLUGIN_ID, path);

+    }

+}

diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java b/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java
new file mode 100644
index 0000000..e8405f9
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java
@@ -0,0 +1,191 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Prop;
+import com.android.glesv2debugger.DebuggerMessage.Message.Type;
+
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Shell;
+
+import java.io.IOException;
+
+public class BreakpointOption extends ScrolledComposite implements SelectionListener,
+        ProcessMessage {
+
+    SampleView sampleView;
+    Button[] buttonsBreak = new Button[Function.values().length];
+    /** cache of buttonsBreak[Function.getNumber()].getSelection */
+    boolean[] breakpoints = new boolean[Function.values().length];
+
+    BreakpointOption(SampleView sampleView, Composite parent) {
+        super(parent, SWT.NO_BACKGROUND | SWT.V_SCROLL | SWT.H_SCROLL);
+        this.sampleView = sampleView;
+
+        Composite composite = new Composite(this, 0);
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 4;
+        composite.setLayout(layout);
+        this.setLayout(new FillLayout());
+
+        for (int i = 0; i < Function.values().length; i++) {
+            Group group = new Group(composite, 0);
+            group.setLayout(new RowLayout());
+            group.setText(Function.values()[i].toString());
+            Button btn = new Button(group, SWT.CHECK);
+            btn.addSelectionListener(this);
+            btn.setText("Break");
+            btn.setSelection(false);
+            breakpoints[Function.values()[i].getNumber()] = btn.getSelection();
+            buttonsBreak[Function.values()[i].getNumber()] = btn;
+        }
+
+        Point size = composite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+        composite.setSize(size);
+        this.setContent(composite);
+        this.setExpandHorizontal(true);
+        this.setExpandVertical(true);
+        this.setMinSize(size);
+        this.layout();
+    }
+
+    void setBreakpoint(final int contextId, final Function function, final boolean enabled) {
+        Message.Builder builder = Message.newBuilder();
+        builder.setContextId(contextId);
+        builder.setType(Type.Response);
+        builder.setExpectResponse(false);
+        builder.setFunction(Function.SETPROP);
+        builder.setProp(Prop.ExpectResponse);
+        builder.setArg0(function.getNumber());
+        builder.setArg1(enabled ? 1 : 0);
+        sampleView.messageQueue.addCommand(builder.build());
+        breakpoints[function.getNumber()] = enabled;
+    }
+
+    @Override
+    public void widgetSelected(SelectionEvent e) {
+        Button btn = (Button) e.widget;
+        Group group = (Group) btn.getParent();
+        int contextId = 0;
+        if (sampleView.current != null)
+            contextId = sampleView.current.contextId;
+        setBreakpoint(contextId, Function.valueOf(group.getText()), btn.getSelection());
+    }
+
+    @Override
+    public void widgetDefaultSelected(SelectionEvent e) {
+    }
+
+    private Function lastFunction = Function.NEG;
+
+    public boolean processMessage(final MessageQueue queue, final Message msg) throws IOException {
+        if (!breakpoints[msg.getFunction().getNumber()])
+            return false;
+        // use DefaultProcessMessage just to register the GL call
+        // but do not send response
+        final int contextId = msg.getContextId();
+        if (msg.getType() == Type.BeforeCall || msg.getType() == Type.AfterCall)
+            queue.defaultProcessMessage(msg, true, false);
+        final Message.Builder builder = Message.newBuilder();
+        builder.setContextId(contextId);
+        builder.setType(Type.Response);
+        builder.setExpectResponse(true);
+        final Shell shell = sampleView.getViewSite().getShell();
+        final boolean send[] = new boolean[1];
+        shell.getDisplay().syncExec(new Runnable() {
+            @Override
+            public void run() {
+                String call = MessageFormatter.format(msg, false);
+                call = call.substring(0, call.indexOf("(")) + ' ' +
+                        msg.getFunction() + call.substring(call.indexOf("("));
+                if (msg.hasData() && msg.getFunction() == Function.glShaderSource)
+                {
+                    int index = call.indexOf("string=") + 7;
+                    String ptr = call.substring(index, call.indexOf(',', index));
+                    call = call.replace(ptr, '"' + msg.getData().toStringUtf8() + '"');
+                }
+                if (msg.getType() == Type.AfterCall)
+                {
+                    call = "skip " + call;
+                    builder.setFunction(Function.SKIP);
+                }
+                else if (msg.getType() == Type.BeforeCall)
+                {
+                    call = "continue " + call;
+                    builder.setFunction(Function.CONTINUE);
+                }
+                else
+                {
+                    assert msg.getType() == Type.AfterGeneratedCall;
+                    assert msg.getFunction() == lastFunction;
+                    call = "skip " + call;
+                    builder.setFunction(Function.SKIP);
+                }
+                InputDialog inputDialog = new InputDialog(shell,
+                            msg.getFunction().toString() + " " + msg.getType().toString(),
+                        "(s)kip, (c)continue, (r)emove bp or glFunction(...)",
+                            call, null);
+                if (Window.OK == inputDialog.open())
+                {
+                    String s = inputDialog.getValue().substring(0, 1).toLowerCase();
+                    if (s.startsWith("s"))
+                    {
+                        builder.setFunction(Function.SKIP);
+                        // AfterCall is skipped, so push BeforeCall to complete
+                        if (queue.getPartialMessage(contextId) != null)
+                            queue.completePartialMessage(contextId);
+                    }
+                    else if (s.startsWith("c"))
+                        builder.setFunction(Function.CONTINUE);
+                    else if (s.startsWith("r"))
+                    {
+                        Button btn = buttonsBreak[msg.getFunction().getNumber()];
+                        btn.setSelection(false);
+                        setBreakpoint(msg.getContextId(), msg.getFunction(), false);
+                        builder.setExpectResponse(false);
+                    }
+                    else
+                    {
+                        MessageParserEx.instance.parse(builder, inputDialog.getValue());
+                        lastFunction = builder.getFunction();
+                        builder.setExpectResponse(true);
+                        // AfterCall is skipped, so push BeforeCall to complete
+                        if (queue.getPartialMessage(contextId) != null)
+                            queue.completePartialMessage(contextId);
+                    }
+                }
+                // else defaults to continue BeforeCall and skip AfterCall
+            }
+        });
+        queue.sendMessage(builder.build());
+        return true;
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/CodeGen.java b/tools/glesv2debugger/src/com/android/glesv2debugger/CodeGen.java
new file mode 100644
index 0000000..28f3a54
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/CodeGen.java
@@ -0,0 +1,1236 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.sdklib.util.SparseIntArray;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.widgets.Shell;
+
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.ByteBuffer;
+
+public class CodeGen implements IRunnableWithProgress {
+    private FileWriter codeFile, makeFile, namesHeaderFile, namesSourceFile;
+    private PrintWriter code, make, namesHeader, namesSource;
+    private FileOutputStream dataOut;
+    private SparseIntArray bufferNames,
+            framebufferNames, programNames, textureNames, shaderNames, renderbufferNames;
+
+    /** return true if msg was a texture upload */
+    private boolean codeGenTextureUpload(final Message msg, final boolean replaceCopy) {
+        String s = null;
+        switch (msg.getFunction()) {
+            case glCompressedTexImage2D:
+                s = MessageFormatter.format(msg, true).replace("arg7", "texData");
+                break;
+            case glCompressedTexSubImage2D:
+            case glTexImage2D:
+            case glTexSubImage2D:
+                s = MessageFormatter.format(msg, true).replace("arg8", "texData");
+                break;
+            case glCopyTexImage2D:
+                if (!replaceCopy) {
+                    code.write(MessageFormatter.format(msg, true));
+                    code.write(";CHKERR;\n");
+                    return true;
+                }
+                assert msg.getArg2() == msg.getPixelFormat(); // TODO
+                s = "//" + MessageFormatter.format(msg, true) + "\n";
+                s += String.format("glTexImage2D(%s, %d, %s, %d, %d, %d, %s, %s, texData);CHKERR;",
+                        GLEnum.valueOf(msg.getArg0()), msg.getArg1(),
+                        GLEnum.valueOf(msg.getArg2()), msg.getArg5(), msg.getArg6(),
+                        msg.getArg7(), GLEnum.valueOf(msg.getPixelFormat()),
+                        GLEnum.valueOf(msg.getPixelType()));
+                break;
+            case glCopyTexSubImage2D:
+                if (!replaceCopy) {
+                    code.write(MessageFormatter.format(msg, true));
+                    code.write(";CHKERR;\n");
+                    return true;
+                }
+                // FIXME: check the texture format & type, and convert
+                s = "//" + MessageFormatter.format(msg, true) + "\n";
+                s += String.format(
+                        "glTexSubImage2D(%s, %d, %d, %d, %d, %d, %s, %s, texData);CHKERR;",
+                        GLEnum.valueOf(msg.getArg0()), msg.getArg1(), msg.getArg2(),
+                        msg.getArg3(), msg.getArg6(), msg.getArg7(),
+                        GLEnum.valueOf(msg.getPixelFormat()), GLEnum.valueOf(msg.getPixelType()));
+                break;
+            default:
+                return false;
+        }
+
+        if (msg.hasData()) {
+            final byte[] data = MessageProcessor.lzfDecompressChunks(msg.getData());
+            try {
+                code.write("{\n");
+                code.format("    void * texData = malloc(%d);CHKERR;\n", data.length);
+                code.format("    FILE * texFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");CHKERR;\n");
+                code.format("    assert(texFile);CHKERR;\n");
+                code.format("    fseek(texFile, %d, SEEK_SET);CHKERR;\n", dataOut.getChannel()
+                        .position());
+                dataOut.write(data);
+                code.format("    fread(texData, %d, 1, texFile);CHKERR;\n", data.length);
+                code.format("    fclose(texFile);CHKERR;\n");
+                code.format("    " + s + ";\n");
+                code.format("    free(texData);CHKERR;\n");
+                code.format("}\n");
+            } catch (IOException e) {
+                e.printStackTrace();
+                assert false;
+            }
+        } else
+            code.write(s.replace("texData", "NULL") + ";\n");
+        return true;
+    }
+
+    private void codeGenServerState(final GLServerState serverState) {
+        code.write("// CodeGenServerState\n");
+        for (int i = 0; i < serverState.enableDisables.size(); i++) {
+            final GLEnum key = GLEnum.valueOf(serverState.enableDisables.keyAt(i));
+            if (serverState.enableDisables.valueAt(i) == 0)
+                code.format("glDisable(%s);CHKERR;\n", key);
+            else
+                code.format("glEnable(%s);CHKERR;\n", key);
+        }
+        for (int i = 0; i < serverState.lastSetter.size(); i++) {
+            final Function key = Function.valueOf(serverState.lastSetter.keyAt(i));
+            final Message msg = serverState.lastSetter.valueAt(i);
+            if (msg == null) {
+                code.format("// %s is default\n", key);
+                continue;
+            }
+            final String s = MessageFormatter.format(msg, true);
+            code.write(s);
+            code.write(";\n");
+        }
+        // TODO: stencil and integers
+    }
+
+    private void codeGenServerShader(final GLServerShader serverShader) {
+        code.write("// CodeGenServerShader\n");
+        for (int i = 0; i < serverShader.shaders.size(); i++) {
+            final int name = serverShader.shaders.keyAt(i);
+            final GLShader shader = serverShader.shaders.valueAt(i);
+            final String id = "shader_" + name;
+            if (shaderNames.indexOfKey(name) < 0) {
+                namesSource.format("GLuint %s = 0;\n", id);
+                namesHeader.format("extern GLuint %s;\n", id);
+            }
+            code.format("%s = glCreateShader(%s);CHKERR;\n", id, shader.type);
+            shaderNames.put(name, name);
+
+            if (shader.source != null) {
+                final String src = shader.source.replace("\r", "").replace("\n", "\\n\\\n")
+                        .replace("\"", "\\\"");
+                code.format("glShaderSource(%s, 1, (const GLchar *[]){\"%s\"}, NULL);CHKERR;\n",
+                                id, src);
+                code.format("glCompileShader(%s);CHKERR;\n", id);
+            }
+        }
+
+        for (int i = 0; i < serverShader.programs.size(); i++) {
+            final int name = serverShader.programs.keyAt(i);
+            final GLProgram program = serverShader.programs.valueAt(i);
+            final String id = "program_" + name;
+            if (programNames.indexOfKey(name) < 0) {
+                namesSource.format("GLuint %s = 0;\n", id);
+                namesHeader.format("extern GLuint %s;\n", id);
+            }
+            code.format("%s = glCreateProgram();CHKERR;\n", id);
+            programNames.put(name, name);
+            code.format("glAttachShader(%s, shader_%d);CHKERR;\n", id,
+                    program.vert);
+            code.format("glAttachShader(%s, shader_%d);CHKERR;\n", id,
+                    program.frag);
+            code.format("glLinkProgram(%s);CHKERR;\n", id);
+            if (serverShader.current == program)
+                code.format("glUseProgram(%s);CHKERR;\n", id);
+        }
+    }
+
+    private void codeGenServerTexture(final GLServerTexture serverTexture, final boolean replaceCopy) {
+        code.write("// CodeGenServerTexture\n");
+        for (int i = 0; i < serverTexture.textures.size(); i++) {
+            final int name = serverTexture.textures.keyAt(i);
+            final GLTexture tex = serverTexture.textures.valueAt(i);
+            final String id = "texture_" + name;
+            if (textureNames.indexOfKey(name) < 0) {
+                namesHeader.format("extern GLuint %s;\n", id);
+                namesSource.format("GLuint %s = 0;\n", id);
+            }
+            code.format("%s = 0;\n", id);
+            textureNames.put(name, name);
+
+            if (name == 0)
+                continue;
+            code.format("glGenTextures(1, &%s);CHKERR;\n", id);
+            String s = String.format("glBindTexture(%s, texture_%d);CHKERR;\n", tex.target,
+                    tex.name);
+            code.write(s);
+            for (final Message msg : tex.contentChanges) {
+                if (codeGenTextureUpload(msg, replaceCopy))
+                    continue;
+                switch (msg.getFunction()) {
+                    case glGenerateMipmap:
+                        s = MessageFormatter.format(msg, true);
+                        break;
+                    default:
+                        assert false;
+                }
+                code.write(s + ";\n");
+            }
+            code.format("glTexParameteriv(%s, GL_TEXTURE_WRAP_S, (GLint[]){%s});CHKERR;\n",
+                    tex.target, tex.wrapS);
+            code.format("glTexParameteriv(%s, GL_TEXTURE_WRAP_T, (GLint[]){%s});CHKERR;\n",
+                    tex.target, tex.wrapT);
+            code.format("glTexParameteriv(%s, GL_TEXTURE_MIN_FILTER, (GLint[]){%s});CHKERR;\n",
+                    tex.target, tex.min);
+            code.format("glTexParameteriv(%s, GL_TEXTURE_MAG_FILTER, (GLint[]){%s});CHKERR;\n",
+                    tex.target, tex.mag);
+        }
+        for (int i = 0; i < serverTexture.tmu2D.length; i++) {
+            code.format("glActiveTexture(%s);CHKERR;\n",
+                    GLEnum.valueOf(GLEnum.GL_TEXTURE0.value + i));
+            code.format("glBindTexture(GL_TEXTURE_2D, texture_%d);CHKERR;\n",
+                    serverTexture.tmu2D[i]);
+        }
+        for (int i = 0; i < serverTexture.tmuCube.length; i++) {
+            code.format("glActiveTexture(%s);CHKERR;\n",
+                    GLEnum.valueOf(GLEnum.GL_TEXTURE0.value + i));
+            code.format("glBindTexture(GL_TEXTURE_CUBE_MAP, texture_%d);CHKERR;\n",
+                    serverTexture.tmuCube[i]);
+        }
+        code.format("glActiveTexture(%s);CHKERR;\n", serverTexture.activeTexture);
+        if (serverTexture.tex2D == null)
+            code.format("glBindTexture(GL_TEXTURE_2D, 0);CHKERR;\n");
+        else
+            code.format("glBindTexture(GL_TEXTURE_2D, texture_%d);CHKERR;\n",
+                    serverTexture.tex2D.name);
+        if (serverTexture.texCube == null)
+            code.format("glBindTexture(GL_TEXTURE_CUBE_MAP, 0);CHKERR;\n");
+        else
+            code.format("glBindTexture(GL_TEXTURE_CUBE_MAP, texture_%d);CHKERR;\n",
+                    serverTexture.texCube.name);
+    }
+
+    private void codeGenBufferData(final ByteBuffer buffer, final String call) {
+        ByteBuffer bfr = buffer;
+        if (buffer.isReadOnly()) {
+            bfr = ByteBuffer.allocate(buffer.capacity());
+            bfr.put(buffer);
+        }
+        final byte[] data = bfr.array();
+        try {
+            code.write("{\n");
+            code.format("    void * bufferData = malloc(%d);\n", data.length);
+            code.format("    FILE * bufferFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");\n");
+            code.format("    assert(bufferFile);\n");
+            code.format("    fseek(bufferFile, %d, SEEK_SET);\n", dataOut.getChannel()
+                    .position());
+            dataOut.write(data);
+            code.format("    fread(bufferData, %d, 1, bufferFile);\n", data.length);
+            code.format("    fclose(bufferFile);\n");
+            code.format("    " + call + ";CHKERR;\n");
+            code.format("    free(bufferData);\n");
+            code.format("}\n");
+        } catch (IOException e) {
+            e.printStackTrace();
+            assert false;
+        }
+    }
+
+    private void codeGenServerVertex(final GLServerVertex v) {
+        code.write("// CodeGenServerVertex\n");
+        for (int i = 0; i < v.buffers.size(); i++) {
+            final int name = v.buffers.keyAt(i);
+            final String id = "buffer_" + name;
+            final GLBuffer buffer = v.buffers.valueAt(i);
+            if (bufferNames.indexOfKey(name) < 0) {
+                namesHeader.format("extern GLuint %s;\n", id);
+                namesSource.format("GLuint %s = 0;\n", id);
+            }
+            code.format("%s = 0;\n", id);
+            bufferNames.put(name, name);
+            if (name == 0)
+                continue;
+            code.format("glGenBuffers(1, &%s);CHKERR;\n", id);
+            if (buffer.target != null) {
+                code.format("glBindBuffer(%s, %s);CHKERR;\n", buffer.target, id);
+                if (buffer.data != null) {
+                    String s = String.format("glBufferData(%s, %d, bufferData, %s)", buffer.target,
+                            buffer.data.capacity(), buffer.usage);
+                    codeGenBufferData(buffer.data, s);
+                }
+            }
+        }
+        // TODO: use MAX_VERTEX_ATTRIBS
+        for (int i = 0; i < v.defaultAttribs.length; i++)
+            code.format("glVertexAttrib4f(%d, %f, %f, %f, %f);CHKERR;\n", i,
+                    v.defaultAttribs[i][0],
+                    v.defaultAttribs[i][1], v.defaultAttribs[i][2], v.defaultAttribs[i][3]);
+        for (int i = 0; i < v.attribPointers.length; i++) {
+            final GLAttribPointer att = v.attribPointers[i];
+            if (att.type == null)
+                continue;
+            if (att.buffer != null)
+                code.format("glBindBuffer(GL_ARRAY_BUFFER, buffer_%d);CHKERR;\n", att.buffer.name);
+            else
+                code.format("glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
+            code.format("glVertexAttribPointer(%d, %d, %s, %b, %d, (const GLvoid *)%d);CHKERR;\n",
+                    i, att.size, att.type, att.normalized, att.stride, att.ptr);
+        }
+        if (v.attribBuffer != null)
+            code.format("glBindBuffer(GL_ARRAY_BUFFER, buffer_%d);CHKERR;\n", v.attribBuffer.name);
+        else
+            code.write("glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
+        if (v.indexBuffer != null)
+            code.format("glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_%d);CHKERR;\n",
+                    v.indexBuffer.name);
+        else
+            code.write("glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);CHKERR;\n");
+    }
+
+    private void codeGenGenNames(final Message msg) {
+        final ByteBuffer names = msg.getData().asReadOnlyByteBuffer();
+        names.order(SampleView.targetByteOrder);
+        SparseIntArray namesArray = null;
+        for (int i = 0; i < msg.getArg0(); i++) {
+            String id = "";
+            final int name = names.getInt();
+            switch (msg.getFunction()) {
+                case glGenBuffers:
+                    id = "buffer";
+                    namesArray = bufferNames;
+                    break;
+                case glGenFramebuffers:
+                    id = "framebuffer";
+                    namesArray = framebufferNames;
+                    break;
+                case glGenRenderbuffers:
+                    id = "renderbuffer";
+                    namesArray = renderbufferNames;
+                    break;
+                case glGenTextures:
+                    id = "texture";
+                    namesArray = textureNames;
+                    break;
+                default:
+                    assert false;
+            }
+            id += "_" + name;
+            if (namesArray.indexOfKey(name) < 0) {
+                namesHeader.format("extern GLuint %s;\n", id);
+                namesSource.format("GLuint %s = 0;\n", id);
+            }
+            code.format("%s = 0;\n", id);
+            namesArray.put(name, name);
+            code.format("%s(1, &%s);CHKERR;\n", msg.getFunction(), id);
+        }
+    }
+
+    private void codeGenDeleteNames(final Message msg) {
+        final ByteBuffer names = msg.getData().asReadOnlyByteBuffer();
+        names.order(SampleView.targetByteOrder);
+        SparseIntArray namesArray = null;
+        for (int i = 0; i < msg.getArg0(); i++) {
+            String id = null;
+            final int name = names.getInt();
+            switch (msg.getFunction()) {
+                case glDeleteBuffers:
+                    id = "buffer";
+                    namesArray = bufferNames;
+                    break;
+                case glDeleteFramebuffers:
+                    id = "framebuffer";
+                    namesArray = framebufferNames;
+                    break;
+                case glDeleteRenderbuffers:
+                    id = "renderbuffer";
+                    namesArray = renderbufferNames;
+                    break;
+                case glDeleteTextures:
+                    id = "texture";
+                    namesArray = textureNames;
+                    break;
+                default:
+                    assert false;
+            }
+            id += "_" + name;
+            code.format("%s = 0;\n", id);
+            namesArray.put(name, 0);
+            code.format("%s(1, &%s);CHKERR;\n", msg.getFunction(), id);
+        }
+    }
+
+    private void codeGenBindNames(final Message msg) {
+        String id = null;
+        SparseIntArray namesArray = null;
+        final int name = msg.getArg1();
+        switch (msg.getFunction()) {
+            case glBindBuffer:
+                id = "buffer";
+                namesArray = bufferNames;
+                break;
+            case glBindFramebuffer:
+                id = "framebuffer";
+                namesArray = framebufferNames;
+                break;
+            case glBindRenderbuffer:
+                id = "renderbuffer";
+                namesArray = renderbufferNames;
+                break;
+            case glBindTexture:
+                id = "texture";
+                namesArray = textureNames;
+                break;
+            default:
+                assert false;
+        }
+        id += "_" + name;
+        if (namesArray.indexOfKey(name) < 0) {
+            namesHeader.format("extern GLuint %s;\n", id);
+            namesSource.format("GLuint %s = 0;\n", id);
+        } else if (namesArray.get(name) != name)
+            code.format("%s = %d;\n", id, name); // name was deleted
+        namesArray.put(name, name);
+        code.write(MessageFormatter.format(msg, true));
+        code.write(";CHKERR;\n");
+    }
+
+    private void codeGenDrawArrays(final GLServerVertex v, final MessageData msgData)
+            throws IOException {
+        final int maxAttrib = msgData.msg.getArg7();
+        if (maxAttrib < 1) {
+            code.write("// no vertex data\n");
+            return;
+        }
+        final byte[] data = msgData.msg.getData().toByteArray();
+        final GLEnum mode = GLEnum.valueOf(msgData.msg.getArg0());
+        final int first = msgData.msg.getArg1(), count = msgData.msg.getArg2();
+        int attribDataStride = 0;
+        for (int i = 0; i < maxAttrib; i++) {
+            final GLAttribPointer att = v.attribPointers[i];
+            if (!att.enabled)
+                continue;
+            if (att.buffer != null)
+                continue;
+            attribDataStride += att.elemSize;
+        }
+        assert attribDataStride * count == data.length;
+        code.write("{\n");
+        if (attribDataStride > 0) {
+            code.format("    FILE * attribFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");CHKERR;\n");
+            code.format("    assert(attribFile);CHKERR;\n");
+            code.format("    fseek(attribFile, %d, SEEK_SET);CHKERR;\n", dataOut.getChannel()
+                    .position());
+            dataOut.write(data);
+            code.format("    char * const attribData = (char *)malloc(%d);\n", first
+                    * attribDataStride + data.length);
+            code.format("    assert(attribData);\n");
+            code.format("    fread(attribData + %d, %d, 1, attribFile);\n",
+                    first * attribDataStride, data.length);
+            code.format("    fclose(attribFile);\n");
+            code.format("    glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
+            int attribDataOffset = 0;
+            for (int i = 0; i < maxAttrib; i++) {
+                final GLAttribPointer att = v.attribPointers[i];
+                if (!att.enabled)
+                    continue;
+                if (att.buffer != null)
+                    continue;
+                code.format(
+                        "    glVertexAttribPointer(%d, %d, %s, %b, %d, attribData + %d);CHKERR;\n",
+                        i, att.size, att.type, att.normalized,
+                        attribDataStride, attribDataOffset);
+                attribDataOffset += att.elemSize;
+            }
+            if (v.attribBuffer != null)
+                code.format("    glBindBuffer(GL_ARRAY_BUFFER, %d);CHKERR;\n",
+                        v.attribBuffer.name);
+        }
+        code.format("    glDrawArrays(%s, %d, %d);CHKERR;\n", mode, first, count);
+        if (attribDataStride > 0)
+            code.format("    free(attribData);CHKERR;\n");
+        code.write("};\n");
+    }
+
+    private void codeGenDrawElements(final GLServerVertex v, final MessageData msgData)
+            throws IOException {
+        final int maxAttrib = msgData.msg.getArg7();
+        if (maxAttrib < 1) {
+            code.write("// no vertex data\n");
+            return;
+        }
+        final GLEnum mode = GLEnum.valueOf(msgData.msg.getArg0());
+        final int count = msgData.msg.getArg1();
+        final GLEnum type = GLEnum.valueOf(msgData.msg.getArg2());
+        String typeName = "GLubyte";
+        if (type == GLEnum.GL_UNSIGNED_SHORT)
+            typeName = "GLushort";
+        int attribDataStride = 0;
+        for (int i = 0; i < maxAttrib; i++) {
+            final GLAttribPointer att = v.attribPointers[i];
+            if (!att.enabled)
+                continue;
+            if (att.buffer != null)
+                continue;
+            attribDataStride += att.elemSize;
+        }
+        code.write("{\n");
+        if (v.indexBuffer == null || attribDataStride > 0) {
+            // need to load user pointer indices and/or attributes
+            final byte[] element = new byte[attribDataStride];
+            final ByteBuffer data = msgData.msg.getData().asReadOnlyByteBuffer();
+            data.order(SampleView.targetByteOrder);
+            final ByteBuffer indexData = ByteBuffer.allocate(count * GLServerVertex.typeSize(type));
+            indexData.order(SampleView.targetByteOrder);
+            final ByteBuffer attribData = ByteBuffer.allocate(count * attribDataStride);
+            attribData.order(SampleView.targetByteOrder);
+            int maxIndex = -1;
+            ByteBuffer indexSrc = data;
+            if (v.indexBuffer != null) {
+                indexSrc = v.indexBuffer.data;
+                indexSrc.position(msgData.msg.getArg3());
+            }
+            indexSrc.order(SampleView.targetByteOrder);
+            for (int i = 0; i < count; i++) {
+                int index = -1;
+                if (type == GLEnum.GL_UNSIGNED_BYTE) {
+                    byte idx = indexSrc.get();
+                    index = idx & 0xff;
+                    indexData.put(idx);
+                } else if (type == GLEnum.GL_UNSIGNED_SHORT) {
+                    short idx = indexSrc.getShort();
+                    index = idx & 0xffff;
+                    indexData.putShort(idx);
+                } else
+                    assert false;
+                data.get(element);
+                attribData.put(element);
+                if (index > maxIndex)
+                    maxIndex = index;
+            }
+            code.format("    FILE * attribFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");CHKERR;\n");
+            code.format("    assert(attribFile);CHKERR;\n");
+            code.format("    fseek(attribFile, 0x%X, SEEK_SET);CHKERR;\n",
+                    dataOut.getChannel().position());
+            dataOut.write(indexData.array());
+            code.format("    %s * const indexData = (%s *)malloc(%d);\n", typeName, typeName,
+                    indexData.capacity());
+            code.format("    assert(indexData);\n");
+            code.format("    fread(indexData, %d, 1, attribFile);\n", indexData.capacity());
+            if (attribDataStride > 0) {
+                code.format("    glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
+                for (int i = 0; i < maxAttrib; i++) {
+                    final GLAttribPointer att = v.attribPointers[i];
+                    if (!att.enabled)
+                        continue;
+                    if (att.buffer != null)
+                        continue;
+                    code.format("    char * const attrib%d = (char *)malloc(%d);\n",
+                            i, att.elemSize * (maxIndex + 1));
+                    code.format("    assert(attrib%d);\n", i);
+                    code.format(
+                            "    glVertexAttribPointer(%d, %d, %s, %b, %d, attrib%d);CHKERR;\n",
+                            i, att.size, att.type, att.normalized, att.elemSize, i);
+                }
+                dataOut.write(attribData.array());
+                code.format("    for (%s i = 0; i < %d; i++) {\n", typeName, count);
+                for (int i = 0; i < maxAttrib; i++) {
+                    final GLAttribPointer att = v.attribPointers[i];
+                    if (!att.enabled)
+                        continue;
+                    if (att.buffer != null)
+                        continue;
+                    code.format(
+                            "        fread(attrib%d + indexData[i] * %d, %d, 1, attribFile);\n",
+                            i, att.elemSize, att.elemSize);
+                }
+                code.format("    }\n");
+                if (v.attribBuffer != null)
+                    code.format("    glBindBuffer(GL_ARRAY_BUFFER, %d);CHKERR;\n",
+                            v.attribBuffer.name);
+            }
+            code.format("    fclose(attribFile);\n");
+        }
+        if (v.indexBuffer != null)
+            code.format("    glDrawElements(%s, %d, %s, (const void *)%d);CHKERR;\n",
+                    mode, count, type, msgData.msg.getArg3());
+        else {
+            code.format("    glDrawElements(%s, %d, %s, indexData);CHKERR;\n",
+                    mode, count, type);
+            code.format("    free(indexData);\n");
+        }
+        for (int i = 0; i < maxAttrib; i++) {
+            final GLAttribPointer att = v.attribPointers[i];
+            if (!att.enabled)
+                continue;
+            if (att.buffer != null)
+                continue;
+            code.format("    free(attrib%d);\n", i);
+        }
+        code.write("};\n");
+    }
+
+    private void codeGenDraw(final GLServerVertex v, final MessageData msgData)
+            throws IOException {
+        final int maxAttrib = msgData.msg.getArg7();
+        if (maxAttrib < 1) {
+            code.write("// no vertex data\n");
+            return;
+        }
+        final int count = msgData.attribs[0].length / 4;
+        final GLEnum mode = GLEnum.valueOf(msgData.msg.getArg0());
+        final ByteBuffer attribData = ByteBuffer.allocate(maxAttrib * count * 16);
+        attribData.order(SampleView.targetByteOrder);
+        for (int i = 0; i < count; i++)
+            for (int j = 0; j < maxAttrib; j++)
+                for (int k = 0; k < 4; k++)
+                    attribData.putFloat(msgData.attribs[j][i * 4 + k]);
+        assert attribData.remaining() == 0;
+        code.write("{\n");
+        code.format("    FILE * attribFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");CHKERR;\n");
+        code.format("    assert(attribFile);CHKERR;\n");
+        code.format("    fseek(attribFile, 0x%X, SEEK_SET);CHKERR;\n",
+                dataOut.getChannel().position());
+        dataOut.write(attribData.array());
+        code.format("    char * const attribData = (char *)malloc(%d);\n", attribData.capacity());
+        code.format("    assert(attribData);\n");
+        code.format("    fread(attribData, %d, 1, attribFile);\n", attribData.capacity());
+        code.format("    fclose(attribFile);\n");
+        code.format("    glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
+        for (int i = 0; i < maxAttrib; i++) {
+            final GLAttribPointer att = v.attribPointers[i];
+            assert msgData.attribs[i].length == count * 4;
+            code.format(
+                    "    glVertexAttribPointer(%d, %d, GL_FLOAT, GL_FALSE, %d, attribData + %d);CHKERR;\n",
+                        i, att.size, maxAttrib * 16, i * 16);
+        }
+        code.format("    glDrawArrays(%s, 0, %d);CHKERR;\n", mode, count);
+        code.format("    free(attribData);\n");
+        if (v.attribBuffer != null)
+            code.format("    glBindBuffer(GL_ARRAY_BUFFER, %d);CHKERR;\n",
+                        v.attribBuffer.name);
+        code.write("};\n");
+    }
+
+    private void codeGenFunction(final Context ctx, final MessageData msgData)
+            throws IOException {
+        final Message msg = msgData.msg;
+        String call = MessageFormatter.format(msg, true);
+        switch (msg.getFunction()) {
+            case glActiveTexture:
+            case glAttachShader:
+            case glBindAttribLocation:
+                break;
+            case glBindBuffer:
+            case glBindFramebuffer:
+            case glBindRenderbuffer:
+            case glBindTexture:
+                codeGenBindNames(msg);
+                return;
+            case glBlendColor:
+            case glBlendEquation:
+            case glBlendEquationSeparate:
+            case glBlendFunc:
+            case glBlendFuncSeparate:
+                break;
+            case glBufferData:
+                call = MessageFormatter.format(msg, true).replace("arg2", "bufferData");
+                codeGenBufferData(msg.getData().asReadOnlyByteBuffer(), call);
+                return;
+            case glBufferSubData:
+                call = MessageFormatter.format(msg, true).replace("arg3", "bufferData");
+                codeGenBufferData(msg.getData().asReadOnlyByteBuffer(), call);
+                return;
+            case glCheckFramebufferStatus:
+            case glClear:
+            case glClearColor:
+            case glClearDepthf:
+            case glClearStencil:
+            case glColorMask:
+            case glCompileShader:
+                break;
+            case glCompressedTexImage2D:
+            case glCompressedTexSubImage2D:
+            case glCopyTexImage2D:
+            case glCopyTexSubImage2D:
+                codeGenTextureUpload(msg, false);
+                return;
+            case glCreateProgram:
+                namesHeader.format("extern GLuint program_%d;\n", msg.getRet());
+                namesSource.format("GLuint program_%d = 0;\n", msg.getRet());
+                code.format("program_%d = glCreateProgram();CHKERR;\n", msg.getRet());
+                return;
+            case glCreateShader:
+                namesHeader.format("extern GLuint shader_%d;\n", msg.getRet());
+                namesSource.format("GLuint shader_%d = 0;\n", msg.getRet());
+                code.format("shader_%d = %s;\n", msg.getRet(), call);
+                return;
+            case glCullFace:
+                break;
+            case glDeleteBuffers:
+            case glDeleteFramebuffers:
+            case glDeleteProgram:
+                programNames.put(msg.getArg0(), 0);
+                break;
+            case glDeleteRenderbuffers:
+                codeGenDeleteNames(msg);
+                return;
+            case glDeleteShader:
+                shaderNames.put(msg.getArg0(), 0);
+                return;
+            case glDeleteTextures:
+                codeGenDeleteNames(msg);
+                return;
+            case glDepthFunc:
+            case glDepthMask:
+            case glDepthRangef:
+            case glDetachShader:
+            case glDisable:
+            case glDisableVertexAttribArray:
+                break;
+            case glDrawArrays:
+                // CodeGenDraw(ctx.serverVertex, msgData);
+                codeGenDrawArrays(ctx.serverVertex, msgData);
+                return;
+            case glDrawElements:
+                // CodeGenDraw(ctx.serverVertex, msgData);
+                codeGenDrawElements(ctx.serverVertex, msgData);
+                return;
+            case glEnable:
+            case glEnableVertexAttribArray:
+            case glFinish:
+            case glFlush:
+            case glFramebufferRenderbuffer:
+            case glFramebufferTexture2D:
+            case glFrontFace:
+                break;
+            case glGenBuffers:
+                codeGenGenNames(msg);
+                return;
+            case glGenerateMipmap:
+                break;
+            case glGenFramebuffers:
+            case glGenRenderbuffers:
+            case glGenTextures:
+                codeGenGenNames(msg);
+                return;
+            case glGetActiveAttrib:
+            case glGetActiveUniform:
+            case glGetAttachedShaders:
+                break;
+            case glGetAttribLocation:
+                call = String.format("assert(%d == %s)", msg.getRet(), call);
+                break;
+            case glGetBooleanv:
+            case glGetBufferParameteriv:
+                return; // TODO
+            case glGetError:
+                code.write("CHKERR;\n");
+                return;
+            case glGetFloatv:
+            case glGetFramebufferAttachmentParameteriv:
+            case glGetIntegerv:
+            case glGetProgramiv:
+            case glGetProgramInfoLog:
+            case glGetRenderbufferParameteriv:
+            case glGetShaderiv:
+            case glGetShaderInfoLog:
+            case glGetShaderPrecisionFormat:
+            case glGetShaderSource:
+            case glGetString:
+            case glGetTexParameterfv:
+            case glGetTexParameteriv:
+            case glGetUniformfv:
+            case glGetUniformiv:
+                return;
+            case glGetUniformLocation:
+                call = String.format("assert(%d == %s)", msg.getRet(), call);
+                break;
+            case glGetVertexAttribfv:
+            case glGetVertexAttribiv:
+            case glGetVertexAttribPointerv:
+                return; // TODO
+            case glHint:
+            case glIsBuffer:
+            case glIsEnabled:
+            case glIsFramebuffer:
+            case glIsProgram:
+            case glIsRenderbuffer:
+            case glIsShader:
+            case glIsTexture:
+            case glLineWidth:
+            case glLinkProgram:
+            case glPixelStorei:
+            case glPolygonOffset:
+                break;
+            case glReadPixels:
+                return; // TODO
+            case glReleaseShaderCompiler:
+            case glRenderbufferStorage:
+            case glSampleCoverage:
+            case glScissor:
+                break;
+            case glShaderBinary:
+                return; // TODO
+            case glShaderSource:
+                call = String.format(
+                        "glShaderSource(shader_%d, 1, (const char * []){\"%s\"}, NULL)",
+                        msg.getArg0(),
+                        msg.getData().toStringUtf8().replace("\r", "").replace("\n", "\\n\\\n")
+                                .replace("\"", "\\\"")
+                                );
+                break;
+            case glStencilFunc:
+            case glStencilFuncSeparate:
+            case glStencilMask:
+            case glStencilMaskSeparate:
+            case glStencilOp:
+            case glStencilOpSeparate:
+                break;
+            case glTexImage2D:
+                codeGenTextureUpload(msg, false);
+                return;
+            case glTexParameterf:
+                break;
+            case glTexParameterfv:
+                return; // TODO
+            case glTexParameteri:
+                break;
+            case glTexParameteriv:
+                return; // TODO
+            case glTexSubImage2D:
+                codeGenTextureUpload(msg, false);
+                return;
+            case glUniform1f:
+            case glUniform1fv:
+            case glUniform1i:
+            case glUniform1iv:
+            case glUniform2f:
+            case glUniform2fv:
+            case glUniform2i:
+            case glUniform2iv:
+            case glUniform3f:
+            case glUniform3fv:
+            case glUniform3i:
+            case glUniform3iv:
+            case glUniform4f:
+            case glUniform4fv:
+            case glUniform4i:
+            case glUniform4iv:
+            case glUniformMatrix2fv:
+            case glUniformMatrix3fv:
+            case glUniformMatrix4fv:
+            case glUseProgram:
+            case glValidateProgram:
+            case glVertexAttrib1f:
+            case glVertexAttrib1fv:
+            case glVertexAttrib2f:
+            case glVertexAttrib2fv:
+            case glVertexAttrib3f:
+            case glVertexAttrib3fv:
+            case glVertexAttrib4f:
+            case glVertexAttrib4fv:
+                break;
+            case glVertexAttribPointer:
+                // if it's user pointer, then CodeGenDrawArrays/Elements will
+                // replace it with loaded data just before the draw
+                call = call.replace("arg5", "(const void *)0x" +
+                        Integer.toHexString(msg.getArg5()));
+                break;
+            case glViewport:
+                break;
+            case eglSwapBuffers:
+                return;
+            default:
+                assert false;
+                return;
+        }
+        if (call.indexOf("glEnable(/*cap*/ GL_TEXTURE_2D)") >= 0)
+            return;
+        else if (call.indexOf("glDisable(/*cap*/ GL_TEXTURE_2D)") >= 0)
+            return;
+        else if (call.indexOf("glActiveTexture(/*texture*/ GL_TEXTURE_2D)") >= 0)
+            return;
+        code.write(call + ";CHKERR;\n");
+    }
+
+    private void codeGenSetup(final Context ctx) {
+        try {
+            codeFile = new FileWriter("frame_setup.cpp", false);
+            code = new PrintWriter(codeFile);
+            dataOut = new FileOutputStream("frame_data.bin", false);
+            namesHeaderFile = new FileWriter("frame_names.h", false);
+            namesHeader = new PrintWriter(namesHeaderFile);
+            namesSourceFile = new FileWriter("frame_names.cpp", false);
+            namesSource = new PrintWriter(namesSourceFile);
+        } catch (IOException e) {
+            e.printStackTrace();
+            assert false;
+        }
+        bufferNames = new SparseIntArray();
+        framebufferNames = new SparseIntArray();
+        programNames = new SparseIntArray();
+        textureNames = new SparseIntArray();
+        shaderNames = new SparseIntArray();
+        renderbufferNames = new SparseIntArray();
+
+        namesHeader.write("#include <stdlib.h>\n");
+        namesHeader.write("#include <stdio.h>\n");
+        namesHeader.write("#include <assert.h>\n");
+        namesHeader.write("#include <GLES2/gl2.h>\n");
+        namesHeader.write("#include <GLES2/gl2ext.h>\n");
+        namesHeader.write("#define CHKERR assert(GL_NO_ERROR == glGetError());/**/\n");
+        namesHeader.write("void FrameSetup();\n");
+        namesHeader.write("extern const unsigned int FrameCount;\n");
+        namesHeader.write("extern const GLuint program_0;\n");
+
+        namesSource.write("/*\n" + 
+        " * Copyright (C) 2011 The Android Open Source Project\n" + 
+        " *\n" + 
+        " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + 
+        " * you may not use this file except in compliance with the License.\n" + 
+        " * You may obtain a copy of the License at\n" + 
+        " *\n" + 
+        " *      http://www.apache.org/licenses/LICENSE-2.0\n" + 
+        " *\n" + 
+        " * Unless required by applicable law or agreed to in writing, software\n" + 
+        " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + 
+        " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + 
+        " * See the License for the specific language governing permissions and\n" + 
+        " * limitations under the License.\n" + 
+        " */\n" + 
+        "\n" + 
+        "#include <stdlib.h>\n" + 
+        "#include <stdio.h>\n" + 
+        "\n" + 
+        "#include <EGL/egl.h>\n" + 
+        "#include <GLES2/gl2.h>\n" + 
+        "#include <GLES2/gl2ext.h>\n" + 
+        "\n" + 
+        "#include <ui/FramebufferNativeWindow.h>\n" + 
+        "#include <ui/EGLUtils.h>\n" + 
+        "\n" + 
+        "#include <private/ui/android_natives_priv.h>\n" + 
+        "\n" + 
+        "#include <surfaceflinger/Surface.h>\n" + 
+        "#include <surfaceflinger/ISurface.h>\n" + 
+        "#include <surfaceflinger/SurfaceComposerClient.h>\n" + 
+        "\n" + 
+        "using namespace android;\n" + 
+        "\n" + 
+        "static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE)\n" + 
+        "{\n" + 
+        "    if (returnVal != EGL_TRUE) {\n" + 
+        "        fprintf(stderr, \"%s() returned %d\\n\", op, returnVal);\n" + 
+        "    }\n" + 
+        "\n" + 
+        "    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error\n" + 
+        "            = eglGetError()) {\n" + 
+        "        fprintf(stderr, \"after %s() eglError %s (0x%x)\\n\", op, EGLUtils::strerror(error),\n" + 
+        "                error);\n" + 
+        "    }\n" + 
+        "}\n" + 
+        "\n" + 
+        "static EGLDisplay dpy;\n" + 
+        "static EGLSurface surface;\n" + 
+        "\n" + 
+        "#include \"frame_names.h\"\n" + 
+        "const GLuint program_0 = 0;\n" + 
+        "int main(int argc, char** argv)\n" + 
+        "{\n" + 
+        "    EGLBoolean returnValue;\n" + 
+        "    EGLConfig myConfig = {0};\n" + 
+        "\n" + 
+        "    EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };\n" + 
+        "    EGLint majorVersion;\n" + 
+        "    EGLint minorVersion;\n" + 
+        "    EGLContext context;\n" + 
+        "    EGLint w, h;\n" + 
+        "\n" + 
+        "\n" + 
+        "    checkEglError(\"<init>\");\n" + 
+        "    dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);\n" + 
+        "    checkEglError(\"eglGetDisplay\");\n" + 
+        "    if (dpy == EGL_NO_DISPLAY) {\n" + 
+        "        printf(\"eglGetDisplay returned EGL_NO_DISPLAY.\\n\");\n" + 
+        "        return 0;\n" + 
+        "    }\n" + 
+        "\n" + 
+        "    returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);\n" + 
+        "    checkEglError(\"eglInitialize\", returnValue);\n" + 
+        "    if (returnValue != EGL_TRUE) {\n" + 
+        "        printf(\"eglInitialize failed\\n\");\n" + 
+        "        return 0;\n" + 
+        "    }\n" + 
+        "\n" + 
+        "    sp<SurfaceComposerClient> spClient;\n" + 
+        "    sp<SurfaceControl> spControl;\n" + 
+        "    sp<Surface> spSurface;\n" + 
+        "\n" + 
+        "    // create a client to surfaceflinger\n" + 
+        "    spClient = new SurfaceComposerClient();\n" + 
+        "\n" + 
+        "    spControl = spClient->createSurface(getpid(), 0, 1280, 752, PIXEL_FORMAT_RGBX_8888);\n" + 
+        "    spClient->openTransaction();\n" + 
+        "    spControl->setLayer(350000);\n" + 
+        "    spControl->show();\n" + 
+        "    spClient->closeTransaction();\n" + 
+        "\n" + 
+        "    spSurface = spControl->getSurface();\n" + 
+        "    EGLNativeWindowType window = spSurface.get();\n" + 
+        "\n" + 
+        "    printf(\"window=%p\\n\", window);\n" + 
+        "    EGLint attrib_list[] = {\n" + 
+        "        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,\n" + 
+        "        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,\n" + 
+        "        EGL_BUFFER_SIZE, 32,\n" + 
+        "        EGL_RED_SIZE, 8,\n" + 
+        "        EGL_GREEN_SIZE, 8,\n" + 
+        "        EGL_BLUE_SIZE, 8,\n" + 
+        "        EGL_NONE\n" + 
+        "    };\n" + 
+        "\n" + 
+        "    EGLConfig configs[12] = {0};\n" + 
+        "    int num_config = -1;\n" + 
+        "    eglChooseConfig(dpy, attrib_list, configs, sizeof(configs) / sizeof(*configs), &num_config);\n" + 
+        "    printf(\"eglChooseConfig %d \\n\", num_config);\n" + 
+        "\n" + 
+        "    surface = eglCreateWindowSurface(dpy, configs[0], window, NULL);\n" + 
+        "    checkEglError(\"eglCreateWindowSurface\");\n" + 
+        "    if (surface == EGL_NO_SURFACE) {\n" + 
+        "        printf(\"gelCreateWindowSurface failed.\\n\");\n" + 
+        "        return 0;\n" + 
+        "    }\n" + 
+        "\n" + 
+        "    context = eglCreateContext(dpy, configs[0], EGL_NO_CONTEXT, context_attribs);\n" + 
+        "    checkEglError(\"eglCreateContext\");\n" + 
+        "    if (context == EGL_NO_CONTEXT) {\n" + 
+        "        printf(\"eglCreateContext failed\\n\");\n" + 
+        "        return 0;\n" + 
+        "    }\n" + 
+        "    printf(\"context=%p \\n\", context);\n" + 
+        "\n" + 
+        "    returnValue = eglMakeCurrent(dpy, surface, surface, context);\n" + 
+        "    checkEglError(\"eglMakeCurrent\", returnValue);\n" + 
+        "    if (returnValue != EGL_TRUE) {\n" + 
+        "        return 0;\n" + 
+        "    }\n" + 
+        "\n" + 
+        "    glClearColor(1,1,1,1);\n" + 
+        "    glClear(GL_COLOR_BUFFER_BIT);\n" + 
+        "\n" + 
+        "    FrameSetup();\n" + 
+        "    while (true)\n" + 
+        "        for (unsigned int i = 0; i < FrameCount; i++) {\n" + 
+        "            Frames[i]();\n" + 
+        "            eglSwapBuffers(dpy, surface);\n" + 
+        "            printf(\"press ENTER after Frame%d \\n\", i);\n" + 
+        "            getchar();\n" + 
+        "        }\n" + 
+        "\n" + 
+        "    return 0;\n" + 
+        "}");
+
+        code.write("#include \"frame_names.h\"\n");
+        code.write("void FrameSetup(){\n");
+
+        codeGenServerState(ctx.serverState);
+        codeGenServerShader(ctx.serverShader);
+        codeGenServerTexture(ctx.serverTexture, true);
+        codeGenServerVertex(ctx.serverVertex);
+
+        code.write("}\n");
+
+        try {
+            codeFile.close();
+            makeFile = new FileWriter("Android.mk", false);
+            make = new PrintWriter(makeFile);
+            make.write("LOCAL_PATH:= $(call my-dir)\n" +
+                    "include $(CLEAR_VARS)\n" +
+                    "LOCAL_SRC_FILES := \\\n");
+        } catch (IOException e) {
+            e.printStackTrace();
+            assert false;
+        }
+    }
+
+    private void codeGenCleanup() {
+        make.write("    frame_setup.cpp \\\n");
+        make.write("    frame_names.cpp \\\n");
+        make.write("#\n");
+        make.write(
+                "LOCAL_SHARED_LIBRARIES := \\\n" + 
+                "    libcutils \\\n" + 
+                "    libutils \\\n" + 
+                "    libEGL \\\n" + 
+                "    libGLESv2 \\\n" + 
+                "    libui \\\n" + 
+                "    libhardware \\\n" + 
+                "    libgui\n" + 
+                "\n" + 
+                "LOCAL_MODULE:= gles2dbg\n" + 
+                "\n" + 
+                "LOCAL_MODULE_TAGS := optional\n" + 
+                "\n" + 
+                "LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -O0 -g -DDEBUG -UNDEBUG\n" + 
+                "\n" + 
+                "include $(BUILD_EXECUTABLE)");
+        try {
+            dataOut.flush();
+            dataOut.close();
+            codeFile.close();
+            makeFile.close();
+            namesHeaderFile.close();
+            namesSourceFile.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+            assert false;
+        }
+        dataOut = null;
+        code = null;
+        codeFile = null;
+        make = null;
+        makeFile = null;
+
+        bufferNames = null;
+        framebufferNames = null;
+        programNames = null;
+        textureNames = null;
+        shaderNames = null;
+        renderbufferNames = null;
+    }
+
+    private DebugContext dbgCtx;
+    private int count;
+    private IProgressMonitor progress;
+
+    @Override
+    public void run(IProgressMonitor monitor) {
+        progress.beginTask("CodeGenFrames", count + 2);
+        Context ctx = dbgCtx.getFrame(0).startContext.clone();
+        codeGenSetup(ctx);
+        progress.worked(1);
+        for (int i = 0; i < count; i++) {
+            try {
+                codeFile = new FileWriter("frame" + i + ".cpp", false);
+                code = new PrintWriter(codeFile);
+            } catch (IOException e1) {
+                e1.printStackTrace();
+                assert false;
+            }
+            make.format("    frame%d.cpp \\\n", i);
+
+            code.write("#include \"frame_names.h\"\n");
+            code.format("void Frame%d(){\n", i);
+            final Frame frame = dbgCtx.getFrame(i);
+            for (int j = 0; j < frame.size(); j++) {
+                final MessageData msgData = frame.get(j);
+                code.format("/* frame function %d: %s %s*/\n", j, msgData.msg.getFunction(),
+                        MessageFormatter.format(msgData.msg, false));
+                ctx.processMessage(msgData.msg);
+                try {
+                    codeGenFunction(ctx, msgData);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    assert false;
+                }
+            }
+            code.write("}\n");
+            try {
+                codeFile.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+                assert false;
+            }
+            progress.worked(1);
+        }
+        for (int i = 0; i < count; i++)
+            namesHeader.format("void Frame%d();\n", i);
+        namesHeader.format("extern void (* Frames[%d])();\n", count);
+        namesSource.format("void (* Frames[%d])() = {\n", count);
+        for (int i = 0; i < count; i++) {
+            namesSource.format("    Frame%d,\n", i);
+        }
+        namesSource.write("};\n");
+        namesSource.format("const unsigned int FrameCount = %d;\n", count);
+        codeGenCleanup();
+        progress.worked(1);
+    }
+
+    void codeGenFrames(final DebugContext dbgCtx, int count, final Shell shell) {
+        this.dbgCtx = dbgCtx;
+        this.count = count;
+        ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell);
+        this.progress = dialog.getProgressMonitor();
+        try {
+            dialog.run(false, true, this);
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+            assert false;
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        this.dbgCtx = null;
+        this.count = 0;
+        progress = null;
+    }
+
+    void codeGenFrame(final Frame frame) {
+        Context ctx = frame.startContext.clone();
+        codeGenSetup(ctx);
+        try {
+            codeFile = new FileWriter("frame0.cpp", false);
+            code = new PrintWriter(codeFile);
+        } catch (IOException e1) {
+            e1.printStackTrace();
+            assert false;
+        }
+        make.format("    frame0.cpp \\\n");
+        code.write("#include \"frame_names.h\"\n");
+        code.format("void Frame0(){\n");
+        for (int i = 0; i < frame.size(); i++) {
+            final MessageData msgData = frame.get(i);
+            code.format("/* frame function %d: %s %s*/\n", i, msgData.msg.getFunction(),
+                    MessageFormatter.format(msgData.msg, false));
+            ctx.processMessage(msgData.msg);
+            try {
+                codeGenFunction(ctx, msgData);
+            } catch (IOException e) {
+                e.printStackTrace();
+                assert false;
+            }
+        }
+        code.write("}\n");
+        namesHeader.write("void Frame0();\n");
+        namesHeader.write("extern void (* Frames[1])();\n");
+        namesSource.write("void (* Frames[1])() = {Frame0};\n");
+        namesSource.write("const unsigned int FrameCount = 1;\n");
+        codeGenCleanup();
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java b/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java
new file mode 100644
index 0000000..122695b
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java
@@ -0,0 +1,532 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.DataType;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Prop;
+import com.android.sdklib.util.SparseArray;
+import com.android.sdklib.util.SparseIntArray;
+import com.google.protobuf.ByteString;
+
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+class Frame {
+    public final long filePosition;
+    private int callsCount;
+
+    final Context startContext;
+    private ArrayList<MessageData> calls = new ArrayList<MessageData>();
+
+    Frame(final Context context, final long filePosition) {
+        this.startContext = context.clone();
+        this.filePosition = filePosition;
+    }
+
+    void add(final MessageData msgData) {
+        calls.add(msgData);
+    }
+
+    void increaseCallsCount() {
+        callsCount++;
+    }
+
+    Context computeContext(final MessageData call) {
+        Context ctx = startContext.clone();
+        for (int i = 0; i < calls.size(); i++)
+            if (call == calls.get(i))
+                return ctx;
+            else
+                ctx.processMessage(calls.get(i).msg);
+        assert false;
+        return ctx;
+    }
+
+    int size() {
+        return callsCount;
+    }
+
+    MessageData get(final int i) {
+        return calls.get(i);
+    }
+
+    ArrayList<MessageData> get() {
+        return calls;
+    }
+
+    void unload() {
+        if (calls == null)
+            return;
+        calls.clear();
+        calls = null;
+    }
+
+    void load(final RandomAccessFile file) {
+        if (calls != null && calls.size() == callsCount)
+            return;
+        try {
+            Context ctx = startContext.clone();
+            calls = new ArrayList<MessageData>(callsCount);
+            final long oriPosition = file.getFilePointer();
+            file.seek(filePosition);
+            for (int i = 0; i < callsCount; i++) {
+                int len = file.readInt();
+                if (SampleView.targetByteOrder == ByteOrder.LITTLE_ENDIAN)
+                    len = Integer.reverseBytes(len);
+                final byte[] data = new byte[len];
+                file.read(data);
+                Message msg = Message.parseFrom(data);
+                ctx.processMessage(msg);
+                final MessageData msgData = new MessageData(Display.getCurrent(), msg, ctx);
+                calls.add(msgData);
+            }
+            file.seek(oriPosition);
+        } catch (IOException e) {
+            e.printStackTrace();
+            assert false;
+        }
+    }
+}
+
+class DebugContext {
+    boolean uiUpdate = false;
+    final int contextId;
+    Context currentContext;
+    private ArrayList<Frame> frames = new ArrayList<Frame>(128);
+    private Frame lastFrame;
+    private Frame loadedFrame;
+    private RandomAccessFile file;
+
+    DebugContext(final int contextId) {
+        this.contextId = contextId;
+        currentContext = new Context(contextId);
+        try {
+            file = new RandomAccessFile("0x" + Integer.toHexString(contextId) +
+                    ".gles2dbg", "rw");
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+            assert false;
+        }
+    }
+
+    /** write message to file; if frame not null, then increase its call count */
+    void saveMessage(final Message msg, final RandomAccessFile file, Frame frame) {
+        synchronized (file) {
+            if (frame != null)
+                frame.increaseCallsCount();
+            final byte[] data = msg.toByteArray();
+            final ByteBuffer len = ByteBuffer.allocate(4);
+            len.order(SampleView.targetByteOrder);
+            len.putInt(data.length);
+            try {
+                if (SampleView.targetByteOrder == ByteOrder.BIG_ENDIAN)
+                    file.writeInt(data.length);
+                else
+                    file.writeInt(Integer.reverseBytes(data.length));
+                file.write(data);
+            } catch (IOException e) {
+                e.printStackTrace();
+                assert false;
+            }
+        }
+    }
+
+    /**
+     * Caches new Message, and formats into MessageData for current frame; this
+     * function is called exactly once for each new Message
+     */
+    void processMessage(final Message newMsg) {
+        Message msg = newMsg;
+        if (msg.getFunction() == Function.SETPROP) {
+            // GL impl. consts should have been sent before any GL call messages
+            assert frames.size() == 0;
+            assert lastFrame == null;
+            assert msg.getProp() == Prop.GLConstant;
+            switch (GLEnum.valueOf(msg.getArg0())) {
+                case GL_MAX_VERTEX_ATTRIBS:
+                    currentContext.serverVertex = new GLServerVertex(msg.getArg1());
+                    break;
+                case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
+                    currentContext.serverTexture = new GLServerTexture(currentContext,
+                            msg.getArg1());
+                    break;
+                default:
+                    assert false;
+                    return;
+            }
+            saveMessage(msg, file, null);
+            return;
+        }
+
+        if (lastFrame == null) {
+            // first real message after the GL impl. consts
+            synchronized (file) {
+                try {
+                    lastFrame = new Frame(currentContext, file.getFilePointer());
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    assert false;
+                }
+            }
+            synchronized (frames) {
+                frames.add(lastFrame);
+            }
+            assert loadedFrame == null;
+            loadedFrame = lastFrame;
+        }
+        currentContext.processMessage(msg);
+        if (msg.hasDataType() && msg.getDataType() == DataType.ReferencedImage) {
+            // decode referenced image so it doesn't rely on context later on
+            final byte[] referenced = MessageProcessor.lzfDecompressChunks(msg.getData());
+            currentContext.readPixelRef = MessageProcessor.decodeReferencedImage(
+                        currentContext.readPixelRef, referenced);
+            final byte[] decoded = MessageProcessor.lzfCompressChunks(
+                        currentContext.readPixelRef, referenced.length);
+            msg = msg.toBuilder().setDataType(DataType.NonreferencedImage)
+                        .setData(ByteString.copyFrom(decoded)).build();
+        }
+        saveMessage(msg, file, lastFrame);
+        if (loadedFrame == lastFrame) {
+            // frame selected for view, so format MessageData
+            final MessageData msgData = new MessageData(Display.getCurrent(), msg, currentContext);
+            lastFrame.add(msgData);
+            uiUpdate = true;
+        }
+        if (msg.getFunction() != Function.eglSwapBuffers)
+            return;
+        synchronized (frames) {
+            if (loadedFrame != lastFrame)
+                lastFrame.unload();
+            try {
+                frames.add(lastFrame = new Frame(currentContext, file.getFilePointer()));
+                // file.getChannel().force(false);
+                uiUpdate = true;
+            } catch (IOException e) {
+                e.printStackTrace();
+                assert false;
+            }
+        }
+        return;
+    }
+
+    Frame getFrame(int index) {
+        synchronized (frames) {
+            Frame newFrame = frames.get(index);
+            if (loadedFrame != null && loadedFrame != lastFrame && newFrame != loadedFrame) {
+                loadedFrame.unload();
+                uiUpdate = true;
+            }
+            loadedFrame = newFrame;
+            synchronized (file) {
+                loadedFrame.load(file);
+            }
+            return loadedFrame;
+        }
+    }
+
+    int frameCount() {
+        synchronized (frames) {
+            return frames.size();
+        }
+    }
+}
+
+/** aggregate of GL states */
+public class Context implements Cloneable {
+    public final int contextId;
+    public ArrayList<Context> shares = new ArrayList<Context>(); // self too
+    public GLServerVertex serverVertex;
+    public GLServerShader serverShader = new GLServerShader(this);
+    public GLServerState serverState = new GLServerState(this);
+    public GLServerTexture serverTexture;
+
+    byte[] readPixelRef = new byte[0];
+
+    public Context(int contextId) {
+        this.contextId = contextId;
+        shares.add(this);
+    }
+
+    @Override
+    public Context clone() {
+        try {
+            Context copy = (Context) super.clone();
+            // FIXME: context sharing list clone
+            copy.shares = new ArrayList<Context>(1);
+            copy.shares.add(copy);
+            if (serverVertex != null)
+                copy.serverVertex = serverVertex.clone();
+            copy.serverShader = serverShader.clone(copy);
+            copy.serverState = serverState.clone();
+            if (serverTexture != null)
+                copy.serverTexture = serverTexture.clone(copy);
+            // don't need to clone readPixelsRef, since referenced images
+            // are decoded when they are encountered
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+
+    /** mainly updating states */
+    public void processMessage(Message msg) {
+        if (serverVertex.process(msg))
+            return;
+        if (serverShader.processMessage(msg))
+            return;
+        if (serverState.processMessage(msg))
+            return;
+        if (serverTexture.processMessage(msg))
+            return;
+    }
+}
+
+class ContextViewProvider extends LabelProvider implements ITreeContentProvider,
+        ISelectionChangedListener {
+    Context context;
+    final SampleView sampleView;
+
+    ContextViewProvider(final SampleView sampleView) {
+        this.sampleView = sampleView;
+    }
+
+    @Override
+    public void dispose() {
+    }
+
+    @Override
+    public String getText(Object obj) {
+        if (obj == null)
+            return "null";
+        if (obj instanceof Entry) {
+            Entry entry = (Entry) obj;
+            String objStr = "null (or default)";
+            if (entry.obj != null) {
+                objStr = entry.obj.toString();
+                if (entry.obj instanceof Message)
+                    objStr = MessageFormatter.format((Message) entry.obj, false);
+            }
+            return entry.name + " = " + objStr;
+        }
+        return obj.toString();
+    }
+
+    @Override
+    public Image getImage(Object obj) {
+        if (!(obj instanceof Entry))
+            return null;
+        final Entry entry = (Entry) obj;
+        if (!(entry.obj instanceof Message))
+            return null;
+        final Message msg = (Message) entry.obj;
+        switch (msg.getFunction()) {
+            case glTexImage2D:
+            case glTexSubImage2D:
+            case glCopyTexImage2D:
+            case glCopyTexSubImage2D: {
+                entry.image = new MessageData(Display.getCurrent(), msg, null).getImage();
+                if (entry.image == null)
+                    return null;
+                return new Image(Display.getCurrent(), entry.image.getImageData().scaledTo(96, 96));
+            }
+            default:
+                return null;
+        }
+    }
+
+    @Override
+    public void selectionChanged(SelectionChangedEvent event) {
+        StructuredSelection selection = (StructuredSelection) event
+                .getSelection();
+        if (null == selection)
+            return;
+        final Object obj = selection.getFirstElement();
+        if (!(obj instanceof Entry))
+            return;
+        final Entry entry = (Entry) obj;
+        if (entry.image == null)
+            return;
+        sampleView.tabFolder.setSelection(sampleView.tabItemImage);
+        sampleView.canvas.setBackgroundImage(entry.image);
+        sampleView.canvas.redraw();
+    }
+
+    @Override
+    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+        context = (Context) newInput;
+    }
+
+    class Entry {
+        String name;
+        Object obj;
+        Image image;
+
+        Entry(String name, Object obj) {
+            this.name = name;
+            this.obj = obj;
+        }
+    }
+
+    @Override
+    public Object[] getElements(Object inputElement) {
+        if (inputElement != context)
+            return null;
+        return getChildren(new Entry("Context", inputElement));
+    }
+
+    @Override
+    public Object[] getChildren(Object parentElement) {
+        if (!(parentElement instanceof Entry))
+            return null;
+        Entry entry = (Entry) parentElement;
+        ArrayList<Object> children = new ArrayList<Object>();
+        if (entry.obj == context.serverState.enableDisables) {
+            for (int i = 0; i < context.serverState.enableDisables.size(); i++) {
+                final int key = context.serverState.enableDisables.keyAt(i);
+                final int value = context.serverState.enableDisables.valueAt(i);
+                children.add(GLEnum.valueOf(key).name() + " = " + value);
+            }
+        } else if (entry.obj == context.serverState.integers) {
+            for (int i = 0; i < context.serverState.integers.size(); i++) {
+                final int key = context.serverState.integers.keyAt(i);
+                final Message val = context.serverState.integers.valueAt(i);
+                if (val != null)
+                    children.add(GLEnum.valueOf(key).name() + " : " +
+                            MessageFormatter.format(val, false));
+                else
+                    children.add(GLEnum.valueOf(key).name() + " : default");
+            }
+        } else if (entry.obj == context.serverState.lastSetter) {
+            for (int i = 0; i < context.serverState.lastSetter.size(); i++) {
+                final int key = context.serverState.lastSetter.keyAt(i);
+                final Message msg = context.serverState.lastSetter.valueAt(i);
+                if (msg == null)
+                    children.add(Function.valueOf(key).name() + " : default");
+                else
+                    children.add(Function.valueOf(key).name() + " : "
+                            + MessageFormatter.format(msg, false));
+            }
+        } else if (entry.obj instanceof SparseArray) {
+            SparseArray<?> sa = (SparseArray<?>) entry.obj;
+            for (int i = 0; i < sa.size(); i++)
+                children.add(new Entry("[" + sa.keyAt(i) + "]", sa.valueAt(i)));
+        } else if (entry.obj instanceof Map) {
+            Set<?> set = ((Map<?, ?>) entry.obj).entrySet();
+            for (Object o : set) {
+                Map.Entry e = (Map.Entry) o;
+                children.add(new Entry(e.getKey().toString(), e.getValue()));
+            }
+        } else if (entry.obj instanceof SparseIntArray) {
+            SparseIntArray sa = (SparseIntArray) entry.obj;
+            for (int i = 0; i < sa.size(); i++)
+                children.add("[" + sa.keyAt(i) + "] = " + sa.valueAt(i));
+        } else if (entry.obj instanceof Collection) {
+            Collection<?> collection = (Collection<?>) entry.obj;
+            for (Object o : collection)
+                children.add(new Entry("[?]", o));
+        } else if (entry.obj.getClass().isArray()) {
+            for (int i = 0; i < Array.getLength(entry.obj); i++)
+                children.add(new Entry("[" + i + "]", Array.get(entry.obj, i)));
+        } else {
+            Field[] fields = entry.obj.getClass().getFields();
+            for (Field f : fields) {
+                try {
+                    children.add(new Entry(f.getName(), f.get(entry.obj)));
+                } catch (IllegalArgumentException e) {
+                    e.printStackTrace();
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return children.toArray();
+    }
+
+    @Override
+    public Object getParent(Object element) {
+        return null;
+    }
+
+    @Override
+    public boolean hasChildren(Object element) {
+        if (element == null)
+            return false;
+        if (!(element instanceof Entry))
+            return false;
+        Object obj = ((Entry) element).obj;
+        if (obj == null)
+            return false;
+        if (obj instanceof SparseArray)
+            return ((SparseArray<?>) obj).size() > 0;
+        else if (obj instanceof SparseIntArray)
+            return ((SparseIntArray) obj).size() > 0;
+        else if (obj instanceof Collection)
+            return ((Collection<?>) obj).size() > 0;
+        else if (obj instanceof Map)
+            return ((Map<?, ?>) obj).size() > 0;
+        else if (obj.getClass().isArray())
+            return Array.getLength(obj) > 0;
+        else if (obj instanceof Message)
+            return false;
+        else if (isPrimitive(obj))
+            return false;
+        else if (obj.getClass().equals(String.class))
+            return false;
+        else if (obj.getClass().equals(Message.class))
+            return false;
+        else if (obj instanceof GLEnum)
+            return false;
+        return obj.getClass().getFields().length > 0;
+    }
+
+    static boolean isPrimitive(final Object obj) {
+        final Class<? extends Object> c = obj.getClass();
+        if (c.isPrimitive())
+            return true;
+        if (c == Integer.class)
+            return true;
+        if (c == Boolean.class)
+            return true;
+        if (c == Float.class)
+            return true;
+        if (c == Short.class)
+            return true;
+        return false;
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java b/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java
new file mode 100644
index 0000000..94133f5
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java
@@ -0,0 +1,1713 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: debugger_message.proto
+
+package com.android.glesv2debugger;
+
+public final class DebuggerMessage {
+  private DebuggerMessage() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+  public static final class Message extends
+      com.google.protobuf.GeneratedMessageLite {
+    // Use Message.newBuilder() to construct.
+    private Message() {
+      initFields();
+    }
+    private Message(boolean noInit) {}
+    
+    private static final Message defaultInstance;
+    public static Message getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public Message getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    public enum Function
+        implements com.google.protobuf.Internal.EnumLite {
+      glActiveTexture(0, 0),
+      glAttachShader(1, 1),
+      glBindAttribLocation(2, 2),
+      glBindBuffer(3, 3),
+      glBindFramebuffer(4, 4),
+      glBindRenderbuffer(5, 5),
+      glBindTexture(6, 6),
+      glBlendColor(7, 7),
+      glBlendEquation(8, 8),
+      glBlendEquationSeparate(9, 9),
+      glBlendFunc(10, 10),
+      glBlendFuncSeparate(11, 11),
+      glBufferData(12, 12),
+      glBufferSubData(13, 13),
+      glCheckFramebufferStatus(14, 14),
+      glClear(15, 15),
+      glClearColor(16, 16),
+      glClearDepthf(17, 17),
+      glClearStencil(18, 18),
+      glColorMask(19, 19),
+      glCompileShader(20, 20),
+      glCompressedTexImage2D(21, 21),
+      glCompressedTexSubImage2D(22, 22),
+      glCopyTexImage2D(23, 23),
+      glCopyTexSubImage2D(24, 24),
+      glCreateProgram(25, 25),
+      glCreateShader(26, 26),
+      glCullFace(27, 27),
+      glDeleteBuffers(28, 28),
+      glDeleteFramebuffers(29, 29),
+      glDeleteProgram(30, 30),
+      glDeleteRenderbuffers(31, 31),
+      glDeleteShader(32, 32),
+      glDeleteTextures(33, 33),
+      glDepthFunc(34, 34),
+      glDepthMask(35, 35),
+      glDepthRangef(36, 36),
+      glDetachShader(37, 37),
+      glDisable(38, 38),
+      glDisableVertexAttribArray(39, 39),
+      glDrawArrays(40, 40),
+      glDrawElements(41, 41),
+      glEnable(42, 42),
+      glEnableVertexAttribArray(43, 43),
+      glFinish(44, 44),
+      glFlush(45, 45),
+      glFramebufferRenderbuffer(46, 46),
+      glFramebufferTexture2D(47, 47),
+      glFrontFace(48, 48),
+      glGenBuffers(49, 49),
+      glGenerateMipmap(50, 50),
+      glGenFramebuffers(51, 51),
+      glGenRenderbuffers(52, 52),
+      glGenTextures(53, 53),
+      glGetActiveAttrib(54, 54),
+      glGetActiveUniform(55, 55),
+      glGetAttachedShaders(56, 56),
+      glGetAttribLocation(57, 57),
+      glGetBooleanv(58, 58),
+      glGetBufferParameteriv(59, 59),
+      glGetError(60, 60),
+      glGetFloatv(61, 61),
+      glGetFramebufferAttachmentParameteriv(62, 62),
+      glGetIntegerv(63, 63),
+      glGetProgramiv(64, 64),
+      glGetProgramInfoLog(65, 65),
+      glGetRenderbufferParameteriv(66, 66),
+      glGetShaderiv(67, 67),
+      glGetShaderInfoLog(68, 68),
+      glGetShaderPrecisionFormat(69, 69),
+      glGetShaderSource(70, 70),
+      glGetString(71, 71),
+      glGetTexParameterfv(72, 72),
+      glGetTexParameteriv(73, 73),
+      glGetUniformfv(74, 74),
+      glGetUniformiv(75, 75),
+      glGetUniformLocation(76, 76),
+      glGetVertexAttribfv(77, 77),
+      glGetVertexAttribiv(78, 78),
+      glGetVertexAttribPointerv(79, 79),
+      glHint(80, 80),
+      glIsBuffer(81, 81),
+      glIsEnabled(82, 82),
+      glIsFramebuffer(83, 83),
+      glIsProgram(84, 84),
+      glIsRenderbuffer(85, 85),
+      glIsShader(86, 86),
+      glIsTexture(87, 87),
+      glLineWidth(88, 88),
+      glLinkProgram(89, 89),
+      glPixelStorei(90, 90),
+      glPolygonOffset(91, 91),
+      glReadPixels(92, 92),
+      glReleaseShaderCompiler(93, 93),
+      glRenderbufferStorage(94, 94),
+      glSampleCoverage(95, 95),
+      glScissor(96, 96),
+      glShaderBinary(97, 97),
+      glShaderSource(98, 98),
+      glStencilFunc(99, 99),
+      glStencilFuncSeparate(100, 100),
+      glStencilMask(101, 101),
+      glStencilMaskSeparate(102, 102),
+      glStencilOp(103, 103),
+      glStencilOpSeparate(104, 104),
+      glTexImage2D(105, 105),
+      glTexParameterf(106, 106),
+      glTexParameterfv(107, 107),
+      glTexParameteri(108, 108),
+      glTexParameteriv(109, 109),
+      glTexSubImage2D(110, 110),
+      glUniform1f(111, 111),
+      glUniform1fv(112, 112),
+      glUniform1i(113, 113),
+      glUniform1iv(114, 114),
+      glUniform2f(115, 115),
+      glUniform2fv(116, 116),
+      glUniform2i(117, 117),
+      glUniform2iv(118, 118),
+      glUniform3f(119, 119),
+      glUniform3fv(120, 120),
+      glUniform3i(121, 121),
+      glUniform3iv(122, 122),
+      glUniform4f(123, 123),
+      glUniform4fv(124, 124),
+      glUniform4i(125, 125),
+      glUniform4iv(126, 126),
+      glUniformMatrix2fv(127, 127),
+      glUniformMatrix3fv(128, 128),
+      glUniformMatrix4fv(129, 129),
+      glUseProgram(130, 130),
+      glValidateProgram(131, 131),
+      glVertexAttrib1f(132, 132),
+      glVertexAttrib1fv(133, 133),
+      glVertexAttrib2f(134, 134),
+      glVertexAttrib2fv(135, 135),
+      glVertexAttrib3f(136, 136),
+      glVertexAttrib3fv(137, 137),
+      glVertexAttrib4f(138, 138),
+      glVertexAttrib4fv(139, 139),
+      glVertexAttribPointer(140, 140),
+      glViewport(141, 141),
+      eglGetDisplay(142, 142),
+      eglInitialize(143, 143),
+      eglTerminate(144, 144),
+      eglGetConfigs(145, 145),
+      eglChooseConfig(146, 146),
+      eglGetConfigAttrib(147, 147),
+      eglCreateWindowSurface(148, 148),
+      eglCreatePixmapSurface(149, 149),
+      eglCreatePbufferSurface(150, 150),
+      eglDestroySurface(151, 151),
+      eglQuerySurface(152, 152),
+      eglCreateContext(153, 153),
+      eglDestroyContext(154, 154),
+      eglMakeCurrent(155, 155),
+      eglGetCurrentContext(156, 156),
+      eglGetCurrentSurface(157, 157),
+      eglGetCurrentDisplay(158, 158),
+      eglQueryContext(159, 159),
+      eglWaitGL(160, 160),
+      eglWaitNative(161, 161),
+      eglSwapBuffers(162, 162),
+      eglCopyBuffers(163, 163),
+      eglGetError(164, 164),
+      eglQueryString(165, 165),
+      eglGetProcAddress(166, 166),
+      eglSurfaceAttrib(167, 167),
+      eglBindTexImage(168, 168),
+      eglReleaseTexImage(169, 169),
+      eglSwapInterval(170, 170),
+      eglBindAPI(171, 171),
+      eglQueryAPI(172, 172),
+      eglWaitClient(173, 173),
+      eglReleaseThread(174, 174),
+      eglCreatePbufferFromClientBuffer(175, 175),
+      eglLockSurfaceKHR(176, 176),
+      eglUnlockSurfaceKHR(177, 177),
+      eglCreateImageKHR(178, 178),
+      eglDestroyImageKHR(179, 179),
+      eglCreateSyncKHR(180, 180),
+      eglDestroySyncKHR(181, 181),
+      eglClientWaitSyncKHR(182, 182),
+      eglGetSyncAttribKHR(183, 183),
+      eglSetSwapRectangleANDROID(184, 184),
+      eglGetRenderBufferANDROID(185, 185),
+      ACK(186, 186),
+      NEG(187, 187),
+      CONTINUE(188, 188),
+      SKIP(189, 189),
+      SETPROP(190, 190),
+      ;
+      
+      
+      public final int getNumber() { return value; }
+      
+      public static Function valueOf(int value) {
+        switch (value) {
+          case 0: return glActiveTexture;
+          case 1: return glAttachShader;
+          case 2: return glBindAttribLocation;
+          case 3: return glBindBuffer;
+          case 4: return glBindFramebuffer;
+          case 5: return glBindRenderbuffer;
+          case 6: return glBindTexture;
+          case 7: return glBlendColor;
+          case 8: return glBlendEquation;
+          case 9: return glBlendEquationSeparate;
+          case 10: return glBlendFunc;
+          case 11: return glBlendFuncSeparate;
+          case 12: return glBufferData;
+          case 13: return glBufferSubData;
+          case 14: return glCheckFramebufferStatus;
+          case 15: return glClear;
+          case 16: return glClearColor;
+          case 17: return glClearDepthf;
+          case 18: return glClearStencil;
+          case 19: return glColorMask;
+          case 20: return glCompileShader;
+          case 21: return glCompressedTexImage2D;
+          case 22: return glCompressedTexSubImage2D;
+          case 23: return glCopyTexImage2D;
+          case 24: return glCopyTexSubImage2D;
+          case 25: return glCreateProgram;
+          case 26: return glCreateShader;
+          case 27: return glCullFace;
+          case 28: return glDeleteBuffers;
+          case 29: return glDeleteFramebuffers;
+          case 30: return glDeleteProgram;
+          case 31: return glDeleteRenderbuffers;
+          case 32: return glDeleteShader;
+          case 33: return glDeleteTextures;
+          case 34: return glDepthFunc;
+          case 35: return glDepthMask;
+          case 36: return glDepthRangef;
+          case 37: return glDetachShader;
+          case 38: return glDisable;
+          case 39: return glDisableVertexAttribArray;
+          case 40: return glDrawArrays;
+          case 41: return glDrawElements;
+          case 42: return glEnable;
+          case 43: return glEnableVertexAttribArray;
+          case 44: return glFinish;
+          case 45: return glFlush;
+          case 46: return glFramebufferRenderbuffer;
+          case 47: return glFramebufferTexture2D;
+          case 48: return glFrontFace;
+          case 49: return glGenBuffers;
+          case 50: return glGenerateMipmap;
+          case 51: return glGenFramebuffers;
+          case 52: return glGenRenderbuffers;
+          case 53: return glGenTextures;
+          case 54: return glGetActiveAttrib;
+          case 55: return glGetActiveUniform;
+          case 56: return glGetAttachedShaders;
+          case 57: return glGetAttribLocation;
+          case 58: return glGetBooleanv;
+          case 59: return glGetBufferParameteriv;
+          case 60: return glGetError;
+          case 61: return glGetFloatv;
+          case 62: return glGetFramebufferAttachmentParameteriv;
+          case 63: return glGetIntegerv;
+          case 64: return glGetProgramiv;
+          case 65: return glGetProgramInfoLog;
+          case 66: return glGetRenderbufferParameteriv;
+          case 67: return glGetShaderiv;
+          case 68: return glGetShaderInfoLog;
+          case 69: return glGetShaderPrecisionFormat;
+          case 70: return glGetShaderSource;
+          case 71: return glGetString;
+          case 72: return glGetTexParameterfv;
+          case 73: return glGetTexParameteriv;
+          case 74: return glGetUniformfv;
+          case 75: return glGetUniformiv;
+          case 76: return glGetUniformLocation;
+          case 77: return glGetVertexAttribfv;
+          case 78: return glGetVertexAttribiv;
+          case 79: return glGetVertexAttribPointerv;
+          case 80: return glHint;
+          case 81: return glIsBuffer;
+          case 82: return glIsEnabled;
+          case 83: return glIsFramebuffer;
+          case 84: return glIsProgram;
+          case 85: return glIsRenderbuffer;
+          case 86: return glIsShader;
+          case 87: return glIsTexture;
+          case 88: return glLineWidth;
+          case 89: return glLinkProgram;
+          case 90: return glPixelStorei;
+          case 91: return glPolygonOffset;
+          case 92: return glReadPixels;
+          case 93: return glReleaseShaderCompiler;
+          case 94: return glRenderbufferStorage;
+          case 95: return glSampleCoverage;
+          case 96: return glScissor;
+          case 97: return glShaderBinary;
+          case 98: return glShaderSource;
+          case 99: return glStencilFunc;
+          case 100: return glStencilFuncSeparate;
+          case 101: return glStencilMask;
+          case 102: return glStencilMaskSeparate;
+          case 103: return glStencilOp;
+          case 104: return glStencilOpSeparate;
+          case 105: return glTexImage2D;
+          case 106: return glTexParameterf;
+          case 107: return glTexParameterfv;
+          case 108: return glTexParameteri;
+          case 109: return glTexParameteriv;
+          case 110: return glTexSubImage2D;
+          case 111: return glUniform1f;
+          case 112: return glUniform1fv;
+          case 113: return glUniform1i;
+          case 114: return glUniform1iv;
+          case 115: return glUniform2f;
+          case 116: return glUniform2fv;
+          case 117: return glUniform2i;
+          case 118: return glUniform2iv;
+          case 119: return glUniform3f;
+          case 120: return glUniform3fv;
+          case 121: return glUniform3i;
+          case 122: return glUniform3iv;
+          case 123: return glUniform4f;
+          case 124: return glUniform4fv;
+          case 125: return glUniform4i;
+          case 126: return glUniform4iv;
+          case 127: return glUniformMatrix2fv;
+          case 128: return glUniformMatrix3fv;
+          case 129: return glUniformMatrix4fv;
+          case 130: return glUseProgram;
+          case 131: return glValidateProgram;
+          case 132: return glVertexAttrib1f;
+          case 133: return glVertexAttrib1fv;
+          case 134: return glVertexAttrib2f;
+          case 135: return glVertexAttrib2fv;
+          case 136: return glVertexAttrib3f;
+          case 137: return glVertexAttrib3fv;
+          case 138: return glVertexAttrib4f;
+          case 139: return glVertexAttrib4fv;
+          case 140: return glVertexAttribPointer;
+          case 141: return glViewport;
+          case 142: return eglGetDisplay;
+          case 143: return eglInitialize;
+          case 144: return eglTerminate;
+          case 145: return eglGetConfigs;
+          case 146: return eglChooseConfig;
+          case 147: return eglGetConfigAttrib;
+          case 148: return eglCreateWindowSurface;
+          case 149: return eglCreatePixmapSurface;
+          case 150: return eglCreatePbufferSurface;
+          case 151: return eglDestroySurface;
+          case 152: return eglQuerySurface;
+          case 153: return eglCreateContext;
+          case 154: return eglDestroyContext;
+          case 155: return eglMakeCurrent;
+          case 156: return eglGetCurrentContext;
+          case 157: return eglGetCurrentSurface;
+          case 158: return eglGetCurrentDisplay;
+          case 159: return eglQueryContext;
+          case 160: return eglWaitGL;
+          case 161: return eglWaitNative;
+          case 162: return eglSwapBuffers;
+          case 163: return eglCopyBuffers;
+          case 164: return eglGetError;
+          case 165: return eglQueryString;
+          case 166: return eglGetProcAddress;
+          case 167: return eglSurfaceAttrib;
+          case 168: return eglBindTexImage;
+          case 169: return eglReleaseTexImage;
+          case 170: return eglSwapInterval;
+          case 171: return eglBindAPI;
+          case 172: return eglQueryAPI;
+          case 173: return eglWaitClient;
+          case 174: return eglReleaseThread;
+          case 175: return eglCreatePbufferFromClientBuffer;
+          case 176: return eglLockSurfaceKHR;
+          case 177: return eglUnlockSurfaceKHR;
+          case 178: return eglCreateImageKHR;
+          case 179: return eglDestroyImageKHR;
+          case 180: return eglCreateSyncKHR;
+          case 181: return eglDestroySyncKHR;
+          case 182: return eglClientWaitSyncKHR;
+          case 183: return eglGetSyncAttribKHR;
+          case 184: return eglSetSwapRectangleANDROID;
+          case 185: return eglGetRenderBufferANDROID;
+          case 186: return ACK;
+          case 187: return NEG;
+          case 188: return CONTINUE;
+          case 189: return SKIP;
+          case 190: return SETPROP;
+          default: return null;
+        }
+      }
+      
+      public static com.google.protobuf.Internal.EnumLiteMap<Function>
+          internalGetValueMap() {
+        return internalValueMap;
+      }
+      private static com.google.protobuf.Internal.EnumLiteMap<Function>
+          internalValueMap =
+            new com.google.protobuf.Internal.EnumLiteMap<Function>() {
+              public Function findValueByNumber(int number) {
+                return Function.valueOf(number)
+      ;        }
+            };
+      
+      private final int index;
+      private final int value;
+      private Function(int index, int value) {
+        this.index = index;
+        this.value = value;
+      }
+      
+      // @@protoc_insertion_point(enum_scope:com.android.glesv2debugger.Message.Function)
+    }
+    
+    public enum Type
+        implements com.google.protobuf.Internal.EnumLite {
+      BeforeCall(0, 0),
+      AfterCall(1, 1),
+      AfterGeneratedCall(2, 2),
+      Response(3, 3),
+      CompleteCall(4, 4),
+      ;
+      
+      
+      public final int getNumber() { return value; }
+      
+      public static Type valueOf(int value) {
+        switch (value) {
+          case 0: return BeforeCall;
+          case 1: return AfterCall;
+          case 2: return AfterGeneratedCall;
+          case 3: return Response;
+          case 4: return CompleteCall;
+          default: return null;
+        }
+      }
+      
+      public static com.google.protobuf.Internal.EnumLiteMap<Type>
+          internalGetValueMap() {
+        return internalValueMap;
+      }
+      private static com.google.protobuf.Internal.EnumLiteMap<Type>
+          internalValueMap =
+            new com.google.protobuf.Internal.EnumLiteMap<Type>() {
+              public Type findValueByNumber(int number) {
+                return Type.valueOf(number)
+      ;        }
+            };
+      
+      private final int index;
+      private final int value;
+      private Type(int index, int value) {
+        this.index = index;
+        this.value = value;
+      }
+      
+      // @@protoc_insertion_point(enum_scope:com.android.glesv2debugger.Message.Type)
+    }
+    
+    public enum DataType
+        implements com.google.protobuf.Internal.EnumLite {
+      ReferencedImage(0, 0),
+      NonreferencedImage(1, 1),
+      ;
+      
+      
+      public final int getNumber() { return value; }
+      
+      public static DataType valueOf(int value) {
+        switch (value) {
+          case 0: return ReferencedImage;
+          case 1: return NonreferencedImage;
+          default: return null;
+        }
+      }
+      
+      public static com.google.protobuf.Internal.EnumLiteMap<DataType>
+          internalGetValueMap() {
+        return internalValueMap;
+      }
+      private static com.google.protobuf.Internal.EnumLiteMap<DataType>
+          internalValueMap =
+            new com.google.protobuf.Internal.EnumLiteMap<DataType>() {
+              public DataType findValueByNumber(int number) {
+                return DataType.valueOf(number)
+      ;        }
+            };
+      
+      private final int index;
+      private final int value;
+      private DataType(int index, int value) {
+        this.index = index;
+        this.value = value;
+      }
+      
+      // @@protoc_insertion_point(enum_scope:com.android.glesv2debugger.Message.DataType)
+    }
+    
+    public enum Prop
+        implements com.google.protobuf.Internal.EnumLite {
+      CaptureDraw(0, 0),
+      TimeMode(1, 1),
+      ExpectResponse(2, 2),
+      CaptureSwap(3, 3),
+      GLConstant(4, 4),
+      ;
+      
+      
+      public final int getNumber() { return value; }
+      
+      public static Prop valueOf(int value) {
+        switch (value) {
+          case 0: return CaptureDraw;
+          case 1: return TimeMode;
+          case 2: return ExpectResponse;
+          case 3: return CaptureSwap;
+          case 4: return GLConstant;
+          default: return null;
+        }
+      }
+      
+      public static com.google.protobuf.Internal.EnumLiteMap<Prop>
+          internalGetValueMap() {
+        return internalValueMap;
+      }
+      private static com.google.protobuf.Internal.EnumLiteMap<Prop>
+          internalValueMap =
+            new com.google.protobuf.Internal.EnumLiteMap<Prop>() {
+              public Prop findValueByNumber(int number) {
+                return Prop.valueOf(number)
+      ;        }
+            };
+      
+      private final int index;
+      private final int value;
+      private Prop(int index, int value) {
+        this.index = index;
+        this.value = value;
+      }
+      
+      // @@protoc_insertion_point(enum_scope:com.android.glesv2debugger.Message.Prop)
+    }
+    
+    // required int32 context_id = 1;
+    public static final int CONTEXT_ID_FIELD_NUMBER = 1;
+    private boolean hasContextId;
+    private int contextId_ = 0;
+    public boolean hasContextId() { return hasContextId; }
+    public int getContextId() { return contextId_; }
+    
+    // required .com.android.glesv2debugger.Message.Function function = 2 [default = NEG];
+    public static final int FUNCTION_FIELD_NUMBER = 2;
+    private boolean hasFunction;
+    private com.android.glesv2debugger.DebuggerMessage.Message.Function function_;
+    public boolean hasFunction() { return hasFunction; }
+    public com.android.glesv2debugger.DebuggerMessage.Message.Function getFunction() { return function_; }
+    
+    // required .com.android.glesv2debugger.Message.Type type = 3;
+    public static final int TYPE_FIELD_NUMBER = 3;
+    private boolean hasType;
+    private com.android.glesv2debugger.DebuggerMessage.Message.Type type_;
+    public boolean hasType() { return hasType; }
+    public com.android.glesv2debugger.DebuggerMessage.Message.Type getType() { return type_; }
+    
+    // required bool expect_response = 4;
+    public static final int EXPECT_RESPONSE_FIELD_NUMBER = 4;
+    private boolean hasExpectResponse;
+    private boolean expectResponse_ = false;
+    public boolean hasExpectResponse() { return hasExpectResponse; }
+    public boolean getExpectResponse() { return expectResponse_; }
+    
+    // optional int32 ret = 5;
+    public static final int RET_FIELD_NUMBER = 5;
+    private boolean hasRet;
+    private int ret_ = 0;
+    public boolean hasRet() { return hasRet; }
+    public int getRet() { return ret_; }
+    
+    // optional int32 arg0 = 6;
+    public static final int ARG0_FIELD_NUMBER = 6;
+    private boolean hasArg0;
+    private int arg0_ = 0;
+    public boolean hasArg0() { return hasArg0; }
+    public int getArg0() { return arg0_; }
+    
+    // optional int32 arg1 = 7;
+    public static final int ARG1_FIELD_NUMBER = 7;
+    private boolean hasArg1;
+    private int arg1_ = 0;
+    public boolean hasArg1() { return hasArg1; }
+    public int getArg1() { return arg1_; }
+    
+    // optional int32 arg2 = 8;
+    public static final int ARG2_FIELD_NUMBER = 8;
+    private boolean hasArg2;
+    private int arg2_ = 0;
+    public boolean hasArg2() { return hasArg2; }
+    public int getArg2() { return arg2_; }
+    
+    // optional int32 arg3 = 9;
+    public static final int ARG3_FIELD_NUMBER = 9;
+    private boolean hasArg3;
+    private int arg3_ = 0;
+    public boolean hasArg3() { return hasArg3; }
+    public int getArg3() { return arg3_; }
+    
+    // optional int32 arg4 = 16;
+    public static final int ARG4_FIELD_NUMBER = 16;
+    private boolean hasArg4;
+    private int arg4_ = 0;
+    public boolean hasArg4() { return hasArg4; }
+    public int getArg4() { return arg4_; }
+    
+    // optional int32 arg5 = 17;
+    public static final int ARG5_FIELD_NUMBER = 17;
+    private boolean hasArg5;
+    private int arg5_ = 0;
+    public boolean hasArg5() { return hasArg5; }
+    public int getArg5() { return arg5_; }
+    
+    // optional int32 arg6 = 18;
+    public static final int ARG6_FIELD_NUMBER = 18;
+    private boolean hasArg6;
+    private int arg6_ = 0;
+    public boolean hasArg6() { return hasArg6; }
+    public int getArg6() { return arg6_; }
+    
+    // optional int32 arg7 = 19;
+    public static final int ARG7_FIELD_NUMBER = 19;
+    private boolean hasArg7;
+    private int arg7_ = 0;
+    public boolean hasArg7() { return hasArg7; }
+    public int getArg7() { return arg7_; }
+    
+    // optional int32 arg8 = 20;
+    public static final int ARG8_FIELD_NUMBER = 20;
+    private boolean hasArg8;
+    private int arg8_ = 0;
+    public boolean hasArg8() { return hasArg8; }
+    public int getArg8() { return arg8_; }
+    
+    // optional bytes data = 10;
+    public static final int DATA_FIELD_NUMBER = 10;
+    private boolean hasData;
+    private com.google.protobuf.ByteString data_ = com.google.protobuf.ByteString.EMPTY;
+    public boolean hasData() { return hasData; }
+    public com.google.protobuf.ByteString getData() { return data_; }
+    
+    // optional .com.android.glesv2debugger.Message.DataType data_type = 23;
+    public static final int DATA_TYPE_FIELD_NUMBER = 23;
+    private boolean hasDataType;
+    private com.android.glesv2debugger.DebuggerMessage.Message.DataType dataType_;
+    public boolean hasDataType() { return hasDataType; }
+    public com.android.glesv2debugger.DebuggerMessage.Message.DataType getDataType() { return dataType_; }
+    
+    // optional int32 pixel_format = 24;
+    public static final int PIXEL_FORMAT_FIELD_NUMBER = 24;
+    private boolean hasPixelFormat;
+    private int pixelFormat_ = 0;
+    public boolean hasPixelFormat() { return hasPixelFormat; }
+    public int getPixelFormat() { return pixelFormat_; }
+    
+    // optional int32 pixel_type = 25;
+    public static final int PIXEL_TYPE_FIELD_NUMBER = 25;
+    private boolean hasPixelType;
+    private int pixelType_ = 0;
+    public boolean hasPixelType() { return hasPixelType; }
+    public int getPixelType() { return pixelType_; }
+    
+    // optional int32 image_width = 26;
+    public static final int IMAGE_WIDTH_FIELD_NUMBER = 26;
+    private boolean hasImageWidth;
+    private int imageWidth_ = 0;
+    public boolean hasImageWidth() { return hasImageWidth; }
+    public int getImageWidth() { return imageWidth_; }
+    
+    // optional int32 image_height = 27;
+    public static final int IMAGE_HEIGHT_FIELD_NUMBER = 27;
+    private boolean hasImageHeight;
+    private int imageHeight_ = 0;
+    public boolean hasImageHeight() { return hasImageHeight; }
+    public int getImageHeight() { return imageHeight_; }
+    
+    // optional float time = 11;
+    public static final int TIME_FIELD_NUMBER = 11;
+    private boolean hasTime;
+    private float time_ = 0F;
+    public boolean hasTime() { return hasTime; }
+    public float getTime() { return time_; }
+    
+    // optional .com.android.glesv2debugger.Message.Prop prop = 21;
+    public static final int PROP_FIELD_NUMBER = 21;
+    private boolean hasProp;
+    private com.android.glesv2debugger.DebuggerMessage.Message.Prop prop_;
+    public boolean hasProp() { return hasProp; }
+    public com.android.glesv2debugger.DebuggerMessage.Message.Prop getProp() { return prop_; }
+    
+    // optional float clock = 22;
+    public static final int CLOCK_FIELD_NUMBER = 22;
+    private boolean hasClock;
+    private float clock_ = 0F;
+    public boolean hasClock() { return hasClock; }
+    public float getClock() { return clock_; }
+    
+    private void initFields() {
+      function_ = com.android.glesv2debugger.DebuggerMessage.Message.Function.NEG;
+      type_ = com.android.glesv2debugger.DebuggerMessage.Message.Type.BeforeCall;
+      dataType_ = com.android.glesv2debugger.DebuggerMessage.Message.DataType.ReferencedImage;
+      prop_ = com.android.glesv2debugger.DebuggerMessage.Message.Prop.CaptureDraw;
+    }
+    public final boolean isInitialized() {
+      if (!hasContextId) return false;
+      if (!hasFunction) return false;
+      if (!hasType) return false;
+      if (!hasExpectResponse) return false;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (hasContextId()) {
+        output.writeInt32(1, getContextId());
+      }
+      if (hasFunction()) {
+        output.writeEnum(2, getFunction().getNumber());
+      }
+      if (hasType()) {
+        output.writeEnum(3, getType().getNumber());
+      }
+      if (hasExpectResponse()) {
+        output.writeBool(4, getExpectResponse());
+      }
+      if (hasRet()) {
+        output.writeInt32(5, getRet());
+      }
+      if (hasArg0()) {
+        output.writeInt32(6, getArg0());
+      }
+      if (hasArg1()) {
+        output.writeInt32(7, getArg1());
+      }
+      if (hasArg2()) {
+        output.writeInt32(8, getArg2());
+      }
+      if (hasArg3()) {
+        output.writeInt32(9, getArg3());
+      }
+      if (hasData()) {
+        output.writeBytes(10, getData());
+      }
+      if (hasTime()) {
+        output.writeFloat(11, getTime());
+      }
+      if (hasArg4()) {
+        output.writeInt32(16, getArg4());
+      }
+      if (hasArg5()) {
+        output.writeInt32(17, getArg5());
+      }
+      if (hasArg6()) {
+        output.writeInt32(18, getArg6());
+      }
+      if (hasArg7()) {
+        output.writeInt32(19, getArg7());
+      }
+      if (hasArg8()) {
+        output.writeInt32(20, getArg8());
+      }
+      if (hasProp()) {
+        output.writeEnum(21, getProp().getNumber());
+      }
+      if (hasClock()) {
+        output.writeFloat(22, getClock());
+      }
+      if (hasDataType()) {
+        output.writeEnum(23, getDataType().getNumber());
+      }
+      if (hasPixelFormat()) {
+        output.writeInt32(24, getPixelFormat());
+      }
+      if (hasPixelType()) {
+        output.writeInt32(25, getPixelType());
+      }
+      if (hasImageWidth()) {
+        output.writeInt32(26, getImageWidth());
+      }
+      if (hasImageHeight()) {
+        output.writeInt32(27, getImageHeight());
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (hasContextId()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(1, getContextId());
+      }
+      if (hasFunction()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeEnumSize(2, getFunction().getNumber());
+      }
+      if (hasType()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeEnumSize(3, getType().getNumber());
+      }
+      if (hasExpectResponse()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBoolSize(4, getExpectResponse());
+      }
+      if (hasRet()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(5, getRet());
+      }
+      if (hasArg0()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(6, getArg0());
+      }
+      if (hasArg1()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(7, getArg1());
+      }
+      if (hasArg2()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(8, getArg2());
+      }
+      if (hasArg3()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(9, getArg3());
+      }
+      if (hasData()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(10, getData());
+      }
+      if (hasTime()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeFloatSize(11, getTime());
+      }
+      if (hasArg4()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(16, getArg4());
+      }
+      if (hasArg5()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(17, getArg5());
+      }
+      if (hasArg6()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(18, getArg6());
+      }
+      if (hasArg7()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(19, getArg7());
+      }
+      if (hasArg8()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(20, getArg8());
+      }
+      if (hasProp()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeEnumSize(21, getProp().getNumber());
+      }
+      if (hasClock()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeFloatSize(22, getClock());
+      }
+      if (hasDataType()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeEnumSize(23, getDataType().getNumber());
+      }
+      if (hasPixelFormat()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(24, getPixelFormat());
+      }
+      if (hasPixelType()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(25, getPixelType());
+      }
+      if (hasImageWidth()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(26, getImageWidth());
+      }
+      if (hasImageHeight()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(27, getImageHeight());
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static com.android.glesv2debugger.DebuggerMessage.Message parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(com.android.glesv2debugger.DebuggerMessage.Message prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          com.android.glesv2debugger.DebuggerMessage.Message, Builder> {
+      private com.android.glesv2debugger.DebuggerMessage.Message result;
+      
+      // Construct using com.android.glesv2debugger.DebuggerMessage.Message.newBuilder()
+      private Builder() {}
+      
+      private static Builder create() {
+        Builder builder = new Builder();
+        builder.result = new com.android.glesv2debugger.DebuggerMessage.Message();
+        return builder;
+      }
+      
+      protected com.android.glesv2debugger.DebuggerMessage.Message internalGetResult() {
+        return result;
+      }
+      
+      public Builder clear() {
+        if (result == null) {
+          throw new IllegalStateException(
+            "Cannot call clear() after build().");
+        }
+        result = new com.android.glesv2debugger.DebuggerMessage.Message();
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(result);
+      }
+      
+      public com.android.glesv2debugger.DebuggerMessage.Message getDefaultInstanceForType() {
+        return com.android.glesv2debugger.DebuggerMessage.Message.getDefaultInstance();
+      }
+      
+      public boolean isInitialized() {
+        return result.isInitialized();
+      }
+      public com.android.glesv2debugger.DebuggerMessage.Message build() {
+        if (result != null && !isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return buildPartial();
+      }
+      
+      private com.android.glesv2debugger.DebuggerMessage.Message buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        if (!isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return buildPartial();
+      }
+      
+      public com.android.glesv2debugger.DebuggerMessage.Message buildPartial() {
+        if (result == null) {
+          throw new IllegalStateException(
+            "build() has already been called on this Builder.");
+        }
+        com.android.glesv2debugger.DebuggerMessage.Message returnMe = result;
+        result = null;
+        return returnMe;
+      }
+      
+      public Builder mergeFrom(com.android.glesv2debugger.DebuggerMessage.Message other) {
+        if (other == com.android.glesv2debugger.DebuggerMessage.Message.getDefaultInstance()) return this;
+        if (other.hasContextId()) {
+          setContextId(other.getContextId());
+        }
+        if (other.hasFunction()) {
+          setFunction(other.getFunction());
+        }
+        if (other.hasType()) {
+          setType(other.getType());
+        }
+        if (other.hasExpectResponse()) {
+          setExpectResponse(other.getExpectResponse());
+        }
+        if (other.hasRet()) {
+          setRet(other.getRet());
+        }
+        if (other.hasArg0()) {
+          setArg0(other.getArg0());
+        }
+        if (other.hasArg1()) {
+          setArg1(other.getArg1());
+        }
+        if (other.hasArg2()) {
+          setArg2(other.getArg2());
+        }
+        if (other.hasArg3()) {
+          setArg3(other.getArg3());
+        }
+        if (other.hasArg4()) {
+          setArg4(other.getArg4());
+        }
+        if (other.hasArg5()) {
+          setArg5(other.getArg5());
+        }
+        if (other.hasArg6()) {
+          setArg6(other.getArg6());
+        }
+        if (other.hasArg7()) {
+          setArg7(other.getArg7());
+        }
+        if (other.hasArg8()) {
+          setArg8(other.getArg8());
+        }
+        if (other.hasData()) {
+          setData(other.getData());
+        }
+        if (other.hasDataType()) {
+          setDataType(other.getDataType());
+        }
+        if (other.hasPixelFormat()) {
+          setPixelFormat(other.getPixelFormat());
+        }
+        if (other.hasPixelType()) {
+          setPixelType(other.getPixelType());
+        }
+        if (other.hasImageWidth()) {
+          setImageWidth(other.getImageWidth());
+        }
+        if (other.hasImageHeight()) {
+          setImageHeight(other.getImageHeight());
+        }
+        if (other.hasTime()) {
+          setTime(other.getTime());
+        }
+        if (other.hasProp()) {
+          setProp(other.getProp());
+        }
+        if (other.hasClock()) {
+          setClock(other.getClock());
+        }
+        return this;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              setContextId(input.readInt32());
+              break;
+            }
+            case 16: {
+              int rawValue = input.readEnum();
+              com.android.glesv2debugger.DebuggerMessage.Message.Function value = com.android.glesv2debugger.DebuggerMessage.Message.Function.valueOf(rawValue);
+              if (value != null) {
+                setFunction(value);
+              }
+              break;
+            }
+            case 24: {
+              int rawValue = input.readEnum();
+              com.android.glesv2debugger.DebuggerMessage.Message.Type value = com.android.glesv2debugger.DebuggerMessage.Message.Type.valueOf(rawValue);
+              if (value != null) {
+                setType(value);
+              }
+              break;
+            }
+            case 32: {
+              setExpectResponse(input.readBool());
+              break;
+            }
+            case 40: {
+              setRet(input.readInt32());
+              break;
+            }
+            case 48: {
+              setArg0(input.readInt32());
+              break;
+            }
+            case 56: {
+              setArg1(input.readInt32());
+              break;
+            }
+            case 64: {
+              setArg2(input.readInt32());
+              break;
+            }
+            case 72: {
+              setArg3(input.readInt32());
+              break;
+            }
+            case 82: {
+              setData(input.readBytes());
+              break;
+            }
+            case 93: {
+              setTime(input.readFloat());
+              break;
+            }
+            case 128: {
+              setArg4(input.readInt32());
+              break;
+            }
+            case 136: {
+              setArg5(input.readInt32());
+              break;
+            }
+            case 144: {
+              setArg6(input.readInt32());
+              break;
+            }
+            case 152: {
+              setArg7(input.readInt32());
+              break;
+            }
+            case 160: {
+              setArg8(input.readInt32());
+              break;
+            }
+            case 168: {
+              int rawValue = input.readEnum();
+              com.android.glesv2debugger.DebuggerMessage.Message.Prop value = com.android.glesv2debugger.DebuggerMessage.Message.Prop.valueOf(rawValue);
+              if (value != null) {
+                setProp(value);
+              }
+              break;
+            }
+            case 181: {
+              setClock(input.readFloat());
+              break;
+            }
+            case 184: {
+              int rawValue = input.readEnum();
+              com.android.glesv2debugger.DebuggerMessage.Message.DataType value = com.android.glesv2debugger.DebuggerMessage.Message.DataType.valueOf(rawValue);
+              if (value != null) {
+                setDataType(value);
+              }
+              break;
+            }
+            case 192: {
+              setPixelFormat(input.readInt32());
+              break;
+            }
+            case 200: {
+              setPixelType(input.readInt32());
+              break;
+            }
+            case 208: {
+              setImageWidth(input.readInt32());
+              break;
+            }
+            case 216: {
+              setImageHeight(input.readInt32());
+              break;
+            }
+          }
+        }
+      }
+      
+      
+      // required int32 context_id = 1;
+      public boolean hasContextId() {
+        return result.hasContextId();
+      }
+      public int getContextId() {
+        return result.getContextId();
+      }
+      public Builder setContextId(int value) {
+        result.hasContextId = true;
+        result.contextId_ = value;
+        return this;
+      }
+      public Builder clearContextId() {
+        result.hasContextId = false;
+        result.contextId_ = 0;
+        return this;
+      }
+      
+      // required .com.android.glesv2debugger.Message.Function function = 2 [default = NEG];
+      public boolean hasFunction() {
+        return result.hasFunction();
+      }
+      public com.android.glesv2debugger.DebuggerMessage.Message.Function getFunction() {
+        return result.getFunction();
+      }
+      public Builder setFunction(com.android.glesv2debugger.DebuggerMessage.Message.Function value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        result.hasFunction = true;
+        result.function_ = value;
+        return this;
+      }
+      public Builder clearFunction() {
+        result.hasFunction = false;
+        result.function_ = com.android.glesv2debugger.DebuggerMessage.Message.Function.NEG;
+        return this;
+      }
+      
+      // required .com.android.glesv2debugger.Message.Type type = 3;
+      public boolean hasType() {
+        return result.hasType();
+      }
+      public com.android.glesv2debugger.DebuggerMessage.Message.Type getType() {
+        return result.getType();
+      }
+      public Builder setType(com.android.glesv2debugger.DebuggerMessage.Message.Type value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        result.hasType = true;
+        result.type_ = value;
+        return this;
+      }
+      public Builder clearType() {
+        result.hasType = false;
+        result.type_ = com.android.glesv2debugger.DebuggerMessage.Message.Type.BeforeCall;
+        return this;
+      }
+      
+      // required bool expect_response = 4;
+      public boolean hasExpectResponse() {
+        return result.hasExpectResponse();
+      }
+      public boolean getExpectResponse() {
+        return result.getExpectResponse();
+      }
+      public Builder setExpectResponse(boolean value) {
+        result.hasExpectResponse = true;
+        result.expectResponse_ = value;
+        return this;
+      }
+      public Builder clearExpectResponse() {
+        result.hasExpectResponse = false;
+        result.expectResponse_ = false;
+        return this;
+      }
+      
+      // optional int32 ret = 5;
+      public boolean hasRet() {
+        return result.hasRet();
+      }
+      public int getRet() {
+        return result.getRet();
+      }
+      public Builder setRet(int value) {
+        result.hasRet = true;
+        result.ret_ = value;
+        return this;
+      }
+      public Builder clearRet() {
+        result.hasRet = false;
+        result.ret_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg0 = 6;
+      public boolean hasArg0() {
+        return result.hasArg0();
+      }
+      public int getArg0() {
+        return result.getArg0();
+      }
+      public Builder setArg0(int value) {
+        result.hasArg0 = true;
+        result.arg0_ = value;
+        return this;
+      }
+      public Builder clearArg0() {
+        result.hasArg0 = false;
+        result.arg0_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg1 = 7;
+      public boolean hasArg1() {
+        return result.hasArg1();
+      }
+      public int getArg1() {
+        return result.getArg1();
+      }
+      public Builder setArg1(int value) {
+        result.hasArg1 = true;
+        result.arg1_ = value;
+        return this;
+      }
+      public Builder clearArg1() {
+        result.hasArg1 = false;
+        result.arg1_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg2 = 8;
+      public boolean hasArg2() {
+        return result.hasArg2();
+      }
+      public int getArg2() {
+        return result.getArg2();
+      }
+      public Builder setArg2(int value) {
+        result.hasArg2 = true;
+        result.arg2_ = value;
+        return this;
+      }
+      public Builder clearArg2() {
+        result.hasArg2 = false;
+        result.arg2_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg3 = 9;
+      public boolean hasArg3() {
+        return result.hasArg3();
+      }
+      public int getArg3() {
+        return result.getArg3();
+      }
+      public Builder setArg3(int value) {
+        result.hasArg3 = true;
+        result.arg3_ = value;
+        return this;
+      }
+      public Builder clearArg3() {
+        result.hasArg3 = false;
+        result.arg3_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg4 = 16;
+      public boolean hasArg4() {
+        return result.hasArg4();
+      }
+      public int getArg4() {
+        return result.getArg4();
+      }
+      public Builder setArg4(int value) {
+        result.hasArg4 = true;
+        result.arg4_ = value;
+        return this;
+      }
+      public Builder clearArg4() {
+        result.hasArg4 = false;
+        result.arg4_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg5 = 17;
+      public boolean hasArg5() {
+        return result.hasArg5();
+      }
+      public int getArg5() {
+        return result.getArg5();
+      }
+      public Builder setArg5(int value) {
+        result.hasArg5 = true;
+        result.arg5_ = value;
+        return this;
+      }
+      public Builder clearArg5() {
+        result.hasArg5 = false;
+        result.arg5_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg6 = 18;
+      public boolean hasArg6() {
+        return result.hasArg6();
+      }
+      public int getArg6() {
+        return result.getArg6();
+      }
+      public Builder setArg6(int value) {
+        result.hasArg6 = true;
+        result.arg6_ = value;
+        return this;
+      }
+      public Builder clearArg6() {
+        result.hasArg6 = false;
+        result.arg6_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg7 = 19;
+      public boolean hasArg7() {
+        return result.hasArg7();
+      }
+      public int getArg7() {
+        return result.getArg7();
+      }
+      public Builder setArg7(int value) {
+        result.hasArg7 = true;
+        result.arg7_ = value;
+        return this;
+      }
+      public Builder clearArg7() {
+        result.hasArg7 = false;
+        result.arg7_ = 0;
+        return this;
+      }
+      
+      // optional int32 arg8 = 20;
+      public boolean hasArg8() {
+        return result.hasArg8();
+      }
+      public int getArg8() {
+        return result.getArg8();
+      }
+      public Builder setArg8(int value) {
+        result.hasArg8 = true;
+        result.arg8_ = value;
+        return this;
+      }
+      public Builder clearArg8() {
+        result.hasArg8 = false;
+        result.arg8_ = 0;
+        return this;
+      }
+      
+      // optional bytes data = 10;
+      public boolean hasData() {
+        return result.hasData();
+      }
+      public com.google.protobuf.ByteString getData() {
+        return result.getData();
+      }
+      public Builder setData(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  result.hasData = true;
+        result.data_ = value;
+        return this;
+      }
+      public Builder clearData() {
+        result.hasData = false;
+        result.data_ = getDefaultInstance().getData();
+        return this;
+      }
+      
+      // optional .com.android.glesv2debugger.Message.DataType data_type = 23;
+      public boolean hasDataType() {
+        return result.hasDataType();
+      }
+      public com.android.glesv2debugger.DebuggerMessage.Message.DataType getDataType() {
+        return result.getDataType();
+      }
+      public Builder setDataType(com.android.glesv2debugger.DebuggerMessage.Message.DataType value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        result.hasDataType = true;
+        result.dataType_ = value;
+        return this;
+      }
+      public Builder clearDataType() {
+        result.hasDataType = false;
+        result.dataType_ = com.android.glesv2debugger.DebuggerMessage.Message.DataType.ReferencedImage;
+        return this;
+      }
+      
+      // optional int32 pixel_format = 24;
+      public boolean hasPixelFormat() {
+        return result.hasPixelFormat();
+      }
+      public int getPixelFormat() {
+        return result.getPixelFormat();
+      }
+      public Builder setPixelFormat(int value) {
+        result.hasPixelFormat = true;
+        result.pixelFormat_ = value;
+        return this;
+      }
+      public Builder clearPixelFormat() {
+        result.hasPixelFormat = false;
+        result.pixelFormat_ = 0;
+        return this;
+      }
+      
+      // optional int32 pixel_type = 25;
+      public boolean hasPixelType() {
+        return result.hasPixelType();
+      }
+      public int getPixelType() {
+        return result.getPixelType();
+      }
+      public Builder setPixelType(int value) {
+        result.hasPixelType = true;
+        result.pixelType_ = value;
+        return this;
+      }
+      public Builder clearPixelType() {
+        result.hasPixelType = false;
+        result.pixelType_ = 0;
+        return this;
+      }
+      
+      // optional int32 image_width = 26;
+      public boolean hasImageWidth() {
+        return result.hasImageWidth();
+      }
+      public int getImageWidth() {
+        return result.getImageWidth();
+      }
+      public Builder setImageWidth(int value) {
+        result.hasImageWidth = true;
+        result.imageWidth_ = value;
+        return this;
+      }
+      public Builder clearImageWidth() {
+        result.hasImageWidth = false;
+        result.imageWidth_ = 0;
+        return this;
+      }
+      
+      // optional int32 image_height = 27;
+      public boolean hasImageHeight() {
+        return result.hasImageHeight();
+      }
+      public int getImageHeight() {
+        return result.getImageHeight();
+      }
+      public Builder setImageHeight(int value) {
+        result.hasImageHeight = true;
+        result.imageHeight_ = value;
+        return this;
+      }
+      public Builder clearImageHeight() {
+        result.hasImageHeight = false;
+        result.imageHeight_ = 0;
+        return this;
+      }
+      
+      // optional float time = 11;
+      public boolean hasTime() {
+        return result.hasTime();
+      }
+      public float getTime() {
+        return result.getTime();
+      }
+      public Builder setTime(float value) {
+        result.hasTime = true;
+        result.time_ = value;
+        return this;
+      }
+      public Builder clearTime() {
+        result.hasTime = false;
+        result.time_ = 0F;
+        return this;
+      }
+      
+      // optional .com.android.glesv2debugger.Message.Prop prop = 21;
+      public boolean hasProp() {
+        return result.hasProp();
+      }
+      public com.android.glesv2debugger.DebuggerMessage.Message.Prop getProp() {
+        return result.getProp();
+      }
+      public Builder setProp(com.android.glesv2debugger.DebuggerMessage.Message.Prop value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        result.hasProp = true;
+        result.prop_ = value;
+        return this;
+      }
+      public Builder clearProp() {
+        result.hasProp = false;
+        result.prop_ = com.android.glesv2debugger.DebuggerMessage.Message.Prop.CaptureDraw;
+        return this;
+      }
+      
+      // optional float clock = 22;
+      public boolean hasClock() {
+        return result.hasClock();
+      }
+      public float getClock() {
+        return result.getClock();
+      }
+      public Builder setClock(float value) {
+        result.hasClock = true;
+        result.clock_ = value;
+        return this;
+      }
+      public Builder clearClock() {
+        result.hasClock = false;
+        result.clock_ = 0F;
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:com.android.glesv2debugger.Message)
+    }
+    
+    static {
+      defaultInstance = new Message(true);
+      com.android.glesv2debugger.DebuggerMessage.internalForceInit();
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:com.android.glesv2debugger.Message)
+  }
+  
+  
+  static {
+  }
+  
+  public static void internalForceInit() {}
+  
+  // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/GLEnum.java b/tools/glesv2debugger/src/com/android/glesv2debugger/GLEnum.java
new file mode 100644
index 0000000..898c6e9
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/GLEnum.java
@@ -0,0 +1,632 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+// auto generated by generate_GLEnum_java.py"
+
+package com.android.glesv2debugger;
+
+public enum GLEnum {
+    GL_POINTS(0x0000),
+    GL_LINES(0x0001),
+    GL_LINE_LOOP(0x0002),
+    GL_LINE_STRIP(0x0003),
+    GL_TRIANGLES(0x0004),
+    GL_TRIANGLE_STRIP(0x0005),
+    GL_TRIANGLE_FAN(0x0006),
+    GL_ADD(0x0104),
+    GL_NEVER(0x0200),
+    GL_LESS(0x0201),
+    GL_EQUAL(0x0202),
+    GL_LEQUAL(0x0203),
+    GL_GREATER(0x0204),
+    GL_NOTEQUAL(0x0205),
+    GL_GEQUAL(0x0206),
+    GL_ALWAYS(0x0207),
+    GL_SRC_COLOR(0x0300),
+    GL_ONE_MINUS_SRC_COLOR(0x0301),
+    GL_SRC_ALPHA(0x0302),
+    GL_ONE_MINUS_SRC_ALPHA(0x0303),
+    GL_DST_ALPHA(0x0304),
+    GL_ONE_MINUS_DST_ALPHA(0x0305),
+    GL_DST_COLOR(0x0306),
+    GL_ONE_MINUS_DST_COLOR(0x0307),
+    GL_SRC_ALPHA_SATURATE(0x0308),
+    GL_FRONT(0x0404),
+    GL_BACK(0x0405),
+    GL_FRONT_AND_BACK(0x0408),
+    GL_INVALID_ENUM(0x0500),
+    GL_INVALID_VALUE(0x0501),
+    GL_INVALID_OPERATION(0x0502),
+    GL_STACK_OVERFLOW(0x0503),
+    GL_STACK_UNDERFLOW(0x0504),
+    GL_OUT_OF_MEMORY(0x0505),
+    GL_INVALID_FRAMEBUFFER_OPERATION(0x0506),
+    GL_EXP(0x0800),
+    GL_EXP2(0x0801),
+    GL_CW(0x0900),
+    GL_CCW(0x0901),
+    GL_CURRENT_COLOR(0x0B00),
+    GL_CURRENT_NORMAL(0x0B02),
+    GL_CURRENT_TEXTURE_COORDS(0x0B03),
+    GL_POINT_SMOOTH(0x0B10),
+    GL_POINT_SIZE(0x0B11),
+    GL_SMOOTH_POINT_SIZE_RANGE(0x0B12),
+    GL_LINE_SMOOTH(0x0B20),
+    GL_LINE_WIDTH(0x0B21),
+    GL_SMOOTH_LINE_WIDTH_RANGE(0x0B22),
+    GL_CULL_FACE(0x0B44),
+    GL_CULL_FACE_MODE(0x0B45),
+    GL_FRONT_FACE(0x0B46),
+    GL_LIGHTING(0x0B50),
+    GL_LIGHT_MODEL_TWO_SIDE(0x0B52),
+    GL_LIGHT_MODEL_AMBIENT(0x0B53),
+    GL_SHADE_MODEL(0x0B54),
+    GL_COLOR_MATERIAL(0x0B57),
+    GL_FOG(0x0B60),
+    GL_FOG_DENSITY(0x0B62),
+    GL_FOG_START(0x0B63),
+    GL_FOG_END(0x0B64),
+    GL_FOG_MODE(0x0B65),
+    GL_FOG_COLOR(0x0B66),
+    GL_DEPTH_RANGE(0x0B70),
+    GL_DEPTH_TEST(0x0B71),
+    GL_DEPTH_WRITEMASK(0x0B72),
+    GL_DEPTH_CLEAR_VALUE(0x0B73),
+    GL_DEPTH_FUNC(0x0B74),
+    GL_STENCIL_TEST(0x0B90),
+    GL_STENCIL_CLEAR_VALUE(0x0B91),
+    GL_STENCIL_FUNC(0x0B92),
+    GL_STENCIL_VALUE_MASK(0x0B93),
+    GL_STENCIL_FAIL(0x0B94),
+    GL_STENCIL_PASS_DEPTH_FAIL(0x0B95),
+    GL_STENCIL_PASS_DEPTH_PASS(0x0B96),
+    GL_STENCIL_REF(0x0B97),
+    GL_STENCIL_WRITEMASK(0x0B98),
+    GL_MATRIX_MODE(0x0BA0),
+    GL_NORMALIZE(0x0BA1),
+    GL_VIEWPORT(0x0BA2),
+    GL_MODELVIEW_STACK_DEPTH(0x0BA3),
+    GL_PROJECTION_STACK_DEPTH(0x0BA4),
+    GL_TEXTURE_STACK_DEPTH(0x0BA5),
+    GL_MODELVIEW_MATRIX(0x0BA6),
+    GL_PROJECTION_MATRIX(0x0BA7),
+    GL_TEXTURE_MATRIX(0x0BA8),
+    GL_ALPHA_TEST(0x0BC0),
+    GL_ALPHA_TEST_FUNC(0x0BC1),
+    GL_ALPHA_TEST_REF(0x0BC2),
+    GL_DITHER(0x0BD0),
+    GL_BLEND_DST(0x0BE0),
+    GL_BLEND_SRC(0x0BE1),
+    GL_BLEND(0x0BE2),
+    GL_LOGIC_OP_MODE(0x0BF0),
+    GL_COLOR_LOGIC_OP(0x0BF2),
+    GL_SCISSOR_BOX(0x0C10),
+    GL_SCISSOR_TEST(0x0C11),
+    GL_COLOR_CLEAR_VALUE(0x0C22),
+    GL_COLOR_WRITEMASK(0x0C23),
+    GL_PERSPECTIVE_CORRECTION_HINT(0x0C50),
+    GL_POINT_SMOOTH_HINT(0x0C51),
+    GL_LINE_SMOOTH_HINT(0x0C52),
+    GL_FOG_HINT(0x0C54),
+    GL_UNPACK_ALIGNMENT(0x0CF5),
+    GL_PACK_ALIGNMENT(0x0D05),
+    GL_ALPHA_SCALE(0x0D1C),
+    GL_MAX_LIGHTS(0x0D31),
+    GL_MAX_CLIP_PLANES(0x0D32),
+    GL_MAX_TEXTURE_SIZE(0x0D33),
+    GL_MAX_MODELVIEW_STACK_DEPTH(0x0D36),
+    GL_MAX_PROJECTION_STACK_DEPTH(0x0D38),
+    GL_MAX_TEXTURE_STACK_DEPTH(0x0D39),
+    GL_MAX_VIEWPORT_DIMS(0x0D3A),
+    GL_SUBPIXEL_BITS(0x0D50),
+    GL_RED_BITS(0x0D52),
+    GL_GREEN_BITS(0x0D53),
+    GL_BLUE_BITS(0x0D54),
+    GL_ALPHA_BITS(0x0D55),
+    GL_DEPTH_BITS(0x0D56),
+    GL_STENCIL_BITS(0x0D57),
+    GL_TEXTURE_2D(0x0DE1),
+    GL_DONT_CARE(0x1100),
+    GL_FASTEST(0x1101),
+    GL_NICEST(0x1102),
+    GL_AMBIENT(0x1200),
+    GL_DIFFUSE(0x1201),
+    GL_SPECULAR(0x1202),
+    GL_POSITION(0x1203),
+    GL_SPOT_DIRECTION(0x1204),
+    GL_SPOT_EXPONENT(0x1205),
+    GL_SPOT_CUTOFF(0x1206),
+    GL_CONSTANT_ATTENUATION(0x1207),
+    GL_LINEAR_ATTENUATION(0x1208),
+    GL_QUADRATIC_ATTENUATION(0x1209),
+    GL_BYTE(0x1400),
+    GL_UNSIGNED_BYTE(0x1401),
+    GL_SHORT(0x1402),
+    GL_UNSIGNED_SHORT(0x1403),
+    GL_INT(0x1404),
+    GL_UNSIGNED_INT(0x1405),
+    GL_FLOAT(0x1406),
+    GL_FIXED(0x140C),
+    GL_CLEAR(0x1500),
+    GL_AND(0x1501),
+    GL_AND_REVERSE(0x1502),
+    GL_COPY(0x1503),
+    GL_AND_INVERTED(0x1504),
+    GL_NOOP(0x1505),
+    GL_XOR(0x1506),
+    GL_OR(0x1507),
+    GL_NOR(0x1508),
+    GL_EQUIV(0x1509),
+    GL_INVERT(0x150A),
+    GL_OR_REVERSE(0x150B),
+    GL_COPY_INVERTED(0x150C),
+    GL_OR_INVERTED(0x150D),
+    GL_NAND(0x150E),
+    GL_SET(0x150F),
+    GL_EMISSION(0x1600),
+    GL_SHININESS(0x1601),
+    GL_AMBIENT_AND_DIFFUSE(0x1602),
+    GL_MODELVIEW(0x1700),
+    GL_PROJECTION(0x1701),
+    GL_TEXTURE(0x1702),
+    GL_COLOR_EXT(0x1800),
+    GL_DEPTH_EXT(0x1801),
+    GL_STENCIL_EXT(0x1802),
+    GL_STENCIL_INDEX(0x1901),
+    GL_DEPTH_COMPONENT(0x1902),
+    GL_ALPHA(0x1906),
+    GL_RGB(0x1907),
+    GL_RGBA(0x1908),
+    GL_LUMINANCE(0x1909),
+    GL_LUMINANCE_ALPHA(0x190A),
+    GL_FLAT(0x1D00),
+    GL_SMOOTH(0x1D01),
+    GL_KEEP(0x1E00),
+    GL_REPLACE(0x1E01),
+    GL_INCR(0x1E02),
+    GL_DECR(0x1E03),
+    GL_VENDOR(0x1F00),
+    GL_RENDERER(0x1F01),
+    GL_VERSION(0x1F02),
+    GL_EXTENSIONS(0x1F03),
+    GL_MODULATE(0x2100),
+    GL_DECAL(0x2101),
+    GL_TEXTURE_ENV_MODE(0x2200),
+    GL_TEXTURE_ENV_COLOR(0x2201),
+    GL_TEXTURE_ENV(0x2300),
+    GL_TEXTURE_GEN_MODE(0x2500),
+    GL_NEAREST(0x2600),
+    GL_LINEAR(0x2601),
+    GL_NEAREST_MIPMAP_NEAREST(0x2700),
+    GL_LINEAR_MIPMAP_NEAREST(0x2701),
+    GL_NEAREST_MIPMAP_LINEAR(0x2702),
+    GL_LINEAR_MIPMAP_LINEAR(0x2703),
+    GL_TEXTURE_MAG_FILTER(0x2800),
+    GL_TEXTURE_MIN_FILTER(0x2801),
+    GL_TEXTURE_WRAP_S(0x2802),
+    GL_TEXTURE_WRAP_T(0x2803),
+    GL_REPEAT(0x2901),
+    GL_POLYGON_OFFSET_UNITS(0x2A00),
+    GL_CLIP_PLANE0(0x3000),
+    GL_CLIP_PLANE1(0x3001),
+    GL_CLIP_PLANE2(0x3002),
+    GL_CLIP_PLANE3(0x3003),
+    GL_CLIP_PLANE4(0x3004),
+    GL_CLIP_PLANE5(0x3005),
+    GL_LIGHT0(0x4000),
+    GL_LIGHT1(0x4001),
+    GL_LIGHT2(0x4002),
+    GL_LIGHT3(0x4003),
+    GL_LIGHT4(0x4004),
+    GL_LIGHT5(0x4005),
+    GL_LIGHT6(0x4006),
+    GL_LIGHT7(0x4007),
+    GL_COVERAGE_BUFFER_BIT_NV(0x8000),
+    GL_CONSTANT_COLOR(0x8001),
+    GL_ONE_MINUS_CONSTANT_COLOR(0x8002),
+    GL_CONSTANT_ALPHA(0x8003),
+    GL_ONE_MINUS_CONSTANT_ALPHA(0x8004),
+    GL_BLEND_COLOR(0x8005),
+    GL_FUNC_ADD(0x8006),
+    GL_MIN_EXT(0x8007),
+    GL_MAX_EXT(0x8008),
+    GL_BLEND_EQUATION_RGB(0x8009),
+    GL_FUNC_SUBTRACT(0x800A),
+    GL_FUNC_REVERSE_SUBTRACT(0x800B),
+    GL_UNSIGNED_SHORT_4_4_4_4(0x8033),
+    GL_UNSIGNED_SHORT_5_5_5_1(0x8034),
+    GL_POLYGON_OFFSET_FILL(0x8037),
+    GL_POLYGON_OFFSET_FACTOR(0x8038),
+    GL_RESCALE_NORMAL(0x803A),
+    GL_RGB8(0x8051),
+    GL_RGBA4(0x8056),
+    GL_RGB5_A1(0x8057),
+    GL_RGBA8(0x8058),
+    GL_TEXTURE_BINDING_2D(0x8069),
+    GL_TEXTURE_BINDING_3D(0x806A),
+    GL_TEXTURE_3D(0x806F),
+    GL_TEXTURE_WRAP_R(0x8072),
+    GL_MAX_3D_TEXTURE_SIZE(0x8073),
+    GL_VERTEX_ARRAY(0x8074),
+    GL_NORMAL_ARRAY(0x8075),
+    GL_COLOR_ARRAY(0x8076),
+    GL_TEXTURE_COORD_ARRAY(0x8078),
+    GL_VERTEX_ARRAY_SIZE(0x807A),
+    GL_VERTEX_ARRAY_TYPE(0x807B),
+    GL_VERTEX_ARRAY_STRIDE(0x807C),
+    GL_NORMAL_ARRAY_TYPE(0x807E),
+    GL_NORMAL_ARRAY_STRIDE(0x807F),
+    GL_COLOR_ARRAY_SIZE(0x8081),
+    GL_COLOR_ARRAY_TYPE(0x8082),
+    GL_COLOR_ARRAY_STRIDE(0x8083),
+    GL_TEXTURE_COORD_ARRAY_SIZE(0x8088),
+    GL_TEXTURE_COORD_ARRAY_TYPE(0x8089),
+    GL_TEXTURE_COORD_ARRAY_STRIDE(0x808A),
+    GL_VERTEX_ARRAY_POINTER(0x808E),
+    GL_NORMAL_ARRAY_POINTER(0x808F),
+    GL_COLOR_ARRAY_POINTER(0x8090),
+    GL_TEXTURE_COORD_ARRAY_POINTER(0x8092),
+    GL_MULTISAMPLE(0x809D),
+    GL_SAMPLE_ALPHA_TO_COVERAGE(0x809E),
+    GL_SAMPLE_ALPHA_TO_ONE(0x809F),
+    GL_SAMPLE_COVERAGE(0x80A0),
+    GL_SAMPLE_BUFFERS(0x80A8),
+    GL_SAMPLES(0x80A9),
+    GL_SAMPLE_COVERAGE_VALUE(0x80AA),
+    GL_SAMPLE_COVERAGE_INVERT(0x80AB),
+    GL_BLEND_DST_RGB(0x80C8),
+    GL_BLEND_SRC_RGB(0x80C9),
+    GL_BLEND_DST_ALPHA(0x80CA),
+    GL_BLEND_SRC_ALPHA(0x80CB),
+    GL_BGRA_EXT(0x80E1),
+    GL_POINT_SIZE_MIN(0x8126),
+    GL_POINT_SIZE_MAX(0x8127),
+    GL_POINT_FADE_THRESHOLD_SIZE(0x8128),
+    GL_POINT_DISTANCE_ATTENUATION(0x8129),
+    GL_CLAMP_TO_EDGE(0x812F),
+    GL_GENERATE_MIPMAP(0x8191),
+    GL_GENERATE_MIPMAP_HINT(0x8192),
+    GL_DEPTH_COMPONENT16(0x81A5),
+    GL_DEPTH_COMPONENT24(0x81A6),
+    GL_DEPTH_COMPONENT32(0x81A7),
+    GL_UNSIGNED_SHORT_5_6_5(0x8363),
+    GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT(0x8365),
+    GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT(0x8366),
+    GL_UNSIGNED_INT_2_10_10_10_REV_EXT(0x8368),
+    GL_MIRRORED_REPEAT(0x8370),
+    GL_COMPRESSED_RGB_S3TC_DXT1_EXT(0x83F0),
+    GL_COMPRESSED_RGBA_S3TC_DXT1_EXT(0x83F1),
+    GL_ALIASED_POINT_SIZE_RANGE(0x846D),
+    GL_ALIASED_LINE_WIDTH_RANGE(0x846E),
+    GL_TEXTURE0(0x84C0),
+    GL_TEXTURE1(0x84C1),
+    GL_TEXTURE2(0x84C2),
+    GL_TEXTURE3(0x84C3),
+    GL_TEXTURE4(0x84C4),
+    GL_TEXTURE5(0x84C5),
+    GL_TEXTURE6(0x84C6),
+    GL_TEXTURE7(0x84C7),
+    GL_TEXTURE8(0x84C8),
+    GL_TEXTURE9(0x84C9),
+    GL_TEXTURE10(0x84CA),
+    GL_TEXTURE11(0x84CB),
+    GL_TEXTURE12(0x84CC),
+    GL_TEXTURE13(0x84CD),
+    GL_TEXTURE14(0x84CE),
+    GL_TEXTURE15(0x84CF),
+    GL_TEXTURE16(0x84D0),
+    GL_TEXTURE17(0x84D1),
+    GL_TEXTURE18(0x84D2),
+    GL_TEXTURE19(0x84D3),
+    GL_TEXTURE20(0x84D4),
+    GL_TEXTURE21(0x84D5),
+    GL_TEXTURE22(0x84D6),
+    GL_TEXTURE23(0x84D7),
+    GL_TEXTURE24(0x84D8),
+    GL_TEXTURE25(0x84D9),
+    GL_TEXTURE26(0x84DA),
+    GL_TEXTURE27(0x84DB),
+    GL_TEXTURE28(0x84DC),
+    GL_TEXTURE29(0x84DD),
+    GL_TEXTURE30(0x84DE),
+    GL_TEXTURE31(0x84DF),
+    GL_ACTIVE_TEXTURE(0x84E0),
+    GL_CLIENT_ACTIVE_TEXTURE(0x84E1),
+    GL_MAX_TEXTURE_UNITS(0x84E2),
+    GL_SUBTRACT(0x84E7),
+    GL_MAX_RENDERBUFFER_SIZE(0x84E8),
+    GL_ALL_COMPLETED_NV(0x84F2),
+    GL_FENCE_STATUS_NV(0x84F3),
+    GL_FENCE_CONDITION_NV(0x84F4),
+    GL_DEPTH_STENCIL(0x84F9),
+    GL_UNSIGNED_INT_24_8(0x84FA),
+    GL_MAX_TEXTURE_LOD_BIAS_EXT(0x84FD),
+    GL_TEXTURE_MAX_ANISOTROPY_EXT(0x84FE),
+    GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT(0x84FF),
+    GL_TEXTURE_FILTER_CONTROL_EXT(0x8500),
+    GL_TEXTURE_LOD_BIAS_EXT(0x8501),
+    GL_INCR_WRAP(0x8507),
+    GL_DECR_WRAP(0x8508),
+    GL_NORMAL_MAP(0x8511),
+    GL_REFLECTION_MAP(0x8512),
+    GL_TEXTURE_CUBE_MAP(0x8513),
+    GL_TEXTURE_BINDING_CUBE_MAP(0x8514),
+    GL_TEXTURE_CUBE_MAP_POSITIVE_X(0x8515),
+    GL_TEXTURE_CUBE_MAP_NEGATIVE_X(0x8516),
+    GL_TEXTURE_CUBE_MAP_POSITIVE_Y(0x8517),
+    GL_TEXTURE_CUBE_MAP_NEGATIVE_Y(0x8518),
+    GL_TEXTURE_CUBE_MAP_POSITIVE_Z(0x8519),
+    GL_TEXTURE_CUBE_MAP_NEGATIVE_Z(0x851A),
+    GL_MAX_CUBE_MAP_TEXTURE_SIZE(0x851C),
+    GL_COMBINE(0x8570),
+    GL_COMBINE_RGB(0x8571),
+    GL_COMBINE_ALPHA(0x8572),
+    GL_RGB_SCALE(0x8573),
+    GL_ADD_SIGNED(0x8574),
+    GL_INTERPOLATE(0x8575),
+    GL_CONSTANT(0x8576),
+    GL_PRIMARY_COLOR(0x8577),
+    GL_PREVIOUS(0x8578),
+    GL_SRC0_RGB(0x8580),
+    GL_SRC1_RGB(0x8581),
+    GL_SRC2_RGB(0x8582),
+    GL_SRC0_ALPHA(0x8588),
+    GL_SRC1_ALPHA(0x8589),
+    GL_SRC2_ALPHA(0x858A),
+    GL_OPERAND0_RGB(0x8590),
+    GL_OPERAND1_RGB(0x8591),
+    GL_OPERAND2_RGB(0x8592),
+    GL_OPERAND0_ALPHA(0x8598),
+    GL_OPERAND1_ALPHA(0x8599),
+    GL_OPERAND2_ALPHA(0x859A),
+    GL_VERTEX_ARRAY_BINDING(0x85B5),
+    GL_VERTEX_ATTRIB_ARRAY_ENABLED(0x8622),
+    GL_VERTEX_ATTRIB_ARRAY_SIZE(0x8623),
+    GL_VERTEX_ATTRIB_ARRAY_STRIDE(0x8624),
+    GL_VERTEX_ATTRIB_ARRAY_TYPE(0x8625),
+    GL_CURRENT_VERTEX_ATTRIB(0x8626),
+    GL_VERTEX_ATTRIB_ARRAY_POINTER(0x8645),
+    GL_NUM_COMPRESSED_TEXTURE_FORMATS(0x86A2),
+    GL_COMPRESSED_TEXTURE_FORMATS(0x86A3),
+    GL_MAX_VERTEX_UNITS(0x86A4),
+    GL_WEIGHT_ARRAY_TYPE(0x86A9),
+    GL_WEIGHT_ARRAY_STRIDE(0x86AA),
+    GL_WEIGHT_ARRAY_SIZE(0x86AB),
+    GL_WEIGHT_ARRAY_POINTER(0x86AC),
+    GL_WEIGHT_ARRAY(0x86AD),
+    GL_DOT3_RGB(0x86AE),
+    GL_DOT3_RGBA(0x86AF),
+    GL_Z400_BINARY_AMD(0x8740),
+    GL_PROGRAM_BINARY_LENGTH(0x8741),
+    GL_BUFFER_SIZE(0x8764),
+    GL_BUFFER_USAGE(0x8765),
+    GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD(0x87EE),
+    GL_3DC_X_AMD(0x87F9),
+    GL_3DC_XY_AMD(0x87FA),
+    GL_NUM_PROGRAM_BINARY_FORMATS(0x87FE),
+    GL_PROGRAM_BINARY_FORMATS(0x87FF),
+    GL_STENCIL_BACK_FUNC(0x8800),
+    GL_STENCIL_BACK_FAIL(0x8801),
+    GL_STENCIL_BACK_PASS_DEPTH_FAIL(0x8802),
+    GL_STENCIL_BACK_PASS_DEPTH_PASS(0x8803),
+    GL_WRITEONLY_RENDERING_QCOM(0x8823),
+    GL_BLEND_EQUATION_ALPHA(0x883D),
+    GL_MATRIX_PALETTE(0x8840),
+    GL_MAX_PALETTE_MATRICES(0x8842),
+    GL_CURRENT_PALETTE_MATRIX(0x8843),
+    GL_MATRIX_INDEX_ARRAY(0x8844),
+    GL_MATRIX_INDEX_ARRAY_SIZE(0x8846),
+    GL_MATRIX_INDEX_ARRAY_TYPE(0x8847),
+    GL_MATRIX_INDEX_ARRAY_STRIDE(0x8848),
+    GL_MATRIX_INDEX_ARRAY_POINTER(0x8849),
+    GL_POINT_SPRITE(0x8861),
+    GL_COORD_REPLACE(0x8862),
+    GL_MAX_VERTEX_ATTRIBS(0x8869),
+    GL_VERTEX_ATTRIB_ARRAY_NORMALIZED(0x886A),
+    GL_MAX_TEXTURE_IMAGE_UNITS(0x8872),
+    GL_ARRAY_BUFFER(0x8892),
+    GL_ELEMENT_ARRAY_BUFFER(0x8893),
+    GL_ARRAY_BUFFER_BINDING(0x8894),
+    GL_ELEMENT_ARRAY_BUFFER_BINDING(0x8895),
+    GL_VERTEX_ARRAY_BUFFER_BINDING(0x8896),
+    GL_NORMAL_ARRAY_BUFFER_BINDING(0x8897),
+    GL_COLOR_ARRAY_BUFFER_BINDING(0x8898),
+    GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING(0x889A),
+    GL_WEIGHT_ARRAY_BUFFER_BINDING(0x889E),
+    GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING(0x889F),
+    GL_WRITE_ONLY(0x88B9),
+    GL_BUFFER_ACCESS(0x88BB),
+    GL_BUFFER_MAPPED(0x88BC),
+    GL_BUFFER_MAP_POINTER(0x88BD),
+    GL_STREAM_DRAW(0x88E0),
+    GL_STATIC_DRAW(0x88E4),
+    GL_DYNAMIC_DRAW(0x88E8),
+    GL_DEPTH24_STENCIL8(0x88F0),
+    GL_POINT_SIZE_ARRAY_TYPE(0x898A),
+    GL_POINT_SIZE_ARRAY_STRIDE(0x898B),
+    GL_POINT_SIZE_ARRAY_POINTER(0x898C),
+    GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS(0x898D),
+    GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS(0x898E),
+    GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS(0x898F),
+    GL_FRAGMENT_SHADER(0x8B30),
+    GL_VERTEX_SHADER(0x8B31),
+    GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS(0x8B4C),
+    GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS(0x8B4D),
+    GL_SHADER_TYPE(0x8B4F),
+    GL_FLOAT_VEC2(0x8B50),
+    GL_FLOAT_VEC3(0x8B51),
+    GL_FLOAT_VEC4(0x8B52),
+    GL_INT_VEC2(0x8B53),
+    GL_INT_VEC3(0x8B54),
+    GL_INT_VEC4(0x8B55),
+    GL_BOOL(0x8B56),
+    GL_BOOL_VEC2(0x8B57),
+    GL_BOOL_VEC3(0x8B58),
+    GL_BOOL_VEC4(0x8B59),
+    GL_FLOAT_MAT2(0x8B5A),
+    GL_FLOAT_MAT3(0x8B5B),
+    GL_FLOAT_MAT4(0x8B5C),
+    GL_SAMPLER_2D(0x8B5E),
+    GL_SAMPLER_3D(0x8B5F),
+    GL_SAMPLER_CUBE(0x8B60),
+    GL_DELETE_STATUS(0x8B80),
+    GL_COMPILE_STATUS(0x8B81),
+    GL_LINK_STATUS(0x8B82),
+    GL_VALIDATE_STATUS(0x8B83),
+    GL_INFO_LOG_LENGTH(0x8B84),
+    GL_ATTACHED_SHADERS(0x8B85),
+    GL_ACTIVE_UNIFORMS(0x8B86),
+    GL_ACTIVE_UNIFORM_MAX_LENGTH(0x8B87),
+    GL_SHADER_SOURCE_LENGTH(0x8B88),
+    GL_ACTIVE_ATTRIBUTES(0x8B89),
+    GL_ACTIVE_ATTRIBUTE_MAX_LENGTH(0x8B8A),
+    GL_FRAGMENT_SHADER_DERIVATIVE_HINT(0x8B8B),
+    GL_SHADING_LANGUAGE_VERSION(0x8B8C),
+    GL_CURRENT_PROGRAM(0x8B8D),
+    GL_PALETTE4_RGB8(0x8B90),
+    GL_PALETTE4_RGBA8(0x8B91),
+    GL_PALETTE4_R5_G6_B5(0x8B92),
+    GL_PALETTE4_RGBA4(0x8B93),
+    GL_PALETTE4_RGB5_A1(0x8B94),
+    GL_PALETTE8_RGB8(0x8B95),
+    GL_PALETTE8_RGBA8(0x8B96),
+    GL_PALETTE8_R5_G6_B5(0x8B97),
+    GL_PALETTE8_RGBA4(0x8B98),
+    GL_PALETTE8_RGB5_A1(0x8B99),
+    GL_IMPLEMENTATION_COLOR_READ_TYPE(0x8B9A),
+    GL_IMPLEMENTATION_COLOR_READ_FORMAT(0x8B9B),
+    GL_POINT_SIZE_ARRAY(0x8B9C),
+    GL_TEXTURE_CROP_RECT(0x8B9D),
+    GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING(0x8B9E),
+    GL_POINT_SIZE_ARRAY_BUFFER_BINDING(0x8B9F),
+    GL_COUNTER_TYPE_AMD(0x8BC0),
+    GL_COUNTER_RANGE_AMD(0x8BC1),
+    GL_UNSIGNED_INT64_AMD(0x8BC2),
+    GL_PERCENTAGE_AMD(0x8BC3),
+    GL_PERFMON_RESULT_AVAILABLE_AMD(0x8BC4),
+    GL_PERFMON_RESULT_SIZE_AMD(0x8BC5),
+    GL_PERFMON_RESULT_AMD(0x8BC6),
+    GL_TEXTURE_WIDTH_QCOM(0x8BD2),
+    GL_TEXTURE_HEIGHT_QCOM(0x8BD3),
+    GL_TEXTURE_DEPTH_QCOM(0x8BD4),
+    GL_TEXTURE_INTERNAL_FORMAT_QCOM(0x8BD5),
+    GL_TEXTURE_FORMAT_QCOM(0x8BD6),
+    GL_TEXTURE_TYPE_QCOM(0x8BD7),
+    GL_TEXTURE_IMAGE_VALID_QCOM(0x8BD8),
+    GL_TEXTURE_NUM_LEVELS_QCOM(0x8BD9),
+    GL_TEXTURE_TARGET_QCOM(0x8BDA),
+    GL_TEXTURE_OBJECT_VALID_QCOM(0x8BDB),
+    GL_STATE_RESTORE(0x8BDC),
+    GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG(0x8C00),
+    GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG(0x8C01),
+    GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG(0x8C02),
+    GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG(0x8C03),
+    GL_MODULATE_COLOR_IMG(0x8C04),
+    GL_RECIP_ADD_SIGNED_ALPHA_IMG(0x8C05),
+    GL_TEXTURE_ALPHA_MODULATE_IMG(0x8C06),
+    GL_FACTOR_ALPHA_MODULATE_IMG(0x8C07),
+    GL_FRAGMENT_ALPHA_MODULATE_IMG(0x8C08),
+    GL_ADD_BLEND_IMG(0x8C09),
+    GL_SGX_BINARY_IMG(0x8C0A),
+    GL_ATC_RGB_AMD(0x8C92),
+    GL_ATC_RGBA_EXPLICIT_ALPHA_AMD(0x8C93),
+    GL_STENCIL_BACK_REF(0x8CA3),
+    GL_STENCIL_BACK_VALUE_MASK(0x8CA4),
+    GL_STENCIL_BACK_WRITEMASK(0x8CA5),
+    GL_FRAMEBUFFER_BINDING(0x8CA6),
+    GL_RENDERBUFFER_BINDING(0x8CA7),
+    GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE(0x8CD0),
+    GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME(0x8CD1),
+    GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL(0x8CD2),
+    GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE(0x8CD3),
+    GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET(0x8CD4),
+    GL_FRAMEBUFFER_COMPLETE(0x8CD5),
+    GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT(0x8CD6),
+    GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT(0x8CD7),
+    GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS(0x8CD9),
+    GL_FRAMEBUFFER_INCOMPLETE_FORMATS(0x8CDA),
+    GL_FRAMEBUFFER_UNSUPPORTED(0x8CDD),
+    GL_COLOR_ATTACHMENT0(0x8CE0),
+    GL_DEPTH_ATTACHMENT(0x8D00),
+    GL_STENCIL_ATTACHMENT(0x8D20),
+    GL_FRAMEBUFFER(0x8D40),
+    GL_RENDERBUFFER(0x8D41),
+    GL_RENDERBUFFER_WIDTH(0x8D42),
+    GL_RENDERBUFFER_HEIGHT(0x8D43),
+    GL_RENDERBUFFER_INTERNAL_FORMAT(0x8D44),
+    GL_STENCIL_INDEX1(0x8D46),
+    GL_STENCIL_INDEX4(0x8D47),
+    GL_STENCIL_INDEX8(0x8D48),
+    GL_RENDERBUFFER_RED_SIZE(0x8D50),
+    GL_RENDERBUFFER_GREEN_SIZE(0x8D51),
+    GL_RENDERBUFFER_BLUE_SIZE(0x8D52),
+    GL_RENDERBUFFER_ALPHA_SIZE(0x8D53),
+    GL_RENDERBUFFER_DEPTH_SIZE(0x8D54),
+    GL_RENDERBUFFER_STENCIL_SIZE(0x8D55),
+    GL_TEXTURE_GEN_STR(0x8D60),
+    GL_HALF_FLOAT(0x8D61),
+    GL_RGB565(0x8D62),
+    GL_ETC1_RGB8(0x8D64),
+    GL_TEXTURE_EXTERNAL(0x8D65),
+    GL_SAMPLER_EXTERNAL(0x8D66),
+    GL_TEXTURE_BINDING_EXTERNAL(0x8D67),
+    GL_REQUIRED_TEXTURE_IMAGE_UNITS(0x8D68),
+    GL_LOW_FLOAT(0x8DF0),
+    GL_MEDIUM_FLOAT(0x8DF1),
+    GL_HIGH_FLOAT(0x8DF2),
+    GL_LOW_INT(0x8DF3),
+    GL_MEDIUM_INT(0x8DF4),
+    GL_HIGH_INT(0x8DF5),
+    GL_UNSIGNED_INT_10_10_10_2(0x8DF6),
+    GL_INT_10_10_10_2(0x8DF7),
+    GL_SHADER_BINARY_FORMATS(0x8DF8),
+    GL_NUM_SHADER_BINARY_FORMATS(0x8DF9),
+    GL_SHADER_COMPILER(0x8DFA),
+    GL_MAX_VERTEX_UNIFORM_VECTORS(0x8DFB),
+    GL_MAX_VARYING_VECTORS(0x8DFC),
+    GL_MAX_FRAGMENT_UNIFORM_VECTORS(0x8DFD),
+    GL_DEPTH_COMPONENT16_NONLINEAR_NV(0x8E2C),
+    GL_COVERAGE_COMPONENT_NV(0x8ED0),
+    GL_COVERAGE_COMPONENT4_NV(0x8ED1),
+    GL_COVERAGE_ATTACHMENT_NV(0x8ED2),
+    GL_COVERAGE_BUFFERS_NV(0x8ED3),
+    GL_COVERAGE_SAMPLES_NV(0x8ED4),
+    GL_COVERAGE_ALL_FRAGMENTS_NV(0x8ED5),
+    GL_COVERAGE_EDGE_FRAGMENTS_NV(0x8ED6),
+    GL_COVERAGE_AUTOMATIC_NV(0x8ED7),
+    GL_PERFMON_GLOBAL_MODE_QCOM(0x8FA0),
+    GL_SGX_PROGRAM_BINARY_IMG(0x9130),
+    GL_RENDERBUFFER_SAMPLES_IMG(0x9133),
+    GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG(0x9134),
+    GL_MAX_SAMPLES_IMG(0x9135),
+    GL_TEXTURE_SAMPLES_IMG(0x9136),
+    ;
+
+    public final int value;
+    GLEnum(final int value) {
+        this.value = value;
+    }
+
+    private static final java.util.HashMap<Integer, GLEnum> reverseMap = new java.util.HashMap<Integer, GLEnum>();
+    static {
+        for (GLEnum e : GLEnum.values())
+        reverseMap.put(e.value, e);
+    }
+
+    public static GLEnum valueOf(final int value) {
+        return reverseMap.get(value);
+    }
+}
\ No newline at end of file
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerShader.java b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerShader.java
new file mode 100644
index 0000000..f13c465
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerShader.java
@@ -0,0 +1,259 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.sdklib.util.SparseArray;
+
+import java.util.ArrayList;
+
+class GLShader implements Cloneable {
+    public final int name;
+    GLServerShader context; // the context this was created in
+    public final GLEnum type;
+    public boolean delete;
+    public ArrayList<Integer> programs = new ArrayList<Integer>();
+    public String source, originalSource;
+
+    GLShader(final int name, final GLServerShader context, final GLEnum type) {
+        this.name = name;
+        this.context = context;
+        this.type = type;
+    }
+
+    /** deep copy */
+    public GLShader clone(final GLServerShader copyContext) {
+        try {
+            GLShader shader = (GLShader) super.clone();
+            shader.programs = (ArrayList<Integer>) programs.clone();
+            shader.context = copyContext;
+            return shader;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+}
+
+class GLProgram implements Cloneable {
+    public final int name;
+    GLServerShader context; // the context this was created in
+    public boolean delete;
+    public int vert, frag;
+
+    GLProgram(final int name, final GLServerShader context) {
+        this.name = name;
+        this.context = context;
+    }
+
+    /** deep copy */
+    public GLProgram clone(final GLServerShader copyContext) {
+        try {
+            GLProgram copy = (GLProgram) super.clone();
+            copy.context = copyContext;
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+}
+
+public class GLServerShader implements Cloneable {
+    Context context;
+    public SparseArray<GLShader> shaders = new SparseArray<GLShader>();
+    public SparseArray<GLProgram> programs = new SparseArray<GLProgram>();
+    public GLProgram current = null;
+    boolean uiUpdate = false;
+
+    GLServerShader(Context context) {
+        this.context = context;
+    }
+
+    /** deep copy */
+    public GLServerShader clone(final Context copyContext) {
+        try {
+            GLServerShader copy = (GLServerShader) super.clone();
+            copy.context = copyContext;
+
+            copy.shaders = new SparseArray<GLShader>(shaders.size());
+            for (int i = 0; i < shaders.size(); i++)
+                copy.shaders.append(shaders.keyAt(i), shaders.valueAt(i).clone(copy));
+
+            copy.programs = new SparseArray<GLProgram>(programs.size());
+            for (int i = 0; i < programs.size(); i++)
+                copy.programs.append(programs.keyAt(i), programs.valueAt(i).clone(copy));
+
+            if (current != null)
+                copy.current = copy.programs.get(current.name);
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+
+    /** returns true if processed */
+    public boolean processMessage(final Message msg) {
+        boolean oldUiUpdate = uiUpdate;
+        uiUpdate = true;
+        switch (msg.getFunction()) {
+            case glAttachShader:
+                glAttachShader(msg);
+                return true;
+            case glCreateProgram:
+                glCreateProgram(msg);
+                return true;
+            case glCreateShader:
+                glCreateShader(msg);
+                return true;
+            case glDeleteProgram:
+                glDeleteProgram(msg);
+                return true;
+            case glDeleteShader:
+                glDeleteShader(msg);
+                return true;
+            case glDetachShader:
+                glDetachShader(msg);
+                return true;
+            case glShaderSource:
+                glShaderSource(msg);
+                return true;
+            case glUseProgram:
+                glUseProgram(msg);
+                return true;
+            default:
+                uiUpdate = oldUiUpdate;
+                return false;
+        }
+    }
+
+    GLShader getShader(int name) {
+        if (name == 0)
+            return null;
+        for (Context ctx : context.shares) {
+            GLShader shader = ctx.serverShader.shaders.get(name);
+            if (shader != null)
+                return shader;
+        }
+        assert false;
+        return null;
+    }
+
+    GLProgram getProgram(int name) {
+        if (name == 0)
+            return null;
+        for (Context ctx : context.shares) {
+            GLProgram program = ctx.serverShader.programs.get(name);
+            if (program != null)
+                return program;
+        }
+        assert false;
+        return null;
+    }
+
+    // void API_ENTRY(glAttachShader)(GLuint program, GLuint shader)
+    void glAttachShader(final Message msg) {
+        GLProgram program = getProgram(msg.getArg0());
+        assert program != null;
+        GLShader shader = getShader(msg.getArg1());
+        assert program != null;
+        if (GLEnum.GL_VERTEX_SHADER == shader.type)
+            program.vert = shader.name;
+        else
+            program.frag = shader.name;
+        shader.programs.add(program.name);
+    }
+
+    // GLuint API_ENTRY(glCreateProgram)(void)
+    void glCreateProgram(final Message msg) {
+        programs.put(msg.getRet(), new GLProgram(msg.getRet(), this));
+    }
+
+    // GLuint API_ENTRY(glCreateShader)(GLenum type)
+    void glCreateShader(final Message msg) {
+        shaders.put(msg.getRet(),
+                new GLShader(msg.getRet(), this, GLEnum.valueOf(msg.getArg0())));
+    }
+
+    // void API_ENTRY(glDeleteProgram)
+    void glDeleteProgram(final Message msg) {
+        if (msg.getArg0() == 0)
+            return;
+        GLProgram program = getProgram(msg.getArg0());
+        program.delete = true;
+        for (Context ctx : context.shares)
+            if (ctx.serverShader.current == program)
+                return;
+        glDetachShader(program, getShader(program.vert));
+        glDetachShader(program, getShader(program.frag));
+        programs.remove(program.name);
+    }
+
+    // void API_ENTRY(glDeleteShader)(GLuint shader)
+    void glDeleteShader(final Message msg) {
+        if (msg.getArg0() == 0)
+            return;
+        GLShader shader = getShader(msg.getArg0());
+        shader.delete = true;
+        if (shader.programs.size() == 0)
+            shaders.remove(shader.name);
+    }
+
+    // void API_ENTRY(glDetachShader)(GLuint program, GLuint shader)
+    void glDetachShader(final Message msg) {
+        glDetachShader(getProgram(msg.getArg0()), getShader(msg.getArg1()));
+    }
+
+    void glDetachShader(final GLProgram program, final GLShader shader) {
+        if (program == null)
+            return;
+        if (program.vert == shader.name)
+            program.vert = 0;
+        else if (program.frag == shader.name)
+            program.frag = 0;
+        else
+            return;
+        shader.programs.remove(new Integer(program.name));
+        if (shader.delete && shader.programs.size() == 0)
+            shaders.remove(shader.name);
+    }
+
+    // void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const
+    // GLchar** string, const GLint* length)
+    void glShaderSource(final Message msg) {
+        if (!msg.hasData())
+            return; // TODO: distinguish between generated calls
+        GLShader shader = getShader(msg.getArg0());
+        shader.source = shader.originalSource = msg.getData().toStringUtf8();
+    }
+
+    // void API_ENTRY(glUseProgram)(GLuint program)
+    void glUseProgram(final Message msg) {
+        GLProgram oldCurrent = current;
+        current = getProgram(msg.getArg0());
+        if (null != oldCurrent && oldCurrent.delete && oldCurrent != current) {
+            for (Context ctx : context.shares)
+                if (ctx.serverShader.current == oldCurrent)
+                    return;
+            oldCurrent.context.programs.remove(new Integer(oldCurrent.name));
+        }
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerState.java b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerState.java
new file mode 100644
index 0000000..addf277
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerState.java
@@ -0,0 +1,271 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.sdklib.util.SparseArray;
+import com.android.sdklib.util.SparseIntArray;
+
+class GLStencilState implements Cloneable {
+    public int ref, mask;
+    public GLEnum func;
+    public GLEnum sf, df, dp; // operation
+
+    @Override
+    public Object clone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}
+
+public class GLServerState implements Cloneable {
+    final Context context;
+    public GLStencilState front = new GLStencilState(), back = new GLStencilState();
+    public SparseIntArray enableDisables;
+
+    /** integer states set via a GL function and GLEnum; keyed by GLEnum.value */
+    public SparseArray<Message> integers;
+
+    /** states set only via a GL function; keyed by Function.getNumber() */
+    public SparseArray<Message> lastSetter;
+
+    GLServerState(final Context context) {
+        this.context = context;
+        enableDisables = new SparseIntArray();
+        enableDisables.put(GLEnum.GL_BLEND.value, 0);
+        enableDisables.put(GLEnum.GL_DITHER.value, 1);
+        enableDisables.put(GLEnum.GL_DEPTH_TEST.value, 0);
+        enableDisables.put(GLEnum.GL_STENCIL_TEST.value, 0);
+        enableDisables.put(GLEnum.GL_SCISSOR_TEST.value, 0);
+        enableDisables.put(GLEnum.GL_SAMPLE_COVERAGE.value, 0);
+        enableDisables.put(GLEnum.GL_SAMPLE_ALPHA_TO_COVERAGE.value, 0);
+        enableDisables.put(GLEnum.GL_POLYGON_OFFSET_FILL.value, 0);
+        enableDisables.put(GLEnum.GL_CULL_FACE.value, 0);
+        // enableDisables.put(GLEnum.GL_TEXTURE_2D.value, 1);
+
+        lastSetter = new SparseArray<Message>();
+        lastSetter.put(Function.glBlendColor.getNumber(), null);
+        // glBlendEquation overwrites glBlendEquationSeparate
+        lastSetter.put(Function.glBlendEquationSeparate.getNumber(), null);
+        // glBlendFunc overwrites glBlendFuncSeparate
+        lastSetter.put(Function.glBlendFuncSeparate.getNumber(), null);
+        lastSetter.put(Function.glClearColor.getNumber(), null);
+        lastSetter.put(Function.glClearDepthf.getNumber(), null);
+        lastSetter.put(Function.glClearStencil.getNumber(), null);
+        lastSetter.put(Function.glColorMask.getNumber(), null);
+        lastSetter.put(Function.glCullFace.getNumber(), null);
+        lastSetter.put(Function.glDepthMask.getNumber(), null);
+        lastSetter.put(Function.glDepthFunc.getNumber(), null);
+        lastSetter.put(Function.glDepthRangef.getNumber(), null);
+        lastSetter.put(Function.glFrontFace.getNumber(), null);
+        lastSetter.put(Function.glLineWidth.getNumber(), null);
+        lastSetter.put(Function.glPolygonOffset.getNumber(), null);
+        lastSetter.put(Function.glSampleCoverage.getNumber(), null);
+        lastSetter.put(Function.glScissor.getNumber(), null);
+        lastSetter.put(Function.glStencilMaskSeparate.getNumber(), null);
+        lastSetter.put(Function.glViewport.getNumber(), null);
+
+        integers = new SparseArray<Message>();
+        integers.put(GLEnum.GL_PACK_ALIGNMENT.value, null);
+        integers.put(GLEnum.GL_UNPACK_ALIGNMENT.value, null);
+    }
+
+    /** returns true if processed */
+    public boolean processMessage(final Message msg) {
+        switch (msg.getFunction()) {
+            case glBlendColor:
+            case glBlendEquation:
+            case glBlendEquationSeparate:
+            case glBlendFunc:
+            case glBlendFuncSeparate:
+            case glClearColor:
+            case glClearDepthf:
+            case glClearStencil:
+            case glColorMask:
+            case glCullFace:
+            case glDepthMask:
+            case glDepthFunc:
+            case glDepthRangef:
+                return setter(msg);
+            case glDisable:
+                return enableDisable(false, msg);
+            case glEnable:
+                return enableDisable(true, msg);
+            case glFrontFace:
+            case glLineWidth:
+                return setter(msg);
+            case glPixelStorei:
+                if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_PACK_ALIGNMENT)
+                    integers.put(msg.getArg0(), msg);
+                else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_UNPACK_ALIGNMENT)
+                    integers.put(msg.getArg0(), msg);
+                else
+                    assert false;
+                return true;
+            case glPolygonOffset:
+            case glSampleCoverage:
+            case glScissor:
+                return setter(msg);
+            case glStencilFunc: {
+                Message.Builder builder = msg.toBuilder();
+                builder.setArg2(msg.getArg1());
+                builder.setArg1(msg.getArg0());
+                builder.setArg0(GLEnum.GL_FRONT_AND_BACK.value);
+                return glStencilFuncSeparate(builder.build());
+            }
+            case glStencilFuncSeparate:
+                return glStencilFuncSeparate(msg);
+            case glStencilMask:
+            case glStencilMaskSeparate:
+                return setter(msg);
+            case glStencilOp: {
+                Message.Builder builder = msg.toBuilder();
+                builder.setArg3(msg.getArg2());
+                builder.setArg2(msg.getArg1());
+                builder.setArg1(msg.getArg0());
+                builder.setArg0(GLEnum.GL_FRONT_AND_BACK.value);
+                return glStencilOpSeparate(builder.build());
+            }
+            case glStencilOpSeparate:
+                return glStencilOpSeparate(msg);
+            case glViewport:
+                return setter(msg);
+            default:
+                return false;
+        }
+    }
+
+    boolean setter(final Message msg) {
+        switch (msg.getFunction()) {
+            case glBlendFunc:
+                lastSetter.put(Function.glBlendFuncSeparate.getNumber(), msg);
+                break;
+            case glBlendEquation:
+                lastSetter.put(Function.glBlendEquationSeparate.getNumber(), msg);
+                break;
+            case glStencilMask:
+                lastSetter.put(Function.glStencilMaskSeparate.getNumber(), msg);
+                break;
+            default:
+                lastSetter.put(msg.getFunction().getNumber(), msg);
+                break;
+        }
+        return true;
+    }
+
+    boolean enableDisable(boolean enable, final Message msg) {
+        int index = enableDisables.indexOfKey(msg.getArg0());
+        if (index < 0) {
+            System.out.print("invalid glDisable/Enable: ");
+            System.out.println(MessageFormatter.format(msg, false));
+            return true;
+        }
+        if ((enableDisables.valueAt(index) != 0) == enable)
+            return true; // TODO: redundant
+        enableDisables.put(msg.getArg0(), enable ? 1 : 0);
+        return true;
+    }
+
+    // void StencilFuncSeparate( enum face, enum func, int ref, uint mask )
+    boolean glStencilFuncSeparate(final Message msg) {
+        GLEnum ff = front.func, bf = back.func;
+        int fr = front.ref, br = back.ref;
+        int fm = front.mask, bm = back.mask;
+        final GLEnum face = GLEnum.valueOf(msg.getArg0());
+        if (face == GLEnum.GL_FRONT || face == GLEnum.GL_FRONT_AND_BACK) {
+            ff = GLEnum.valueOf(msg.getArg1());
+            fr = msg.getArg2();
+            fm = msg.getArg3();
+        }
+        if (face == GLEnum.GL_BACK || face == GLEnum.GL_FRONT_AND_BACK) {
+            bf = GLEnum.valueOf(msg.getArg1());
+            br = msg.getArg2();
+            bm = msg.getArg3();
+        }
+        if (ff == front.func && fr == front.ref && fm == front.mask)
+            if (bf == back.func && br == back.ref && bm == back.mask)
+                return true; // TODO: redundant
+        front.func = ff;
+        front.ref = fr;
+        front.mask = fm;
+        back.func = bf;
+        back.ref = br;
+        back.mask = bm;
+        return true;
+    }
+
+    // void StencilOpSeparate( enum face, enum sfail, enum dpfail, enum dppass )
+    boolean glStencilOpSeparate(final Message msg) {
+        GLEnum fsf = front.sf, fdf = front.df, fdp = front.dp;
+        GLEnum bsf = back.sf, bdf = back.df, bdp = back.dp;
+        final GLEnum face = GLEnum.valueOf(msg.getArg0());
+        if (face == GLEnum.GL_FRONT || face == GLEnum.GL_FRONT_AND_BACK) {
+            fsf = GLEnum.valueOf(msg.getArg1());
+            fdf = GLEnum.valueOf(msg.getArg2());
+            fdp = GLEnum.valueOf(msg.getArg3());
+        }
+        if (face == GLEnum.GL_BACK || face == GLEnum.GL_FRONT_AND_BACK) {
+            bsf = GLEnum.valueOf(msg.getArg1());
+            bdf = GLEnum.valueOf(msg.getArg2());
+            bdp = GLEnum.valueOf(msg.getArg3());
+        }
+        if (fsf == front.sf && fdf == front.df && fdp == front.dp)
+            if (bsf == back.sf && bdf == back.df && bdp == back.dp)
+                return true; // TODO: redundant
+        front.sf = fsf;
+        front.df = fdf;
+        front.dp = fdp;
+        back.sf = bsf;
+        back.df = bdf;
+        back.dp = bdp;
+        return true;
+    }
+
+    /** deep copy */
+    @Override
+    public GLServerState clone() {
+        try {
+            GLServerState newState = (GLServerState) super.clone();
+            newState.front = (GLStencilState) front.clone();
+            newState.back = (GLStencilState) back.clone();
+
+            newState.enableDisables = new SparseIntArray(enableDisables.size());
+            for (int i = 0; i < enableDisables.size(); i++)
+                newState.enableDisables.append(enableDisables.keyAt(i),
+                        enableDisables.valueAt(i));
+
+            newState.integers = new SparseArray<Message>(integers.size());
+            for (int i = 0; i < integers.size(); i++)
+                newState.integers.append(integers.keyAt(i), integers.valueAt(i));
+
+            newState.lastSetter = new SparseArray<Message>(lastSetter.size());
+            for (int i = 0; i < lastSetter.size(); i++)
+                newState.lastSetter.append(lastSetter.keyAt(i), lastSetter.valueAt(i));
+
+            return newState;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerTexture.java b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerTexture.java
new file mode 100644
index 0000000..27676dd
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerTexture.java
@@ -0,0 +1,235 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.sdklib.util.SparseArray;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+class GLTexture implements Cloneable {
+    public final int name;
+    public final GLEnum target;
+    public ArrayList<Message> contentChanges = new ArrayList<Message>();
+    public GLEnum wrapS = GLEnum.GL_REPEAT, wrapT = GLEnum.GL_REPEAT;
+    public GLEnum min = GLEnum.GL_NEAREST_MIPMAP_LINEAR;
+    public GLEnum mag = GLEnum.GL_LINEAR;
+    public GLEnum format;
+    public int width, height;
+
+    GLTexture(final int name, final GLEnum target) {
+        this.name = name;
+        this.target = target;
+    }
+
+    @Override
+    public GLTexture clone() {
+        try {
+            GLTexture copy = (GLTexture) super.clone();
+            copy.contentChanges = (ArrayList<Message>) contentChanges.clone();
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+
+    boolean processMessage(final Message msg) {
+        switch (msg.getFunction()) {
+            case glCompressedTexImage2D:
+            case glCopyTexImage2D:
+            case glTexImage2D:
+                if (msg.getArg1() == 0) { // level 0
+                    format = GLEnum.valueOf(msg.getArg2());
+                    width = msg.getArg3();
+                    height = msg.getArg4();
+                }
+                //$FALL-THROUGH$
+            case glCompressedTexSubImage2D:
+            case glCopyTexSubImage2D:
+            case glTexSubImage2D:
+            case glGenerateMipmap:
+                contentChanges.add(msg);
+                break;
+            default:
+                assert false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s %s %d*%d %d change(s)", target, format, width, height,
+                contentChanges.size());
+    }
+}
+
+public class GLServerTexture implements Cloneable {
+    Context context;
+
+    public GLEnum activeTexture = GLEnum.GL_TEXTURE0;
+    public int[] tmu2D;
+    public int[] tmuCube;
+    public SparseArray<GLTexture> textures = new SparseArray<GLTexture>();
+    public GLTexture tex2D = null, texCube = null;
+
+    GLServerTexture(final Context context, final int MAX_COMBINED_TEXTURE_IMAGE_UNITS) {
+        this.context = context;
+        textures.append(0, null);
+        tmu2D = new int[MAX_COMBINED_TEXTURE_IMAGE_UNITS];
+        tmuCube = new int[MAX_COMBINED_TEXTURE_IMAGE_UNITS];
+    }
+
+    public GLServerTexture clone(final Context copyContext) {
+        try {
+            GLServerTexture copy = (GLServerTexture) super.clone();
+            copy.context = copyContext;
+
+            copy.tmu2D = tmu2D.clone();
+            copy.tmuCube = tmuCube.clone();
+
+            copy.textures = new SparseArray<GLTexture>(textures.size());
+            for (int i = 0; i < textures.size(); i++)
+                if (textures.valueAt(i) != null)
+                    copy.textures.append(textures.keyAt(i), textures.valueAt(i).clone());
+                else
+                    copy.textures.append(textures.keyAt(i), null);
+
+            if (tex2D != null)
+                copy.tex2D = copy.textures.get(tex2D.name);
+            if (texCube != null)
+                copy.texCube = copy.textures.get(texCube.name);
+
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+
+    public boolean processMessage(final Message msg) {
+        switch (msg.getFunction()) {
+            case glActiveTexture:
+                activeTexture = GLEnum.valueOf(msg.getArg0());
+                return true;
+            case glBindTexture:
+                return bindTexture(msg.getArg0(), msg.getArg1());
+            case glCompressedTexImage2D:
+            case glCompressedTexSubImage2D:
+            case glCopyTexImage2D:
+            case glCopyTexSubImage2D:
+            case glTexImage2D:
+            case glTexSubImage2D:
+                switch (GLEnum.valueOf(msg.getArg0())) {
+                    case GL_TEXTURE_2D:
+                        if (tex2D != null)
+                            return tex2D.processMessage(msg);
+                        return true;
+                    case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+                    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+                    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+                    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+                    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+                    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+                        if (texCube != null)
+                            return texCube.processMessage(msg);
+                        return true;
+                    default:
+                        return true;
+                }
+            case glDeleteTextures: {
+                final ByteBuffer names = msg.getData().asReadOnlyByteBuffer();
+                names.order(SampleView.targetByteOrder);
+                for (int i = 0; i < msg.getArg0(); i++) {
+                    final int name = names.getInt();
+                    if (tex2D != null && tex2D.name == name)
+                        bindTexture(GLEnum.GL_TEXTURE_2D.value, 0);
+                    if (texCube != null && texCube.name == name)
+                        bindTexture(GLEnum.GL_TEXTURE_CUBE_MAP.value, 0);
+                    if (name != 0)
+                        textures.remove(name);
+                }
+                return true;
+            }
+            case glGenerateMipmap:
+                if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_TEXTURE_2D && tex2D != null)
+                    return tex2D.processMessage(msg);
+                else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_TEXTURE_CUBE_MAP
+                        && texCube != null)
+                    return texCube.processMessage(msg);
+                return true;
+            case glTexParameteri:
+                return texParameter(msg.getArg0(), msg.getArg1(), msg.getArg2());
+            case glTexParameterf:
+                return texParameter(msg.getArg0(), msg.getArg1(),
+                        (int) Float.intBitsToFloat(msg.getArg2()));
+            default:
+                return false;
+        }
+    }
+
+    boolean bindTexture(final int target, final int name) {
+        final int index = activeTexture.value - GLEnum.GL_TEXTURE0.value;
+        if (GLEnum.valueOf(target) == GLEnum.GL_TEXTURE_2D) {
+            tex2D = textures.get(name);
+            if (name != 0 && tex2D == null)
+                textures.put(name, tex2D = new GLTexture(name,
+                        GLEnum.GL_TEXTURE_2D));
+            if (index >= 0 && index < tmu2D.length)
+                tmu2D[index] = name;
+        } else if (GLEnum.valueOf(target) == GLEnum.GL_TEXTURE_CUBE_MAP) {
+            texCube = textures.get(name);
+            if (name != 0 && texCube == null)
+                textures.put(name, texCube = new GLTexture(name,
+                        GLEnum.GL_TEXTURE_CUBE_MAP));
+            if (index >= 0 && index < tmu2D.length)
+                tmu2D[index] = name;
+        } else
+            assert false;
+        return true;
+    }
+
+    boolean texParameter(final int target, final int pname, final int param) {
+        GLTexture tex = null;
+        if (GLEnum.valueOf(target) == GLEnum.GL_TEXTURE_2D)
+            tex = tex2D;
+        else if (GLEnum.valueOf(target) == GLEnum.GL_TEXTURE_CUBE_MAP)
+            tex = texCube;
+        if (tex == null)
+            return true;
+        final GLEnum p = GLEnum.valueOf(param);
+        switch (GLEnum.valueOf(pname)) {
+            case GL_TEXTURE_WRAP_S:
+                tex.wrapS = p;
+                return true;
+            case GL_TEXTURE_WRAP_T:
+                tex.wrapT = p;
+                return true;
+            case GL_TEXTURE_MIN_FILTER:
+                tex.min = p;
+                return true;
+            case GL_TEXTURE_MAG_FILTER:
+                tex.mag = p;
+                return true;
+            default:
+                return true;
+        }
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerVertex.java b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerVertex.java
new file mode 100644
index 0000000..5f9d513
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerVertex.java
@@ -0,0 +1,542 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.sdklib.util.SparseArray;
+
+import java.nio.ByteBuffer;
+
+class GLBuffer implements Cloneable {
+    public final int name;
+    public GLEnum usage;
+    public GLEnum target;
+    /** in SampleView.targetByteOrder */
+    public ByteBuffer data;
+
+    public GLBuffer(final int name) {
+        this.name = name;
+    }
+
+    /** deep copy */
+    @Override
+    public GLBuffer clone() {
+        try {
+            GLBuffer copy = (GLBuffer) super.clone();
+            if (data != null) {
+                copy.data = ByteBuffer.allocate(data.capacity());
+                copy.data.order(SampleView.targetByteOrder);
+                data.position(0);
+                copy.data.put(data);
+            }
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+}
+
+class GLAttribPointer implements Cloneable {
+    public int size; // number of values per vertex
+    public GLEnum type; // data type
+    public int stride; // bytes
+    /**
+     * element stride in bytes, used when fetching from buffer; not for fetching
+     * from user pointer since server already packed elements
+     */
+    int elemStride; // in bytes
+    /** element size in bytes */
+    int elemSize;
+    public int ptr; // pointer in debugger server or byte offset into buffer
+    public GLBuffer buffer;
+    public boolean normalized;
+    public boolean enabled;
+
+    /** deep copy, re-maps buffer into copyBuffers */
+    public GLAttribPointer clone(SparseArray<GLBuffer> copyBuffers) {
+        try {
+            GLAttribPointer copy = (GLAttribPointer) super.clone();
+            if (buffer != null)
+                copy.buffer = copyBuffers.get(buffer.name);
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+}
+
+public class GLServerVertex implements Cloneable {
+    public SparseArray<GLBuffer> buffers = new SparseArray<GLBuffer>();
+    public GLBuffer attribBuffer, indexBuffer; // current binding
+    public GLAttribPointer attribPointers[];
+    public float defaultAttribs[][];
+
+    public GLServerVertex(final int MAX_VERTEX_ATTRIBS) {
+        buffers.append(0, null);
+        attribPointers = new GLAttribPointer[MAX_VERTEX_ATTRIBS];
+        for (int i = 0; i < attribPointers.length; i++)
+            attribPointers[i] = new GLAttribPointer();
+        defaultAttribs = new float[MAX_VERTEX_ATTRIBS][4];
+        for (int i = 0; i < defaultAttribs.length; i++) {
+            defaultAttribs[i][0] = 0;
+            defaultAttribs[i][1] = 0;
+            defaultAttribs[i][2] = 0;
+            defaultAttribs[i][3] = 1;
+        }
+    }
+
+    /** deep copy */
+    @Override
+    public GLServerVertex clone() {
+        try {
+            GLServerVertex copy = (GLServerVertex) super.clone();
+
+            copy.buffers = new SparseArray<GLBuffer>(buffers.size());
+            for (int i = 0; i < buffers.size(); i++)
+                if (buffers.valueAt(i) != null)
+                    copy.buffers.append(buffers.keyAt(i), buffers.valueAt(i).clone());
+                else
+                    copy.buffers.append(buffers.keyAt(i), null);
+
+            if (attribBuffer != null)
+                copy.attribBuffer = copy.buffers.get(attribBuffer.name);
+            if (indexBuffer != null)
+                copy.indexBuffer = copy.buffers.get(indexBuffer.name);
+
+            copy.attribPointers = new GLAttribPointer[attribPointers.length];
+            for (int i = 0; i < attribPointers.length; i++)
+                copy.attribPointers[i] = attribPointers[i].clone(copy.buffers);
+
+            copy.defaultAttribs = defaultAttribs.clone();
+
+            return copy;
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+            assert false;
+            return null;
+        }
+    }
+
+    /** returns true if processed */
+    public boolean process(final Message msg) {
+        switch (msg.getFunction()) {
+            case glBindBuffer:
+                glBindBuffer(msg);
+                return true;
+            case glBufferData:
+                glBufferData(msg);
+                return true;
+            case glBufferSubData:
+                glBufferSubData(msg);
+                return true;
+            case glDeleteBuffers:
+                glDeleteBuffers(msg);
+                return true;
+            case glDrawArrays:
+            case glDrawElements:
+                return true;
+            case glDisableVertexAttribArray:
+                glDisableVertexAttribArray(msg);
+                return true;
+            case glEnableVertexAttribArray:
+                glEnableVertexAttribArray(msg);
+                return true;
+            case glGenBuffers:
+                glGenBuffers(msg);
+                return true;
+            case glVertexAttribPointer:
+                glVertexAttribPointer(msg);
+                return true;
+            case glVertexAttrib1f:
+                glVertexAttrib1f(msg);
+                return true;
+            case glVertexAttrib1fv:
+                glVertexAttrib1fv(msg);
+                return true;
+            case glVertexAttrib2f:
+                glVertexAttrib2f(msg);
+                return true;
+            case glVertexAttrib2fv:
+                glVertexAttrib2fv(msg);
+                return true;
+            case glVertexAttrib3f:
+                glVertexAttrib3f(msg);
+                return true;
+            case glVertexAttrib3fv:
+                glVertexAttrib3fv(msg);
+                return true;
+            case glVertexAttrib4f:
+                glVertexAttrib4f(msg);
+                return true;
+            case glVertexAttrib4fv:
+                glVertexAttrib4fv(msg);
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    // void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer)
+    public void glBindBuffer(Message msg) {
+        if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ARRAY_BUFFER) {
+            attribBuffer = buffers.get(msg.getArg1());
+            if (null != attribBuffer)
+                attribBuffer.target = GLEnum.GL_ARRAY_BUFFER;
+        } else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ELEMENT_ARRAY_BUFFER) {
+            indexBuffer = buffers.get(msg.getArg1());
+            if (null != indexBuffer)
+                indexBuffer.target = GLEnum.GL_ELEMENT_ARRAY_BUFFER;
+        } else
+            assert false;
+    }
+
+    // void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const
+    // GLvoid:size:in data, GLenum usage)
+    public void glBufferData(Message msg) {
+        if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ARRAY_BUFFER) {
+            attribBuffer.usage = GLEnum.valueOf(msg.getArg3());
+            attribBuffer.data = msg.getData().asReadOnlyByteBuffer();
+            attribBuffer.data.order(SampleView.targetByteOrder);
+        } else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ELEMENT_ARRAY_BUFFER) {
+            indexBuffer.usage = GLEnum.valueOf(msg.getArg3());
+            indexBuffer.data = msg.getData().asReadOnlyByteBuffer();
+            indexBuffer.data.order(SampleView.targetByteOrder);
+        } else
+            assert false;
+    }
+
+    // void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset,
+    // GLsizeiptr size, const GLvoid:size:in data)
+    public void glBufferSubData(Message msg) {
+        if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ARRAY_BUFFER) {
+            if (attribBuffer.data.isReadOnly()) {
+                ByteBuffer buffer = ByteBuffer.allocate(attribBuffer.data.capacity());
+                buffer.order(SampleView.targetByteOrder);
+                buffer.put(attribBuffer.data);
+                attribBuffer.data = buffer;
+            }
+            attribBuffer.data.position(msg.getArg1());
+            attribBuffer.data.put(msg.getData().asReadOnlyByteBuffer());
+        } else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ELEMENT_ARRAY_BUFFER) {
+            if (indexBuffer.data.isReadOnly()) {
+                ByteBuffer buffer = ByteBuffer.allocate(indexBuffer.data.capacity());
+                buffer.order(SampleView.targetByteOrder);
+                buffer.put(indexBuffer.data);
+                indexBuffer.data = buffer;
+            }
+            indexBuffer.data.position(msg.getArg1());
+            indexBuffer.data.put(msg.getData().asReadOnlyByteBuffer());
+        } else
+            assert false;
+    }
+
+    // void glDeleteBuffers(GLsizei n, const GLuint* buffers)
+    public void glDeleteBuffers(Message msg) {
+        final int n = msg.getArg0();
+        final ByteBuffer names = msg.getData().asReadOnlyByteBuffer();
+        names.order(SampleView.targetByteOrder);
+        for (int i = 0; i < n; i++) {
+            final int name = names.getInt();
+            final GLBuffer buffer = buffers.get(name);
+            for (int j = 0; j < attribPointers.length; j++)
+                if (attribPointers[j].buffer == buffer) {
+                    attribPointers[j].buffer = null;
+                    attribPointers[j].enabled = false;
+                }
+            if (attribBuffer == buffer)
+                attribBuffer = null;
+            if (indexBuffer == buffer)
+                indexBuffer = null;
+            buffers.remove(name);
+        }
+    }
+
+    // void glDisableVertexAttribArray(GLuint index)
+    public void glDisableVertexAttribArray(Message msg) {
+        if (msg.getArg0() >= 0 && msg.getArg0() < attribPointers.length)
+            attribPointers[msg.getArg0()].enabled = false;
+    }
+
+    float fetchConvert(final ByteBuffer src, final GLEnum type, final boolean normalized) {
+        if (GLEnum.GL_FLOAT == type)
+            return Float.intBitsToFloat(src.getInt());
+        else if (GLEnum.GL_UNSIGNED_INT == type)
+            if (normalized)
+                return (src.getInt() & 0xffffffffL) / (2e32f - 1);
+            else
+                return src.getInt() & 0xffffffffL;
+        else if (GLEnum.GL_INT == type)
+            if (normalized)
+                return (src.getInt() * 2 + 1) / (2e32f - 1);
+            else
+                return src.getInt();
+        else if (GLEnum.GL_UNSIGNED_SHORT == type)
+            if (normalized)
+                return (src.getShort() & 0xffff) / (2e16f - 1);
+            else
+                return src.getShort() & 0xffff;
+        else if (GLEnum.GL_SHORT == type)
+            if (normalized)
+                return (src.getShort() * 2 + 1) / (2e16f - 1);
+            else
+                return src.getShort();
+        else if (GLEnum.GL_UNSIGNED_BYTE == type)
+            if (normalized)
+                return (src.get() & 0xff) / (2e8f - 1);
+            else
+                return src.get() & 0xff;
+        else if (GLEnum.GL_BYTE == type)
+            if (normalized)
+                return (src.get() * 2 + 1) / (2e8f - 1);
+            else
+                return src.get();
+        else if (GLEnum.GL_FIXED == type)
+            if (normalized)
+                return (src.getInt() * 2 + 1) / (2e32f - 1);
+            else
+                return src.getInt() / (2e16f);
+        else
+            assert false;
+        return 0;
+    }
+
+    static int typeSize(final GLEnum type) {
+        switch (type) {
+            case GL_FLOAT:
+            case GL_UNSIGNED_INT:
+            case GL_INT:
+            case GL_FIXED:
+                return 4;
+            case GL_UNSIGNED_SHORT:
+            case GL_SHORT:
+                return 2;
+            case GL_UNSIGNED_BYTE:
+            case GL_BYTE:
+                return 1;
+            default:
+                assert false;
+                return 0;
+        }
+    }
+
+    void fetch(final int maxAttrib, final int index, final int dstIdx, final ByteBuffer nonVBO,
+            final float[][] fetchedAttribs) {
+        for (int i = 0; i < maxAttrib; i++) {
+            final GLAttribPointer attrib = attribPointers[i];
+            int size = 0;
+            if (attrib.enabled) {
+                size = attrib.size;
+                if (null != attrib.buffer) {
+                    final ByteBuffer src = attrib.buffer.data;
+                    src.position(attrib.ptr + index * attrib.elemStride);
+                    for (int j = 0; j < size; j++)
+                        fetchedAttribs[i][dstIdx * 4 + j] = fetchConvert(src, attrib.type,
+                                attrib.normalized);
+                } else
+                    for (int j = 0; j < size; j++)
+                        fetchedAttribs[i][dstIdx * 4 + j] = fetchConvert(nonVBO, attrib.type,
+                                attrib.normalized);
+            }
+            if (size < 1)
+                fetchedAttribs[i][dstIdx * 4 + 0] = defaultAttribs[i][0];
+            if (size < 2)
+                fetchedAttribs[i][dstIdx * 4 + 1] = defaultAttribs[i][1];
+            if (size < 3)
+                fetchedAttribs[i][dstIdx * 4 + 2] = defaultAttribs[i][2];
+            if (size < 4)
+                fetchedAttribs[i][dstIdx * 4 + 3] = defaultAttribs[i][3];
+        }
+    }
+
+    /**
+     * fetches and converts vertex data from buffers, defaults and user pointers
+     * into MessageData; mainly for display use
+     */
+    public void glDrawArrays(MessageData msgData) {
+        final Message msg = msgData.msg;
+        if (!msg.hasArg7())
+            return;
+        final int maxAttrib = msg.getArg7();
+        final int first = msg.getArg1(), count = msg.getArg2();
+        msgData.attribs = new float[maxAttrib][count * 4];
+        ByteBuffer arrays = null;
+        if (msg.hasData()) // server sends user pointer attribs
+        {
+            arrays = msg.getData().asReadOnlyByteBuffer();
+            arrays.order(SampleView.targetByteOrder);
+        }
+        for (int i = 0; i < count; i++)
+            fetch(maxAttrib, first + i, i, arrays, msgData.attribs);
+        assert null == arrays || arrays.remaining() == 0;
+    }
+
+    // void glDrawElements(GLenum mode, GLsizei count, GLenum type, const
+    // GLvoid* indices)
+    /**
+     * fetches and converts vertex data from buffers, defaults and user pointers
+     * and indices from buffer/pointer into MessageData; mainly for display use
+     */
+    public void glDrawElements(MessageData msgData) {
+        final Message msg = msgData.msg;
+        if (!msg.hasArg7())
+            return;
+        final int maxAttrib = msg.getArg7();
+        final int count = msg.getArg1();
+        final GLEnum type = GLEnum.valueOf(msg.getArg2());
+        msgData.attribs = new float[maxAttrib][count * 4];
+        msgData.indices = new short[count];
+        ByteBuffer arrays = null, index = null;
+        if (msg.hasData()) // server sends user pointer attribs
+        {
+            arrays = msg.getData().asReadOnlyByteBuffer();
+            arrays.order(SampleView.targetByteOrder);
+        }
+        if (null == indexBuffer)
+            index = arrays; // server also interleaves user pointer indices
+        else {
+            index = indexBuffer.data;
+            index.position(msg.getArg3());
+        }
+        if (GLEnum.GL_UNSIGNED_SHORT == type) {
+            for (int i = 0; i < count; i++) {
+                msgData.indices[i] = index.getShort();
+                fetch(maxAttrib, msgData.indices[i] & 0xffff, i, arrays, msgData.attribs);
+            }
+        } else if (GLEnum.GL_UNSIGNED_BYTE == type) {
+            for (int i = 0; i < count; i++) {
+                msgData.indices[i] = (short) (index.get() & 0xff);
+                fetch(maxAttrib, msgData.indices[i], i, arrays, msgData.attribs);
+            }
+        } else
+            assert false;
+        assert null == arrays || arrays.remaining() == 0;
+    }
+
+    // void glEnableVertexAttribArray(GLuint index)
+    public void glEnableVertexAttribArray(Message msg) {
+        if (msg.getArg0() >= 0 && msg.getArg0() < attribPointers.length)
+            attribPointers[msg.getArg0()].enabled = true;
+    }
+
+    // void API_ENTRY(glGenBuffers)(GLsizei n, GLuint:n:out buffers)
+    public void glGenBuffers(Message msg) {
+        final int n = msg.getArg0();
+        final ByteBuffer buffer = msg.getData().asReadOnlyByteBuffer();
+        buffer.order(SampleView.targetByteOrder);
+        for (int i = 0; i < n; i++) {
+            final int name = buffer.getInt();
+            final int index = buffers.indexOfKey(name);
+            if (index < 0)
+                buffers.append(name, new GLBuffer(name));
+        }
+    }
+
+    // void glVertexAttribPointer(GLuint index, GLint size, GLenum type,
+    // GLboolean normalized, GLsizei stride, const GLvoid* ptr)
+    public void glVertexAttribPointer(Message msg) {
+        GLAttribPointer attrib = attribPointers[msg.getArg0()];
+        attrib.size = msg.getArg1();
+        attrib.type = GLEnum.valueOf(msg.getArg2());
+        attrib.normalized = msg.getArg3() != 0;
+        attrib.stride = msg.getArg4();
+        attrib.elemSize = attrib.size * typeSize(attrib.type);
+        if (attrib.stride == 0)
+            attrib.elemStride = attrib.elemSize;
+        else
+            attrib.elemStride = attrib.stride;
+        attrib.ptr = msg.getArg5();
+        attrib.buffer = attribBuffer;
+    }
+
+    // void glVertexAttrib1f(GLuint indx, GLfloat x)
+    public void glVertexAttrib1f(Message msg) {
+        glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()),
+                0, 0, 1);
+    }
+
+    // void glVertexAttrib1fv(GLuint indx, const GLfloat* values)
+    public void glVertexAttrib1fv(Message msg) {
+        final ByteBuffer values = msg.getData().asReadOnlyByteBuffer();
+        values.order(SampleView.targetByteOrder);
+        glVertexAttrib4f(msg.getArg0(),
+                Float.intBitsToFloat(values.getInt()),
+                0, 0, 1);
+    }
+
+    // void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y)
+    public void glVertexAttrib2f(Message msg) {
+        glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()),
+                Float.intBitsToFloat(msg.getArg2()), 0, 1);
+    }
+
+    // void glVertexAttrib2fv(GLuint indx, const GLfloat* values)
+    public void glVertexAttrib2fv(Message msg) {
+        final ByteBuffer values = msg.getData().asReadOnlyByteBuffer();
+        values.order(SampleView.targetByteOrder);
+        glVertexAttrib4f(msg.getArg0(),
+                Float.intBitsToFloat(values.getInt()),
+                Float.intBitsToFloat(values.getInt()), 0, 1);
+    }
+
+    // void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z)
+    public void glVertexAttrib3f(Message msg) {
+        glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()),
+                Float.intBitsToFloat(msg.getArg2()),
+                Float.intBitsToFloat(msg.getArg3()), 1);
+    }
+
+    // void glVertexAttrib3fv(GLuint indx, const GLfloat* values)
+    public void glVertexAttrib3fv(Message msg) {
+        final ByteBuffer values = msg.getData().asReadOnlyByteBuffer();
+        values.order(SampleView.targetByteOrder);
+        glVertexAttrib4f(msg.getArg0(),
+                Float.intBitsToFloat(values.getInt()),
+                Float.intBitsToFloat(values.getInt()),
+                Float.intBitsToFloat(values.getInt()), 1);
+    }
+
+    public void glVertexAttrib4f(Message msg) {
+        glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()),
+                Float.intBitsToFloat(msg.getArg2()),
+                Float.intBitsToFloat(msg.getArg3()),
+                Float.intBitsToFloat(msg.getArg4()));
+    }
+
+    void glVertexAttrib4f(int indx, float x, float y, float z, float w) {
+        if (indx < 0 || indx >= defaultAttribs.length)
+            return;
+        defaultAttribs[indx][0] = x;
+        defaultAttribs[indx][1] = y;
+        defaultAttribs[indx][2] = z;
+        defaultAttribs[indx][3] = w;
+    }
+
+    // void glVertexAttrib4fv(GLuint indx, const GLfloat* values)
+    public void glVertexAttrib4fv(Message msg) {
+        final ByteBuffer values = msg.getData().asReadOnlyByteBuffer();
+        values.order(SampleView.targetByteOrder);
+        glVertexAttrib4f(msg.getArg0(),
+                Float.intBitsToFloat(values.getInt()),
+                Float.intBitsToFloat(values.getInt()),
+                Float.intBitsToFloat(values.getInt()),
+                Float.intBitsToFloat(values.getInt()));
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java
new file mode 100644
index 0000000..321c538
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java
@@ -0,0 +1,128 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Type;
+
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+
+public class MessageData {
+    public final Message msg;
+    private Image image = null; // texture
+    public String shader = null; // shader source
+    public String text;
+    public String[] columns = new String[3];
+
+    float[][] attribs = null;
+    short[] indices;
+
+    public MessageData(final Device device, final Message msg, final Context context) {
+        this.msg = msg;
+        StringBuilder builder = new StringBuilder();
+        final Function function = msg.getFunction();
+        if (function != Message.Function.ACK && msg.getType() != Type.BeforeCall)
+            assert msg.hasTime();
+        builder.append(columns[0] = function.name());
+        while (builder.length() < 30)
+            builder.append(' ');
+        columns[1] = String.format("%.3f", msg.getTime());
+        if (msg.hasClock())
+            columns[1] += String.format(":%.3f", msg.getClock());
+        builder.append(columns[1]);
+
+        builder.append("  ");
+        builder.append(String.format("0x%08X", msg.getContextId()));
+        builder.append("  ");
+        columns[2] = "";
+        if (msg.getType() == Type.BeforeCall) // incomplete call, client SKIPPED
+            columns[2] = "[BeforeCall(AfterCall missing)] ";
+        else if (msg.getType() == Type.AfterGeneratedCall)
+            columns[2] = "[AfterGeneratedCall] ";
+        else
+            assert msg.getType() == Type.CompleteCall;
+        columns[2] += MessageFormatter.format(msg, false);
+        builder.append(columns[2]);
+        switch (function) {
+            case glDrawArrays:
+                if (!msg.hasArg7())
+                    break;
+                context.serverVertex.glDrawArrays(this);
+                break;
+            case glDrawElements:
+                if (!msg.hasArg7())
+                    break;
+                context.serverVertex.glDrawElements(this);
+                break;
+            case glShaderSource:
+                shader = msg.getData().toStringUtf8();
+                break;
+
+        }
+        text = builder.toString();
+    }
+
+    public Image getImage() {
+        if (image != null)
+            return image;
+        ImageData imageData = null;
+        switch (msg.getFunction()) {
+            case glTexImage2D:
+                if (!msg.hasData())
+                    return null;
+                imageData = MessageProcessor.receiveImage(msg.getArg3(), msg
+                        .getArg4(), msg.getArg6(), msg.getArg7(), msg.getData());
+                return image = new Image(Display.getCurrent(), imageData);
+            case glTexSubImage2D:
+                assert msg.hasData();
+                imageData = MessageProcessor.receiveImage(msg.getArg4(), msg
+                        .getArg5(), msg.getArg6(), msg.getArg7(), msg.getData());
+                return image = new Image(Display.getCurrent(), imageData);
+            case glCopyTexImage2D:
+                imageData = MessageProcessor.receiveImage(msg.getArg5(), msg.getArg6(),
+                        msg.getPixelFormat(), msg.getPixelType(), msg.getData());
+                imageData = imageData.scaledTo(imageData.width, -imageData.height);
+                return image = new Image(Display.getCurrent(), imageData);
+            case glCopyTexSubImage2D:
+                imageData = MessageProcessor.receiveImage(msg.getArg6(), msg.getArg7(),
+                        msg.getPixelFormat(), msg.getPixelType(), msg.getData());
+                imageData = imageData.scaledTo(imageData.width, -imageData.height);
+                return image = new Image(Display.getCurrent(), imageData);
+            case glReadPixels:
+                if (!msg.hasData())
+                    return null;
+                imageData = MessageProcessor.receiveImage(msg.getArg2(), msg.getArg3(),
+                        msg.getArg4(), msg.getArg5(), msg.getData());
+                imageData = imageData.scaledTo(imageData.width, -imageData.height);
+                return image = new Image(Display.getCurrent(), imageData);
+            case eglSwapBuffers:
+                if (!msg.hasData())
+                    return null;
+                imageData = MessageProcessor.receiveImage(msg.getImageWidth(),
+                        msg.getImageHeight(), msg.getPixelFormat(), msg.getPixelType(),
+                        msg.getData());
+                imageData = imageData.scaledTo(imageData.width, -imageData.height);
+                return image = new Image(Display.getCurrent(), imageData);
+            default:
+                return null;
+        }
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageFormatter.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageFormatter.java
new file mode 100644
index 0000000..b9fa681
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageFormatter.java
@@ -0,0 +1,1488 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+// auto generated by generate_MessageFormatter_java.py"
+
+package com.android.glesv2debugger;
+
+import java.nio.ByteBuffer;
+
+public class MessageFormatter {
+
+    static String formatFloats(int count, final ByteBuffer data) {
+        if (data.remaining() == 0)
+            return "{}";
+        data.order(SampleView.targetByteOrder);
+        String ret = "{";
+        for (int i = 0; i < count; i++) {
+            ret += Float.intBitsToFloat(data.getInt());
+            if (i < count - 1)
+                ret += ", ";
+        }
+        return ret + "}";
+    }
+
+    static String formatInts(int count, final ByteBuffer data) {
+        if (data.remaining() == 0)
+            return "{}";
+        data.order(SampleView.targetByteOrder);
+        String ret = "{";
+        for (int i = 0; i < count; i++) {
+            ret += data.getInt();
+            if (i < count - 1)
+                ret += ", ";
+        }
+        return ret + "}";
+    }
+
+    static String formatUInts(int count, final ByteBuffer data) {
+        if (data.remaining() == 0)
+            return "{}";
+        data.order(SampleView.targetByteOrder);
+        String ret = "{";
+        for (int i = 0; i < count; i++) {
+            long bits = data.getInt() & 0xffffffff;
+            ret += bits;
+            if (i < count - 1)
+                ret += ", ";
+        }
+        return ret + "}";
+    }
+
+    static String formatMatrix(int columns, int count, final ByteBuffer data) {
+        if (data.remaining() == 0)
+            return "{}";
+        data.order(SampleView.targetByteOrder);
+        String ret = "{";
+        for (int i = 0; i < count; i++) {
+            ret += Float.intBitsToFloat(data.getInt());
+            if (i < count - 1)
+                ret += ", ";
+            if (i % columns == columns - 1)
+                ret += "\n                                             ";
+        }
+        return ret + "}";
+    }
+
+    public static String format(final DebuggerMessage.Message msg,
+                                final boolean code) {
+        String str;
+        switch (msg.getFunction()) {
+            case glActiveTexture:
+                str = String.format("%s(%s%s)",
+                    (code ? "glActiveTexture" : "void")
+                    , (code ? "/*texture*/ " : "texture=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glAttachShader:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glAttachShader" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg1());
+                break;
+            case glBindAttribLocation:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glBindAttribLocation" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*index*/ " : "index=")
+                    , msg.getArg1()
+                    , (code ? "/*name*/ " : "name=")
+                    , (code ? "\"" : "") + msg.getData().toStringUtf8() + (code ? "\"" : ""));
+                break;
+            case glBindBuffer:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glBindBuffer" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*buffer*/ " : "buffer=")
+                    , (code ? "buffer_" : "") + msg.getArg1());
+                break;
+            case glBindFramebuffer:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glBindFramebuffer" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*framebuffer*/ " : "framebuffer=")
+                    , (code ? "framebuffer_" : "") + msg.getArg1());
+                break;
+            case glBindRenderbuffer:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glBindRenderbuffer" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*renderbuffer*/ " : "renderbuffer=")
+                    , (code ? "renderbuffer_" : "") + msg.getArg1());
+                break;
+            case glBindTexture:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glBindTexture" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*texture*/ " : "texture=")
+                    , (code ? "texture_" : "") + msg.getArg1());
+                break;
+            case glBlendColor:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glBlendColor" : "void")
+                    , (code ? "/*red*/ " : "red=")
+                    , Float.intBitsToFloat(msg.getArg0())
+                    , (code ? "/*green*/ " : "green=")
+                    , Float.intBitsToFloat(msg.getArg1())
+                    , (code ? "/*blue*/ " : "blue=")
+                    , Float.intBitsToFloat(msg.getArg2())
+                    , (code ? "/*alpha*/ " : "alpha=")
+                    , Float.intBitsToFloat(msg.getArg3()));
+                break;
+            case glBlendEquation:
+                str = String.format("%s(%s%s)",
+                    (code ? "glBlendEquation" : "void")
+                    , (code ? "/*mode*/ " : "mode=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glBlendEquationSeparate:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glBlendEquationSeparate" : "void")
+                    , (code ? "/*modeRGB*/ " : "modeRGB=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*modeAlpha*/ " : "modeAlpha=")
+                    , GLEnum.valueOf(msg.getArg1()));
+                break;
+            case glBlendFunc:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glBlendFunc" : "void")
+                    , (code ? "/*sfactor*/ " : "sfactor=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*dfactor*/ " : "dfactor=")
+                    , GLEnum.valueOf(msg.getArg1()));
+                break;
+            case glBlendFuncSeparate:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glBlendFuncSeparate" : "void")
+                    , (code ? "/*srcRGB*/ " : "srcRGB=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*dstRGB*/ " : "dstRGB=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*srcAlpha*/ " : "srcAlpha=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*dstAlpha*/ " : "dstAlpha=")
+                    , GLEnum.valueOf(msg.getArg3()));
+                break;
+            case glBufferData:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glBufferData" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*size*/ " : "size=")
+                    , msg.getArg1()
+                    , (code ? "/*data*/ " : "data=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2()))
+                    , (code ? "/*usage*/ " : "usage=")
+                    , GLEnum.valueOf(msg.getArg3()));
+                break;
+            case glBufferSubData:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glBufferSubData" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*offset*/ " : "offset=")
+                    , msg.getArg1()
+                    , (code ? "/*size*/ " : "size=")
+                    , msg.getArg2()
+                    , (code ? "/*data*/ " : "data=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3())));
+                break;
+            case glCheckFramebufferStatus:
+                str = String.format("%s(%s%s)",
+                    (code ? "glCheckFramebufferStatus" : GLEnum.valueOf(msg.getRet()))
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glClear:
+                str = String.format("%s(%s%s)",
+                    (code ? "glClear" : "void")
+                    , (code ? "/*mask*/ " : "mask=")
+                    , msg.getArg0());
+                break;
+            case glClearColor:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glClearColor" : "void")
+                    , (code ? "/*red*/ " : "red=")
+                    , Float.intBitsToFloat(msg.getArg0())
+                    , (code ? "/*green*/ " : "green=")
+                    , Float.intBitsToFloat(msg.getArg1())
+                    , (code ? "/*blue*/ " : "blue=")
+                    , Float.intBitsToFloat(msg.getArg2())
+                    , (code ? "/*alpha*/ " : "alpha=")
+                    , Float.intBitsToFloat(msg.getArg3()));
+                break;
+            case glClearDepthf:
+                str = String.format("%s(%s%s)",
+                    (code ? "glClearDepthf" : "void")
+                    , (code ? "/*depth*/ " : "depth=")
+                    , Float.intBitsToFloat(msg.getArg0()));
+                break;
+            case glClearStencil:
+                str = String.format("%s(%s%s)",
+                    (code ? "glClearStencil" : "void")
+                    , (code ? "/*s*/ " : "s=")
+                    , msg.getArg0());
+                break;
+            case glColorMask:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glColorMask" : "void")
+                    , (code ? "/*red*/ " : "red=")
+                    , msg.getArg0()
+                    , (code ? "/*green*/ " : "green=")
+                    , msg.getArg1()
+                    , (code ? "/*blue*/ " : "blue=")
+                    , msg.getArg2()
+                    , (code ? "/*alpha*/ " : "alpha=")
+                    , msg.getArg3());
+                break;
+            case glCompileShader:
+                str = String.format("%s(%s%s)",
+                    (code ? "glCompileShader" : "void")
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg0());
+                break;
+            case glCompressedTexImage2D:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glCompressedTexImage2D" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*level*/ " : "level=")
+                    , msg.getArg1()
+                    , (code ? "/*internalformat*/ " : "internalformat=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg3()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg4()
+                    , (code ? "/*border*/ " : "border=")
+                    , msg.getArg5()
+                    , (code ? "/*imageSize*/ " : "imageSize=")
+                    , msg.getArg6()
+                    , (code ? "/*data*/ " : "data=")
+                    , (code ? "arg7" : "0x" + Integer.toHexString(msg.getArg7())));
+                break;
+            case glCompressedTexSubImage2D:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glCompressedTexSubImage2D" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*level*/ " : "level=")
+                    , msg.getArg1()
+                    , (code ? "/*xoffset*/ " : "xoffset=")
+                    , msg.getArg2()
+                    , (code ? "/*yoffset*/ " : "yoffset=")
+                    , msg.getArg3()
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg4()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg5()
+                    , (code ? "/*format*/ " : "format=")
+                    , GLEnum.valueOf(msg.getArg6())
+                    , (code ? "/*imageSize*/ " : "imageSize=")
+                    , msg.getArg7()
+                    , (code ? "/*data*/ " : "data=")
+                    , (code ? "arg8" : "0x" + Integer.toHexString(msg.getArg8())));
+                break;
+            case glCopyTexImage2D:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glCopyTexImage2D" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*level*/ " : "level=")
+                    , msg.getArg1()
+                    , (code ? "/*internalformat*/ " : "internalformat=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg3()
+                    , (code ? "/*y*/ " : "y=")
+                    , msg.getArg4()
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg5()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg6()
+                    , (code ? "/*border*/ " : "border=")
+                    , msg.getArg7());
+                break;
+            case glCopyTexSubImage2D:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glCopyTexSubImage2D" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*level*/ " : "level=")
+                    , msg.getArg1()
+                    , (code ? "/*xoffset*/ " : "xoffset=")
+                    , msg.getArg2()
+                    , (code ? "/*yoffset*/ " : "yoffset=")
+                    , msg.getArg3()
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg4()
+                    , (code ? "/*y*/ " : "y=")
+                    , msg.getArg5()
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg6()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg7());
+                break;
+            case glCreateProgram:
+                str = String.format("%s()",
+                    (code ? "glCreateProgram" : msg.getRet())
+);
+                break;
+            case glCreateShader:
+                str = String.format("%s(%s%s)",
+                    (code ? "glCreateShader" : msg.getRet())
+                    , (code ? "/*type*/ " : "type=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glCullFace:
+                str = String.format("%s(%s%s)",
+                    (code ? "glCullFace" : "void")
+                    , (code ? "/*mode*/ " : "mode=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glDeleteBuffers:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glDeleteBuffers" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*buffers*/ " : "buffers=")
+                    , (code ? "(GLuint [])" : "") +  formatUInts(1 * msg.getArg0(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glDeleteFramebuffers:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glDeleteFramebuffers" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*framebuffers*/ " : "framebuffers=")
+                    , (code ? "(GLuint [])" : "") +  formatUInts(1 * msg.getArg0(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glDeleteProgram:
+                str = String.format("%s(%s%s)",
+                    (code ? "glDeleteProgram" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0());
+                break;
+            case glDeleteRenderbuffers:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glDeleteRenderbuffers" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*renderbuffers*/ " : "renderbuffers=")
+                    , (code ? "(GLuint [])" : "") +  formatUInts(1 * msg.getArg0(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glDeleteShader:
+                str = String.format("%s(%s%s)",
+                    (code ? "glDeleteShader" : "void")
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg0());
+                break;
+            case glDeleteTextures:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glDeleteTextures" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*textures*/ " : "textures=")
+                    , (code ? "(GLuint [])" : "") +  formatUInts(1 * msg.getArg0(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glDepthFunc:
+                str = String.format("%s(%s%s)",
+                    (code ? "glDepthFunc" : "void")
+                    , (code ? "/*func*/ " : "func=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glDepthMask:
+                str = String.format("%s(%s%s)",
+                    (code ? "glDepthMask" : "void")
+                    , (code ? "/*flag*/ " : "flag=")
+                    , msg.getArg0());
+                break;
+            case glDepthRangef:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glDepthRangef" : "void")
+                    , (code ? "/*zNear*/ " : "zNear=")
+                    , Float.intBitsToFloat(msg.getArg0())
+                    , (code ? "/*zFar*/ " : "zFar=")
+                    , Float.intBitsToFloat(msg.getArg1()));
+                break;
+            case glDetachShader:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glDetachShader" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg1());
+                break;
+            case glDisable:
+                str = String.format("%s(%s%s)",
+                    (code ? "glDisable" : "void")
+                    , (code ? "/*cap*/ " : "cap=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glDisableVertexAttribArray:
+                str = String.format("%s(%s%s)",
+                    (code ? "glDisableVertexAttribArray" : "void")
+                    , (code ? "/*index*/ " : "index=")
+                    , msg.getArg0());
+                break;
+            case glDrawArrays:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glDrawArrays" : "void")
+                    , (code ? "/*mode*/ " : "mode=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*first*/ " : "first=")
+                    , msg.getArg1()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg2());
+                break;
+            case glDrawElements:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glDrawElements" : "void")
+                    , (code ? "/*mode*/ " : "mode=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*type*/ " : "type=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*indices*/ " : "indices=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3())));
+                break;
+            case glEnable:
+                str = String.format("%s(%s%s)",
+                    (code ? "glEnable" : "void")
+                    , (code ? "/*cap*/ " : "cap=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glEnableVertexAttribArray:
+                str = String.format("%s(%s%s)",
+                    (code ? "glEnableVertexAttribArray" : "void")
+                    , (code ? "/*index*/ " : "index=")
+                    , msg.getArg0());
+                break;
+            case glFinish:
+                str = String.format("%s()",
+                    (code ? "glFinish" : "void")
+);
+                break;
+            case glFlush:
+                str = String.format("%s()",
+                    (code ? "glFlush" : "void")
+);
+                break;
+            case glFramebufferRenderbuffer:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glFramebufferRenderbuffer" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*attachment*/ " : "attachment=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*renderbuffertarget*/ " : "renderbuffertarget=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*renderbuffer*/ " : "renderbuffer=")
+                    , (code ? "renderbuffer_" : "") + msg.getArg3());
+                break;
+            case glFramebufferTexture2D:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glFramebufferTexture2D" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*attachment*/ " : "attachment=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*textarget*/ " : "textarget=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*texture*/ " : "texture=")
+                    , (code ? "texture_" : "") + msg.getArg3()
+                    , (code ? "/*level*/ " : "level=")
+                    , msg.getArg4());
+                break;
+            case glFrontFace:
+                str = String.format("%s(%s%s)",
+                    (code ? "glFrontFace" : "void")
+                    , (code ? "/*mode*/ " : "mode=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glGenBuffers:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGenBuffers" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*buffers*/ " : "buffers=")
+                    , (code ? "(GLuint [])" : "") +  formatUInts(1 * msg.getArg0(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glGenerateMipmap:
+                str = String.format("%s(%s%s)",
+                    (code ? "glGenerateMipmap" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glGenFramebuffers:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGenFramebuffers" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*framebuffers*/ " : "framebuffers=")
+                    , (code ? "(GLuint [])" : "") +  formatUInts(1 * msg.getArg0(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glGenRenderbuffers:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGenRenderbuffers" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*renderbuffers*/ " : "renderbuffers=")
+                    , (code ? "(GLuint [])" : "") +  formatUInts(1 * msg.getArg0(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glGenTextures:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGenTextures" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*textures*/ " : "textures=")
+                    , (code ? "(GLuint [])" : "") +  formatUInts(1 * msg.getArg0(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glGetActiveAttrib:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glGetActiveAttrib" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*index*/ " : "index=")
+                    , msg.getArg1()
+                    , (code ? "/*bufsize*/ " : "bufsize=")
+                    , msg.getArg2()
+                    , (code ? "/*length*/ " : "length=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3()))
+                    , (code ? "/*size*/ " : "size=")
+                    , (code ? "arg4" : "0x" + Integer.toHexString(msg.getArg4()))
+                    , (code ? "/*type*/ " : "type=")
+                    , (code ? "arg5" : "0x" + Integer.toHexString(msg.getArg5()))
+                    , (code ? "/*name*/ " : "name=")
+                    , (code ? "\"" : "") + msg.getData().toStringUtf8() + (code ? "\"" : ""));
+                break;
+            case glGetActiveUniform:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glGetActiveUniform" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*index*/ " : "index=")
+                    , msg.getArg1()
+                    , (code ? "/*bufsize*/ " : "bufsize=")
+                    , msg.getArg2()
+                    , (code ? "/*length*/ " : "length=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3()))
+                    , (code ? "/*size*/ " : "size=")
+                    , (code ? "arg4" : "0x" + Integer.toHexString(msg.getArg4()))
+                    , (code ? "/*type*/ " : "type=")
+                    , (code ? "arg5" : "0x" + Integer.toHexString(msg.getArg5()))
+                    , (code ? "/*name*/ " : "name=")
+                    , (code ? "\"" : "") + msg.getData().toStringUtf8() + (code ? "\"" : ""));
+                break;
+            case glGetAttachedShaders:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glGetAttachedShaders" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*maxcount*/ " : "maxcount=")
+                    , msg.getArg1()
+                    , (code ? "/*count*/ " : "count=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2()))
+                    , (code ? "/*shaders*/ " : "shaders=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3())));
+                break;
+            case glGetAttribLocation:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGetAttribLocation" : msg.getRet())
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*name*/ " : "name=")
+                    , (code ? "\"" : "") + msg.getData().toStringUtf8() + (code ? "\"" : ""));
+                break;
+            case glGetBooleanv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGetBooleanv" : "void")
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg1" : "0x" + Integer.toHexString(msg.getArg1())));
+                break;
+            case glGetBufferParameteriv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetBufferParameteriv" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glGetError:
+                str = String.format("%s()",
+                    (code ? "glGetError" : GLEnum.valueOf(msg.getRet()))
+);
+                break;
+            case glGetFloatv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGetFloatv" : "void")
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg1" : "0x" + Integer.toHexString(msg.getArg1())));
+                break;
+            case glGetFramebufferAttachmentParameteriv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glGetFramebufferAttachmentParameteriv" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*attachment*/ " : "attachment=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3())));
+                break;
+            case glGetIntegerv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGetIntegerv" : "void")
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg1" : "0x" + Integer.toHexString(msg.getArg1())));
+                break;
+            case glGetProgramiv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetProgramiv" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "(GLint [])" : "") + formatInts(1, msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glGetProgramInfoLog:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glGetProgramInfoLog" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*bufsize*/ " : "bufsize=")
+                    , msg.getArg1()
+                    , (code ? "/*length*/ " : "length=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2()))
+                    , (code ? "/*infolog*/ " : "infolog=")
+                    , (code ? "\"" : "") + msg.getData().toStringUtf8() + (code ? "\"" : ""));
+                break;
+            case glGetRenderbufferParameteriv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetRenderbufferParameteriv" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glGetShaderiv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetShaderiv" : "void")
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg0()
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "(GLint [])" : "") + formatInts(1, msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glGetShaderInfoLog:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glGetShaderInfoLog" : "void")
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg0()
+                    , (code ? "/*bufsize*/ " : "bufsize=")
+                    , msg.getArg1()
+                    , (code ? "/*length*/ " : "length=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2()))
+                    , (code ? "/*infolog*/ " : "infolog=")
+                    , (code ? "\"" : "") + msg.getData().toStringUtf8() + (code ? "\"" : ""));
+                break;
+            case glGetShaderPrecisionFormat:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glGetShaderPrecisionFormat" : "void")
+                    , (code ? "/*shadertype*/ " : "shadertype=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*precisiontype*/ " : "precisiontype=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*range*/ " : "range=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2()))
+                    , (code ? "/*precision*/ " : "precision=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3())));
+                break;
+            case glGetShaderSource:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glGetShaderSource" : "void")
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg0()
+                    , (code ? "/*bufsize*/ " : "bufsize=")
+                    , msg.getArg1()
+                    , (code ? "/*length*/ " : "length=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2()))
+                    , (code ? "/*source*/ " : "source=")
+                    , (code ? "\"" : "") + msg.getData().toStringUtf8() + (code ? "\"" : ""));
+                break;
+            case glGetString:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s)",
+                    (code ? "glGetString" : "0x" + Integer.toHexString(msg.getRet()))
+                    , (code ? "/*name*/ " : "name=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glGetTexParameterfv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetTexParameterfv" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glGetTexParameteriv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetTexParameteriv" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glGetUniformfv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetUniformfv" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg1()
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glGetUniformiv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetUniformiv" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg1()
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glGetUniformLocation:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glGetUniformLocation" : msg.getRet())
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0()
+                    , (code ? "/*name*/ " : "name=")
+                    , (code ? "\"" : "") + msg.getData().toStringUtf8() + (code ? "\"" : ""));
+                break;
+            case glGetVertexAttribfv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetVertexAttribfv" : "void")
+                    , (code ? "/*index*/ " : "index=")
+                    , msg.getArg0()
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glGetVertexAttribiv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetVertexAttribiv" : "void")
+                    , (code ? "/*index*/ " : "index=")
+                    , msg.getArg0()
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glGetVertexAttribPointerv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glGetVertexAttribPointerv" : "void")
+                    , (code ? "/*index*/ " : "index=")
+                    , msg.getArg0()
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*pointer*/ " : "pointer=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glHint:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glHint" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*mode*/ " : "mode=")
+                    , GLEnum.valueOf(msg.getArg1()));
+                break;
+            case glIsBuffer:
+                str = String.format("%s(%s%s)",
+                    (code ? "glIsBuffer" : msg.getRet())
+                    , (code ? "/*buffer*/ " : "buffer=")
+                    , (code ? "buffer_" : "") + msg.getArg0());
+                break;
+            case glIsEnabled:
+                str = String.format("%s(%s%s)",
+                    (code ? "glIsEnabled" : msg.getRet())
+                    , (code ? "/*cap*/ " : "cap=")
+                    , GLEnum.valueOf(msg.getArg0()));
+                break;
+            case glIsFramebuffer:
+                str = String.format("%s(%s%s)",
+                    (code ? "glIsFramebuffer" : msg.getRet())
+                    , (code ? "/*framebuffer*/ " : "framebuffer=")
+                    , (code ? "framebuffer_" : "") + msg.getArg0());
+                break;
+            case glIsProgram:
+                str = String.format("%s(%s%s)",
+                    (code ? "glIsProgram" : msg.getRet())
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0());
+                break;
+            case glIsRenderbuffer:
+                str = String.format("%s(%s%s)",
+                    (code ? "glIsRenderbuffer" : msg.getRet())
+                    , (code ? "/*renderbuffer*/ " : "renderbuffer=")
+                    , (code ? "renderbuffer_" : "") + msg.getArg0());
+                break;
+            case glIsShader:
+                str = String.format("%s(%s%s)",
+                    (code ? "glIsShader" : msg.getRet())
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg0());
+                break;
+            case glIsTexture:
+                str = String.format("%s(%s%s)",
+                    (code ? "glIsTexture" : msg.getRet())
+                    , (code ? "/*texture*/ " : "texture=")
+                    , (code ? "texture_" : "") + msg.getArg0());
+                break;
+            case glLineWidth:
+                str = String.format("%s(%s%s)",
+                    (code ? "glLineWidth" : "void")
+                    , (code ? "/*width*/ " : "width=")
+                    , Float.intBitsToFloat(msg.getArg0()));
+                break;
+            case glLinkProgram:
+                str = String.format("%s(%s%s)",
+                    (code ? "glLinkProgram" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0());
+                break;
+            case glPixelStorei:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glPixelStorei" : "void")
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*param*/ " : "param=")
+                    , msg.getArg1());
+                break;
+            case glPolygonOffset:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glPolygonOffset" : "void")
+                    , (code ? "/*factor*/ " : "factor=")
+                    , Float.intBitsToFloat(msg.getArg0())
+                    , (code ? "/*units*/ " : "units=")
+                    , Float.intBitsToFloat(msg.getArg1()));
+                break;
+            case glReadPixels:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glReadPixels" : "void")
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg0()
+                    , (code ? "/*y*/ " : "y=")
+                    , msg.getArg1()
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg2()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg3()
+                    , (code ? "/*format*/ " : "format=")
+                    , GLEnum.valueOf(msg.getArg4())
+                    , (code ? "/*type*/ " : "type=")
+                    , GLEnum.valueOf(msg.getArg5())
+                    , (code ? "/*pixels*/ " : "pixels=")
+                    , (code ? "arg6" : "0x" + Integer.toHexString(msg.getArg6())));
+                break;
+            case glReleaseShaderCompiler:
+                str = String.format("%s()",
+                    (code ? "glReleaseShaderCompiler" : "void")
+);
+                break;
+            case glRenderbufferStorage:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glRenderbufferStorage" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*internalformat*/ " : "internalformat=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg2()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg3());
+                break;
+            case glSampleCoverage:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glSampleCoverage" : "void")
+                    , (code ? "/*value*/ " : "value=")
+                    , Float.intBitsToFloat(msg.getArg0())
+                    , (code ? "/*invert*/ " : "invert=")
+                    , msg.getArg1());
+                break;
+            case glScissor:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glScissor" : "void")
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg0()
+                    , (code ? "/*y*/ " : "y=")
+                    , msg.getArg1()
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg2()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg3());
+                break;
+            case glShaderBinary:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glShaderBinary" : "void")
+                    , (code ? "/*n*/ " : "n=")
+                    , msg.getArg0()
+                    , (code ? "/*shaders*/ " : "shaders=")
+                    , (code ? "arg1" : "0x" + Integer.toHexString(msg.getArg1()))
+                    , (code ? "/*binaryformat*/ " : "binaryformat=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*binary*/ " : "binary=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3()))
+                    , (code ? "/*length*/ " : "length=")
+                    , msg.getArg4());
+                break;
+            case glShaderSource:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glShaderSource" : "void")
+                    , (code ? "/*shader*/ " : "shader=")
+                    , (code ? "shader_" : "") + msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*string*/ " : "string=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2()))
+                    , (code ? "/*length*/ " : "length=")
+                    , (code ? "arg3" : "0x" + Integer.toHexString(msg.getArg3())));
+                break;
+            case glStencilFunc:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glStencilFunc" : "void")
+                    , (code ? "/*func*/ " : "func=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*ref*/ " : "ref=")
+                    , msg.getArg1()
+                    , (code ? "/*mask*/ " : "mask=")
+                    , msg.getArg2());
+                break;
+            case glStencilFuncSeparate:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glStencilFuncSeparate" : "void")
+                    , (code ? "/*face*/ " : "face=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*func*/ " : "func=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*ref*/ " : "ref=")
+                    , msg.getArg2()
+                    , (code ? "/*mask*/ " : "mask=")
+                    , msg.getArg3());
+                break;
+            case glStencilMask:
+                str = String.format("%s(%s%s)",
+                    (code ? "glStencilMask" : "void")
+                    , (code ? "/*mask*/ " : "mask=")
+                    , msg.getArg0());
+                break;
+            case glStencilMaskSeparate:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glStencilMaskSeparate" : "void")
+                    , (code ? "/*face*/ " : "face=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*mask*/ " : "mask=")
+                    , msg.getArg1());
+                break;
+            case glStencilOp:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glStencilOp" : "void")
+                    , (code ? "/*fail*/ " : "fail=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*zfail*/ " : "zfail=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*zpass*/ " : "zpass=")
+                    , GLEnum.valueOf(msg.getArg2()));
+                break;
+            case glStencilOpSeparate:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glStencilOpSeparate" : "void")
+                    , (code ? "/*face*/ " : "face=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*fail*/ " : "fail=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*zfail*/ " : "zfail=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*zpass*/ " : "zpass=")
+                    , GLEnum.valueOf(msg.getArg3()));
+                break;
+            case glTexImage2D:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glTexImage2D" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*level*/ " : "level=")
+                    , msg.getArg1()
+                    , (code ? "/*internalformat*/ " : "internalformat=")
+                    , msg.getArg2()
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg3()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg4()
+                    , (code ? "/*border*/ " : "border=")
+                    , msg.getArg5()
+                    , (code ? "/*format*/ " : "format=")
+                    , GLEnum.valueOf(msg.getArg6())
+                    , (code ? "/*type*/ " : "type=")
+                    , GLEnum.valueOf(msg.getArg7())
+                    , (code ? "/*pixels*/ " : "pixels=")
+                    , (code ? "arg8" : "0x" + Integer.toHexString(msg.getArg8())));
+                break;
+            case glTexParameterf:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glTexParameterf" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*param*/ " : "param=")
+                    , Float.intBitsToFloat(msg.getArg2()));
+                break;
+            case glTexParameterfv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glTexParameterfv" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glTexParameteri:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glTexParameteri" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*param*/ " : "param=")
+                    , msg.getArg2());
+                break;
+            case glTexParameteriv:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glTexParameteriv" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*pname*/ " : "pname=")
+                    , GLEnum.valueOf(msg.getArg1())
+                    , (code ? "/*params*/ " : "params=")
+                    , (code ? "arg2" : "0x" + Integer.toHexString(msg.getArg2())));
+                break;
+            case glTexSubImage2D:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glTexSubImage2D" : "void")
+                    , (code ? "/*target*/ " : "target=")
+                    , GLEnum.valueOf(msg.getArg0())
+                    , (code ? "/*level*/ " : "level=")
+                    , msg.getArg1()
+                    , (code ? "/*xoffset*/ " : "xoffset=")
+                    , msg.getArg2()
+                    , (code ? "/*yoffset*/ " : "yoffset=")
+                    , msg.getArg3()
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg4()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg5()
+                    , (code ? "/*format*/ " : "format=")
+                    , GLEnum.valueOf(msg.getArg6())
+                    , (code ? "/*type*/ " : "type=")
+                    , GLEnum.valueOf(msg.getArg7())
+                    , (code ? "/*pixels*/ " : "pixels=")
+                    , (code ? "arg8" : "0x" + Integer.toHexString(msg.getArg8())));
+                break;
+            case glUniform1f:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glUniform1f" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , Float.intBitsToFloat(msg.getArg1()));
+                break;
+            case glUniform1fv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform1fv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*v*/ " : "v=")
+                    , (code ? "(GLfloat [])" : "") +  formatFloats(1 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniform1i:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glUniform1i" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg1());
+                break;
+            case glUniform1iv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform1iv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*v*/ " : "v=")
+                    , (code ? "(GLint [])" : "") +  formatInts(1 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniform2f:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform2f" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , Float.intBitsToFloat(msg.getArg1())
+                    , (code ? "/*y*/ " : "y=")
+                    , Float.intBitsToFloat(msg.getArg2()));
+                break;
+            case glUniform2fv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform2fv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*v*/ " : "v=")
+                    , (code ? "(GLfloat [])" : "") +  formatFloats(2 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniform2i:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform2i" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg1()
+                    , (code ? "/*y*/ " : "y=")
+                    , msg.getArg2());
+                break;
+            case glUniform2iv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform2iv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*v*/ " : "v=")
+                    , (code ? "(GLint [])" : "") +  formatInts(2 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniform3f:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glUniform3f" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , Float.intBitsToFloat(msg.getArg1())
+                    , (code ? "/*y*/ " : "y=")
+                    , Float.intBitsToFloat(msg.getArg2())
+                    , (code ? "/*z*/ " : "z=")
+                    , Float.intBitsToFloat(msg.getArg3()));
+                break;
+            case glUniform3fv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform3fv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*v*/ " : "v=")
+                    , (code ? "(GLfloat [])" : "") +  formatFloats(3 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniform3i:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glUniform3i" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg1()
+                    , (code ? "/*y*/ " : "y=")
+                    , msg.getArg2()
+                    , (code ? "/*z*/ " : "z=")
+                    , msg.getArg3());
+                break;
+            case glUniform3iv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform3iv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*v*/ " : "v=")
+                    , (code ? "(GLint [])" : "") +  formatInts(3 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniform4f:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glUniform4f" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , Float.intBitsToFloat(msg.getArg1())
+                    , (code ? "/*y*/ " : "y=")
+                    , Float.intBitsToFloat(msg.getArg2())
+                    , (code ? "/*z*/ " : "z=")
+                    , Float.intBitsToFloat(msg.getArg3())
+                    , (code ? "/*w*/ " : "w=")
+                    , Float.intBitsToFloat(msg.getArg4()));
+                break;
+            case glUniform4fv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform4fv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*v*/ " : "v=")
+                    , (code ? "(GLfloat [])" : "") +  formatFloats(4 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniform4i:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glUniform4i" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg1()
+                    , (code ? "/*y*/ " : "y=")
+                    , msg.getArg2()
+                    , (code ? "/*z*/ " : "z=")
+                    , msg.getArg3()
+                    , (code ? "/*w*/ " : "w=")
+                    , msg.getArg4());
+                break;
+            case glUniform4iv:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glUniform4iv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*v*/ " : "v=")
+                    , (code ? "(GLint [])" : "") +  formatInts(4 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniformMatrix2fv:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glUniformMatrix2fv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*transpose*/ " : "transpose=")
+                    , msg.getArg2()
+                    , (code ? "/*value*/ " : "value=")
+                    , (code ? "(GLfloat [])" : "") + formatMatrix(2, 4 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniformMatrix3fv:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glUniformMatrix3fv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*transpose*/ " : "transpose=")
+                    , msg.getArg2()
+                    , (code ? "/*value*/ " : "value=")
+                    , (code ? "(GLfloat [])" : "") + formatMatrix(3, 9 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUniformMatrix4fv:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glUniformMatrix4fv" : "void")
+                    , (code ? "/*location*/ " : "location=")
+                    , msg.getArg0()
+                    , (code ? "/*count*/ " : "count=")
+                    , msg.getArg1()
+                    , (code ? "/*transpose*/ " : "transpose=")
+                    , msg.getArg2()
+                    , (code ? "/*value*/ " : "value=")
+                    , (code ? "(GLfloat [])" : "") + formatMatrix(4, 16 * msg.getArg1(), msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glUseProgram:
+                str = String.format("%s(%s%s)",
+                    (code ? "glUseProgram" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0());
+                break;
+            case glValidateProgram:
+                str = String.format("%s(%s%s)",
+                    (code ? "glValidateProgram" : "void")
+                    , (code ? "/*program*/ " : "program=")
+                    , (code ? "program_" : "") + msg.getArg0());
+                break;
+            case glVertexAttrib1f:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glVertexAttrib1f" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , Float.intBitsToFloat(msg.getArg1()));
+                break;
+            case glVertexAttrib1fv:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glVertexAttrib1fv" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*values*/ " : "values=")
+                    , (code ? "(GLfloat [])" : "") + formatFloats(1, msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glVertexAttrib2f:
+                str = String.format("%s(%s%s, %s%s, %s%s)",
+                    (code ? "glVertexAttrib2f" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , Float.intBitsToFloat(msg.getArg1())
+                    , (code ? "/*y*/ " : "y=")
+                    , Float.intBitsToFloat(msg.getArg2()));
+                break;
+            case glVertexAttrib2fv:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glVertexAttrib2fv" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*values*/ " : "values=")
+                    , (code ? "(GLfloat [])" : "") + formatFloats(2, msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glVertexAttrib3f:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glVertexAttrib3f" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , Float.intBitsToFloat(msg.getArg1())
+                    , (code ? "/*y*/ " : "y=")
+                    , Float.intBitsToFloat(msg.getArg2())
+                    , (code ? "/*z*/ " : "z=")
+                    , Float.intBitsToFloat(msg.getArg3()));
+                break;
+            case glVertexAttrib3fv:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glVertexAttrib3fv" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*values*/ " : "values=")
+                    , (code ? "(GLfloat [])" : "") + formatFloats(3, msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glVertexAttrib4f:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glVertexAttrib4f" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*x*/ " : "x=")
+                    , Float.intBitsToFloat(msg.getArg1())
+                    , (code ? "/*y*/ " : "y=")
+                    , Float.intBitsToFloat(msg.getArg2())
+                    , (code ? "/*z*/ " : "z=")
+                    , Float.intBitsToFloat(msg.getArg3())
+                    , (code ? "/*w*/ " : "w=")
+                    , Float.intBitsToFloat(msg.getArg4()));
+                break;
+            case glVertexAttrib4fv:
+                str = String.format("%s(%s%s, %s%s)",
+                    (code ? "glVertexAttrib4fv" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*values*/ " : "values=")
+                    , (code ? "(GLfloat [])" : "") + formatFloats(4, msg.getData().asReadOnlyByteBuffer()));
+                break;
+            case glVertexAttribPointer:
+                // FIXME: this function uses pointers, debugger may send data in msg.data
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glVertexAttribPointer" : "void")
+                    , (code ? "/*indx*/ " : "indx=")
+                    , msg.getArg0()
+                    , (code ? "/*size*/ " : "size=")
+                    , msg.getArg1()
+                    , (code ? "/*type*/ " : "type=")
+                    , GLEnum.valueOf(msg.getArg2())
+                    , (code ? "/*normalized*/ " : "normalized=")
+                    , msg.getArg3()
+                    , (code ? "/*stride*/ " : "stride=")
+                    , msg.getArg4()
+                    , (code ? "/*ptr*/ " : "ptr=")
+                    , (code ? "arg5" : "0x" + Integer.toHexString(msg.getArg5())));
+                break;
+            case glViewport:
+                str = String.format("%s(%s%s, %s%s, %s%s, %s%s)",
+                    (code ? "glViewport" : "void")
+                    , (code ? "/*x*/ " : "x=")
+                    , msg.getArg0()
+                    , (code ? "/*y*/ " : "y=")
+                    , msg.getArg1()
+                    , (code ? "/*width*/ " : "width=")
+                    , msg.getArg2()
+                    , (code ? "/*height*/ " : "height=")
+                    , msg.getArg3());
+                break;
+            default:
+                str = msg.toString();
+        }
+        return str;
+    }
+}
\ No newline at end of file
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParser.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParser.java
new file mode 100644
index 0000000..8536728
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParser.java
@@ -0,0 +1,747 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+// auto generated by generate_MessageParser_java.py,
+//  which also prints skeleton code for MessageParserEx.java
+
+package com.android.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.google.protobuf.ByteString;
+
+import java.nio.ByteBuffer;
+
+public abstract class MessageParser {
+
+    String args;
+
+    String[] getList()
+    {
+        String arg = args;
+        args = args.substring(args.lastIndexOf('}') + 1);
+        final int comma = args.indexOf(',');
+        if (comma >= 0)
+            args = args.substring(comma + 1).trim();
+        else
+            args = null;
+
+        final int comment = arg.indexOf('=');
+        if (comment >= 0)
+            arg = arg.substring(comment + 1);
+        arg = arg.trim();
+        assert arg.charAt(0) == '{';
+        arg = arg.substring(1, arg.lastIndexOf('}')).trim();
+        return arg.split("\\s*,\\s*");
+    }
+
+    ByteString parseFloats(int count) {
+        ByteBuffer buffer = ByteBuffer.allocate(count * 4);
+        buffer.order(SampleView.targetByteOrder);
+        String [] arg = getList();
+        for (int i = 0; i < count; i++)
+            buffer.putFloat(Float.parseFloat(arg[i].trim()));
+        buffer.rewind();
+        return ByteString.copyFrom(buffer);
+    }
+
+    ByteString parseInts(int count) {
+        ByteBuffer buffer = ByteBuffer.allocate(count * 4);
+        buffer.order(SampleView.targetByteOrder);
+        String [] arg = getList();
+        for (int i = 0; i < count; i++)
+            buffer.putInt(Integer.parseInt(arg[i].trim()));
+        buffer.rewind();
+        return ByteString.copyFrom(buffer);
+    }
+
+    ByteString parseUInts(int count) {
+        ByteBuffer buffer = ByteBuffer.allocate(count * 4);
+        buffer.order(SampleView.targetByteOrder);
+        String [] arg = getList();
+        for (int i = 0; i < count; i++)
+            buffer.putInt((int)(Long.parseLong(arg[i].trim()) & 0xffffffff));
+        buffer.rewind();
+        return ByteString.copyFrom(buffer);
+    }
+
+    ByteString parseMatrix(int columns, int count) {
+        return parseFloats(columns * columns * count);
+    }
+
+    ByteString parseString() {
+        // TODO: escape sequence and proper string literal
+        String arg = args.substring(args.indexOf('"') + 1, args.lastIndexOf('"'));
+        args = args.substring(args.lastIndexOf('"'));
+        int comma = args.indexOf(',');
+        if (comma >= 0)
+            args = args.substring(comma + 1).trim();
+        else
+            args = null;
+        return ByteString.copyFromUtf8(arg);
+    }
+
+    String getArgument()
+    {
+        final int comma = args.indexOf(',');
+        String arg = null;
+        if (comma >= 0)
+        {
+            arg = args.substring(0, comma);
+            args = args.substring(comma + 1);
+        }
+        else
+        {
+            arg = args;
+            args = null;
+        }
+        final int comment = arg.indexOf('=');
+        if (comment >= 0)
+            arg = arg.substring(comment + 1);
+        return arg.trim();
+    }
+
+    int parseArgument()
+    {
+        String arg = getArgument();
+        if (arg.startsWith("GL_"))
+            return GLEnum.valueOf(arg).value;
+        else if (arg.toLowerCase().startsWith("0x"))
+            return Integer.parseInt(arg.substring(2), 16);
+        else
+            return Integer.parseInt(arg);
+    }
+
+    int parseFloat()
+    {
+        String arg = getArgument();
+        return Float.floatToRawIntBits(Float.parseFloat(arg));
+    }
+
+    public void parse(final Message.Builder builder, String string) {
+        int lparen = string.indexOf("("), rparen = string.lastIndexOf(")");
+        String s = string.substring(0, lparen).trim();
+        args = string.substring(lparen + 1, rparen);
+        String[] t = s.split(" ");
+        Function function = Function.valueOf(t[t.length - 1]);
+        builder.setFunction(function);
+        switch (function) {
+            case glActiveTexture:
+                builder.setArg0(parseArgument()); // GLenum texture
+                break;
+            case glAttachShader:
+                builder.setArg0(parseArgument()); // GLuint program
+                builder.setArg1(parseArgument()); // GLuint shader
+                break;
+            case glBindAttribLocation:
+                builder.setArg0(parseArgument()); // GLuint program
+                builder.setArg1(parseArgument()); // GLuint index
+                builder.setData(parseString()); // GLchar name
+                break;
+            case glBindBuffer:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLuint buffer
+                break;
+            case glBindFramebuffer:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLuint framebuffer
+                break;
+            case glBindRenderbuffer:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLuint renderbuffer
+                break;
+            case glBindTexture:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLuint texture
+                break;
+            case glBlendColor:
+                builder.setArg0(parseFloat()); // GLclampf red
+                builder.setArg1(parseFloat()); // GLclampf green
+                builder.setArg2(parseFloat()); // GLclampf blue
+                builder.setArg3(parseFloat()); // GLclampf alpha
+                break;
+            case glBlendEquation:
+                builder.setArg0(parseArgument()); // GLenum mode
+                break;
+            case glBlendEquationSeparate:
+                builder.setArg0(parseArgument()); // GLenum modeRGB
+                builder.setArg1(parseArgument()); // GLenum modeAlpha
+                break;
+            case glBlendFunc:
+                builder.setArg0(parseArgument()); // GLenum sfactor
+                builder.setArg1(parseArgument()); // GLenum dfactor
+                break;
+            case glBlendFuncSeparate:
+                builder.setArg0(parseArgument()); // GLenum srcRGB
+                builder.setArg1(parseArgument()); // GLenum dstRGB
+                builder.setArg2(parseArgument()); // GLenum srcAlpha
+                builder.setArg3(parseArgument()); // GLenum dstAlpha
+                break;
+            case glBufferData:
+                parse_glBufferData(builder);
+                break;
+            case glBufferSubData:
+                parse_glBufferSubData(builder);
+                break;
+            case glCheckFramebufferStatus:
+                builder.setArg0(parseArgument()); // GLenum target
+                break;
+            case glClear:
+                builder.setArg0(parseArgument()); // GLbitfield mask
+                break;
+            case glClearColor:
+                builder.setArg0(parseFloat()); // GLclampf red
+                builder.setArg1(parseFloat()); // GLclampf green
+                builder.setArg2(parseFloat()); // GLclampf blue
+                builder.setArg3(parseFloat()); // GLclampf alpha
+                break;
+            case glClearDepthf:
+                builder.setArg0(parseFloat()); // GLclampf depth
+                break;
+            case glClearStencil:
+                builder.setArg0(parseArgument()); // GLint s
+                break;
+            case glColorMask:
+                builder.setArg0(parseArgument()); // GLboolean red
+                builder.setArg1(parseArgument()); // GLboolean green
+                builder.setArg2(parseArgument()); // GLboolean blue
+                builder.setArg3(parseArgument()); // GLboolean alpha
+                break;
+            case glCompileShader:
+                builder.setArg0(parseArgument()); // GLuint shader
+                break;
+            case glCompressedTexImage2D:
+                parse_glCompressedTexImage2D(builder);
+                break;
+            case glCompressedTexSubImage2D:
+                parse_glCompressedTexSubImage2D(builder);
+                break;
+            case glCopyTexImage2D:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLint level
+                builder.setArg2(parseArgument()); // GLenum internalformat
+                builder.setArg3(parseArgument()); // GLint x
+                builder.setArg4(parseArgument()); // GLint y
+                builder.setArg5(parseArgument()); // GLsizei width
+                builder.setArg6(parseArgument()); // GLsizei height
+                builder.setArg7(parseArgument()); // GLint border
+                break;
+            case glCopyTexSubImage2D:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLint level
+                builder.setArg2(parseArgument()); // GLint xoffset
+                builder.setArg3(parseArgument()); // GLint yoffset
+                builder.setArg4(parseArgument()); // GLint x
+                builder.setArg5(parseArgument()); // GLint y
+                builder.setArg6(parseArgument()); // GLsizei width
+                builder.setArg7(parseArgument()); // GLsizei height
+                break;
+            case glCreateProgram:
+                break;
+            case glCreateShader:
+                builder.setArg0(parseArgument()); // GLenum type
+                break;
+            case glCullFace:
+                builder.setArg0(parseArgument()); // GLenum mode
+                break;
+            case glDeleteBuffers:
+                builder.setArg0(parseArgument()); // GLsizei n
+                builder.setData(parseUInts(1 * builder.getArg0())); // GLuint buffers
+                break;
+            case glDeleteFramebuffers:
+                builder.setArg0(parseArgument()); // GLsizei n
+                builder.setData(parseUInts(1 * builder.getArg0())); // GLuint framebuffers
+                break;
+            case glDeleteProgram:
+                builder.setArg0(parseArgument()); // GLuint program
+                break;
+            case glDeleteRenderbuffers:
+                builder.setArg0(parseArgument()); // GLsizei n
+                builder.setData(parseUInts(1 * builder.getArg0())); // GLuint renderbuffers
+                break;
+            case glDeleteShader:
+                builder.setArg0(parseArgument()); // GLuint shader
+                break;
+            case glDeleteTextures:
+                builder.setArg0(parseArgument()); // GLsizei n
+                builder.setData(parseUInts(1 * builder.getArg0())); // GLuint textures
+                break;
+            case glDepthFunc:
+                builder.setArg0(parseArgument()); // GLenum func
+                break;
+            case glDepthMask:
+                builder.setArg0(parseArgument()); // GLboolean flag
+                break;
+            case glDepthRangef:
+                builder.setArg0(parseFloat()); // GLclampf zNear
+                builder.setArg1(parseFloat()); // GLclampf zFar
+                break;
+            case glDetachShader:
+                builder.setArg0(parseArgument()); // GLuint program
+                builder.setArg1(parseArgument()); // GLuint shader
+                break;
+            case glDisable:
+                builder.setArg0(parseArgument()); // GLenum cap
+                break;
+            case glDisableVertexAttribArray:
+                builder.setArg0(parseArgument()); // GLuint index
+                break;
+            case glDrawArrays:
+                builder.setArg0(parseArgument()); // GLenum mode
+                builder.setArg1(parseArgument()); // GLint first
+                builder.setArg2(parseArgument()); // GLsizei count
+                break;
+            case glDrawElements:
+                parse_glDrawElements(builder);
+                break;
+            case glEnable:
+                builder.setArg0(parseArgument()); // GLenum cap
+                break;
+            case glEnableVertexAttribArray:
+                builder.setArg0(parseArgument()); // GLuint index
+                break;
+            case glFinish:
+                break;
+            case glFlush:
+                break;
+            case glFramebufferRenderbuffer:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLenum attachment
+                builder.setArg2(parseArgument()); // GLenum renderbuffertarget
+                builder.setArg3(parseArgument()); // GLuint renderbuffer
+                break;
+            case glFramebufferTexture2D:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLenum attachment
+                builder.setArg2(parseArgument()); // GLenum textarget
+                builder.setArg3(parseArgument()); // GLuint texture
+                builder.setArg4(parseArgument()); // GLint level
+                break;
+            case glFrontFace:
+                builder.setArg0(parseArgument()); // GLenum mode
+                break;
+            case glGenBuffers:
+                builder.setArg0(parseArgument()); // GLsizei n
+                builder.setData(parseUInts(1 * builder.getArg0())); // GLuint buffers
+                break;
+            case glGenerateMipmap:
+                builder.setArg0(parseArgument()); // GLenum target
+                break;
+            case glGenFramebuffers:
+                builder.setArg0(parseArgument()); // GLsizei n
+                builder.setData(parseUInts(1 * builder.getArg0())); // GLuint framebuffers
+                break;
+            case glGenRenderbuffers:
+                builder.setArg0(parseArgument()); // GLsizei n
+                builder.setData(parseUInts(1 * builder.getArg0())); // GLuint renderbuffers
+                break;
+            case glGenTextures:
+                builder.setArg0(parseArgument()); // GLsizei n
+                builder.setData(parseUInts(1 * builder.getArg0())); // GLuint textures
+                break;
+            case glGetActiveAttrib:
+                parse_glGetActiveAttrib(builder);
+                break;
+            case glGetActiveUniform:
+                parse_glGetActiveUniform(builder);
+                break;
+            case glGetAttachedShaders:
+                parse_glGetAttachedShaders(builder);
+                break;
+            case glGetAttribLocation:
+                builder.setArg0(parseArgument()); // GLuint program
+                builder.setData(parseString()); // GLchar name
+                break;
+            case glGetBooleanv:
+                parse_glGetBooleanv(builder);
+                break;
+            case glGetBufferParameteriv:
+                parse_glGetBufferParameteriv(builder);
+                break;
+            case glGetError:
+                break;
+            case glGetFloatv:
+                parse_glGetFloatv(builder);
+                break;
+            case glGetFramebufferAttachmentParameteriv:
+                parse_glGetFramebufferAttachmentParameteriv(builder);
+                break;
+            case glGetIntegerv:
+                parse_glGetIntegerv(builder);
+                break;
+            case glGetProgramiv:
+                builder.setArg0(parseArgument()); // GLuint program
+                builder.setArg1(parseArgument()); // GLenum pname
+                builder.setData(parseInts(1)); // GLint params
+                break;
+            case glGetProgramInfoLog:
+                parse_glGetProgramInfoLog(builder);
+                break;
+            case glGetRenderbufferParameteriv:
+                parse_glGetRenderbufferParameteriv(builder);
+                break;
+            case glGetShaderiv:
+                builder.setArg0(parseArgument()); // GLuint shader
+                builder.setArg1(parseArgument()); // GLenum pname
+                builder.setData(parseInts(1)); // GLint params
+                break;
+            case glGetShaderInfoLog:
+                parse_glGetShaderInfoLog(builder);
+                break;
+            case glGetShaderPrecisionFormat:
+                parse_glGetShaderPrecisionFormat(builder);
+                break;
+            case glGetShaderSource:
+                parse_glGetShaderSource(builder);
+                break;
+            case glGetString:
+                builder.setArg0(parseArgument()); // GLenum name
+                break;
+            case glGetTexParameterfv:
+                parse_glGetTexParameterfv(builder);
+                break;
+            case glGetTexParameteriv:
+                parse_glGetTexParameteriv(builder);
+                break;
+            case glGetUniformfv:
+                parse_glGetUniformfv(builder);
+                break;
+            case glGetUniformiv:
+                parse_glGetUniformiv(builder);
+                break;
+            case glGetUniformLocation:
+                builder.setArg0(parseArgument()); // GLuint program
+                builder.setData(parseString()); // GLchar name
+                break;
+            case glGetVertexAttribfv:
+                parse_glGetVertexAttribfv(builder);
+                break;
+            case glGetVertexAttribiv:
+                parse_glGetVertexAttribiv(builder);
+                break;
+            case glGetVertexAttribPointerv:
+                parse_glGetVertexAttribPointerv(builder);
+                break;
+            case glHint:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLenum mode
+                break;
+            case glIsBuffer:
+                builder.setArg0(parseArgument()); // GLuint buffer
+                break;
+            case glIsEnabled:
+                builder.setArg0(parseArgument()); // GLenum cap
+                break;
+            case glIsFramebuffer:
+                builder.setArg0(parseArgument()); // GLuint framebuffer
+                break;
+            case glIsProgram:
+                builder.setArg0(parseArgument()); // GLuint program
+                break;
+            case glIsRenderbuffer:
+                builder.setArg0(parseArgument()); // GLuint renderbuffer
+                break;
+            case glIsShader:
+                builder.setArg0(parseArgument()); // GLuint shader
+                break;
+            case glIsTexture:
+                builder.setArg0(parseArgument()); // GLuint texture
+                break;
+            case glLineWidth:
+                builder.setArg0(parseFloat()); // GLfloat width
+                break;
+            case glLinkProgram:
+                builder.setArg0(parseArgument()); // GLuint program
+                break;
+            case glPixelStorei:
+                builder.setArg0(parseArgument()); // GLenum pname
+                builder.setArg1(parseArgument()); // GLint param
+                break;
+            case glPolygonOffset:
+                builder.setArg0(parseFloat()); // GLfloat factor
+                builder.setArg1(parseFloat()); // GLfloat units
+                break;
+            case glReadPixels:
+                parse_glReadPixels(builder);
+                break;
+            case glReleaseShaderCompiler:
+                break;
+            case glRenderbufferStorage:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLenum internalformat
+                builder.setArg2(parseArgument()); // GLsizei width
+                builder.setArg3(parseArgument()); // GLsizei height
+                break;
+            case glSampleCoverage:
+                builder.setArg0(parseFloat()); // GLclampf value
+                builder.setArg1(parseArgument()); // GLboolean invert
+                break;
+            case glScissor:
+                builder.setArg0(parseArgument()); // GLint x
+                builder.setArg1(parseArgument()); // GLint y
+                builder.setArg2(parseArgument()); // GLsizei width
+                builder.setArg3(parseArgument()); // GLsizei height
+                break;
+            case glShaderBinary:
+                parse_glShaderBinary(builder);
+                break;
+            case glShaderSource:
+                parse_glShaderSource(builder);
+                break;
+            case glStencilFunc:
+                builder.setArg0(parseArgument()); // GLenum func
+                builder.setArg1(parseArgument()); // GLint ref
+                builder.setArg2(parseArgument()); // GLuint mask
+                break;
+            case glStencilFuncSeparate:
+                builder.setArg0(parseArgument()); // GLenum face
+                builder.setArg1(parseArgument()); // GLenum func
+                builder.setArg2(parseArgument()); // GLint ref
+                builder.setArg3(parseArgument()); // GLuint mask
+                break;
+            case glStencilMask:
+                builder.setArg0(parseArgument()); // GLuint mask
+                break;
+            case glStencilMaskSeparate:
+                builder.setArg0(parseArgument()); // GLenum face
+                builder.setArg1(parseArgument()); // GLuint mask
+                break;
+            case glStencilOp:
+                builder.setArg0(parseArgument()); // GLenum fail
+                builder.setArg1(parseArgument()); // GLenum zfail
+                builder.setArg2(parseArgument()); // GLenum zpass
+                break;
+            case glStencilOpSeparate:
+                builder.setArg0(parseArgument()); // GLenum face
+                builder.setArg1(parseArgument()); // GLenum fail
+                builder.setArg2(parseArgument()); // GLenum zfail
+                builder.setArg3(parseArgument()); // GLenum zpass
+                break;
+            case glTexImage2D:
+                parse_glTexImage2D(builder);
+                break;
+            case glTexParameterf:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLenum pname
+                builder.setArg2(parseFloat()); // GLfloat param
+                break;
+            case glTexParameterfv:
+                parse_glTexParameterfv(builder);
+                break;
+            case glTexParameteri:
+                builder.setArg0(parseArgument()); // GLenum target
+                builder.setArg1(parseArgument()); // GLenum pname
+                builder.setArg2(parseArgument()); // GLint param
+                break;
+            case glTexParameteriv:
+                parse_glTexParameteriv(builder);
+                break;
+            case glTexSubImage2D:
+                parse_glTexSubImage2D(builder);
+                break;
+            case glUniform1f:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseFloat()); // GLfloat x
+                break;
+            case glUniform1fv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setData(parseFloats(1 * builder.getArg1())); // GLfloat v
+                break;
+            case glUniform1i:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLint x
+                break;
+            case glUniform1iv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setData(parseInts(1 * builder.getArg1())); // GLint v
+                break;
+            case glUniform2f:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseFloat()); // GLfloat x
+                builder.setArg2(parseFloat()); // GLfloat y
+                break;
+            case glUniform2fv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setData(parseFloats(2 * builder.getArg1())); // GLfloat v
+                break;
+            case glUniform2i:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLint x
+                builder.setArg2(parseArgument()); // GLint y
+                break;
+            case glUniform2iv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setData(parseInts(2 * builder.getArg1())); // GLint v
+                break;
+            case glUniform3f:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseFloat()); // GLfloat x
+                builder.setArg2(parseFloat()); // GLfloat y
+                builder.setArg3(parseFloat()); // GLfloat z
+                break;
+            case glUniform3fv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setData(parseFloats(3 * builder.getArg1())); // GLfloat v
+                break;
+            case glUniform3i:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLint x
+                builder.setArg2(parseArgument()); // GLint y
+                builder.setArg3(parseArgument()); // GLint z
+                break;
+            case glUniform3iv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setData(parseInts(3 * builder.getArg1())); // GLint v
+                break;
+            case glUniform4f:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseFloat()); // GLfloat x
+                builder.setArg2(parseFloat()); // GLfloat y
+                builder.setArg3(parseFloat()); // GLfloat z
+                builder.setArg4(parseFloat()); // GLfloat w
+                break;
+            case glUniform4fv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setData(parseFloats(4 * builder.getArg1())); // GLfloat v
+                break;
+            case glUniform4i:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLint x
+                builder.setArg2(parseArgument()); // GLint y
+                builder.setArg3(parseArgument()); // GLint z
+                builder.setArg4(parseArgument()); // GLint w
+                break;
+            case glUniform4iv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setData(parseInts(4 * builder.getArg1())); // GLint v
+                break;
+            case glUniformMatrix2fv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setArg2(parseArgument()); // GLboolean transpose
+                builder.setData(parseMatrix(2, builder.getArg1())); // GLfloat value
+                break;
+            case glUniformMatrix3fv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setArg2(parseArgument()); // GLboolean transpose
+                builder.setData(parseMatrix(3, builder.getArg1())); // GLfloat value
+                break;
+            case glUniformMatrix4fv:
+                builder.setArg0(parseArgument()); // GLint location
+                builder.setArg1(parseArgument()); // GLsizei count
+                builder.setArg2(parseArgument()); // GLboolean transpose
+                builder.setData(parseMatrix(4, builder.getArg1())); // GLfloat value
+                break;
+            case glUseProgram:
+                builder.setArg0(parseArgument()); // GLuint program
+                break;
+            case glValidateProgram:
+                builder.setArg0(parseArgument()); // GLuint program
+                break;
+            case glVertexAttrib1f:
+                builder.setArg0(parseArgument()); // GLuint indx
+                builder.setArg1(parseFloat()); // GLfloat x
+                break;
+            case glVertexAttrib1fv:
+                builder.setArg0(parseArgument()); // GLuint indx
+                builder.setData(parseFloats(1)); // GLfloat values
+                break;
+            case glVertexAttrib2f:
+                builder.setArg0(parseArgument()); // GLuint indx
+                builder.setArg1(parseFloat()); // GLfloat x
+                builder.setArg2(parseFloat()); // GLfloat y
+                break;
+            case glVertexAttrib2fv:
+                builder.setArg0(parseArgument()); // GLuint indx
+                builder.setData(parseFloats(2)); // GLfloat values
+                break;
+            case glVertexAttrib3f:
+                builder.setArg0(parseArgument()); // GLuint indx
+                builder.setArg1(parseFloat()); // GLfloat x
+                builder.setArg2(parseFloat()); // GLfloat y
+                builder.setArg3(parseFloat()); // GLfloat z
+                break;
+            case glVertexAttrib3fv:
+                builder.setArg0(parseArgument()); // GLuint indx
+                builder.setData(parseFloats(3)); // GLfloat values
+                break;
+            case glVertexAttrib4f:
+                builder.setArg0(parseArgument()); // GLuint indx
+                builder.setArg1(parseFloat()); // GLfloat x
+                builder.setArg2(parseFloat()); // GLfloat y
+                builder.setArg3(parseFloat()); // GLfloat z
+                builder.setArg4(parseFloat()); // GLfloat w
+                break;
+            case glVertexAttrib4fv:
+                builder.setArg0(parseArgument()); // GLuint indx
+                builder.setData(parseFloats(4)); // GLfloat values
+                break;
+            case glVertexAttribPointer:
+                parse_glVertexAttribPointer(builder);
+                break;
+            case glViewport:
+                builder.setArg0(parseArgument()); // GLint x
+                builder.setArg1(parseArgument()); // GLint y
+                builder.setArg2(parseArgument()); // GLsizei width
+                builder.setArg3(parseArgument()); // GLsizei height
+                break;
+            default:
+                assert false;
+        }
+    }
+    abstract void parse_glBufferData(Message.Builder builder);
+    abstract void parse_glBufferSubData(Message.Builder builder);
+    abstract void parse_glCompressedTexImage2D(Message.Builder builder);
+    abstract void parse_glCompressedTexSubImage2D(Message.Builder builder);
+    abstract void parse_glDrawElements(Message.Builder builder);
+    abstract void parse_glGetActiveAttrib(Message.Builder builder);
+    abstract void parse_glGetActiveUniform(Message.Builder builder);
+    abstract void parse_glGetAttachedShaders(Message.Builder builder);
+    abstract void parse_glGetBooleanv(Message.Builder builder);
+    abstract void parse_glGetBufferParameteriv(Message.Builder builder);
+    abstract void parse_glGetFloatv(Message.Builder builder);
+    abstract void parse_glGetFramebufferAttachmentParameteriv(Message.Builder builder);
+    abstract void parse_glGetIntegerv(Message.Builder builder);
+    abstract void parse_glGetProgramInfoLog(Message.Builder builder);
+    abstract void parse_glGetRenderbufferParameteriv(Message.Builder builder);
+    abstract void parse_glGetShaderInfoLog(Message.Builder builder);
+    abstract void parse_glGetShaderPrecisionFormat(Message.Builder builder);
+    abstract void parse_glGetShaderSource(Message.Builder builder);
+    abstract void parse_glGetTexParameterfv(Message.Builder builder);
+    abstract void parse_glGetTexParameteriv(Message.Builder builder);
+    abstract void parse_glGetUniformfv(Message.Builder builder);
+    abstract void parse_glGetUniformiv(Message.Builder builder);
+    abstract void parse_glGetVertexAttribfv(Message.Builder builder);
+    abstract void parse_glGetVertexAttribiv(Message.Builder builder);
+    abstract void parse_glGetVertexAttribPointerv(Message.Builder builder);
+    abstract void parse_glReadPixels(Message.Builder builder);
+    abstract void parse_glShaderBinary(Message.Builder builder);
+    abstract void parse_glShaderSource(Message.Builder builder);
+    abstract void parse_glTexImage2D(Message.Builder builder);
+    abstract void parse_glTexParameterfv(Message.Builder builder);
+    abstract void parse_glTexParameteriv(Message.Builder builder);
+    abstract void parse_glTexSubImage2D(Message.Builder builder);
+    abstract void parse_glVertexAttribPointer(Message.Builder builder);
+}
\ No newline at end of file
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParserEx.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParserEx.java
new file mode 100644
index 0000000..5099146
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParserEx.java
@@ -0,0 +1,306 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+// skeleton from stdout of generate_MessageParser_java.py
+
+package com.android.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+
+public class MessageParserEx extends MessageParser {
+
+    @Override
+    void parse_glBufferData(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLsizeiptr size
+        // TODO // GLvoid data
+        builder.setArg3(parseArgument()); // GLenum usage
+    }
+
+    @Override
+    void parse_glBufferSubData(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLintptr offset
+        builder.setArg2(parseArgument()); // GLsizeiptr size
+        // TODO // GLvoid data
+    }
+
+    @Override
+    void parse_glCompressedTexImage2D(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLint level
+        builder.setArg2(parseArgument()); // GLenum internalformat
+        builder.setArg3(parseArgument()); // GLsizei width
+        builder.setArg4(parseArgument()); // GLsizei height
+        builder.setArg5(parseArgument()); // GLint border
+        builder.setArg6(parseArgument()); // GLsizei imageSize
+        // TODO: GLvoid* data
+    }
+
+    @Override
+    void parse_glCompressedTexSubImage2D(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLint level
+        builder.setArg2(parseArgument()); // GLint xoffset
+        builder.setArg3(parseArgument()); // GLint yoffset
+        builder.setArg4(parseArgument()); // GLsizei width
+        builder.setArg5(parseArgument()); // GLsizei height
+        builder.setArg6(parseArgument()); // GLenum format
+        builder.setArg7(parseArgument()); // GLsizei imageSize
+        // TODO: GLvoid* data
+    }
+
+    @Override
+    void parse_glDrawElements(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum mode
+        builder.setArg1(parseArgument()); // GLsizei count
+        builder.setArg2(parseArgument()); // GLenum type
+        // TODO: GLvoid* indices
+    }
+
+    @Override
+    void parse_glGetActiveAttrib(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint program
+        builder.setArg1(parseArgument()); // GLuint index
+        builder.setArg2(parseArgument()); // GLsizei bufsize
+        // TODO: GLsizei* length
+        // TODO: GLint* size
+        // TODO: GLenum* type
+        builder.setData(parseString()); // GLchar name
+    }
+
+    @Override
+    void parse_glGetActiveUniform(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint program
+        builder.setArg1(parseArgument()); // GLuint index
+        builder.setArg2(parseArgument()); // GLsizei bufsize
+        // TODO: GLsizei* length
+        // TODO: GLint* size
+        // TODO: GLenum* type
+        builder.setData(parseString()); // GLchar name
+    }
+
+    @Override
+    void parse_glGetAttachedShaders(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint program
+        builder.setArg1(parseArgument()); // GLsizei maxcount
+        // TODO: GLsizei* count
+        // TODO: GLuint* shaders
+    }
+
+    @Override
+    void parse_glGetBooleanv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum pname
+        // TODO: GLboolean* params
+    }
+
+    @Override
+    void parse_glGetBufferParameteriv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLint* params
+    }
+
+    @Override
+    void parse_glGetFloatv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum pname
+        // TODO: GLfloat* params
+    }
+
+    @Override
+    void parse_glGetFramebufferAttachmentParameteriv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLenum attachment
+        builder.setArg2(parseArgument()); // GLenum pname
+        // TODO: GLint* params
+    }
+
+    @Override
+    void parse_glGetIntegerv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum pname
+        // TODO: GLint* params
+    }
+
+    @Override
+    void parse_glGetProgramInfoLog(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint program
+        builder.setArg1(parseArgument()); // GLsizei bufsize
+        // TODO: GLsizei* length
+        builder.setData(parseString()); // GLchar infolog
+    }
+
+    @Override
+    void parse_glGetRenderbufferParameteriv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLint* params
+    }
+
+    @Override
+    void parse_glGetShaderInfoLog(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint shader
+        builder.setArg1(parseArgument()); // GLsizei bufsize
+        // TODO: GLsizei* length
+        builder.setData(parseString()); // GLchar infolog
+    }
+
+    @Override
+    void parse_glGetShaderPrecisionFormat(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum shadertype
+        builder.setArg1(parseArgument()); // GLenum precisiontype
+        // TODO: GLint* range
+        // TODO: GLint* precision
+    }
+
+    @Override
+    void parse_glGetShaderSource(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint shader
+        builder.setArg1(parseArgument()); // GLsizei bufsize
+        // TODO: GLsizei* length
+        builder.setData(parseString()); // GLchar source
+    }
+
+    @Override
+    void parse_glGetTexParameterfv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLfloat* params
+    }
+
+    @Override
+    void parse_glGetTexParameteriv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLint* params
+    }
+
+    @Override
+    void parse_glGetUniformfv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint program
+        builder.setArg1(parseArgument()); // GLint location
+        // TODO: GLfloat* params
+    }
+
+    @Override
+    void parse_glGetUniformiv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint program
+        builder.setArg1(parseArgument()); // GLint location
+        // TODO: GLint* params
+    }
+
+    @Override
+    void parse_glGetVertexAttribfv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint index
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLfloat* params
+    }
+
+    @Override
+    void parse_glGetVertexAttribiv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint index
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLint* params
+    }
+
+    @Override
+    void parse_glGetVertexAttribPointerv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint index
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLvoid** pointer
+    }
+
+    @Override
+    void parse_glReadPixels(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLint x
+        builder.setArg1(parseArgument()); // GLint y
+        builder.setArg2(parseArgument()); // GLsizei width
+        builder.setArg3(parseArgument()); // GLsizei height
+        builder.setArg4(parseArgument()); // GLenum format
+        builder.setArg5(parseArgument()); // GLenum type
+        // TODO: GLvoid* pixels
+    }
+
+    @Override
+    void parse_glShaderBinary(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLsizei n
+        // TODO: GLuint* shaders
+        builder.setArg2(parseArgument()); // GLenum binaryformat
+        // TODO: GLvoid* binary
+        builder.setArg4(parseArgument()); // GLsizei length
+    }
+
+    @Override
+    void parse_glShaderSource(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint shader
+        builder.setArg1(parseArgument()); // GLsizei count
+        assert 1 == builder.getArg1();
+        builder.setData(parseString()); // GLchar** string
+        builder.setArg3(parseArgument());// not used, always 1 null terminated
+                                         // string; GLint* length
+    }
+
+    @Override
+    void parse_glTexImage2D(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLint level
+        builder.setArg2(parseArgument()); // GLint internalformat
+        builder.setArg3(parseArgument()); // GLsizei width
+        builder.setArg4(parseArgument()); // GLsizei height
+        builder.setArg5(parseArgument()); // GLint border
+        builder.setArg6(parseArgument()); // GLenum format
+        builder.setArg7(parseArgument()); // GLenum type
+        // TODO: GLvoid* pixels
+    }
+
+    @Override
+    void parse_glTexParameterfv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLfloat* params
+    }
+
+    @Override
+    void parse_glTexParameteriv(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLenum pname
+        // TODO: GLint* params
+    }
+
+    @Override
+    void parse_glTexSubImage2D(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLenum target
+        builder.setArg1(parseArgument()); // GLint level
+        builder.setArg2(parseArgument()); // GLint xoffset
+        builder.setArg3(parseArgument()); // GLint yoffset
+        builder.setArg4(parseArgument()); // GLsizei width
+        builder.setArg5(parseArgument()); // GLsizei height
+        builder.setArg6(parseArgument()); // GLenum format
+        builder.setArg7(parseArgument()); // GLenum type
+        // TODO: GLvoid* pixels
+    }
+
+    @Override
+    void parse_glVertexAttribPointer(Message.Builder builder) {
+        builder.setArg0(parseArgument()); // GLuint indx
+        builder.setArg1(parseArgument()); // GLint size
+        builder.setArg2(parseArgument()); // GLenum type
+        builder.setArg3(parseArgument()); // GLboolean normalized
+        builder.setArg4(parseArgument()); // GLsizei stride
+        // TODO: GLvoid* ptr
+    }
+
+    public final static MessageParserEx instance = new MessageParserEx();
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageProcessor.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageProcessor.java
new file mode 100644
index 0000000..bdd53d1
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageProcessor.java
@@ -0,0 +1,173 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.google.protobuf.ByteString;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+public class MessageProcessor {
+    static void showError(final String message) {
+        // need to call SWT from UI thread
+        MessageDialog.openError(null, "MessageProcessor", message);
+    }
+
+    /**
+     * data layout: uint32 total decompressed length, (chunks: uint32 chunk
+     * decompressed size, uint32 chunk compressed size, chunk data)+. 0 chunk
+     * compressed size means chunk is not compressed
+     */
+    public static byte[] lzfDecompressChunks(final ByteString data) {
+        ByteBuffer in = data.asReadOnlyByteBuffer();
+        in.order(SampleView.targetByteOrder);
+        ByteBuffer out = ByteBuffer.allocate(in.getInt());
+        byte[] inChunk = new byte[0];
+        byte[] outChunk = new byte[0];
+        while (in.remaining() > 0) {
+            int decompressed = in.getInt();
+            int compressed = in.getInt();
+            if (decompressed > outChunk.length)
+                outChunk = new byte[decompressed];
+            if (compressed == 0) {
+                in.get(outChunk, 0, decompressed);
+                out.put(outChunk, 0, decompressed);
+            } else {
+                if (compressed > inChunk.length)
+                    inChunk = new byte[compressed];
+                in.get(inChunk, 0, compressed);
+                int size = org.liblzf.CLZF
+                        .lzf_decompress(inChunk, compressed, outChunk, outChunk.length);
+                assert size == decompressed;
+                out.put(outChunk, 0, size);
+            }
+        }
+        assert !out.hasRemaining();
+        return out.array();
+    }
+
+    /** same data layout as LZFDecompressChunks */
+    public static byte[] lzfCompressChunks(final byte[] in, final int inSize) {
+        byte[] chunk = new byte[256 * 1024]; // chunk size is arbitrary
+        final ByteBuffer out = ByteBuffer.allocate(4 + (inSize + chunk.length - 1)
+                / chunk.length * (chunk.length + 4 * 2));
+        out.order(SampleView.targetByteOrder);
+        out.putInt(inSize);
+        for (int i = 0; i < inSize; i += chunk.length) {
+            int chunkIn = chunk.length;
+            if (i + chunkIn > inSize)
+                chunkIn = inSize - i;
+            final byte[] inChunk = java.util.Arrays.copyOfRange(in, i, i + chunkIn);
+            final int chunkOut = org.liblzf.CLZF
+                    .lzf_compress(inChunk, chunkIn, chunk, chunk.length);
+            out.putInt(chunkIn);
+            out.putInt(chunkOut);
+            if (chunkOut == 0) // compressed bigger than chunk (uncompressed)
+                out.put(inChunk);
+            else
+                out.put(chunk, 0, chunkOut);
+        }
+        return Arrays.copyOf(out.array(), out.position());
+    }
+
+    /**
+     * returns new ref, which is also the decoded image; ref could be bigger
+     * than pixels, in which case the first pixels.length bytes form the image
+     */
+    public static byte[] decodeReferencedImage(byte[] ref, byte[] pixels) {
+        if (ref.length < pixels.length)
+            ref = new byte[pixels.length];
+        for (int i = 0; i < pixels.length; i++)
+            ref[i] ^= pixels[i];
+        for (int i = pixels.length; i < ref.length; i++)
+            ref[i] = 0; // clear unused ref to maintain consistency
+        return ref;
+    }
+
+    public static ImageData receiveImage(int width, int height, int format,
+            int type, final ByteString data) {
+        assert width > 0 && height > 0;
+        int bpp = 0;
+        int redMask = 0, blueMask = 0, greenMask = 0;
+        switch (GLEnum.valueOf(type)) {
+            case GL_UNSIGNED_SHORT_5_6_5:
+            case GL_UNSIGNED_SHORT_4_4_4_4:
+            case GL_UNSIGNED_SHORT_5_5_5_1:
+                format = type;
+                break;
+            case GL_UNSIGNED_BYTE:
+                break;
+            default:
+                showError("unsupported texture type " + type);
+                return null;
+        }
+
+        switch (GLEnum.valueOf(format)) {
+            case GL_ALPHA:
+            case GL_LUMINANCE:
+                redMask = blueMask = greenMask = 0xff;
+                bpp = 8;
+                break;
+            case GL_LUMINANCE_ALPHA:
+                blueMask = 0xff;
+                redMask = 0xff00;
+                bpp = 16;
+                break;
+            case GL_RGB:
+                blueMask = 0xff;
+                greenMask = 0xff00;
+                redMask = 0xff0000;
+                bpp = 24;
+                break;
+            case GL_RGBA:
+                blueMask = 0xff00;
+                greenMask = 0xff0000;
+                redMask = 0xff000000;
+                bpp = 32;
+                break;
+            case GL_UNSIGNED_SHORT_5_6_5:
+                blueMask = ((1 << 5) - 1) << 0;
+                greenMask = ((1 << 6) - 1) << 5;
+                redMask = ((1 << 5) - 1) << 11;
+                bpp = 16;
+                break;
+            case GL_UNSIGNED_SHORT_4_4_4_4:
+                blueMask = ((1 << 4) - 1) << 4;
+                greenMask = ((1 << 4) - 1) << 8;
+                redMask = ((1 << 4) - 1) << 12;
+                bpp = 16;
+                break;
+            case GL_UNSIGNED_SHORT_5_5_5_1:
+                blueMask = ((1 << 5) - 1) << 1;
+                greenMask = ((1 << 5) - 1) << 6;
+                redMask = ((1 << 5) - 1) << 11;
+                bpp = 16;
+                break;
+            default:
+                showError("unsupported texture format: " + format);
+                return null;
+        }
+        byte[] pixels = lzfDecompressChunks(data);
+        assert pixels.length == width * height * (bpp / 8);
+        PaletteData palette = new PaletteData(redMask, greenMask, blueMask);
+        return new ImageData(width, height, bpp, palette, 1, pixels);
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java
new file mode 100644
index 0000000..c633d06
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java
@@ -0,0 +1,322 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Type;
+import com.android.sdklib.util.SparseArray;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.Socket;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+
+abstract interface ProcessMessage {
+    abstract boolean processMessage(final MessageQueue queue, final Message msg)
+            throws IOException;
+}
+
+public class MessageQueue implements Runnable {
+
+    private boolean running = false;
+    private ByteOrder byteOrder;
+    private FileInputStream file; // if null, create and use socket
+    Thread thread = null;
+    private final ProcessMessage[] processes;
+    private ArrayList<Message> complete = new ArrayList<Message>(); // synchronized
+    private ArrayList<Message> commands = new ArrayList<Message>(); // synchronized
+    private SampleView sampleView;
+
+    public MessageQueue(SampleView sampleView, final ProcessMessage[] processes) {
+        this.sampleView = sampleView;
+        this.processes = processes;
+    }
+
+    public void start(final ByteOrder byteOrder, final FileInputStream file) {
+        if (running)
+            return;
+        running = true;
+        this.byteOrder = byteOrder;
+        this.file = file;
+        thread = new Thread(this);
+        thread.start();
+    }
+
+    public void stop() {
+        if (!running)
+            return;
+        running = false;
+    }
+
+    public boolean isRunning() {
+        return running;
+    }
+
+    private void sendCommands(final int contextId) throws IOException {
+        synchronized (commands) {
+            for (int i = 0; i < commands.size(); i++) {
+                Message command = commands.get(i);
+                if (command.getContextId() == contextId || command.getContextId() == 0) {
+                    sendMessage(commands.remove(i));
+                    i--;
+                }
+            }
+        }
+    }
+
+    public void addCommand(Message command) {
+        synchronized (commands) {
+            commands.add(command);
+        }
+    }
+
+    // these should only be accessed from the network thread;
+    // access call chain starts with run()
+    private DataInputStream dis = null;
+    private DataOutputStream dos = null;
+    private SparseArray<ArrayList<Message>> incoming = new SparseArray<ArrayList<Message>>();
+
+    @Override
+    public void run() {
+        Socket socket = null;
+        if (file == null)
+            try {
+                socket = new Socket();
+                socket.connect(new java.net.InetSocketAddress("127.0.0.1", Integer
+                        .parseInt(sampleView.actionPort.getText())));
+                dis = new DataInputStream(socket.getInputStream());
+                dos = new DataOutputStream(socket.getOutputStream());
+            } catch (Exception e) {
+                running = false;
+                Error(e);
+            }
+        else
+            dis = new DataInputStream(file);
+
+        while (running) {
+            try {
+                if (file != null && file.available() == 0) {
+                    running = false;
+                    break;
+                }
+            } catch (IOException e1) {
+                e1.printStackTrace();
+                assert false;
+            }
+
+            Message msg = null;
+            if (incoming.size() > 0) { // find queued incoming
+                for (int i = 0; i < incoming.size(); i++) {
+                    final ArrayList<Message> messages = incoming.valueAt(i);
+                    if (messages.size() > 0) {
+                        msg = messages.remove(0);
+                        break;
+                    }
+                }
+            }
+            try {
+                if (null == msg) // get incoming from network
+                    msg = receiveMessage(dis);
+                processMessage(dos, msg);
+            } catch (IOException e) {
+                Error(e);
+                running = false;
+                break;
+            }
+        }
+
+        try {
+            if (socket != null)
+                socket.close();
+            else
+                file.close();
+        } catch (IOException e) {
+            Error(e);
+            running = false;
+        }
+
+    }
+
+    private void putMessage(final Message msg) {
+        ArrayList<Message> existing = incoming.get(msg.getContextId());
+        if (existing == null)
+            incoming.put(msg.getContextId(), existing = new ArrayList<Message>());
+        existing.add(msg);
+    }
+
+    Message receiveMessage(final int contextId) throws IOException {
+        Message msg = receiveMessage(dis);
+        while (msg.getContextId() != contextId) {
+            putMessage(msg);
+            msg = receiveMessage(dis);
+        }
+        return msg;
+    }
+
+    void sendMessage(final Message msg) throws IOException {
+        sendMessage(dos, msg);
+    }
+
+    // should only be used by DefaultProcessMessage
+    private SparseArray<Message> partials = new SparseArray<Message>();
+
+    Message getPartialMessage(final int contextId) {
+        return partials.get(contextId);
+    }
+
+    // used to add BeforeCall to complete if it was skipped
+    void completePartialMessage(final int contextId) {
+        final Message msg = partials.get(contextId);
+        partials.remove(contextId);
+        assert msg != null;
+        assert msg.getType() == Type.BeforeCall;
+        if (msg != null)
+            synchronized (complete) {
+                complete.add(msg);
+            }
+    }
+
+    // can be used by other message processor as default processor
+    void defaultProcessMessage(final Message msg, boolean expectResponse,
+            boolean sendResponse) throws IOException {
+        final int contextId = msg.getContextId();
+        if (msg.getType() == Type.BeforeCall) {
+            if (sendResponse) {
+                final Message.Builder builder = Message.newBuilder();
+                builder.setContextId(contextId);
+                builder.setType(Type.Response);
+                builder.setExpectResponse(expectResponse);
+                builder.setFunction(Function.CONTINUE);
+                sendMessage(dos, builder.build());
+            }
+            assert partials.indexOfKey(contextId) < 0;
+            partials.put(contextId, msg);
+        } else if (msg.getType() == Type.AfterCall) {
+            if (sendResponse) {
+                final Message.Builder builder = Message.newBuilder();
+                builder.setContextId(contextId);
+                builder.setType(Type.Response);
+                builder.setExpectResponse(expectResponse);
+                builder.setFunction(Function.SKIP);
+                sendMessage(dos, builder.build());
+            }
+            assert partials.indexOfKey(contextId) >= 0;
+            final Message before = partials.get(contextId);
+            partials.remove(contextId);
+            assert before.getFunction() == msg.getFunction();
+            final Message completed = before.toBuilder().mergeFrom(msg)
+                    .setType(Type.CompleteCall).build();
+            synchronized (complete) {
+                complete.add(completed);
+            }
+        } else if (msg.getType() == Type.CompleteCall) {
+            // this type should only be encountered on client after processing
+            assert file != null;
+            assert !msg.getExpectResponse();
+            assert !sendResponse;
+            assert partials.indexOfKey(contextId) < 0;
+            synchronized (complete) {
+                complete.add(msg);
+            }
+        } else if (msg.getType() == Type.Response && msg.getFunction() == Function.SETPROP) {
+            synchronized (complete) {
+                complete.add(msg);
+            }
+        } else
+            assert false;
+    }
+
+    public Message removeCompleteMessage(int contextId) {
+        synchronized (complete) {
+            if (complete.size() == 0)
+                return null;
+            if (0 == contextId) // get a message for any context
+                return complete.remove(0);
+            for (int i = 0; i < complete.size(); i++) {
+                Message msg = complete.get(i);
+                if (msg.getContextId() == contextId) {
+                    complete.remove(i);
+                    return msg;
+                }
+            }
+        }
+        return null;
+    }
+
+    private Message receiveMessage(final DataInputStream dis)
+            throws IOException {
+        int len = 0;
+        try {
+            len = dis.readInt();
+            if (byteOrder == ByteOrder.LITTLE_ENDIAN)
+                len = Integer.reverseBytes(len); // readInt reads BIT_ENDIAN
+        } catch (EOFException e) {
+            Error(new Exception("EOF"));
+        }
+        byte[] buffer = new byte[len];
+        int readLen = 0;
+        while (readLen < len) {
+            int read = -1;
+            try {
+                read = dis.read(buffer, readLen, len - readLen);
+            } catch (EOFException e) {
+                Error(new Exception("EOF"));
+            }
+            if (read < 0) {
+                Error(new Exception("read length = " + read));
+                return null;
+            } else
+                readLen += read;
+        }
+        Message msg = Message.parseFrom(buffer);
+        sendCommands(msg.getContextId());
+        return msg;
+    }
+
+    private void sendMessage(final DataOutputStream dos, final Message message)
+            throws IOException {
+        if (dos == null)
+            return;
+        assert message.getFunction() != Function.NEG;
+        final byte[] data = message.toByteArray();
+        if (byteOrder == ByteOrder.BIG_ENDIAN)
+            dos.writeInt(data.length);
+        else
+            dos.writeInt(Integer.reverseBytes(data.length));
+        dos.write(data);
+    }
+
+    private void processMessage(final DataOutputStream dos, final Message msg) throws IOException {
+        if (msg.getExpectResponse()) {
+            assert dos != null; // readonly source cannot expectResponse
+            for (ProcessMessage process : processes)
+                if (process.processMessage(this, msg))
+                    return;
+            defaultProcessMessage(msg, msg.getExpectResponse(), msg.getExpectResponse());
+        } else
+            defaultProcessMessage(msg, msg.getExpectResponse(), msg.getExpectResponse());
+    }
+
+    void Error(Exception e) {
+        sampleView.showError(e);
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
new file mode 100644
index 0000000..4a8cdc9
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
@@ -0,0 +1,835 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Prop;
+import com.android.glesv2debugger.DebuggerMessage.Message.Type;
+import com.android.sdklib.util.SparseArray;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.nio.ByteOrder;
+
+/**
+ * This sample class demonstrates how to plug-in a new workbench view. The view
+ * shows data obtained from the model. The sample creates a dummy model on the
+ * fly, but a real implementation would connect to the model available either in
+ * this or another plug-in (e.g. the workspace). The view is connected to the
+ * model using a content provider.
+ * <p>
+ * The view uses a label provider to define how model objects should be
+ * presented in the view. Each view can present the same model objects using
+ * different labels and icons, if needed. Alternatively, a single label provider
+ * can be shared between views in order to ensure that objects of the same type
+ * are presented in the same way everywhere.
+ * <p>
+ */
+
+public class SampleView extends ViewPart implements Runnable, SelectionListener {
+    public static final ByteOrder targetByteOrder = ByteOrder.LITTLE_ENDIAN;
+
+    boolean running = false;
+    Thread thread;
+    MessageQueue messageQueue;
+    SparseArray<DebugContext> debugContexts = new SparseArray<DebugContext>();
+
+    /** The ID of the view as specified by the extension. */
+    public static final String ID = "glesv2debuggerclient.views.SampleView";
+
+    TabFolder tabFolder;
+    TabItem tabItemText, tabItemImage, tabItemBreakpointOption;
+    TabItem tabItemShaderEditor, tabContextViewer;
+    ListViewer viewer; // ListViewer / TableViewer
+    Slider frameNum; // scale max cannot overlap min, so max is array size
+    TreeViewer contextViewer;
+    BreakpointOption breakpointOption;
+    ShaderEditor shaderEditor;
+    Canvas canvas;
+    Text text;
+    Action actionConnect; // connect / disconnect
+
+    Action actionAutoScroll;
+    Action actionFilter;
+    Action actionPort;
+
+    Action actContext; // for toggling contexts
+    DebugContext current = null;
+
+    Point origin = new Point(0, 0); // for smooth scrolling canvas
+    String[] filters = null;
+
+    class ViewContentProvider extends LabelProvider implements IStructuredContentProvider,
+            ITableLabelProvider {
+        Frame frame = null;
+
+        @Override
+        public void inputChanged(Viewer v, Object oldInput, Object newInput) {
+            frame = (Frame) newInput;
+        }
+
+        @Override
+        public void dispose() {
+        }
+
+        @Override
+        public Object[] getElements(Object parent) {
+            return frame.get().toArray();
+        }
+
+        @Override
+        public String getText(Object obj) {
+            MessageData msgData = (MessageData) obj;
+            return msgData.text;
+        }
+
+        @Override
+        public Image getImage(Object obj) {
+            MessageData msgData = (MessageData) obj;
+            return msgData.getImage();
+        }
+
+        @Override
+        public String getColumnText(Object obj, int index) {
+            MessageData msgData = (MessageData) obj;
+            if (index >= msgData.columns.length)
+                return null;
+            return msgData.columns[index];
+        }
+
+        @Override
+        public Image getColumnImage(Object obj, int index) {
+            if (index > -1)
+                return null;
+            MessageData msgData = (MessageData) obj;
+            return msgData.getImage();
+        }
+    }
+
+    class NameSorter extends ViewerSorter {
+        @Override
+        public int compare(Viewer viewer, Object e1, Object e2) {
+            MessageData m1 = (MessageData) e1;
+            MessageData m2 = (MessageData) e2;
+            return (int) ((m1.msg.getTime() - m2.msg.getTime()) * 100);
+        }
+    }
+
+    class Filter extends ViewerFilter {
+        @Override
+        public boolean select(Viewer viewer, Object parentElement,
+                Object element) {
+            MessageData msgData = (MessageData) element;
+            if (null == filters)
+                return true;
+            for (int i = 0; i < filters.length; i++)
+                if (msgData.text.contains(filters[i]))
+                    return true;
+            return false;
+        }
+    }
+
+    public SampleView() {
+
+    }
+
+    public void createLeftPane(Composite parent) {
+        Composite composite = new Composite(parent, 0);
+
+        GridLayout gridLayout = new GridLayout();
+        gridLayout.numColumns = 1;
+        composite.setLayout(gridLayout);
+
+        frameNum = new Slider(composite, SWT.BORDER | SWT.HORIZONTAL);
+        frameNum.setMinimum(0);
+        frameNum.setMaximum(1);
+        frameNum.setSelection(0);
+        frameNum.addSelectionListener(this);
+
+        GridData gridData = new GridData();
+        gridData.horizontalAlignment = SWT.FILL;
+        gridData.grabExcessHorizontalSpace = true;
+        gridData.verticalAlignment = SWT.FILL;
+        frameNum.setLayoutData(gridData);
+
+        // Table table = new Table(composite, SWT.H_SCROLL | SWT.V_SCROLL |
+        // SWT.MULTI
+        // | SWT.FULL_SELECTION);
+        // TableLayout layout = new TableLayout();
+        // table.setLayout(layout);
+        // table.setLinesVisible(true);
+        // table.setHeaderVisible(true);
+        // String[] headings = {
+        // "Name", "Elapsed (ms)", "Detail"
+        // };
+        // int[] weights = {
+        // 50, 16, 60
+        // };
+        // int[] widths = {
+        // 180, 90, 200
+        // };
+        // for (int i = 0; i < headings.length; i++) {
+        // layout.addColumnData(new ColumnWeightData(weights[i], widths[i],
+        // true));
+        // TableColumn nameCol = new TableColumn(table, SWT.NONE, i);
+        // nameCol.setText(headings[i]);
+        // }
+
+        // viewer = new TableViewer(table);
+        viewer = new ListViewer(composite, SWT.DEFAULT);
+        viewer.getList().setFont(new Font(viewer.getList().getDisplay(),
+                "Courier", 10, SWT.BOLD));
+        ViewContentProvider contentProvider = new ViewContentProvider();
+        viewer.setContentProvider(contentProvider);
+        viewer.setLabelProvider(contentProvider);
+        // viewer.setSorter(new NameSorter());
+        viewer.setFilters(new ViewerFilter[] {
+                new Filter()
+        });
+
+        gridData = new GridData();
+        gridData.horizontalAlignment = SWT.FILL;
+        gridData.grabExcessHorizontalSpace = true;
+        gridData.verticalAlignment = SWT.FILL;
+        gridData.grabExcessVerticalSpace = true;
+        viewer.getControl().setLayoutData(gridData);
+    }
+
+    /**
+     * This is a callback that will allow us to create the viewer and initialize
+     * it.
+     */
+    @Override
+    public void createPartControl(Composite parent) {
+        createLeftPane(parent);
+
+        // Create the help context id for the viewer's control
+        PlatformUI.getWorkbench().getHelpSystem()
+                .setHelp(viewer.getControl(), "GLESv2DebuggerClient.viewer");
+
+        tabFolder = new TabFolder(parent, SWT.BORDER);
+
+        text = new Text(tabFolder, SWT.NO_BACKGROUND | SWT.READ_ONLY
+                | SWT.V_SCROLL | SWT.H_SCROLL);
+
+        tabItemText = new TabItem(tabFolder, SWT.NONE);
+        tabItemText.setText("Text");
+        tabItemText.setControl(text);
+
+        canvas = new Canvas(tabFolder, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE
+                | SWT.V_SCROLL | SWT.H_SCROLL);
+        tabItemImage = new TabItem(tabFolder, SWT.NONE);
+        tabItemImage.setText("Image");
+        tabItemImage.setControl(canvas);
+
+        breakpointOption = new BreakpointOption(this, tabFolder);
+        tabItemBreakpointOption = new TabItem(tabFolder, SWT.NONE);
+        tabItemBreakpointOption.setText("Breakpoint Option");
+        tabItemBreakpointOption.setControl(breakpointOption);
+
+        shaderEditor = new ShaderEditor(this, tabFolder);
+        tabItemShaderEditor = new TabItem(tabFolder, SWT.NONE);
+        tabItemShaderEditor.setText("Shader Editor");
+        tabItemShaderEditor.setControl(shaderEditor);
+
+        contextViewer = new TreeViewer(tabFolder);
+        ContextViewProvider contextViewProvider = new ContextViewProvider(this);
+        contextViewer.addSelectionChangedListener(contextViewProvider);
+        contextViewer.setContentProvider(contextViewProvider);
+        contextViewer.setLabelProvider(contextViewProvider);
+        tabContextViewer = new TabItem(tabFolder, SWT.NONE);
+        tabContextViewer.setText("Context Viewer");
+        tabContextViewer.setControl(contextViewer.getTree());
+
+        final ScrollBar hBar = canvas.getHorizontalBar();
+        hBar.addListener(SWT.Selection, new Listener() {
+            @Override
+            public void handleEvent(Event e) {
+                if (null == canvas.getBackgroundImage())
+                    return;
+                Image image = canvas.getBackgroundImage();
+                int hSelection = hBar.getSelection();
+                int destX = -hSelection - origin.x;
+                Rectangle rect = image.getBounds();
+                canvas.scroll(destX, 0, 0, 0, rect.width, rect.height, false);
+                origin.x = -hSelection;
+            }
+        });
+        final ScrollBar vBar = canvas.getVerticalBar();
+        vBar.addListener(SWT.Selection, new Listener() {
+            @Override
+            public void handleEvent(Event e) {
+                if (null == canvas.getBackgroundImage())
+                    return;
+                Image image = canvas.getBackgroundImage();
+                int vSelection = vBar.getSelection();
+                int destY = -vSelection - origin.y;
+                Rectangle rect = image.getBounds();
+                canvas.scroll(0, destY, 0, 0, rect.width, rect.height, false);
+                origin.y = -vSelection;
+            }
+        });
+        canvas.addListener(SWT.Resize, new Listener() {
+            @Override
+            public void handleEvent(Event e) {
+                if (null == canvas.getBackgroundImage())
+                    return;
+                Image image = canvas.getBackgroundImage();
+                Rectangle rect = image.getBounds();
+                Rectangle client = canvas.getClientArea();
+                hBar.setMaximum(rect.width);
+                vBar.setMaximum(rect.height);
+                hBar.setThumb(Math.min(rect.width, client.width));
+                vBar.setThumb(Math.min(rect.height, client.height));
+                int hPage = rect.width - client.width;
+                int vPage = rect.height - client.height;
+                int hSelection = hBar.getSelection();
+                int vSelection = vBar.getSelection();
+                if (hSelection >= hPage) {
+                    if (hPage <= 0)
+                        hSelection = 0;
+                    origin.x = -hSelection;
+                }
+                if (vSelection >= vPage) {
+                    if (vPage <= 0)
+                        vSelection = 0;
+                    origin.y = -vSelection;
+                }
+                canvas.redraw();
+            }
+        });
+        canvas.addListener(SWT.Paint, new Listener() {
+            @Override
+            public void handleEvent(Event e) {
+                if (null == canvas.getBackgroundImage())
+                    return;
+                Image image = canvas.getBackgroundImage();
+                GC gc = e.gc;
+                gc.drawImage(image, origin.x, origin.y);
+                Rectangle rect = image.getBounds();
+                Rectangle client = canvas.getClientArea();
+                int marginWidth = client.width - rect.width;
+                if (marginWidth > 0) {
+                    gc.fillRectangle(rect.width, 0, marginWidth, client.height);
+                }
+                int marginHeight = client.height - rect.height;
+                if (marginHeight > 0) {
+                    gc.fillRectangle(0, rect.height, client.width, marginHeight);
+                }
+            }
+        });
+
+        hookContextMenu();
+        hookSelectionChanged();
+        contributeToActionBars();
+
+        messageQueue = new MessageQueue(this, new ProcessMessage[] {
+                breakpointOption, shaderEditor
+        });
+    }
+
+    private void hookContextMenu() {
+        MenuManager menuMgr = new MenuManager("#PopupMenu");
+        menuMgr.setRemoveAllWhenShown(true);
+        menuMgr.addMenuListener(new IMenuListener() {
+            @Override
+            public void menuAboutToShow(IMenuManager manager) {
+                SampleView.this.fillContextMenu(manager);
+            }
+        });
+        Menu menu = menuMgr.createContextMenu(viewer.getControl());
+        viewer.getControl().setMenu(menu);
+        getSite().registerContextMenu(menuMgr, viewer);
+    }
+
+    private void contributeToActionBars() {
+        IActionBars bars = getViewSite().getActionBars();
+        fillLocalPullDown(bars.getMenuManager());
+        fillLocalToolBar(bars.getToolBarManager());
+    }
+
+    private void fillLocalPullDown(IMenuManager manager) {
+        // manager.add(actionConnect);
+        // manager.add(new Separator());
+        // manager.add(actionDisconnect);
+    }
+
+    private void fillContextMenu(IMenuManager manager) {
+        // manager.add(actionConnect);
+        // manager.add(actionDisconnect);
+        // Other plug-ins can contribute there actions here
+        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+    }
+
+    private void fillLocalToolBar(final IToolBarManager manager) {
+        actionConnect = new Action("Connect", Action.AS_PUSH_BUTTON) {
+            @Override
+            public void run() {
+                if (!running)
+                    changeContext(null); // viewer will switch to newest context
+                connectDisconnect();
+            }
+        };
+        manager.add(actionConnect);
+
+        manager.add(new Action("Open File", Action.AS_PUSH_BUTTON)
+        {
+            @Override
+            public void run()
+            {
+                if (!running)
+                {
+                    changeContext(null); // viewer will switch to newest context
+                    openFile();
+                }
+            }
+        });
+
+        final Shell shell = this.getViewSite().getShell();
+        actionAutoScroll = new Action("Auto Scroll", Action.AS_CHECK_BOX) {
+            @Override
+            public void run() {
+            }
+        };
+        actionAutoScroll.setChecked(true);
+        manager.add(actionAutoScroll);
+
+        actionFilter = new Action("*", Action.AS_DROP_DOWN_MENU) {
+            @Override
+            public void run() {
+                org.eclipse.jface.dialogs.InputDialog dialog = new org.eclipse.jface.dialogs.InputDialog(
+                        shell, "Contains Filter",
+                        "case sensitive substring or *",
+                        actionFilter.getText(), null);
+                if (Window.OK == dialog.open()) {
+                    actionFilter.setText(dialog.getValue());
+                    manager.update(true);
+                    filters = dialog.getValue().split("\\|");
+                    if (filters.length == 1 && filters[0].equals("*"))
+                        filters = null;
+                    viewer.refresh();
+                }
+
+            }
+        };
+        manager.add(actionFilter);
+
+        manager.add(new Action("CaptureDraw", Action.AS_DROP_DOWN_MENU)
+        {
+            @Override
+            public void run()
+            {
+                int contextId = 0;
+                if (current != null)
+                    contextId = current.contextId;
+                InputDialog inputDialog = new InputDialog(shell,
+                        "Capture glDrawArrays/Elements",
+                        "Enter number of glDrawArrays/Elements to glReadPixels for "
+                                + "context 0x" + Integer.toHexString(contextId) +
+                                "\n(0x0 is any context)", "9001", null);
+                if (inputDialog.open() != Window.OK)
+                    return;
+                Message.Builder builder = Message.newBuilder();
+                builder.setContextId(contextId);
+                builder.setType(Type.Response);
+                builder.setExpectResponse(false);
+                builder.setFunction(Function.SETPROP);
+                builder.setProp(Prop.CaptureDraw);
+                builder.setArg0(Integer.parseInt(inputDialog.getValue()));
+                messageQueue.addCommand(builder.build());
+            }
+        });
+
+        manager.add(new Action("CaptureSwap", Action.AS_DROP_DOWN_MENU)
+        {
+            @Override
+            public void run()
+            {
+                int contextId = 0;
+                if (current != null)
+                    contextId = current.contextId;
+                InputDialog inputDialog = new InputDialog(shell,
+                        "Capture eglSwapBuffers",
+                        "Enter number of eglSwapBuffers to glReadPixels for "
+                                + "context 0x" + Integer.toHexString(contextId) +
+                                "\n(0x0 is any context)", "9001", null);
+                if (inputDialog.open() != Window.OK)
+                    return;
+                Message.Builder builder = Message.newBuilder();
+                builder.setContextId(contextId);
+                builder.setType(Type.Response);
+                builder.setExpectResponse(false);
+                builder.setFunction(Function.SETPROP);
+                builder.setProp(Prop.CaptureSwap);
+                builder.setArg0(Integer.parseInt(inputDialog.getValue()));
+                messageQueue.addCommand(builder.build());
+            }
+        });
+
+        manager.add(new Action("SYSTEM_TIME_THREAD", Action.AS_DROP_DOWN_MENU)
+        {
+            @Override
+            public void run()
+            {
+                final String[] timeModes = {
+                        "SYSTEM_TIME_REALTIME", "SYSTEM_TIME_MONOTONIC", "SYSTEM_TIME_PROCESS",
+                        "SYSTEM_TIME_THREAD"
+                };
+                int i = java.util.Arrays.asList(timeModes).indexOf(this.getText());
+                i = (i + 1) % timeModes.length;
+                Message.Builder builder = Message.newBuilder();
+                builder.setContextId(0); // FIXME: proper context id
+                builder.setType(Type.Response);
+                builder.setExpectResponse(false);
+                builder.setFunction(Message.Function.SETPROP);
+                builder.setProp(Prop.TimeMode);
+                builder.setArg0(i);
+                messageQueue.addCommand(builder.build());
+                this.setText(timeModes[i]);
+                manager.update(true);
+            }
+        });
+
+        actContext = new Action("Context: 0x", Action.AS_DROP_DOWN_MENU) {
+            @Override
+            public void run() {
+                if (debugContexts.size() < 2)
+                    return;
+                final String idStr = this.getText().substring(
+                                          "Context: 0x".length());
+                if (idStr.length() == 0)
+                    return;
+                final int contextId = Integer.parseInt(idStr, 16);
+                int index = debugContexts.indexOfKey(contextId);
+                index = (index + 1) % debugContexts.size();
+                changeContext(debugContexts.valueAt(index));
+            }
+        };
+        manager.add(actContext);
+
+        actionPort = new Action("5039", Action.AS_DROP_DOWN_MENU)
+        {
+            @Override
+            public void run() {
+                InputDialog dialog = new InputDialog(shell, "Port", "Debugger port",
+                        actionPort.getText(), null);
+                if (Window.OK == dialog.open()) {
+                    actionPort.setText(dialog.getValue());
+                    manager.update(true);
+                }
+            }
+        };
+        manager.add(actionPort);
+
+        manager.add(new Action("CodeGen Frame", Action.AS_PUSH_BUTTON)
+        {
+            @Override
+            public void run()
+            {
+                if (current != null)
+                {
+                    new CodeGen().codeGenFrame((Frame) viewer.getInput());
+                    // need to reload current frame
+                    viewer.setInput(current.getFrame(frameNum.getSelection()));
+                }
+            }
+        });
+
+        manager.add(new Action("CodeGen Frames", Action.AS_PUSH_BUTTON)
+        {
+            @Override
+            public void run()
+            {
+                if (current != null)
+                {
+                    new CodeGen().codeGenFrames(current, frameNum.getSelection() + 1,
+                            getSite().getShell());
+                    // need to reload current frame
+                    viewer.setInput(current.getFrame(frameNum.getSelection()));
+                }
+            }
+        });
+    }
+
+    private void openFile() {
+        FileDialog dialog = new FileDialog(getSite().getShell(), SWT.OPEN);
+        dialog.setText("Open");
+        dialog.setFilterExtensions(new String[] {
+                "*.gles2dbg"
+        });
+        String filePath = dialog.open();
+        if (filePath == null)
+            return;
+        FileInputStream file = null;
+        try {
+            file = new FileInputStream(filePath);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+            return;
+        }
+        running = true;
+        messageQueue.start(targetByteOrder, file);
+        thread = new Thread(this);
+        thread.start();
+        actionConnect.setText("Disconnect");
+        getViewSite().getActionBars().getToolBarManager().update(true);
+    }
+
+    private void connectDisconnect() {
+        if (!running) {
+            running = true;
+            messageQueue.start(targetByteOrder, null);
+            thread = new Thread(this);
+            thread.start();
+            actionConnect.setText("Disconnect");
+        } else {
+            running = false;
+            messageQueue.stop();
+            actionConnect.setText("Connect");
+        }
+        this.getSite().getShell().getDisplay().syncExec(new Runnable() {
+            @Override
+            public void run() {
+                getViewSite().getActionBars().getToolBarManager().update(true);
+            }
+        });
+    }
+
+    void messageDataSelected(final MessageData msgData) {
+        if (null == msgData)
+            return;
+        if (frameNum.getSelection() == frameNum.getMaximum())
+            return; // scale max cannot overlap min, so max is array size
+        final Frame frame = current.getFrame(frameNum.getSelection());
+        final Context context = frame.computeContext(msgData);
+        contextViewer.setInput(context);
+        if (msgData.getImage() != null) {
+            canvas.setBackgroundImage(msgData.getImage());
+            tabFolder.setSelection(tabItemImage);
+            canvas.redraw();
+        } else if (null != msgData.shader) {
+            text.setText(msgData.shader);
+            tabFolder.setSelection(tabItemText);
+        } else if (null != msgData.attribs) {
+            StringBuilder builder = new StringBuilder();
+            final int maxAttrib = msgData.msg.getArg7();
+            for (int i = 0; i < msgData.attribs[0].length / 4; i++) {
+                if (msgData.indices != null) {
+                    builder.append(msgData.indices[i] & 0xffff);
+                    builder.append(": ");
+                }
+                for (int j = 0; j < maxAttrib; j++) {
+                    for (int k = 0; k < 4; k++)
+                        builder.append(String.format("%.3g ", msgData.attribs[j][i * 4 + k]));
+                    if (j < maxAttrib - 1)
+                        builder.append("|| ");
+                }
+                builder.append('\n');
+            }
+            text.setText(builder.toString());
+            tabFolder.setSelection(tabItemText);
+        }
+    }
+
+    private void hookSelectionChanged() {
+        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+            @Override
+            public void selectionChanged(SelectionChangedEvent event) {
+                StructuredSelection selection = (StructuredSelection) event
+                        .getSelection();
+                if (null == selection)
+                    return;
+                MessageData msgData = (MessageData) selection.getFirstElement();
+                messageDataSelected(msgData);
+            }
+        });
+    }
+
+    public void showError(final Exception e) {
+        viewer.getControl().getDisplay().syncExec(new Runnable() {
+            @Override
+            public void run() {
+                MessageDialog.openError(viewer.getControl().getShell(),
+                        "GL ES 2.0 Debugger Client", e.getMessage());
+            }
+        });
+    }
+
+    /**
+     * Passing the focus request to the viewer's control.
+     */
+    @Override
+    public void setFocus() {
+        viewer.getControl().setFocus();
+    }
+
+    @Override
+    public void run() {
+        int newMessages = 0;
+
+        boolean shaderEditorUpdate = false;
+        while (running) {
+            final Message oriMsg = messageQueue.removeCompleteMessage(0);
+            if (oriMsg == null && !messageQueue.isRunning())
+                break;
+            if (newMessages > 60 || (newMessages > 0 && null == oriMsg)) {
+                newMessages = 0;
+                if (current != null && current.uiUpdate)
+                    getSite().getShell().getDisplay().syncExec(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (frameNum.getSelection() == current.frameCount() - 1 ||
+                                    frameNum.getSelection() == current.frameCount() - 2)
+                            {
+                                viewer.refresh(false);
+                                if (actionAutoScroll.isChecked())
+                                    viewer.getList().setSelection(
+                                            viewer.getList().getItemCount() - 1);
+                            }
+                            frameNum.setMaximum(current.frameCount());
+                        }
+                    });
+                current.uiUpdate = false;
+
+                if (shaderEditorUpdate)
+                    this.getSite().getShell().getDisplay().syncExec(new Runnable() {
+                        @Override
+                        public void run() {
+                            shaderEditor.updateUI();
+                        }
+                    });
+                shaderEditorUpdate = false;
+            }
+            if (null == oriMsg) {
+                try {
+                    Thread.sleep(1);
+                    continue;
+                } catch (InterruptedException e) {
+                    showError(e);
+                }
+            }
+            DebugContext debugContext = debugContexts.get(oriMsg.getContextId());
+            if (debugContext == null) {
+                debugContext = new DebugContext(oriMsg.getContextId());
+                debugContexts.put(oriMsg.getContextId(), debugContext);
+            }
+            debugContext.processMessage(oriMsg);
+            shaderEditorUpdate |= debugContext.currentContext.serverShader.uiUpdate;
+            debugContext.currentContext.serverShader.uiUpdate = false;
+            if (current == null && debugContext.frameCount() > 0)
+                changeContext(debugContext);
+            newMessages++;
+        }
+        if (running)
+            connectDisconnect(); // error occurred, disconnect
+    }
+
+    /** can be called from non-UI thread */
+    void changeContext(final DebugContext newContext) {
+        getSite().getShell().getDisplay().syncExec(new Runnable() {
+            @Override
+            public void run() {
+                current = newContext;
+                if (current != null)
+                {
+                    frameNum.setMaximum(current.frameCount());
+                    if (frameNum.getSelection() >= current.frameCount())
+                        if (current.frameCount() > 0)
+                            frameNum.setSelection(current.frameCount() - 1);
+                        else
+                            frameNum.setSelection(0);
+                    viewer.setInput(current.getFrame(frameNum.getSelection()));
+                    actContext.setText("Context: 0x" + Integer.toHexString(current.contextId));
+                }
+                else
+                {
+                    frameNum.setMaximum(1); // cannot overlap min
+                    frameNum.setSelection(0);
+                    viewer.setInput(null);
+                    actContext.setText("Context: 0x");
+                }
+                shaderEditor.updateUI();
+                getViewSite().getActionBars().getToolBarManager().update(true);
+            }
+        });
+    }
+
+    @Override
+    public void widgetSelected(SelectionEvent e) {
+        if (e.widget != frameNum)
+            assert false;
+        if (current == null)
+            return;
+        if (frameNum.getSelection() == current.frameCount())
+            return; // scale maximum cannot overlap minimum
+        Frame frame = current.getFrame(frameNum.getSelection());
+        viewer.setInput(frame);
+    }
+
+    @Override
+    public void widgetDefaultSelected(SelectionEvent e) {
+        widgetSelected(e);
+    }
+}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java b/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java
new file mode 100644
index 0000000..c125143
--- /dev/null
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java
@@ -0,0 +1,407 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Type;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ExtendedModifyEvent;
+import org.eclipse.swt.custom.ExtendedModifyListener;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class ShaderEditor extends Composite implements SelectionListener, ExtendedModifyListener,
+        ProcessMessage {
+    SampleView sampleView;
+
+    ToolBar toolbar;
+    ToolItem uploadShader, restoreShader, currentPrograms;
+    List list;
+    StyledText styledText;
+
+    GLShader current;
+
+    ArrayList<GLShader> shadersToUpload = new ArrayList<GLShader>();
+
+    ShaderEditor(SampleView sampleView, Composite parent) {
+        super(parent, 0);
+        this.sampleView = sampleView;
+
+        GridLayout gridLayout = new GridLayout();
+        gridLayout.numColumns = 1;
+        this.setLayout(gridLayout);
+
+        toolbar = new ToolBar(this, SWT.BORDER);
+
+        uploadShader = new ToolItem(toolbar, SWT.PUSH);
+        uploadShader.setText("Upload Shader");
+        uploadShader.addSelectionListener(this);
+
+        restoreShader = new ToolItem(toolbar, SWT.PUSH);
+        restoreShader.setText("Original Shader");
+        restoreShader.addSelectionListener(this);
+
+        currentPrograms = new ToolItem(toolbar, SWT.PUSH);
+        currentPrograms.setText("Current Programs: ");
+
+        list = new List(this, SWT.V_SCROLL);
+        list.setFont(new Font(parent.getDisplay(), "Courier", 10, 0));
+        list.addSelectionListener(this);
+        GridData gridData = new GridData();
+        gridData.horizontalAlignment = SWT.FILL;
+        gridData.grabExcessHorizontalSpace = true;
+        gridData.verticalAlignment = SWT.FILL;
+        gridData.grabExcessVerticalSpace = true;
+        list.setLayoutData(gridData);
+
+        styledText = new StyledText(this, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI);
+        gridData = new GridData();
+        gridData.horizontalAlignment = SWT.FILL;
+        gridData.grabExcessHorizontalSpace = true;
+        gridData.verticalAlignment = SWT.FILL;
+        gridData.grabExcessVerticalSpace = true;
+        gridData.verticalSpan = 2;
+        styledText.setLayoutData(gridData);
+        styledText.addExtendedModifyListener(this);
+    }
+
+    public void updateUI() {
+        list.removeAll();
+        String progs = "Current Programs: ";
+        for (int j = 0; j < sampleView.debugContexts.size(); j++) {
+            final Context context = sampleView.debugContexts.valueAt(j).currentContext;
+
+            if (context.serverShader.current != null) {
+                progs += context.serverShader.current.name + "(0x";
+                progs += Integer.toHexString(context.contextId) + ") ";
+            }
+            for (int i = 0; i < context.serverShader.shaders.size(); i++) {
+                GLShader shader = context.serverShader.shaders.valueAt(i);
+                StringBuilder builder = new StringBuilder();
+                builder.append(String.format("%08X", context.contextId));
+                builder.append(' ');
+                builder.append(shader.type);
+                while (builder.length() < 30)
+                    builder.append(" ");
+                builder.append(shader.name);
+                while (builder.length() < 40)
+                    builder.append(" ");
+                builder.append(" : ");
+                for (Context ctx : context.shares) {
+                    builder.append(String.format("%08X", ctx.contextId));
+                    builder.append(' ');
+                }
+                builder.append(": ");
+                for (int program : shader.programs) {
+                    builder.append(program);
+                    builder.append(" ");
+                }
+                list.add(builder.toString());
+            }
+
+        }
+
+        currentPrograms.setText(progs);
+        toolbar.redraw();
+        toolbar.pack(true);
+        toolbar.update();
+    }
+
+    void uploadShader() {
+        current.source = styledText.getText();
+
+        // optional syntax check by glsl_compiler, built from external/mesa3d
+        if (new File("./glsl_compiler").exists())
+            try {
+                File file = File.createTempFile("shader",
+                        current.type == GLEnum.GL_VERTEX_SHADER ? ".vert" : ".frag");
+                FileWriter fileWriter = new FileWriter(file, false);
+                fileWriter.write(current.source);
+                fileWriter.close();
+
+                ProcessBuilder processBuilder = new ProcessBuilder(
+                        "./glsl_compiler", "--glsl-es", file.getAbsolutePath());
+                final Process process = processBuilder.start();
+                InputStream is = process.getInputStream();
+                InputStreamReader isr = new InputStreamReader(is);
+                BufferedReader br = new BufferedReader(isr);
+                String line;
+                String infolog = "";
+
+                styledText.setLineBackground(0, styledText.getLineCount(), null);
+
+                while ((line = br.readLine()) != null) {
+                    infolog += line;
+                    if (!line.startsWith("0:"))
+                        continue;
+                    String[] details = line.split(":|\\(|\\)");
+                    final int ln = Integer.parseInt(details[1]);
+                    if (ln > 0) // usually line 0 means errors other than syntax
+                        styledText.setLineBackground(ln - 1, 1,
+                                new Color(Display.getCurrent(), 255, 230, 230));
+                }
+                file.delete();
+                if (infolog.length() > 0) {
+                    if (!MessageDialog.openConfirm(getShell(),
+                            "Shader Syntax Error, Continue?", infolog))
+                        return;
+                }
+            } catch (IOException e) {
+                sampleView.showError(e);
+            }
+
+        // add the initial command, which when read by server will set
+        // expectResponse for the message loop and go into message exchange
+        synchronized (shadersToUpload) {
+            for (GLShader shader : shadersToUpload) {
+                if (shader.context.context.contextId != current.context.context.contextId)
+                    continue;
+                MessageDialog.openWarning(this.getShell(), "Context 0x" +
+                        Integer.toHexString(current.context.context.contextId),
+                        "Previous shader upload not complete, try again");
+                return;
+            }
+            shadersToUpload.add(current);
+            final int contextId = current.context.context.contextId;
+            Message.Builder builder = getBuilder(contextId);
+            MessageParserEx.instance.parse(builder,
+                    String.format("glShaderSource(%d,1,\"%s\",0)", current.name, current.source));
+            sampleView.messageQueue.addCommand(builder.build());
+        }
+    }
+
+    Message.Builder getBuilder(int contextId) {
+        Message.Builder builder = Message.newBuilder();
+        builder.setContextId(contextId);
+        builder.setType(Type.Response);
+        builder.setExpectResponse(true);
+        return builder;
+    }
+
+    Message exchangeMessage(final int contextId, final MessageQueue queue,
+            String format, Object... args) throws IOException {
+        Message.Builder builder = getBuilder(contextId);
+        MessageParserEx.instance.parse(builder, String.format(format, args));
+        final Function function = builder.getFunction();
+        queue.sendMessage(builder.build());
+        final Message msg = queue.receiveMessage(contextId);
+        assert msg.getContextId() == contextId;
+        assert msg.getType() == Type.AfterGeneratedCall;
+        assert msg.getFunction() == function;
+        return msg;
+    }
+
+    // this is called from network thread
+    public boolean processMessage(final MessageQueue queue, final Message msg)
+            throws IOException {
+        GLShader shader = null;
+        final int contextId = msg.getContextId();
+        synchronized (shadersToUpload) {
+            if (shadersToUpload.size() == 0)
+                return false;
+            boolean matchingContext = false;
+            for (int i = 0; i < shadersToUpload.size(); i++) {
+                shader = shadersToUpload.get(i);
+                for (Context ctx : shader.context.context.shares)
+                    if (ctx.contextId == contextId) {
+                        matchingContext = true;
+                        break;
+                    }
+                if (matchingContext) {
+                    shadersToUpload.remove(i);
+                    break;
+                }
+            }
+            if (!matchingContext)
+                return false;
+        }
+
+        // glShaderSource was already sent to trigger set expectResponse
+        assert msg.getType() == Type.AfterGeneratedCall;
+        assert msg.getFunction() == Function.glShaderSource;
+
+        exchangeMessage(contextId, queue, "glCompileShader(%d)", shader.name);
+
+        // the 0, "" and {0} are dummies for the parser
+        Message rcv = exchangeMessage(contextId, queue,
+                "glGetShaderiv(%d, GL_COMPILE_STATUS, {0})", shader.name);
+        assert rcv.hasData();
+        if (rcv.getData().asReadOnlyByteBuffer().getInt() == 0) {
+            // compile failed
+            rcv = exchangeMessage(contextId, queue,
+                    "glGetShaderInfoLog(%d, 0, 0, \"\")", shader.name);
+            final String title = String.format("Shader %d in 0x%s failed to compile",
+                    shader.name, Integer.toHexString(shader.context.context.contextId));
+            final String message = rcv.getData().toStringUtf8();
+            sampleView.getSite().getShell().getDisplay().syncExec(new Runnable() {
+                @Override
+                public void run()
+                {
+                    MessageDialog.openWarning(getShell(), title, message);
+                }
+            });
+        } else
+            for (int programName : shader.programs) {
+                GLProgram program = shader.context.getProgram(programName);
+                exchangeMessage(contextId, queue, "glLinkProgram(%d)", program.name);
+                rcv = exchangeMessage(contextId, queue,
+                        "glGetProgramiv(%d, GL_LINK_STATUS, {0})", program.name);
+                assert rcv.hasData();
+                if (rcv.getData().asReadOnlyByteBuffer().getInt() != 0)
+                    continue;
+                // link failed
+                rcv = exchangeMessage(contextId, queue,
+                            "glGetProgramInfoLog(%d, 0, 0, \"\")", program.name);
+                final String title = String.format("Program %d in 0x%s failed to link",
+                        program.name, Integer.toHexString(program.context.context.contextId));
+                final String message = rcv.getData().toStringUtf8();
+                sampleView.getSite().getShell().getDisplay().syncExec(new Runnable() {
+                    @Override
+                    public void run()
+                    {
+                        MessageDialog.openWarning(getShell(), title, message);
+                    }
+                });
+                // break;
+            }
+
+        // TODO: add to upload results if failed
+
+        Message.Builder builder = getBuilder(contextId);
+        builder.setExpectResponse(false);
+        if (queue.getPartialMessage(contextId) != null)
+            // the glShaderSource interrupted a BeforeCall, so continue
+            builder.setFunction(Function.CONTINUE);
+        else
+            builder.setFunction(Function.SKIP);
+        queue.sendMessage(builder.build());
+
+        return true;
+    }
+
+    @Override
+    public void widgetSelected(SelectionEvent e) {
+        if (e.getSource() == uploadShader && null != current) {
+            uploadShader();
+            return;
+        } else if (e.getSource() == restoreShader && null != current) {
+            current.source = styledText.getText();
+            styledText.setText(current.originalSource);
+            return;
+        }
+
+        if (list.getSelectionCount() < 1)
+            return;
+        if (null != current && !current.source.equals(styledText.getText())) {
+            String[] btns = {
+                    "&Upload", "&Save", "&Discard"
+            };
+            MessageDialog dialog = new MessageDialog(this.getShell(), "Shader Edited",
+                    null, "Shader source has been edited", MessageDialog.QUESTION, btns, 0);
+            int rc = dialog.open();
+            if (rc == SWT.DEFAULT || rc == 0)
+                uploadShader();
+            else if (rc == 1)
+                current.source = styledText.getText();
+            // else if (rc == 2) do nothing; selection is changing
+        }
+        String[] details = list.getSelection()[0].split("\\s+");
+        final int contextId = Integer.parseInt(details[0], 16);
+        int name = Integer.parseInt(details[2]);
+        current = sampleView.debugContexts.get(contextId).currentContext.serverShader.shaders
+                .get(name);
+        styledText.setText(current.source);
+    }
+
+    @Override
+    public void widgetDefaultSelected(SelectionEvent e) {
+        widgetSelected(e);
+    }
+
+    @Override
+    public void modifyText(ExtendedModifyEvent event) {
+        final String[] keywords = {
+                "gl_Position", "gl_FragColor"
+        };
+        // FIXME: proper scanner for syntax highlighting
+        String text = styledText.getText();
+        int start = event.start;
+        int end = event.start + event.length;
+        start -= 20; // deleting chars from keyword causes rescan
+        end += 20;
+        if (start < 0)
+            start = 0;
+        if (end > text.length())
+            end = text.length();
+        if (null != styledText.getStyleRangeAtOffset(event.start)) {
+            StyleRange clearStyleRange = new StyleRange();
+            clearStyleRange.start = start;
+            clearStyleRange.length = end - start;
+            clearStyleRange.foreground = event.display.getSystemColor(SWT.COLOR_BLACK);
+            styledText.setStyleRange(clearStyleRange);
+        }
+
+        while (start < end) {
+            for (final String keyword : keywords) {
+                if (!text.substring(start).startsWith(keyword))
+                    continue;
+                if (start > 0) {
+                    final char before = text.charAt(start - 1);
+                    if (Character.isLetterOrDigit(before))
+                        continue;
+                    else if (before == '_')
+                        continue;
+                }
+                if (start + keyword.length() < text.length()) {
+                    final char after = text.charAt(start + keyword.length());
+                    if (Character.isLetterOrDigit(after))
+                        continue;
+                    else if (after == '_')
+                        continue;
+                }
+                StyleRange style1 = new StyleRange();
+                style1.start = start;
+                style1.length = keyword.length();
+                style1.foreground = event.display.getSystemColor(SWT.COLOR_BLUE);
+                styledText.setStyleRange(style1);
+            }
+            start++;
+        }
+    }
+}
diff --git a/tools/glesv2debugger/test/com/android/glesv2debugger/MessageParserExTest.java b/tools/glesv2debugger/test/com/android/glesv2debugger/MessageParserExTest.java
new file mode 100644
index 0000000..d2a9a7e
--- /dev/null
+++ b/tools/glesv2debugger/test/com/android/glesv2debugger/MessageParserExTest.java
@@ -0,0 +1,115 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Type;
+import com.google.protobuf.ByteString;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+public class MessageParserExTest {
+    /**
+     * @throws java.lang.Exception
+     */
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    @Test
+    public void testParseFloats() {
+        final MessageParserEx parser = new MessageParserEx();
+        final String args = "{0, 1    ,2,3  }";
+        parser.args = args;
+        final ByteBuffer data = parser.parseFloats(4).asReadOnlyByteBuffer();
+        data.order(SampleView.targetByteOrder);
+        for (int i = 0; i < 4; i++)
+            assertEquals(i, data.getFloat(), 0);
+    }
+
+    @Test
+    public void testParseArgument() {
+        final MessageParserEx parser = new MessageParserEx();
+        final String args = "sdfa   =  GL_VERTEX_SHADER , -5421 ,0x443=0x54f";
+        parser.args = args;
+        assertEquals(GLEnum.GL_VERTEX_SHADER.value, parser.parseArgument());
+        assertEquals(-5421, parser.parseArgument());
+        assertEquals(0x54f, parser.parseArgument());
+    }
+
+    /**
+     * Test method for
+     * {@link com.android.glesv2debugger.MessageParserEx#parse_glShaderSource(com.android.glesv2debugger.DebuggerMessage.Message.Builder)}
+     * .
+     */
+    @Test
+    public void testParse_glShaderSource() {
+        final Message.Builder builder = Message.newBuilder();
+        final MessageParserEx messageParserEx = new MessageParserEx();
+        final String source = "dks \n jafhskjaho { urehg ; } hskjg";
+        messageParserEx.parse(builder, "void glShaderSource ( shader=4, count= 1, "
+                                + "string =\"" + source + "\"  , 0x0)");
+        assertEquals(Function.glShaderSource, builder.getFunction());
+        assertEquals(4, builder.getArg0());
+        assertEquals(1, builder.getArg1());
+        assertEquals(source, builder.getData().toStringUtf8());
+        assertEquals(0, builder.getArg3());
+    }
+
+    @Test
+    public void testParse_glBlendEquation() {
+        assertNotNull(MessageParserEx.instance);
+        final Message.Builder builder = Message.newBuilder();
+        MessageParserEx.instance.parse(builder, "void glBlendEquation ( mode= GL_ADD ) ; ");
+        assertEquals(Function.glBlendEquation, builder.getFunction());
+        assertEquals(GLEnum.GL_ADD.value, builder.getArg0());
+    }
+
+    /** loopback testing of typical generated MessageFormatter and MessageParser */
+    @Test
+    public void testParseFormatterMessage() {
+        final ByteBuffer srcData = ByteBuffer.allocate(4 * 2 * 4);
+        srcData.order(SampleView.targetByteOrder);
+        for (int i = 0; i < 4 * 2; i++)
+            srcData.putFloat(i);
+        srcData.rewind();
+        Message.Builder builder = Message.newBuilder();
+        builder.setContextId(3752).setExpectResponse(false).setType(Type.CompleteCall);
+        builder.setFunction(Function.glUniformMatrix2fv);
+        builder.setArg0(54).setArg1(2).setArg2(0).setData(ByteString.copyFrom(srcData));
+        Message msg = builder.build();
+        builder = msg.toBuilder();
+        String formatted = MessageFormatter.format(msg, false);
+        formatted = formatted.substring(0, formatted.indexOf('(')) + ' ' + builder.getFunction() +
+                formatted.substring(formatted.indexOf('('));
+        Message.Builder parsed = Message.newBuilder();
+        MessageParserEx.instance.parse(parsed, formatted);
+        assertEquals(builder.getFunction(), parsed.getFunction());
+        assertEquals(builder.getArg0(), parsed.getArg0());
+        assertEquals(builder.getArg1(), parsed.getArg1());
+        assertEquals(builder.getArg2(), parsed.getArg2());
+        assertEquals(builder.getData().toStringUtf8(), parsed.getData().toStringUtf8());
+    }
+
+}
diff --git a/tools/glesv2debugger/test/com/android/glesv2debugger/MessageQueueTest.java b/tools/glesv2debugger/test/com/android/glesv2debugger/MessageQueueTest.java
new file mode 100644
index 0000000..5f8e93d
--- /dev/null
+++ b/tools/glesv2debugger/test/com/android/glesv2debugger/MessageQueueTest.java
@@ -0,0 +1,183 @@
+/*
+ ** Copyright 2011, 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.glesv2debugger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.android.glesv2debugger.DebuggerMessage.Message;
+import com.android.glesv2debugger.DebuggerMessage.Message.Function;
+import com.android.glesv2debugger.DebuggerMessage.Message.Type;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteOrder;
+
+public class MessageQueueTest {
+    private MessageQueue queue;
+
+    /**
+     * @throws java.lang.Exception
+     */
+    @Before
+    public void setUp() throws Exception {
+        queue = new MessageQueue(null, new ProcessMessage[0]);
+    }
+
+    /**
+     * Test method for
+     * {@link com.android.glesv2debugger.MessageQueue#defaultProcessMessage(com.android.glesv2debugger.DebuggerMessage.Message, boolean, boolean)}
+     * .
+     * 
+     * @throws IOException
+     */
+    @Test
+    public void testDefaultProcessMessage() throws IOException {
+        final int contextId = 8784;
+        assertNull(queue.getPartialMessage(contextId));
+        Message.Builder builder = Message.newBuilder();
+        builder.setContextId(contextId);
+        builder.setExpectResponse(false);
+        builder.setFunction(Function.glFinish);
+        builder.setType(Type.BeforeCall);
+        Message msg = builder.build();
+        queue.defaultProcessMessage(msg, false, false);
+        assertNotNull(queue.getPartialMessage(contextId));
+
+        builder = msg.toBuilder();
+        builder.setType(Type.AfterCall);
+        builder.setTime(5);
+        msg = builder.build();
+        queue.defaultProcessMessage(msg, false, false);
+        assertNull(queue.getPartialMessage(contextId));
+        Message complete = queue.removeCompleteMessage(contextId);
+        assertNotNull(complete);
+        assertEquals(contextId, complete.getContextId());
+        assertEquals(msg.getFunction(), complete.getFunction());
+        assertEquals(msg.getTime(), complete.getTime(), 0);
+        assertEquals(Type.CompleteCall, complete.getType());
+
+        // an already complete message should just be added to complete queue
+        queue.defaultProcessMessage(complete, false, false);
+        assertNull(queue.getPartialMessage(contextId));
+        complete = queue.removeCompleteMessage(contextId);
+        assertNotNull(complete);
+        assertEquals(contextId, complete.getContextId());
+        assertEquals(msg.getFunction(), complete.getFunction());
+        assertEquals(msg.getTime(), complete.getTime(), 0);
+        assertEquals(Type.CompleteCall, complete.getType());
+    }
+
+    @Test
+    public void testCompletePartialMessage() throws IOException {
+        final int contextId = 8784;
+        assertNull(queue.getPartialMessage(contextId));
+        Message.Builder builder = Message.newBuilder();
+        builder.setContextId(contextId);
+        builder.setExpectResponse(false);
+        builder.setFunction(Function.glFinish);
+        builder.setType(Type.BeforeCall);
+        Message msg = builder.build();
+        queue.defaultProcessMessage(msg, false, false);
+        assertNotNull(queue.getPartialMessage(contextId));
+        queue.completePartialMessage(contextId);
+
+        final Message complete = queue.removeCompleteMessage(contextId);
+        assertNotNull(complete);
+        assertEquals(contextId, complete.getContextId());
+        assertEquals(msg.getFunction(), complete.getFunction());
+        assertEquals(msg.getTime(), complete.getTime(), 0);
+        assertEquals(Type.BeforeCall, complete.getType());
+    }
+
+    /** Write two messages from two contexts to file and test handling them */
+    @Test
+    public void testRunWithFile() throws FileNotFoundException, IOException, InterruptedException {
+        final File filePath = File.createTempFile("test", ".gles2dbg");
+        DataOutputStream file = new DataOutputStream(new FileOutputStream(filePath));
+        Message.Builder builder = Message.newBuilder();
+        final int contextId0 = 521643, contextId1 = 87634;
+        assertNull(queue.removeCompleteMessage(contextId0));
+        assertNull(queue.removeCompleteMessage(contextId1));
+
+        builder.setContextId(contextId0).setExpectResponse(false).setType(Type.BeforeCall);
+        builder.setFunction(Function.glClear).setArg0(contextId0);
+        Message msg0 = builder.build();
+        byte[] data = msg0.toByteArray();
+        file.writeInt(data.length);
+        file.write(data);
+
+        builder = Message.newBuilder();
+        builder.setContextId(contextId1).setExpectResponse(false).setType(Type.BeforeCall);
+        builder.setFunction(Function.glDisable).setArg0(contextId1);
+        Message msg1 = builder.build();
+        data = msg1.toByteArray();
+        file.writeInt(data.length);
+        file.write(data);
+
+        builder = Message.newBuilder();
+        msg0 = builder.setContextId(msg0.getContextId()).setExpectResponse(false)
+                .setType(Type.AfterCall).setFunction(msg0.getFunction()).setTime(2).build();
+        data = msg0.toByteArray();
+        file.writeInt(data.length);
+        file.write(data);
+
+        builder = Message.newBuilder();
+        msg1 = builder.setContextId(msg1.getContextId()).setExpectResponse(false)
+                .setType(Type.AfterCall).setFunction(msg1.getFunction()).setTime(465).build();
+        data = msg1.toByteArray();
+        file.writeInt(data.length);
+        file.write(data);
+
+        file.close();
+
+        FileInputStream fis = new FileInputStream(filePath);
+        // Java VM uses big endian, so the file was written in big endian
+        queue.start(ByteOrder.BIG_ENDIAN, fis);
+        queue.thread.join();
+
+        Message complete0 = queue.removeCompleteMessage(msg0.getContextId());
+        assertNotNull(complete0);
+        assertNull(queue.removeCompleteMessage(contextId0));
+        assertEquals(contextId0, complete0.getContextId());
+        assertEquals(false, complete0.getExpectResponse());
+        assertEquals(Type.CompleteCall, complete0.getType());
+        assertEquals(msg0.getFunction(), complete0.getFunction());
+        assertEquals(contextId0, complete0.getArg0());
+        assertEquals(msg0.getTime(), complete0.getTime(), 0);
+
+        Message complete1 = queue.removeCompleteMessage(msg1.getContextId());
+        assertNotNull(complete1);
+        assertNull(queue.removeCompleteMessage(contextId1));
+        assertEquals(contextId1, complete1.getContextId());
+        assertEquals(false, complete1.getExpectResponse());
+        assertEquals(Type.CompleteCall, complete1.getType());
+        assertEquals(msg1.getFunction(), complete1.getFunction());
+        assertEquals(contextId1, complete1.getArg0());
+        assertEquals(msg1.getTime(), complete1.getTime(), 0);
+
+        filePath.delete();
+    }
+}