Merge "Seperate RIL constants from the framework."
diff --git a/Android.mk b/Android.mk
index 5acfb86..79f6220 100644
--- a/Android.mk
+++ b/Android.mk
@@ -67,7 +67,6 @@
 	core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
 	core/java/android/app/IActivityController.aidl \
 	core/java/android/app/IActivityPendingResult.aidl \
-	core/java/android/app/IActivityWatcher.aidl \
 	core/java/android/app/IAlarmManager.aidl \
 	core/java/android/app/IBackupAgent.aidl \
 	core/java/android/app/IInstrumentationWatcher.aidl \
@@ -110,6 +109,7 @@
 	core/java/android/content/pm/IPackageMoveObserver.aidl \
 	core/java/android/content/pm/IPackageStatsObserver.aidl \
 	core/java/android/database/IContentObserver.aidl \
+	core/java/android/hardware/ISerialManager.aidl \
 	core/java/android/hardware/usb/IUsbManager.aidl \
 	core/java/android/net/IConnectivityManager.aidl \
 	core/java/android/net/INetworkManagementEventObserver.aidl \
diff --git a/api/16.txt b/api/16.txt
index 8e07844..be544de 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -15052,7 +15052,7 @@
     method public static final deprecated boolean supportsProcesses();
     field public static final int BLUETOOTH_GID = 2000; // 0x7d0
     field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
-    field public static final int LAST_APPLICATION_UID = 99999; // 0x1869f
+    field public static final int LAST_APPLICATION_UID = 19999; // 0x1869f
     field public static final int PHONE_UID = 1001; // 0x3e9
     field public static final int SIGNAL_KILL = 9; // 0x9
     field public static final int SIGNAL_QUIT = 3; // 0x3
diff --git a/api/current.txt b/api/current.txt
index 574af6b..66c8eb2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -562,6 +562,7 @@
     field public static final int isRepeatable = 16843336; // 0x1010248
     field public static final int isScrollContainer = 16843342; // 0x101024e
     field public static final int isSticky = 16843335; // 0x1010247
+    field public static final int isolatedProcess = 16843687; // 0x10103a7
     field public static final int itemBackground = 16843056; // 0x1010130
     field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131
     field public static final int itemPadding = 16843565; // 0x101032d
@@ -613,8 +614,10 @@
     field public static final int layout_height = 16842997; // 0x10100f5
     field public static final int layout_margin = 16842998; // 0x10100f6
     field public static final int layout_marginBottom = 16843002; // 0x10100fa
+    field public static final int layout_marginEnd = 16843692; // 0x10103ac
     field public static final int layout_marginLeft = 16842999; // 0x10100f7
     field public static final int layout_marginRight = 16843001; // 0x10100f9
+    field public static final int layout_marginStart = 16843691; // 0x10103ab
     field public static final int layout_marginTop = 16843000; // 0x10100f8
     field public static final int layout_row = 16843643; // 0x101037b
     field public static final int layout_rowSpan = 16843644; // 0x101037c
@@ -710,8 +713,10 @@
     field public static final int packageNames = 16843649; // 0x1010381
     field public static final int padding = 16842965; // 0x10100d5
     field public static final int paddingBottom = 16842969; // 0x10100d9
+    field public static final int paddingEnd = 16843690; // 0x10103aa
     field public static final int paddingLeft = 16842966; // 0x10100d6
     field public static final int paddingRight = 16842968; // 0x10100d8
+    field public static final int paddingStart = 16843689; // 0x10103a9
     field public static final int paddingTop = 16842967; // 0x10100d7
     field public static final int panelBackground = 16842846; // 0x101005e
     field public static final int panelColorBackground = 16842849; // 0x1010061
@@ -991,6 +996,7 @@
     field public static final int textColorTertiary = 16843282; // 0x1010212
     field public static final int textColorTertiaryInverse = 16843283; // 0x1010213
     field public static final int textCursorDrawable = 16843618; // 0x1010362
+    field public static final int textDirection = 16843688; // 0x10103a8
     field public static final int textEditNoPasteWindowLayout = 16843541; // 0x1010315
     field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314
     field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f
@@ -3625,12 +3631,22 @@
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
     field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
     field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
-    field public static final int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
     field public static final int FLAG_INSISTENT = 4; // 0x4
     field public static final int FLAG_NO_CLEAR = 32; // 0x20
     field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
     field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
     field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final java.lang.String KIND_CALL = "android.call";
+    field public static final java.lang.String KIND_EMAIL = "android.email";
+    field public static final java.lang.String KIND_EVENT = "android.event";
+    field public static final java.lang.String KIND_MESSAGE = "android.message";
+    field public static final java.lang.String KIND_PROMO = "android.promo";
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
     field public static final int STREAM_DEFAULT = -1; // 0xffffffff
     field public int audioStreamType;
     field public android.app.PendingIntent contentIntent;
@@ -3641,11 +3657,13 @@
     field public android.app.PendingIntent fullScreenIntent;
     field public int icon;
     field public int iconLevel;
+    field public java.lang.String[] kind;
     field public android.graphics.Bitmap largeIcon;
     field public int ledARGB;
     field public int ledOffMS;
     field public int ledOnMS;
     field public int number;
+    field public int priority;
     field public android.net.Uri sound;
     field public java.lang.CharSequence tickerText;
     field public android.widget.RemoteViews tickerView;
@@ -3655,6 +3673,7 @@
 
   public static class Notification.Builder {
     ctor public Notification.Builder(android.content.Context);
+    method public android.app.Notification.Builder addKind(java.lang.String);
     method public android.app.Notification getNotification();
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
@@ -3670,6 +3689,7 @@
     method public android.app.Notification.Builder setNumber(int);
     method public android.app.Notification.Builder setOngoing(boolean);
     method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
+    method public android.app.Notification.Builder setPriority(int);
     method public android.app.Notification.Builder setProgress(int, int, boolean);
     method public android.app.Notification.Builder setSmallIcon(int);
     method public android.app.Notification.Builder setSmallIcon(int, int);
@@ -6469,6 +6489,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
     field public int flags;
     field public java.lang.String permission;
@@ -14877,7 +14898,7 @@
 
   public class Looper {
     method public void dump(android.util.Printer, java.lang.String);
-    method public static synchronized android.os.Looper getMainLooper();
+    method public static android.os.Looper getMainLooper();
     method public java.lang.Thread getThread();
     method public static void loop();
     method public static android.os.Looper myLooper();
@@ -15178,7 +15199,7 @@
     method public static final deprecated boolean supportsProcesses();
     field public static final int BLUETOOTH_GID = 2000; // 0x7d0
     field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
-    field public static final int LAST_APPLICATION_UID = 99999; // 0x1869f
+    field public static final int LAST_APPLICATION_UID = 19999; // 0x4e1f
     field public static final int PHONE_UID = 1001; // 0x3e9
     field public static final int SIGNAL_KILL = 9; // 0x9
     field public static final int SIGNAL_QUIT = 3; // 0x3
@@ -20476,6 +20497,19 @@
     method public int getTopPadding();
   }
 
+  public abstract interface TextDirectionHeuristic {
+  }
+
+  public class TextDirectionHeuristics {
+    ctor public TextDirectionHeuristics();
+    field public static final android.text.TextDirectionHeuristic ANYRTL_LTR;
+    field public static final android.text.TextDirectionHeuristic FIRSTSTRONG_LTR;
+    field public static final android.text.TextDirectionHeuristic FIRSTSTRONG_RTL;
+    field public static final android.text.TextDirectionHeuristic LOCALE;
+    field public static final android.text.TextDirectionHeuristic LTR;
+    field public static final android.text.TextDirectionHeuristic RTL;
+  }
+
   public class TextPaint extends android.graphics.Paint {
     ctor public TextPaint();
     ctor public TextPaint(int);
@@ -21598,6 +21632,26 @@
     method public void println(java.lang.String);
   }
 
+  public class LongSparseArray implements java.lang.Cloneable {
+    ctor public LongSparseArray();
+    ctor public LongSparseArray(int);
+    method public void append(long, E);
+    method public void clear();
+    method public android.util.LongSparseArray<E> clone();
+    method public void delete(long);
+    method public E get(long);
+    method public E get(long, E);
+    method public int indexOfKey(long);
+    method public int indexOfValue(E);
+    method public long keyAt(int);
+    method public void put(long, E);
+    method public void remove(long);
+    method public void removeAt(int);
+    method public void setValueAt(int, E);
+    method public int size();
+    method public E valueAt(int);
+  }
+
   public class LruCache {
     ctor public LruCache(int);
     method protected V create(K);
@@ -23143,12 +23197,15 @@
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public int getOverScrollMode();
     method public int getPaddingBottom();
+    method public int getPaddingEnd();
     method public int getPaddingLeft();
     method public int getPaddingRight();
+    method public int getPaddingStart();
     method public int getPaddingTop();
     method public final android.view.ViewParent getParent();
     method public float getPivotX();
     method public float getPivotY();
+    method public int getResolvedTextDirection();
     method public android.content.res.Resources getResources();
     method public final int getRight();
     method protected float getRightFadingEdgeStrength();
@@ -23168,6 +23225,7 @@
     method public int getSystemUiVisibility();
     method public java.lang.Object getTag();
     method public java.lang.Object getTag(int);
+    method public int getTextDirection();
     method public final int getTop();
     method protected float getTopFadingEdgeStrength();
     method protected int getTopPaddingOffset();
@@ -23218,6 +23276,7 @@
     method public boolean isLongClickable();
     method public boolean isOpaque();
     method protected boolean isPaddingOffsetRequired();
+    method public boolean isPaddingRelative();
     method public boolean isPressed();
     method public boolean isSaveEnabled();
     method public boolean isSaveFromParentEnabled();
@@ -23265,6 +23324,9 @@
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onResetResolvedTextDirection();
+    method public void onResolvePadding(int);
+    method public void onResolveTextDirection();
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method protected android.os.Parcelable onSaveInstanceState();
     method protected void onScrollChanged(int, int, int, int);
@@ -23299,8 +23361,11 @@
     method public void requestLayout();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+    method public void resetResolvedTextDirection();
+    method public void resolvePadding();
     method public static int resolveSize(int, int);
     method public static int resolveSizeAndState(int, int, int);
+    method public void resolveTextDirection();
     method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
@@ -23359,6 +23424,7 @@
     method public void setOnTouchListener(android.view.View.OnTouchListener);
     method public void setOverScrollMode(int);
     method public void setPadding(int, int, int, int);
+    method public void setPaddingRelative(int, int, int, int);
     method public void setPivotX(float);
     method public void setPivotY(float);
     method public void setPressed(boolean);
@@ -23380,6 +23446,7 @@
     method public void setSystemUiVisibility(int);
     method public void setTag(java.lang.Object);
     method public void setTag(int, java.lang.Object);
+    method public void setTextDirection(int);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
     method public void setTranslationX(float);
@@ -23402,6 +23469,7 @@
     method public boolean willNotCacheDrawing();
     method public boolean willNotDraw();
     field public static final android.util.Property ALPHA;
+    field protected static int DEFAULT_TEXT_DIRECTION;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
     field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
@@ -23478,6 +23546,12 @@
     field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
+    field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
+    field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
+    field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
+    field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
+    field public static final int TEXT_DIRECTION_LTR = 3; // 0x3
+    field public static final int TEXT_DIRECTION_RTL = 4; // 0x4
     field public static final android.util.Property TRANSLATION_X;
     field public static final android.util.Property TRANSLATION_Y;
     field protected static final java.lang.String VIEW_LOG_TAG = "View";
@@ -23744,7 +23818,6 @@
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
     method protected void resetResolvedLayoutDirection();
-    method protected void resetResolvedTextDirection();
     method public void scheduleLayoutAnimation();
     method public void setAddStatesFromChildren(boolean);
     method public void setAlwaysDrawnWithCacheEnabled(boolean);
@@ -23796,10 +23869,15 @@
     ctor public ViewGroup.MarginLayoutParams(int, int);
     ctor public ViewGroup.MarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
     ctor public ViewGroup.MarginLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public int getMarginEnd();
+    method public int getMarginStart();
+    method public boolean isMarginRelative();
     method public void setMargins(int, int, int, int);
     field public int bottomMargin;
+    field public int endMargin;
     field public int leftMargin;
     field public int rightMargin;
+    field public int startMargin;
     field public int topMargin;
   }
 
@@ -24890,6 +24968,7 @@
     method public void showSoftInputFromInputMethod(android.os.IBinder, int);
     method public void showStatusIcon(android.os.IBinder, java.lang.String, int);
     method public boolean switchToLastInputMethod(android.os.IBinder);
+    method public boolean switchToNextInputMethod(android.os.IBinder, boolean);
     method public void toggleSoftInput(int, int);
     method public void toggleSoftInputFromWindow(android.os.IBinder, int, int);
     method public void updateCursor(android.view.View, int, int, int, int);
@@ -26384,10 +26463,12 @@
     field public static final android.widget.GridLayout.Alignment BASELINE;
     field public static final android.widget.GridLayout.Alignment BOTTOM;
     field public static final android.widget.GridLayout.Alignment CENTER;
+    field public static final android.widget.GridLayout.Alignment END;
     field public static final android.widget.GridLayout.Alignment FILL;
     field public static final int HORIZONTAL = 0; // 0x0
     field public static final android.widget.GridLayout.Alignment LEFT;
     field public static final android.widget.GridLayout.Alignment RIGHT;
+    field public static final android.widget.GridLayout.Alignment START;
     field public static final android.widget.GridLayout.Alignment TOP;
     field public static final int UNDEFINED = -2147483648; // 0x80000000
     field public static final int VERTICAL = 1; // 0x1
@@ -27570,7 +27651,6 @@
     method protected void resetResolvedDrawables();
     method protected void resetResolvedLayoutDirection();
     method protected void resolveDrawables();
-    method protected void resolveTextDirection();
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
     method public void setCompoundDrawablePadding(int);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 3d36ebf..901f7c7 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -215,6 +215,22 @@
                     list[i] = Long.valueOf(strings[i]);
                 }
                 intent.putExtra(key, list);
+                hasIntentInfo = true;
+            } else if (opt.equals("--ef")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                intent.putExtra(key, Float.valueOf(value));
+                hasIntentInfo = true;
+            } else if (opt.equals("--efa")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                String[] strings = value.split(",");
+                float[] list = new float[strings.length];
+                for (int i = 0; i < strings.length; i++) {
+                    list[i] = Float.valueOf(strings[i]);
+                }
+                intent.putExtra(key, list);
+                hasIntentInfo = true;
             } else if (opt.equals("--ez")) {
                 String key = nextArgRequired();
                 String value = nextArgRequired();
@@ -1342,9 +1358,11 @@
                 "    [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
                 "    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
                 "    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
+                "    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
                 "    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
                 "    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
                 "    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
+                "    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
                 "    [-n <COMPONENT>] [-f <FLAGS>]\n" +
                 "    [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
                 "    [--debug-log-resolution] [--exclude-stopped-packages]\n" +
diff --git a/cmds/backup/Android.mk b/cmds/backup/Android.mk
index 508aec0..73af0bc 100644
--- a/cmds/backup/Android.mk
+++ b/cmds/backup/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_SRC_FILES:= backup.cpp
 
-LOCAL_SHARED_LIBRARIES := libcutils libutils
+LOCAL_SHARED_LIBRARIES := libcutils libutils libandroidfw
 
 LOCAL_MODULE:= btool
 
diff --git a/cmds/backup/backup.cpp b/cmds/backup/backup.cpp
index d4e669b..ea1888b 100644
--- a/cmds/backup/backup.cpp
+++ b/cmds/backup/backup.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <utils/BackupHelpers.h>
+#include <androidfw/BackupHelpers.h>
 #include <utils/String8.h>
 
 #include <fcntl.h>
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 7d39912..8c46b21 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -9,6 +9,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
+	libandroidfw \
 	libutils \
 	libbinder \
     libui \
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 0d5b4ca..3545ace 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -25,12 +25,12 @@
 
 #include <cutils/properties.h>
 
+#include <androidfw/AssetManager.h>
 #include <binder/IPCThreadState.h>
-#include <utils/threads.h>
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
-#include <utils/AssetManager.h>
+#include <utils/threads.h>
 
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 8e28bba..c85d72c 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -20,8 +20,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <androidfw/AssetManager.h>
 #include <utils/threads.h>
-#include <utils/AssetManager.h>
 
 #include <surfaceflinger/ISurfaceComposer.h>
 #include <surfaceflinger/SurfaceComposerClient.h>
diff --git a/cmds/content/Android.mk b/cmds/content/Android.mk
new file mode 100644
index 0000000..88c46f2
--- /dev/null
+++ b/cmds/content/Android.mk
@@ -0,0 +1,33 @@
+# Copyright 2012 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_MODULE := content
+
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+ALL_PREBUILT += $(TARGET_OUT)/bin/content
+$(TARGET_OUT)/bin/content : $(LOCAL_PATH)/content | $(ACP)
+	$(transform-prebuilt-to-target)
+
+NOTICE_FILE := NOTICE
+files_noticed := bin/content
+
+# Generate rules for a single file. The argument is the file path relative to
+# the installation root
+define make-notice-file
+
+$(TARGET_OUT_NOTICE_FILES)/src/$(1).txt: $(LOCAL_PATH)/$(NOTICE_FILE)
+	@echo Notice file: $$< -- $$@
+	@mkdir -p $$(dir $$@)
+	@cat $$< >> $$@
+
+$(TARGET_OUT_NOTICE_FILES)/hash-timestamp: $(TARGET_OUT_NOTICE_FILES)/src/$(1).txt
+
+endef
+
+$(foreach file,$(files_noticed),$(eval $(call make-notice-file,$(file))))
diff --git a/cmds/content/MODULE_LICENSE_APACHE2 b/cmds/content/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/content/MODULE_LICENSE_APACHE2
diff --git a/cmds/content/NOTICE b/cmds/content/NOTICE
new file mode 100644
index 0000000..33ff961
--- /dev/null
+++ b/cmds/content/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2012, 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/cmds/content/content b/cmds/content/content
new file mode 100755
index 0000000..a8e056d
--- /dev/null
+++ b/cmds/content/content
@@ -0,0 +1,5 @@
+# Script to start "content" on the device, which has a very rudimentary shell.
+base=/system
+export CLASSPATH=$base/framework/content.jar
+exec app_process $base/bin com.android.commands.content.Content "$@"
+
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
new file mode 100644
index 0000000..1dcba70
--- /dev/null
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -0,0 +1,442 @@
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.commands.content;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IActivityManager.ContentProviderHolder;
+import android.content.ContentValues;
+import android.content.IContentProvider;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+/**
+ * This class is a command line utility for manipulating content. A client
+ * can insert, update, and remove records in a content provider. For example,
+ * some settings may be configured before running the CTS tests, etc.
+ * <p>
+ * Examples:
+ * <ul>
+ * <li>
+ * # Add "new_setting" secure setting with value "new_value".</br>
+ * adb shell content insert --uri content://settings/secure --bind name:s:new_setting
+ *  --bind value:s:new_value
+ * </li>
+ * <li>
+ * # Change "new_setting" secure setting to "newer_value" (You have to escape single quotes in
+ * the where clause).</br>
+ * adb shell content update --uri content://settings/secure --bind value:s:newer_value
+ *  --where "name=\'new_setting\'"
+ * </li>
+ * <li>
+ * # Remove "new_setting" secure setting.</br>
+ * adb shell content delete --uri content://settings/secure --where "name=\'new_setting\'"
+ * </li>
+ * <li>
+ * # Query \"name\" and \"value\" columns from secure settings where \"name\" is equal to"
+ *    \"new_setting\" and sort the result by name in ascending order.\n"
+ * adb shell content query --uri content://settings/secure --projection name:value
+ *  --where "name=\'new_setting\'" --sort \"name ASC\"
+ * </li>
+ * </ul>
+ * </p>
+ */
+public class Content {
+
+    private static final String USAGE =
+        "usage: adb shell content [subcommand] [options]\n"
+        + "\n"
+        + "usage: adb shell content insert --uri <URI> --bind <BINDING> [--bind <BINDING>...]\n"
+        + "  <URI> a content provider URI.\n"
+        + "  <BINDING> binds a typed value to a column and is formatted:\n"
+        + "  <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
+        + "  <TYPE> specifies data type such as:\n"
+        + "  b - boolean, s - string, i - integer, l - long, f - float, d - double\n"
+        + "  Example:\n"
+        + "  # Add \"new_setting\" secure setting with value \"new_value\".\n"
+        + "  adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
+                + " --bind value:s:new_value\n"
+        + "\n"
+        + "usage: adb shell content update --uri <URI> [--where <WHERE>]\n"
+        + "  <WHERE> is a SQL style where clause in quotes (You have to escape single quotes"
+                + " - see example below).\n"
+        + "  Example:\n"
+        + "  # Change \"new_setting\" secure setting to \"newer_value\".\n"
+        + "  adb shell content update --uri content://settings/secure --bind"
+                + " value:s:newer_value --where \"name=\'new_setting\'\"\n"
+        + "\n"
+        + "usage: adb shell content delete --uri <URI> --bind <BINDING>"
+                + " [--bind <BINDING>...] [--where <WHERE>]\n"
+        + "  Example:\n"
+        + "  # Remove \"new_setting\" secure setting.\n"
+        + "  adb shell content delete --uri content://settings/secure "
+                + "--where \"name=\'new_setting\'\"\n"
+        + "\n"
+        + "usage: adb shell content query --uri <URI> [--projection <PROJECTION>]"
+                + " [--where <WHERE>] [--sort <SORT_ORDER>]\n"
+        + "  <PROJECTION> is a list of colon separated column names and is formatted:\n"
+        + "  <COLUMN_NAME>[:<COLUMN_NAME>...]\n"
+        + "  <SORT_OREDER> is the order in which rows in the result should be sorted.\n"
+        + "  Example:\n"
+        + "  # Select \"name\" and \"value\" columns from secure settings where \"name\" is "
+                + "equal to \"new_setting\" and sort the result by name in ascending order.\n"
+        + "  adb shell content query --uri content://settings/secure --projection name:value"
+                + " --where \"name=\'new_setting\'\" --sort \"name ASC\"\n"
+        + "\n";
+
+    private static class Parser {
+        private static final String ARGUMENT_INSERT = "insert";
+        private static final String ARGUMENT_DELETE = "delete";
+        private static final String ARGUMENT_UPDATE = "update";
+        private static final String ARGUMENT_QUERY = "query";
+        private static final String ARGUMENT_WHERE = "--where";
+        private static final String ARGUMENT_BIND = "--bind";
+        private static final String ARGUMENT_URI = "--uri";
+        private static final String ARGUMENT_PROJECTION = "--projection";
+        private static final String ARGUMENT_SORT = "--sort";
+        private static final String TYPE_BOOLEAN = "b";
+        private static final String TYPE_STRING = "s";
+        private static final String TYPE_INTEGER = "i";
+        private static final String TYPE_LONG = "l";
+        private static final String TYPE_FLOAT = "f";
+        private static final String TYPE_DOUBLE = "d";
+        private static final String COLON = ":";
+        private static final String ARGUMENT_PREFIX = "--";
+
+        private final Tokenizer mTokenizer;
+
+        public Parser(String[] args) {
+            mTokenizer = new Tokenizer(args);
+        }
+
+        public Command parseCommand() {
+            try {
+                String operation = mTokenizer.nextArg();
+                if (ARGUMENT_INSERT.equals(operation)) {
+                    return parseInsertCommand();
+                } else if (ARGUMENT_DELETE.equals(operation)) {
+                    return parseDeleteCommand();
+                } else if (ARGUMENT_UPDATE.equals(operation)) {
+                    return parseUpdateCommand();
+                } else if (ARGUMENT_QUERY.equals(operation)) {
+                    return parseQueryCommand();
+                } else {
+                    throw new IllegalArgumentException("Unsupported operation: " + operation);
+                }
+            } catch (IllegalArgumentException iae) {
+                System.out.println(USAGE);
+                System.out.println("[ERROR] " + iae.getMessage());
+                return null;
+            }
+        }
+
+        private InsertCommand parseInsertCommand() {
+            Uri uri = null;
+            ContentValues values = new ContentValues();
+            for (String argument; (argument = mTokenizer.nextArg()) != null;) {
+                if (ARGUMENT_URI.equals(argument)) {
+                    uri = Uri.parse(argumentValueRequired(argument));
+                } else if (ARGUMENT_BIND.equals(argument)) {
+                    parseBindValue(values);
+                } else {
+                    throw new IllegalArgumentException("Unsupported argument: " + argument);
+                }
+            }
+            if (uri == null) {
+                throw new IllegalArgumentException("Content provider URI not specified."
+                        + " Did you specify --uri argument?");
+            }
+            if (values.size() == 0) {
+                throw new IllegalArgumentException("Bindings not specified."
+                        + " Did you specify --bind argument(s)?");
+            }
+            return new InsertCommand(uri, values);
+        }
+
+        private DeleteCommand parseDeleteCommand() {
+            Uri uri = null;
+            String where = null;
+            for (String argument; (argument = mTokenizer.nextArg())!= null;) {
+                if (ARGUMENT_URI.equals(argument)) {
+                    uri = Uri.parse(argumentValueRequired(argument));
+                } else if (ARGUMENT_WHERE.equals(argument)) {
+                    where = argumentValueRequired(argument);
+                } else {
+                    throw new IllegalArgumentException("Unsupported argument: " + argument);
+                }
+            }
+            if (uri == null) {
+                throw new IllegalArgumentException("Content provider URI not specified."
+                        + " Did you specify --uri argument?");
+            }
+            return new DeleteCommand(uri, where);
+        }
+
+        private UpdateCommand parseUpdateCommand() {
+            Uri uri = null;
+            String where = null;
+            ContentValues values = new ContentValues();
+            for (String argument; (argument = mTokenizer.nextArg())!= null;) {
+                if (ARGUMENT_URI.equals(argument)) {
+                    uri = Uri.parse(argumentValueRequired(argument));
+                } else if (ARGUMENT_WHERE.equals(argument)) {
+                    where = argumentValueRequired(argument);
+                } else if (ARGUMENT_BIND.equals(argument)) {
+                    parseBindValue(values);
+                } else {
+                    throw new IllegalArgumentException("Unsupported argument: " + argument);
+                }
+            }
+            if (uri == null) {
+                throw new IllegalArgumentException("Content provider URI not specified."
+                        + " Did you specify --uri argument?");
+            }
+            if (values.size() == 0) {
+                throw new IllegalArgumentException("Bindings not specified."
+                        + " Did you specify --bind argument(s)?");
+            }
+            return new UpdateCommand(uri, values, where);
+        }
+
+        public QueryCommand parseQueryCommand() {
+            Uri uri = null;
+            String[] projection = null;
+            String sort = null;
+            String where = null;
+            for (String argument; (argument = mTokenizer.nextArg())!= null;) {
+                if (ARGUMENT_URI.equals(argument)) {
+                    uri = Uri.parse(argumentValueRequired(argument));
+                } else if (ARGUMENT_WHERE.equals(argument)) {
+                    where = argumentValueRequired(argument);
+                } else if (ARGUMENT_SORT.equals(argument)) {
+                    sort = argumentValueRequired(argument);
+                } else if (ARGUMENT_PROJECTION.equals(argument)) {
+                    projection = argumentValueRequired(argument).split("[\\s]*:[\\s]*");
+                } else {
+                    throw new IllegalArgumentException("Unsupported argument: " + argument);
+                }
+            }
+            if (uri == null) {
+                throw new IllegalArgumentException("Content provider URI not specified."
+                        + " Did you specify --uri argument?");
+            }
+            return new QueryCommand(uri, projection, where, sort);
+        }
+
+        private void parseBindValue(ContentValues values) {
+            String argument = mTokenizer.nextArg();
+            if (TextUtils.isEmpty(argument)) {
+                throw new IllegalArgumentException("Binding not well formed: " + argument);
+            }
+            String[] binding = argument.split(COLON);
+            if (binding.length != 3) {
+                throw new IllegalArgumentException("Binding not well formed: " + argument);
+            }
+            String column = binding[0];
+            String type = binding[1];
+            String value = binding[2];
+            if (TYPE_STRING.equals(type)) {
+                values.put(column, value);
+            } else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) {
+                values.put(column, Boolean.parseBoolean(value));
+            } else if (TYPE_INTEGER.equalsIgnoreCase(type) || TYPE_LONG.equalsIgnoreCase(type)) {
+                values.put(column, Long.parseLong(value));
+            } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) {
+                values.put(column, Double.parseDouble(value));
+            } else {
+                throw new IllegalArgumentException("Unsupported type: " + type);
+            }
+        }
+
+        private String argumentValueRequired(String argument) {
+            String value = mTokenizer.nextArg();
+            if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) {
+                throw new IllegalArgumentException("No value for argument: " + argument);
+            }
+            return value;
+        }
+    }
+
+    private static class Tokenizer {
+        private final String[] mArgs;
+        private int mNextArg;
+
+        public Tokenizer(String[] args) {
+            mArgs = args;
+        }
+
+        private String nextArg() {
+            if (mNextArg < mArgs.length) {
+                return mArgs[mNextArg++];
+            } else {
+                return null;
+            }
+        }
+    }
+
+    private static abstract class Command {
+        final Uri mUri;
+
+        public Command(Uri uri) {
+            mUri = uri;
+        }
+
+        public final void execute() {
+            String providerName = mUri.getAuthority();
+            try {
+                IActivityManager activityManager = ActivityManagerNative.getDefault();
+                IContentProvider provider = null;
+                IBinder token = new Binder();
+                try {
+                    ContentProviderHolder holder = activityManager.getContentProviderExternal(
+                            providerName, token);
+                    if (holder == null) {
+                        throw new IllegalStateException("Could not find provider: " + providerName);
+                    }
+                    provider = holder.provider;
+                    onExecute(provider);
+                } finally {
+                    if (provider != null) {
+                        activityManager.removeContentProviderExternal(providerName, token);
+                    }
+                }
+            } catch (Exception e) {
+                System.err.println("Error while accessing provider:" + providerName);
+                e.printStackTrace();
+            }
+        }
+
+        protected abstract void onExecute(IContentProvider provider) throws Exception;
+    }
+
+    private static class InsertCommand extends Command {
+        final ContentValues mContentValues;
+
+        public InsertCommand(Uri uri, ContentValues contentValues) {
+            super(uri);
+            mContentValues = contentValues;
+        }
+
+        @Override
+        public void onExecute(IContentProvider provider) throws Exception {
+            provider.insert(mUri, mContentValues);
+        }
+    }
+
+    private static class DeleteCommand extends Command {
+        final String mWhere;
+
+        public DeleteCommand(Uri uri, String where) {
+            super(uri);
+            mWhere = where;
+        }
+
+        @Override
+        public void onExecute(IContentProvider provider) throws Exception {
+            provider.delete(mUri, mWhere, null);
+        }
+    }
+
+    private static class QueryCommand extends DeleteCommand {
+        final String[] mProjection;
+        final String mSortOrder;
+
+        public QueryCommand(Uri uri, String[] projection, String where, String sortOrder) {
+            super(uri, where);
+            mProjection = projection;
+            mSortOrder = sortOrder;
+        }
+
+        @Override
+        public void onExecute(IContentProvider provider) throws Exception {
+            Cursor cursor = provider.query(mUri, mProjection, mWhere, null, mSortOrder, null);
+            if (cursor == null) {
+                System.out.println("No result found.");
+                return;
+            }
+            try {
+                if (cursor.moveToFirst()) {
+                    int rowIndex = 0;
+                    StringBuilder builder = new StringBuilder();
+                    do {
+                        builder.setLength(0);
+                        builder.append("Row: ").append(rowIndex).append(" ");
+                        rowIndex++;
+                        final int columnCount = cursor.getColumnCount();
+                        for (int i = 0; i < columnCount; i++) {
+                            if (i > 0) {
+                                builder.append(", ");
+                            }
+                            String columnName = cursor.getColumnName(i);
+                            String columnValue = null;
+                            final int columnIndex = cursor.getColumnIndex(columnName);
+                            final int type = cursor.getType(columnIndex);
+                            switch (type) {
+                                case Cursor.FIELD_TYPE_FLOAT:
+                                    columnValue = String.valueOf(cursor.getFloat(columnIndex));
+                                    break;
+                                case Cursor.FIELD_TYPE_INTEGER:
+                                    columnValue = String.valueOf(cursor.getInt(columnIndex));
+                                    break;
+                                case Cursor.FIELD_TYPE_STRING:
+                                    columnValue = cursor.getString(columnIndex);
+                                    break;
+                                case Cursor.FIELD_TYPE_BLOB:
+                                    columnValue = "BLOB";
+                                    break;
+                                case Cursor.FIELD_TYPE_NULL:
+                                    columnValue = "NULL";
+                                    break;
+                            }
+                            builder.append(columnName).append("=").append(columnValue);
+                        }
+                        System.out.println(builder);
+                    } while (cursor.moveToNext());
+                } else {
+                    System.out.println("No reuslt found.");
+                }
+            } finally {
+                cursor.close();
+            }
+        }
+    }
+
+    private static class UpdateCommand extends InsertCommand {
+        final String mWhere;
+
+        public UpdateCommand(Uri uri, ContentValues contentValues, String where) {
+            super(uri, contentValues);
+            mWhere = where;
+        }
+
+        @Override
+        public void onExecute(IContentProvider provider) throws Exception {
+            provider.update(mUri, mContentValues, mWhere, null);
+        }
+    }
+
+    public static void main(String[] args) {
+        Parser parser = new Parser(args);
+        Command command = parser.parseCommand();
+        if (command != null) {
+            command.execute();
+        }
+    }
+}
diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk
deleted file mode 100644
index 5a9b979..0000000
--- a/cmds/keystore/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# Copyright (C) 2009 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := keystore.cpp
-LOCAL_C_INCLUDES := external/openssl/include
-LOCAL_SHARED_LIBRARIES := libcutils libcrypto
-LOCAL_MODULE:= keystore
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := keystore_cli.cpp
-LOCAL_C_INCLUDES := external/openssl/include
-LOCAL_SHARED_LIBRARIES := libcutils libcrypto
-LOCAL_MODULE:= keystore_cli
-LOCAL_MODULE_TAGS := debug
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp
deleted file mode 100644
index 05f77e5..0000000
--- a/cmds/keystore/keystore.cpp
+++ /dev/null
@@ -1,810 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-
-#include <openssl/aes.h>
-#include <openssl/evp.h>
-#include <openssl/md5.h>
-
-#define LOG_TAG "keystore"
-#include <cutils/log.h>
-#include <cutils/sockets.h>
-#include <private/android_filesystem_config.h>
-
-#include "keystore.h"
-
-/* KeyStore is a secured storage for key-value pairs. In this implementation,
- * each file stores one key-value pair. Keys are encoded in file names, and
- * values are encrypted with checksums. The encryption key is protected by a
- * user-defined password. To keep things simple, buffers are always larger than
- * the maximum space we needed, so boundary checks on buffers are omitted. */
-
-#define KEY_SIZE        ((NAME_MAX - 15) / 2)
-#define VALUE_SIZE      32768
-#define PASSWORD_SIZE   VALUE_SIZE
-
-struct Value {
-    int length;
-    uint8_t value[VALUE_SIZE];
-};
-
-/* Here is the encoding of keys. This is necessary in order to allow arbitrary
- * characters in keys. Characters in [0-~] are not encoded. Others are encoded
- * into two bytes. The first byte is one of [+-.] which represents the first
- * two bits of the character. The second byte encodes the rest of the bits into
- * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
- * that Base64 cannot be used here due to the need of prefix match on keys. */
-
-static int encode_key(char* out, uid_t uid, const Value* key) {
-    int n = snprintf(out, NAME_MAX, "%u_", uid);
-    out += n;
-    const uint8_t* in = key->value;
-    int length = key->length;
-    for (int i = length; i > 0; --i, ++in, ++out) {
-        if (*in >= '0' && *in <= '~') {
-            *out = *in;
-        } else {
-            *out = '+' + (*in >> 6);
-            *++out = '0' + (*in & 0x3F);
-            ++length;
-        }
-    }
-    *out = '\0';
-    return n + length;
-}
-
-static int decode_key(uint8_t* out, char* in, int length) {
-    for (int i = 0; i < length; ++i, ++in, ++out) {
-        if (*in >= '0' && *in <= '~') {
-            *out = *in;
-        } else {
-            *out = (*in - '+') << 6;
-            *out |= (*++in - '0') & 0x3F;
-            --length;
-        }
-    }
-    *out = '\0';
-    return length;
-}
-
-static size_t readFully(int fd, uint8_t* data, size_t size) {
-    size_t remaining = size;
-    while (remaining > 0) {
-        ssize_t n = TEMP_FAILURE_RETRY(read(fd, data, size));
-        if (n == -1 || n == 0) {
-            return size-remaining;
-        }
-        data += n;
-        remaining -= n;
-    }
-    return size;
-}
-
-static size_t writeFully(int fd, uint8_t* data, size_t size) {
-    size_t remaining = size;
-    while (remaining > 0) {
-        ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, size));
-        if (n == -1 || n == 0) {
-            return size-remaining;
-        }
-        data += n;
-        remaining -= n;
-    }
-    return size;
-}
-
-class Entropy {
-public:
-    Entropy() : mRandom(-1) {}
-    ~Entropy() {
-        if (mRandom != -1) {
-            close(mRandom);
-        }
-    }
-
-    bool open() {
-        const char* randomDevice = "/dev/urandom";
-        mRandom = ::open(randomDevice, O_RDONLY);
-        if (mRandom == -1) {
-            ALOGE("open: %s: %s", randomDevice, strerror(errno));
-            return false;
-        }
-        return true;
-    }
-
-    bool generate_random_data(uint8_t* data, size_t size) {
-        return (readFully(mRandom, data, size) == size);
-    }
-
-private:
-    int mRandom;
-};
-
-/* Here is the file format. There are two parts in blob.value, the secret and
- * the description. The secret is stored in ciphertext, and its original size
- * can be found in blob.length. The description is stored after the secret in
- * plaintext, and its size is specified in blob.info. The total size of the two
- * parts must be no more than VALUE_SIZE bytes. The first three bytes of the
- * file are reserved for future use and are always set to zero. Fields other
- * than blob.info, blob.length, and blob.value are modified by encryptBlob()
- * and decryptBlob(). Thus they should not be accessed from outside. */
-
-struct __attribute__((packed)) blob {
-    uint8_t reserved[3];
-    uint8_t info;
-    uint8_t vector[AES_BLOCK_SIZE];
-    uint8_t encrypted[0];
-    uint8_t digest[MD5_DIGEST_LENGTH];
-    uint8_t digested[0];
-    int32_t length; // in network byte order when encrypted
-    uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE];
-};
-
-class Blob {
-public:
-    Blob(uint8_t* value, int32_t valueLength, uint8_t* info, uint8_t infoLength) {
-        mBlob.length = valueLength;
-        memcpy(mBlob.value, value, valueLength);
-
-        mBlob.info = infoLength;
-        memcpy(mBlob.value + valueLength, info, infoLength);
-    }
-
-    Blob(blob b) {
-        mBlob = b;
-    }
-
-    Blob() {}
-
-    uint8_t* getValue() {
-        return mBlob.value;
-    }
-
-    int32_t getLength() {
-        return mBlob.length;
-    }
-
-    uint8_t getInfo() {
-        return mBlob.info;
-    }
-
-    ResponseCode encryptBlob(const char* filename, AES_KEY *aes_key, Entropy* entropy) {
-        if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) {
-            return SYSTEM_ERROR;
-        }
-
-        // data includes the value and the value's length
-        size_t dataLength = mBlob.length + sizeof(mBlob.length);
-        // pad data to the AES_BLOCK_SIZE
-        size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1)
-                                 / AES_BLOCK_SIZE * AES_BLOCK_SIZE);
-        // encrypted data includes the digest value
-        size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH;
-        // move info after space for padding
-        memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info);
-        // zero padding area
-        memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength);
-
-        mBlob.length = htonl(mBlob.length);
-        MD5(mBlob.digested, digestedLength, mBlob.digest);
-
-        uint8_t vector[AES_BLOCK_SIZE];
-        memcpy(vector, mBlob.vector, AES_BLOCK_SIZE);
-        AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength,
-                        aes_key, vector, AES_ENCRYPT);
-
-        memset(mBlob.reserved, 0, sizeof(mBlob.reserved));
-        size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob);
-        size_t fileLength = encryptedLength + headerLength + mBlob.info;
-
-        const char* tmpFileName = ".tmp";
-        int out = open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
-        if (out == -1) {
-            return SYSTEM_ERROR;
-        }
-        size_t writtenBytes = writeFully(out, (uint8_t*) &mBlob, fileLength);
-        if (close(out) != 0) {
-            return SYSTEM_ERROR;
-        }
-        if (writtenBytes != fileLength) {
-            unlink(tmpFileName);
-            return SYSTEM_ERROR;
-        }
-        return (rename(tmpFileName, filename) == 0) ? NO_ERROR : SYSTEM_ERROR;
-    }
-
-    ResponseCode decryptBlob(const char* filename, AES_KEY *aes_key) {
-        int in = open(filename, O_RDONLY);
-        if (in == -1) {
-            return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR;
-        }
-        // fileLength may be less than sizeof(mBlob) since the in
-        // memory version has extra padding to tolerate rounding up to
-        // the AES_BLOCK_SIZE
-        size_t fileLength = readFully(in, (uint8_t*) &mBlob, sizeof(mBlob));
-        if (close(in) != 0) {
-            return SYSTEM_ERROR;
-        }
-        size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob);
-        if (fileLength < headerLength) {
-            return VALUE_CORRUPTED;
-        }
-
-        ssize_t encryptedLength = fileLength - (headerLength + mBlob.info);
-        if (encryptedLength < 0 || encryptedLength % AES_BLOCK_SIZE != 0) {
-            return VALUE_CORRUPTED;
-        }
-        AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key,
-                        mBlob.vector, AES_DECRYPT);
-        size_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
-        uint8_t computedDigest[MD5_DIGEST_LENGTH];
-        MD5(mBlob.digested, digestedLength, computedDigest);
-        if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
-            return VALUE_CORRUPTED;
-        }
-
-        ssize_t maxValueLength = digestedLength - sizeof(mBlob.length);
-        mBlob.length = ntohl(mBlob.length);
-        if (mBlob.length < 0 || mBlob.length > maxValueLength) {
-            return VALUE_CORRUPTED;
-        }
-        if (mBlob.info != 0) {
-            // move info from after padding to after data
-            memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info);
-        }
-        return NO_ERROR;
-    }
-
-private:
-    struct blob mBlob;
-};
-
-class KeyStore {
-public:
-    KeyStore(Entropy* entropy) : mEntropy(entropy), mRetry(MAX_RETRY) {
-        if (access(MASTER_KEY_FILE, R_OK) == 0) {
-            setState(STATE_LOCKED);
-        } else {
-            setState(STATE_UNINITIALIZED);
-        }
-    }
-
-    State getState() {
-        return mState;
-    }
-
-    int8_t getRetry() {
-        return mRetry;
-    }
-
-    ResponseCode initialize(Value* pw) {
-        if (!generateMasterKey()) {
-            return SYSTEM_ERROR;
-        }
-        ResponseCode response = writeMasterKey(pw);
-        if (response != NO_ERROR) {
-            return response;
-        }
-        setupMasterKeys();
-        return NO_ERROR;
-    }
-
-    ResponseCode writeMasterKey(Value* pw) {
-        uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
-        generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt);
-        AES_KEY passwordAesKey;
-        AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
-        Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt));
-        return masterKeyBlob.encryptBlob(MASTER_KEY_FILE, &passwordAesKey, mEntropy);
-    }
-
-    ResponseCode readMasterKey(Value* pw) {
-        int in = open(MASTER_KEY_FILE, O_RDONLY);
-        if (in == -1) {
-            return SYSTEM_ERROR;
-        }
-
-        // we read the raw blob to just to get the salt to generate
-        // the AES key, then we create the Blob to use with decryptBlob
-        blob rawBlob;
-        size_t length = readFully(in, (uint8_t*) &rawBlob, sizeof(rawBlob));
-        if (close(in) != 0) {
-            return SYSTEM_ERROR;
-        }
-        // find salt at EOF if present, otherwise we have an old file
-        uint8_t* salt;
-        if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) {
-            salt = (uint8_t*) &rawBlob + length - SALT_SIZE;
-        } else {
-            salt = NULL;
-        }
-        uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
-        generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt);
-        AES_KEY passwordAesKey;
-        AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
-        Blob masterKeyBlob(rawBlob);
-        ResponseCode response = masterKeyBlob.decryptBlob(MASTER_KEY_FILE, &passwordAesKey);
-        if (response == SYSTEM_ERROR) {
-            return SYSTEM_ERROR;
-        }
-        if (response == NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) {
-            // if salt was missing, generate one and write a new master key file with the salt.
-            if (salt == NULL) {
-                if (!generateSalt()) {
-                    return SYSTEM_ERROR;
-                }
-                response = writeMasterKey(pw);
-            }
-            if (response == NO_ERROR) {
-                memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES);
-                setupMasterKeys();
-            }
-            return response;
-        }
-        if (mRetry <= 0) {
-            reset();
-            return UNINITIALIZED;
-        }
-        --mRetry;
-        switch (mRetry) {
-            case 0: return WRONG_PASSWORD_0;
-            case 1: return WRONG_PASSWORD_1;
-            case 2: return WRONG_PASSWORD_2;
-            case 3: return WRONG_PASSWORD_3;
-            default: return WRONG_PASSWORD_3;
-        }
-    }
-
-    bool reset() {
-        clearMasterKeys();
-        setState(STATE_UNINITIALIZED);
-
-        DIR* dir = opendir(".");
-        struct dirent* file;
-
-        if (!dir) {
-            return false;
-        }
-        while ((file = readdir(dir)) != NULL) {
-            unlink(file->d_name);
-        }
-        closedir(dir);
-        return true;
-    }
-
-    bool isEmpty() {
-        DIR* dir = opendir(".");
-        struct dirent* file;
-        if (!dir) {
-            return true;
-        }
-        bool result = true;
-        while ((file = readdir(dir)) != NULL) {
-            if (isKeyFile(file->d_name)) {
-                result = false;
-                break;
-            }
-        }
-        closedir(dir);
-        return result;
-    }
-
-    void lock() {
-        clearMasterKeys();
-        setState(STATE_LOCKED);
-    }
-
-    ResponseCode get(const char* filename, Blob* keyBlob) {
-        return keyBlob->decryptBlob(filename, &mMasterKeyDecryption);
-    }
-
-    ResponseCode put(const char* filename, Blob* keyBlob) {
-        return keyBlob->encryptBlob(filename, &mMasterKeyEncryption, mEntropy);
-    }
-
-private:
-    static const char* MASTER_KEY_FILE;
-    static const int MASTER_KEY_SIZE_BYTES = 16;
-    static const int MASTER_KEY_SIZE_BITS = MASTER_KEY_SIZE_BYTES * 8;
-
-    static const int MAX_RETRY = 4;
-    static const size_t SALT_SIZE = 16;
-
-    Entropy* mEntropy;
-
-    State mState;
-    int8_t mRetry;
-
-    uint8_t mMasterKey[MASTER_KEY_SIZE_BYTES];
-    uint8_t mSalt[SALT_SIZE];
-
-    AES_KEY mMasterKeyEncryption;
-    AES_KEY mMasterKeyDecryption;
-
-    void setState(State state) {
-        mState = state;
-        if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) {
-            mRetry = MAX_RETRY;
-        }
-    }
-
-    bool generateSalt() {
-        return mEntropy->generate_random_data(mSalt, sizeof(mSalt));
-    }
-
-    bool generateMasterKey() {
-        if (!mEntropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) {
-            return false;
-        }
-        if (!generateSalt()) {
-            return false;
-        }
-        return true;
-    }
-
-    void setupMasterKeys() {
-        AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption);
-        AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption);
-        setState(STATE_NO_ERROR);
-    }
-
-    void clearMasterKeys() {
-        memset(mMasterKey, 0, sizeof(mMasterKey));
-        memset(mSalt, 0, sizeof(mSalt));
-        memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption));
-        memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption));
-    }
-
-    static void generateKeyFromPassword(uint8_t* key, ssize_t keySize, Value* pw, uint8_t* salt) {
-        size_t saltSize;
-        if (salt != NULL) {
-            saltSize = SALT_SIZE;
-        } else {
-            // pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
-            salt = (uint8_t*) "keystore";
-            // sizeof = 9, not strlen = 8
-            saltSize = sizeof("keystore");
-        }
-        PKCS5_PBKDF2_HMAC_SHA1((char*) pw->value, pw->length, salt, saltSize, 8192, keySize, key);
-    }
-
-    static bool isKeyFile(const char* filename) {
-        return ((strcmp(filename, MASTER_KEY_FILE) != 0)
-                && (strcmp(filename, ".") != 0)
-                && (strcmp(filename, "..") != 0));
-    }
-};
-
-const char* KeyStore::MASTER_KEY_FILE = ".masterkey";
-
-/* Here is the protocol used in both requests and responses:
- *     code [length_1 message_1 ... length_n message_n] end-of-file
- * where code is one byte long and lengths are unsigned 16-bit integers in
- * network order. Thus the maximum length of a message is 65535 bytes. */
-
-static int recv_code(int sock, int8_t* code) {
-    return recv(sock, code, 1, 0) == 1;
-}
-
-static int recv_message(int sock, uint8_t* message, int length) {
-    uint8_t bytes[2];
-    if (recv(sock, &bytes[0], 1, 0) != 1 ||
-        recv(sock, &bytes[1], 1, 0) != 1) {
-        return -1;
-    } else {
-        int offset = bytes[0] << 8 | bytes[1];
-        if (length < offset) {
-            return -1;
-        }
-        length = offset;
-        offset = 0;
-        while (offset < length) {
-            int n = recv(sock, &message[offset], length - offset, 0);
-            if (n <= 0) {
-                return -1;
-            }
-            offset += n;
-        }
-    }
-    return length;
-}
-
-static int recv_end_of_file(int sock) {
-    uint8_t byte;
-    return recv(sock, &byte, 1, 0) == 0;
-}
-
-static void send_code(int sock, int8_t code) {
-    send(sock, &code, 1, 0);
-}
-
-static void send_message(int sock, uint8_t* message, int length) {
-    uint16_t bytes = htons(length);
-    send(sock, &bytes, 2, 0);
-    send(sock, message, length, 0);
-}
-
-/* Here are the actions. Each of them is a function without arguments. All
- * information is defined in global variables, which are set properly before
- * performing an action. The number of parameters required by each action is
- * fixed and defined in a table. If the return value of an action is positive,
- * it will be treated as a response code and transmitted to the client. Note
- * that the lengths of parameters are checked when they are received, so
- * boundary checks on parameters are omitted. */
-
-static const ResponseCode NO_ERROR_RESPONSE_CODE_SENT = (ResponseCode) 0;
-
-static ResponseCode test(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
-    return (ResponseCode) keyStore->getState();
-}
-
-static ResponseCode get(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
-    char filename[NAME_MAX];
-    encode_key(filename, uid, keyName);
-    Blob keyBlob;
-    ResponseCode responseCode = keyStore->get(filename, &keyBlob);
-    if (responseCode != NO_ERROR) {
-        return responseCode;
-    }
-    send_code(sock, NO_ERROR);
-    send_message(sock, keyBlob.getValue(), keyBlob.getLength());
-    return NO_ERROR_RESPONSE_CODE_SENT;
-}
-
-static ResponseCode insert(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value* val) {
-    char filename[NAME_MAX];
-    encode_key(filename, uid, keyName);
-    Blob keyBlob(val->value, val->length, 0, NULL);
-    return keyStore->put(filename, &keyBlob);
-}
-
-static ResponseCode del(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
-    char filename[NAME_MAX];
-    encode_key(filename, uid, keyName);
-    return (unlink(filename) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR;
-}
-
-static ResponseCode exist(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
-    char filename[NAME_MAX];
-    encode_key(filename, uid, keyName);
-    if (access(filename, R_OK) == -1) {
-        return (errno != ENOENT) ? SYSTEM_ERROR : KEY_NOT_FOUND;
-    }
-    return NO_ERROR;
-}
-
-static ResponseCode saw(KeyStore* keyStore, int sock, uid_t uid, Value* keyPrefix, Value*) {
-    DIR* dir = opendir(".");
-    if (!dir) {
-        return SYSTEM_ERROR;
-    }
-    char filename[NAME_MAX];
-    int n = encode_key(filename, uid, keyPrefix);
-    send_code(sock, NO_ERROR);
-
-    struct dirent* file;
-    while ((file = readdir(dir)) != NULL) {
-        if (!strncmp(filename, file->d_name, n)) {
-            char* p = &file->d_name[n];
-            keyPrefix->length = decode_key(keyPrefix->value, p, strlen(p));
-            send_message(sock, keyPrefix->value, keyPrefix->length);
-        }
-    }
-    closedir(dir);
-    return NO_ERROR_RESPONSE_CODE_SENT;
-}
-
-static ResponseCode reset(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
-    return keyStore->reset() ? NO_ERROR : SYSTEM_ERROR;
-}
-
-/* Here is the history. To improve the security, the parameters to generate the
- * master key has been changed. To make a seamless transition, we update the
- * file using the same password when the user unlock it for the first time. If
- * any thing goes wrong during the transition, the new file will not overwrite
- * the old one. This avoids permanent damages of the existing data. */
-
-static ResponseCode password(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value*) {
-    switch (keyStore->getState()) {
-        case STATE_UNINITIALIZED: {
-            // generate master key, encrypt with password, write to file, initialize mMasterKey*.
-            return keyStore->initialize(pw);
-        }
-        case STATE_NO_ERROR: {
-            // rewrite master key with new password.
-            return keyStore->writeMasterKey(pw);
-        }
-        case STATE_LOCKED: {
-            // read master key, decrypt with password, initialize mMasterKey*.
-            return keyStore->readMasterKey(pw);
-        }
-    }
-    return SYSTEM_ERROR;
-}
-
-static ResponseCode lock(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
-    keyStore->lock();
-    return NO_ERROR;
-}
-
-static ResponseCode unlock(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value* unused) {
-    return password(keyStore, sock, uid, pw, unused);
-}
-
-static ResponseCode zero(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
-    return keyStore->isEmpty() ? KEY_NOT_FOUND : NO_ERROR;
-}
-
-/* Here are the permissions, actions, users, and the main function. */
-
-enum perm {
-    TEST     =    1,
-    GET      =    2,
-    INSERT   =    4,
-    DELETE   =    8,
-    EXIST    =   16,
-    SAW      =   32,
-    RESET    =   64,
-    PASSWORD =  128,
-    LOCK     =  256,
-    UNLOCK   =  512,
-    ZERO     = 1024,
-};
-
-static const int MAX_PARAM = 2;
-
-static const State STATE_ANY = (State) 0;
-
-static struct action {
-    ResponseCode (*run)(KeyStore* keyStore, int sock, uid_t uid, Value* param1, Value* param2);
-    int8_t code;
-    State state;
-    uint32_t perm;
-    int lengths[MAX_PARAM];
-} actions[] = {
-    {test,     't', STATE_ANY,      TEST,     {0, 0}},
-    {get,      'g', STATE_NO_ERROR, GET,      {KEY_SIZE, 0}},
-    {insert,   'i', STATE_NO_ERROR, INSERT,   {KEY_SIZE, VALUE_SIZE}},
-    {del,      'd', STATE_ANY,      DELETE,   {KEY_SIZE, 0}},
-    {exist,    'e', STATE_ANY,      EXIST,    {KEY_SIZE, 0}},
-    {saw,      's', STATE_ANY,      SAW,      {KEY_SIZE, 0}},
-    {reset,    'r', STATE_ANY,      RESET,    {0, 0}},
-    {password, 'p', STATE_ANY,      PASSWORD, {PASSWORD_SIZE, 0}},
-    {lock,     'l', STATE_NO_ERROR, LOCK,     {0, 0}},
-    {unlock,   'u', STATE_LOCKED,   UNLOCK,   {PASSWORD_SIZE, 0}},
-    {zero,     'z', STATE_ANY,      ZERO,     {0, 0}},
-    {NULL,      0 , STATE_ANY,      0,        {0, 0}},
-};
-
-static struct user {
-    uid_t uid;
-    uid_t euid;
-    uint32_t perms;
-} users[] = {
-    {AID_SYSTEM,   ~0,         ~0},
-    {AID_VPN,      AID_SYSTEM, GET},
-    {AID_WIFI,     AID_SYSTEM, GET},
-    {AID_ROOT,     AID_SYSTEM, GET},
-    {~0,           ~0,         TEST | GET | INSERT | DELETE | EXIST | SAW},
-};
-
-static ResponseCode process(KeyStore* keyStore, int sock, uid_t uid, int8_t code) {
-    struct user* user = users;
-    struct action* action = actions;
-    int i;
-
-    while (~user->uid && user->uid != uid) {
-        ++user;
-    }
-    while (action->code && action->code != code) {
-        ++action;
-    }
-    if (!action->code) {
-        return UNDEFINED_ACTION;
-    }
-    if (!(action->perm & user->perms)) {
-        return PERMISSION_DENIED;
-    }
-    if (action->state != STATE_ANY && action->state != keyStore->getState()) {
-        return (ResponseCode) keyStore->getState();
-    }
-    if (~user->euid) {
-        uid = user->euid;
-    }
-    Value params[MAX_PARAM];
-    for (i = 0; i < MAX_PARAM && action->lengths[i] != 0; ++i) {
-        params[i].length = recv_message(sock, params[i].value, action->lengths[i]);
-        if (params[i].length < 0) {
-            return PROTOCOL_ERROR;
-        }
-    }
-    if (!recv_end_of_file(sock)) {
-        return PROTOCOL_ERROR;
-    }
-    return action->run(keyStore, sock, uid, &params[0], &params[1]);
-}
-
-int main(int argc, char* argv[]) {
-    int controlSocket = android_get_control_socket("keystore");
-    if (argc < 2) {
-        ALOGE("A directory must be specified!");
-        return 1;
-    }
-    if (chdir(argv[1]) == -1) {
-        ALOGE("chdir: %s: %s", argv[1], strerror(errno));
-        return 1;
-    }
-
-    Entropy entropy;
-    if (!entropy.open()) {
-        return 1;
-    }
-    if (listen(controlSocket, 3) == -1) {
-        ALOGE("listen: %s", strerror(errno));
-        return 1;
-    }
-
-    signal(SIGPIPE, SIG_IGN);
-
-    KeyStore keyStore(&entropy);
-    int sock;
-    while ((sock = accept(controlSocket, NULL, 0)) != -1) {
-        struct timeval tv;
-        tv.tv_sec = 3;
-        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
-        setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
-
-        struct ucred cred;
-        socklen_t size = sizeof(cred);
-        int credResult = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cred, &size);
-        if (credResult != 0) {
-            ALOGW("getsockopt: %s", strerror(errno));
-        } else {
-            int8_t request;
-            if (recv_code(sock, &request)) {
-                State old_state = keyStore.getState();
-                ResponseCode response = process(&keyStore, sock, cred.uid, request);
-                if (response == NO_ERROR_RESPONSE_CODE_SENT) {
-                    response = NO_ERROR;
-                } else {
-                    send_code(sock, response);
-                }
-                ALOGI("uid: %d action: %c -> %d state: %d -> %d retry: %d",
-                     cred.uid,
-                     request, response,
-                     old_state, keyStore.getState(),
-                     keyStore.getRetry());
-            }
-        }
-        close(sock);
-    }
-    ALOGE("accept: %s", strerror(errno));
-    return 1;
-}
diff --git a/cmds/keystore/keystore.h b/cmds/keystore/keystore.h
deleted file mode 100644
index 5ae3d24..0000000
--- a/cmds/keystore/keystore.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __KEYSTORE_H__
-#define __KEYSTORE_H__
-
-// note state values overlap with ResponseCode for the purposes of the state() API
-enum State {
-    STATE_NO_ERROR      = 1,
-    STATE_LOCKED        = 2,
-    STATE_UNINITIALIZED = 3,
-};
-
-enum ResponseCode {
-    NO_ERROR          =  STATE_NO_ERROR, // 1
-    LOCKED            =  STATE_LOCKED, // 2
-    UNINITIALIZED     =  STATE_UNINITIALIZED, // 3
-    SYSTEM_ERROR      =  4,
-    PROTOCOL_ERROR    =  5,
-    PERMISSION_DENIED =  6,
-    KEY_NOT_FOUND     =  7,
-    VALUE_CORRUPTED   =  8,
-    UNDEFINED_ACTION  =  9,
-    WRONG_PASSWORD_0  = 10,
-    WRONG_PASSWORD_1  = 11,
-    WRONG_PASSWORD_2  = 12,
-    WRONG_PASSWORD_3  = 13, // MAX_RETRY = 4
-};
-
-#endif
diff --git a/cmds/keystore/keystore_cli.cpp b/cmds/keystore/keystore_cli.cpp
deleted file mode 100644
index dcd3bcb..0000000
--- a/cmds/keystore/keystore_cli.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <cutils/sockets.h>
-
-#include "keystore.h"
-
-static const char* responses[] = {
-    NULL,
-    /* [NO_ERROR]           = */ "No error",
-    /* [LOCKED]             = */ "Locked",
-    /* [UNINITIALIZED]      = */ "Uninitialized",
-    /* [SYSTEM_ERROR]       = */ "System error",
-    /* [PROTOCOL_ERROR]     = */ "Protocol error",
-    /* [PERMISSION_DENIED]  = */ "Permission denied",
-    /* [KEY_NOT_FOUND]      = */ "Key not found",
-    /* [VALUE_CORRUPTED]    = */ "Value corrupted",
-    /* [UNDEFINED_ACTION]   = */ "Undefined action",
-    /* [WRONG_PASSWORD]     = */ "Wrong password (last chance)",
-    /* [WRONG_PASSWORD + 1] = */ "Wrong password (2 tries left)",
-    /* [WRONG_PASSWORD + 2] = */ "Wrong password (3 tries left)",
-    /* [WRONG_PASSWORD + 3] = */ "Wrong password (4 tries left)",
-};
-
-int main(int argc, char* argv[])
-{
-    if (argc < 2) {
-        printf("Usage: %s action [parameter ...]\n", argv[0]);
-        return 0;
-    }
-
-    int sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                   SOCK_STREAM);
-    if (sock == -1) {
-        puts("Failed to connect");
-        return 1;
-    }
-
-    send(sock, argv[1], 1, 0);
-    uint8_t bytes[65536];
-    for (int i = 2; i < argc; ++i) {
-        uint16_t length = strlen(argv[i]);
-        bytes[0] = length >> 8;
-        bytes[1] = length;
-        send(sock, &bytes, 2, 0);
-        send(sock, argv[i], length, 0);
-    }
-    shutdown(sock, SHUT_WR);
-
-    uint8_t code;
-    if (recv(sock, &code, 1, 0) != 1) {
-        puts("Failed to receive");
-        return 1;
-    }
-    printf("%d %s\n", code , responses[code] ? responses[code] : "Unknown");
-    int i;
-    while ((i = recv(sock, &bytes[0], 1, 0)) == 1) {
-        int length;
-        int offset;
-        if ((i = recv(sock, &bytes[1], 1, 0)) != 1) {
-            puts("Failed to receive");
-            return 1;
-        }
-        length = bytes[0] << 8 | bytes[1];
-        for (offset = 0; offset < length; offset += i) {
-            i = recv(sock, &bytes[offset], length - offset, 0);
-            if (i <= 0) {
-                puts("Failed to receive");
-                return 1;
-            }
-        }
-        fwrite(bytes, 1, length, stdout);
-        puts("");
-    }
-    return 0;
-}
diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h
deleted file mode 100644
index 4b4923e..0000000
--- a/cmds/keystore/keystore_get.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __KEYSTORE_GET_H__
-#define __KEYSTORE_GET_H__
-
-#include <stdio.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <cutils/sockets.h>
-
-#define KEYSTORE_MESSAGE_SIZE 65535
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* This function is provided for native components to get values from keystore.
- * Users are required to link against libcutils. Keys and values are 8-bit safe.
- * The first two arguments are the key and its length. The third argument
- * specifies the buffer to store the retrieved value, which must be an array of
- * KEYSTORE_MESSAGE_SIZE bytes. This function returns the length of the value or
- * -1 if an error happens. */
-static int keystore_get(const char *key, int length, char *value)
-{
-    uint8_t bytes[2] = {length >> 8, length};
-    uint8_t code = 'g';
-    int sock;
-
-    if (length < 0 || length > KEYSTORE_MESSAGE_SIZE) {
-        return -1;
-    }
-    sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                               SOCK_STREAM);
-    if (sock == -1) {
-        return -1;
-    }
-    if (send(sock, &code, 1, 0) == 1 && send(sock, bytes, 2, 0) == 2 &&
-        send(sock, key, length, 0) == length && shutdown(sock, SHUT_WR) == 0 &&
-        recv(sock, &code, 1, 0) == 1 && code == /* NO_ERROR */ 1 &&
-        recv(sock, &bytes[0], 1, 0) == 1 && recv(sock, &bytes[1], 1, 0) == 1) {
-        int offset = 0;
-        length = bytes[0] << 8 | bytes[1];
-        while (offset < length) {
-            int n = recv(sock, &value[offset], length - offset, 0);
-            if (n <= 0) {
-                length = -1;
-                break;
-            }
-            offset += n;
-        }
-    } else {
-        length = -1;
-    }
-
-    close(sock);
-    return length;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/cmds/keystore/test-keystore b/cmds/keystore/test-keystore
deleted file mode 100755
index 3be51b3..0000000
--- a/cmds/keystore/test-keystore
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-set -e
-
-prefix=$0
-log_file=$prefix.log
-baseline_file=$prefix.baseline
-
-function cleanup_output() {
-    rm -f $log_file
-    rm -f $baseline_file
-}
-
-function log() {
-    echo "$@"
-    append $log_file \# "$@"
-    append $baseline_file \# "$@"
-}
-
-function expect() {
-    append $baseline_file "$@"
-}
-
-function append() {
-    declare -r file=$1
-    shift
-    echo "$@" >> $file
-}
-
-function run() {
-    # strip out carriage returns from adb
-    # strip out date/time from ls -l
-    "$@" | tr --delete '\r' | sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} +[0-9]{1,2}:[0-9]{2} //' >> $log_file
-}
-
-function keystore() {
-    declare -r user=$1
-    shift
-    run adb shell su $user keystore_cli "$@"
-}
-
-function list_keystore_directory() {
-    run adb shell ls -al /data/misc/keystore
-}
-
-function compare() {
-    log "comparing $baseline_file and $log_file"
-    diff $baseline_file $log_file || (log $tag FAILED && exit 1)
-}
-
-function test_basic() {
-
-    #
-    # reset
-    #
-    log "reset keystore as system user"
-    keystore system r
-    expect "1 No error"
-    list_keystore_directory
-
-    #
-    # basic tests as system/root
-    #
-    log "root does not have permission to run test"
-    keystore root t
-    expect "6 Permission denied"
-    
-    log "but system user does"
-    keystore system t
-    expect "3 Uninitialized"
-    list_keystore_directory
-
-    log "password is now bar"
-    keystore system p bar
-    expect "1 No error"
-    list_keystore_directory
-    expect "-rw------- keystore keystore       84 .masterkey"
-    
-    log "no error implies initialized and unlocked"
-    keystore system t
-    expect "1 No error"
-    
-    log "saw with no argument"
-    keystore system s
-    expect "5 Protocol error"
-
-    log "saw nothing"
-    keystore system s ""
-    expect "1 No error"
-
-    log "add key baz"
-    keystore system i baz quux
-    expect "1 No error"
-
-    log "1000 is uid of system"
-    list_keystore_directory
-    expect "-rw------- keystore keystore       84 .masterkey"
-    expect "-rw------- keystore keystore       52 1000_baz"
-
-    log "saw baz"
-    keystore system s ""
-    expect "1 No error"
-    expect "baz"
-
-    log "get baz"
-    keystore system g baz
-    expect "1 No error"
-    expect "quux"
-
-    log "root can read system user keys (as can wifi or vpn users)"
-    keystore root g baz
-    expect "1 No error"
-    expect "quux"
-
-    #
-    # app user tests
-    #
-
-    # app_0 has uid 10000, as seen below
-    log "other uses cannot see the system keys"
-    keystore app_0 g baz
-    expect "7 Key not found"
-    
-    log "app user cannot use reset, password, lock, unlock"
-    keystore app_0 r
-    expect "6 Permission denied"
-    keystore app_0 p
-    expect "6 Permission denied"
-    keystore app_0 l
-    expect "6 Permission denied"
-    keystore app_0 u
-    expect "6 Permission denied"
-
-    log "install app_0 key"
-    keystore app_0 i 0x deadbeef
-    expect 1 No error
-    list_keystore_directory
-    expect "-rw------- keystore keystore       84 .masterkey"
-    expect "-rw------- keystore keystore       52 10000_0x"
-    expect "-rw------- keystore keystore       52 1000_baz"
-
-    log "get with no argument"
-    keystore app_0 g
-    expect "5 Protocol error"
-    
-    keystore app_0 g 0x
-    expect "1 No error"
-    expect "deadbeef"
-    
-    keystore app_0 i fred barney
-    expect "1 No error"
-    
-    keystore app_0 s ""
-    expect "1 No error"
-    expect "0x"
-    expect "fred"
-
-    log "note that saw returns the suffix of prefix matches"
-    keystore app_0 s fr # fred
-    expect "1 No error"
-    expect "ed" # fred
-
-    #
-    # lock tests
-    #
-    log "lock the store as system"
-    keystore system l
-    expect "1 No error"
-    keystore system t
-    expect "2 Locked"
-    
-    log "saw works while locked"
-    keystore app_0 s ""
-    expect "1 No error"
-    expect "0x"
-    expect "fred"
-
-    log "...but cannot read keys..."
-    keystore app_0 g 0x
-    expect "2 Locked"
-    
-    log "...but they can be deleted."
-    keystore app_0 e 0x
-    expect "1 No error"
-    keystore app_0 d 0x
-    expect "1 No error"
-    keystore app_0 e 0x
-    expect "7 Key not found"
-
-    #
-    # password
-    #
-    log "wrong password"
-    keystore system u foo
-    expect "13 Wrong password (4 tries left)"
-    log "right password"
-    keystore system u bar
-    expect "1 No error"
-    
-    log "make the password foo"
-    keystore system p foo
-    expect "1 No error"
-    
-    #
-    # final reset
-    #
-    log "reset wipes everything for all users"
-    keystore system r
-    expect "1 No error"
-    list_keystore_directory
-    
-    keystore system t
-    expect "3 Uninitialized"
-
-}
-
-function test_4599735() {
-    # http://b/4599735
-    log "start regression test for b/4599735"
-    keystore system r
-    expect "1 No error"
-
-    keystore system p foo
-    expect "1 No error"
-
-    keystore system i baz quux
-    expect "1 No error"
-    
-    keystore root g baz
-    expect "1 No error"
-    expect "quux"
-
-    keystore system l
-    expect "1 No error"
-
-    keystore system p foo
-    expect "1 No error"
-
-    log "after unlock, regression led to result of '8 Value corrupted'"
-    keystore root g baz
-    expect "1 No error"
-    expect "quux"
-
-    keystore system r
-    expect "1 No error"
-    log "end regression test for b/4599735"
-}
-
-function main() {
-    cleanup_output
-    log $tag START
-    test_basic
-    test_4599735
-    compare
-    log $tag PASSED
-    cleanup_output
-}
-
-main
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index bee5880..90dfe76 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -49,7 +49,6 @@
 {
     switch (f) {
         case PIXEL_FORMAT_A_8:
-        case PIXEL_FORMAT_L_8:
             return SkBitmap::kA8_Config;
         case PIXEL_FORMAT_RGB_565:
             return SkBitmap::kRGB_565_Config;
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 4ed2489..71c5622 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -43,6 +43,8 @@
     { AID_RADIO, "isms" },
     { AID_RADIO, "iphonesubinfo" },
     { AID_RADIO, "simphonebook" },
+    { AID_MEDIA, "common_time.clock" },
+    { AID_MEDIA, "common_time.config" },
 };
 
 void *svcmgr_handle;
@@ -90,6 +92,7 @@
     struct svcinfo *next;
     void *ptr;
     struct binder_death death;
+    int allow_isolated;
     unsigned len;
     uint16_t name[0];
 };
@@ -125,13 +128,21 @@
 };
   
 
-void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len)
+void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len, unsigned uid)
 {
     struct svcinfo *si;
     si = find_svc(s, len);
 
 //    ALOGI("check_service('%s') ptr = %p\n", str8(s), si ? si->ptr : 0);
     if (si && si->ptr) {
+        if (!si->allow_isolated) {
+            // If this service doesn't allow access from isolated processes,
+            // then check the uid to see if it is isolated.
+            unsigned appid = uid % AID_USER;
+            if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
+                return 0;
+            }
+        }
         return si->ptr;
     } else {
         return 0;
@@ -140,10 +151,11 @@
 
 int do_add_service(struct binder_state *bs,
                    uint16_t *s, unsigned len,
-                   void *ptr, unsigned uid)
+                   void *ptr, unsigned uid, int allow_isolated)
 {
     struct svcinfo *si;
-//    ALOGI("add_service('%s',%p) uid=%d\n", str8(s), ptr, uid);
+    //ALOGI("add_service('%s',%p,%s) uid=%d\n", str8(s), ptr,
+    //        allow_isolated ? "allow_isolated" : "!allow_isolated", uid);
 
     if (!ptr || (len == 0) || (len > 127))
         return -1;
@@ -175,6 +187,7 @@
         si->name[len] = '\0';
         si->death.func = svcinfo_death;
         si->death.ptr = si;
+        si->allow_isolated = allow_isolated;
         si->next = svclist;
         svclist = si;
     }
@@ -194,6 +207,7 @@
     unsigned len;
     void *ptr;
     uint32_t strict_policy;
+    int allow_isolated;
 
 //    ALOGI("target=%p code=%d pid=%d uid=%d\n",
 //         txn->target, txn->code, txn->sender_pid, txn->sender_euid);
@@ -217,7 +231,7 @@
     case SVC_MGR_GET_SERVICE:
     case SVC_MGR_CHECK_SERVICE:
         s = bio_get_string16(msg, &len);
-        ptr = do_find_service(bs, s, len);
+        ptr = do_find_service(bs, s, len, txn->sender_euid);
         if (!ptr)
             break;
         bio_put_ref(reply, ptr);
@@ -226,7 +240,8 @@
     case SVC_MGR_ADD_SERVICE:
         s = bio_get_string16(msg, &len);
         ptr = bio_get_ref(msg);
-        if (do_add_service(bs, s, len, ptr, txn->sender_euid))
+        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
+        if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated))
             return -1;
         break;
 
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index e9642f7..f26747b 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -35,7 +35,7 @@
         record.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright liblog libutils libbinder
+	libstagefright liblog libutils libbinder libstagefright_foundation
 
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
@@ -59,7 +59,7 @@
         recordvideo.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright liblog libutils libbinder
+	libstagefright liblog libutils libbinder libstagefright_foundation
 
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
@@ -84,7 +84,7 @@
         audioloop.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright liblog libutils libbinder
+	libstagefright liblog libutils libbinder libstagefright_foundation
 
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
@@ -147,4 +147,28 @@
 
 include $(BUILD_EXECUTABLE)
 
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=               \
+        codec.cpp               \
+        SimplePlayer.cpp        \
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright liblog libutils libbinder libstagefright_foundation \
+        libmedia libgui libcutils libui
+
+LOCAL_C_INCLUDES:= \
+	$(JNI_H_INCLUDE) \
+	frameworks/base/media/libstagefright \
+	$(TOP)/frameworks/base/include/media/stagefright/openmax
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_MODULE:= codec
+
+include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
new file mode 100644
index 0000000..f269e80
--- /dev/null
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SimplePlayer"
+#include <utils/Log.h>
+
+#include "SimplePlayer.h"
+
+#include <gui/SurfaceTextureClient.h>
+#include <media/AudioTrack.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/NativeWindowWrapper.h>
+#include <media/stagefright/NuMediaExtractor.h>
+
+namespace android {
+
+SimplePlayer::SimplePlayer()
+    : mState(UNINITIALIZED),
+      mDoMoreStuffGeneration(0),
+      mStartTimeRealUs(-1ll) {
+}
+
+SimplePlayer::~SimplePlayer() {
+}
+
+// static
+status_t PostAndAwaitResponse(
+        const sp<AMessage> &msg, sp<AMessage> *response) {
+    status_t err = msg->postAndAwaitResponse(response);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (!(*response)->findInt32("err", &err)) {
+        err = OK;
+    }
+
+    return err;
+}
+status_t SimplePlayer::setDataSource(const char *path) {
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+    msg->setString("path", path);
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::setSurface(const sp<ISurfaceTexture> &surfaceTexture) {
+    sp<AMessage> msg = new AMessage(kWhatSetSurface, id());
+
+    sp<SurfaceTextureClient> surfaceTextureClient;
+    if (surfaceTexture != NULL) {
+        surfaceTextureClient = new SurfaceTextureClient(surfaceTexture);
+    }
+
+    msg->setObject(
+            "native-window", new NativeWindowWrapper(surfaceTextureClient));
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::prepare() {
+    sp<AMessage> msg = new AMessage(kWhatPrepare, id());
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::start() {
+    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::stop() {
+    sp<AMessage> msg = new AMessage(kWhatStop, id());
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::reset() {
+    sp<AMessage> msg = new AMessage(kWhatReset, id());
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSetDataSource:
+        {
+            status_t err;
+            if (mState != UNINITIALIZED) {
+                err = INVALID_OPERATION;
+            } else {
+                CHECK(msg->findString("path", &mPath));
+                mState = UNPREPARED;
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatSetSurface:
+        {
+            status_t err;
+            if (mState != UNPREPARED) {
+                err = INVALID_OPERATION;
+            } else {
+                sp<RefBase> obj;
+                CHECK(msg->findObject("native-window", &obj));
+
+                mNativeWindow = static_cast<NativeWindowWrapper *>(obj.get());
+
+                err = OK;
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatPrepare:
+        {
+            status_t err;
+            if (mState != UNPREPARED) {
+                err = INVALID_OPERATION;
+            } else {
+                err = onPrepare();
+
+                if (err == OK) {
+                    mState = STOPPED;
+                }
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatStart:
+        {
+            status_t err = OK;
+
+            if (mState == UNPREPARED) {
+                err = onPrepare();
+
+                if (err == OK) {
+                    mState = STOPPED;
+                }
+            }
+
+            if (err == OK) {
+                if (mState != STOPPED) {
+                    err = INVALID_OPERATION;
+                } else {
+                    err = onStart();
+
+                    if (err == OK) {
+                        mState = STARTED;
+                    }
+                }
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatStop:
+        {
+            status_t err;
+
+            if (mState != STARTED) {
+                err = INVALID_OPERATION;
+            } else {
+                err = onStop();
+
+                if (err == OK) {
+                    mState = STOPPED;
+                }
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatReset:
+        {
+            status_t err = OK;
+
+            if (mState == STARTED) {
+                CHECK_EQ(onStop(), (status_t)OK);
+                mState = STOPPED;
+            }
+
+            if (mState == STOPPED) {
+                err = onReset();
+                mState = UNINITIALIZED;
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatDoMoreStuff:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mDoMoreStuffGeneration) {
+                break;
+            }
+
+            status_t err = onDoMoreStuff();
+
+            if (err == OK) {
+                msg->post(10000ll);
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+status_t SimplePlayer::onPrepare() {
+    CHECK_EQ(mState, UNPREPARED);
+
+    mExtractor = new NuMediaExtractor;
+
+    status_t err = mExtractor->setDataSource(mPath.c_str());
+
+    if (err != OK) {
+        mExtractor.clear();
+        return err;
+    }
+
+    if (mCodecLooper == NULL) {
+        mCodecLooper = new ALooper;
+        mCodecLooper->start();
+    }
+
+    bool haveAudio = false;
+    bool haveVideo = false;
+    for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+        sp<AMessage> format;
+        status_t err = mExtractor->getTrackFormat(i, &format);
+        CHECK_EQ(err, (status_t)OK);
+
+        AString mime;
+        CHECK(format->findString("mime", &mime));
+
+        if (!haveAudio && !strncasecmp(mime.c_str(), "audio/", 6)) {
+            haveAudio = true;
+        } else if (!haveVideo && !strncasecmp(mime.c_str(), "video/", 6)) {
+            haveVideo = true;
+        } else {
+            continue;
+        }
+
+        err = mExtractor->selectTrack(i);
+        CHECK_EQ(err, (status_t)OK);
+
+        CodecState *state =
+            &mStateByTrackIndex.editValueAt(
+                    mStateByTrackIndex.add(i, CodecState()));
+
+        state->mNumFramesWritten = 0;
+        state->mCodec = MediaCodec::CreateByType(
+                mCodecLooper, mime.c_str(), false /* encoder */);
+
+        CHECK(state->mCodec != NULL);
+
+        err = state->mCodec->configure(
+                format, mNativeWindow->getSurfaceTextureClient(),
+                0 /* flags */);
+
+        CHECK_EQ(err, (status_t)OK);
+
+        size_t j = 0;
+        sp<ABuffer> buffer;
+        while (format->findBuffer(StringPrintf("csd-%d", j).c_str(), &buffer)) {
+            state->mCSD.push_back(buffer);
+
+            ++j;
+        }
+    }
+
+    for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
+        CodecState *state = &mStateByTrackIndex.editValueAt(i);
+
+        status_t err = state->mCodec->start();
+        CHECK_EQ(err, (status_t)OK);
+
+        err = state->mCodec->getInputBuffers(&state->mBuffers[0]);
+        CHECK_EQ(err, (status_t)OK);
+
+        err = state->mCodec->getOutputBuffers(&state->mBuffers[1]);
+        CHECK_EQ(err, (status_t)OK);
+
+        for (size_t j = 0; j < state->mCSD.size(); ++j) {
+            const sp<ABuffer> &srcBuffer = state->mCSD.itemAt(j);
+
+            size_t index;
+            err = state->mCodec->dequeueInputBuffer(&index, -1ll);
+            CHECK_EQ(err, (status_t)OK);
+
+            const sp<ABuffer> &dstBuffer = state->mBuffers[0].itemAt(index);
+
+            CHECK_LE(srcBuffer->size(), dstBuffer->capacity());
+            dstBuffer->setRange(0, srcBuffer->size());
+            memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size());
+
+            err = state->mCodec->queueInputBuffer(
+                    index,
+                    0,
+                    dstBuffer->size(),
+                    0ll,
+                    MediaCodec::BUFFER_FLAG_CODECCONFIG);
+            CHECK_EQ(err, (status_t)OK);
+        }
+    }
+
+    return OK;
+}
+
+status_t SimplePlayer::onStart() {
+    CHECK_EQ(mState, STOPPED);
+
+    mStartTimeRealUs = -1ll;
+
+    sp<AMessage> msg = new AMessage(kWhatDoMoreStuff, id());
+    msg->setInt32("generation", ++mDoMoreStuffGeneration);
+    msg->post();
+
+    return OK;
+}
+
+status_t SimplePlayer::onStop() {
+    CHECK_EQ(mState, STARTED);
+
+    ++mDoMoreStuffGeneration;
+
+    return OK;
+}
+
+status_t SimplePlayer::onReset() {
+    CHECK_EQ(mState, STOPPED);
+
+    for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
+        CodecState *state = &mStateByTrackIndex.editValueAt(i);
+
+        CHECK_EQ(state->mCodec->stop(), (status_t)OK);
+    }
+
+    mStartTimeRealUs = -1ll;
+
+    mStateByTrackIndex.clear();
+    mCodecLooper.clear();
+    mExtractor.clear();
+    mNativeWindow.clear();
+    mPath.clear();
+
+    return OK;
+}
+
+status_t SimplePlayer::onDoMoreStuff() {
+    ALOGV("onDoMoreStuff");
+    for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
+        CodecState *state = &mStateByTrackIndex.editValueAt(i);
+
+        status_t err;
+        do {
+            size_t index;
+            err = state->mCodec->dequeueInputBuffer(&index);
+
+            if (err == OK) {
+                ALOGV("dequeued input buffer on track %d",
+                      mStateByTrackIndex.keyAt(i));
+
+                state->mAvailInputBufferIndices.push_back(index);
+            } else {
+                ALOGV("dequeueInputBuffer on track %d returned %d",
+                      mStateByTrackIndex.keyAt(i), err);
+            }
+        } while (err == OK);
+
+        do {
+            BufferInfo info;
+            err = state->mCodec->dequeueOutputBuffer(
+                    &info.mIndex,
+                    &info.mOffset,
+                    &info.mSize,
+                    &info.mPresentationTimeUs,
+                    &info.mFlags);
+
+            if (err == OK) {
+                ALOGV("dequeued output buffer on track %d",
+                      mStateByTrackIndex.keyAt(i));
+
+                state->mAvailOutputBufferInfos.push_back(info);
+            } else if (err == INFO_FORMAT_CHANGED) {
+                err = onOutputFormatChanged(mStateByTrackIndex.keyAt(i), state);
+                CHECK_EQ(err, (status_t)OK);
+            } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
+                err = state->mCodec->getOutputBuffers(&state->mBuffers[1]);
+                CHECK_EQ(err, (status_t)OK);
+            } else {
+                ALOGV("dequeueOutputBuffer on track %d returned %d",
+                      mStateByTrackIndex.keyAt(i), err);
+            }
+        } while (err == OK
+                || err == INFO_FORMAT_CHANGED
+                || err == INFO_OUTPUT_BUFFERS_CHANGED);
+    }
+
+    for (;;) {
+        size_t trackIndex;
+        status_t err = mExtractor->getSampleTrackIndex(&trackIndex);
+
+        if (err != OK) {
+            ALOGI("encountered input EOS.");
+            break;
+        } else {
+            CodecState *state = &mStateByTrackIndex.editValueFor(trackIndex);
+
+            if (state->mAvailInputBufferIndices.empty()) {
+                break;
+            }
+
+            size_t index = *state->mAvailInputBufferIndices.begin();
+            state->mAvailInputBufferIndices.erase(
+                    state->mAvailInputBufferIndices.begin());
+
+            const sp<ABuffer> &dstBuffer =
+                state->mBuffers[0].itemAt(index);
+
+            err = mExtractor->readSampleData(dstBuffer);
+            CHECK_EQ(err, (status_t)OK);
+
+            int64_t timeUs;
+            CHECK_EQ(mExtractor->getSampleTime(&timeUs), (status_t)OK);
+
+            err = state->mCodec->queueInputBuffer(
+                    index,
+                    dstBuffer->offset(),
+                    dstBuffer->size(),
+                    timeUs,
+                    0);
+            CHECK_EQ(err, (status_t)OK);
+
+            ALOGV("enqueued input data on track %d", trackIndex);
+
+            err = mExtractor->advance();
+            CHECK_EQ(err, (status_t)OK);
+        }
+    }
+
+    int64_t nowUs = ALooper::GetNowUs();
+
+    if (mStartTimeRealUs < 0ll) {
+        mStartTimeRealUs = nowUs + 1000000ll;
+    }
+
+    for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
+        CodecState *state = &mStateByTrackIndex.editValueAt(i);
+
+        while (!state->mAvailOutputBufferInfos.empty()) {
+            BufferInfo *info = &*state->mAvailOutputBufferInfos.begin();
+
+            int64_t whenRealUs = info->mPresentationTimeUs + mStartTimeRealUs;
+            int64_t lateByUs = nowUs - whenRealUs;
+
+            if (lateByUs > -10000ll) {
+                bool release = true;
+
+                if (lateByUs > 30000ll) {
+                    ALOGI("track %d buffer late by %lld us, dropping.",
+                          mStateByTrackIndex.keyAt(i), lateByUs);
+                    state->mCodec->releaseOutputBuffer(info->mIndex);
+                } else {
+                    if (state->mAudioTrack != NULL) {
+                        const sp<ABuffer> &srcBuffer =
+                            state->mBuffers[1].itemAt(info->mIndex);
+
+                        renderAudio(state, info, srcBuffer);
+
+                        if (info->mSize > 0) {
+                            release = false;
+                        }
+                    }
+
+                    if (release) {
+                        state->mCodec->renderOutputBufferAndRelease(
+                                info->mIndex);
+                    }
+                }
+
+                if (release) {
+                    state->mAvailOutputBufferInfos.erase(
+                            state->mAvailOutputBufferInfos.begin());
+
+                    info = NULL;
+                } else {
+                    break;
+                }
+            } else {
+                ALOGV("track %d buffer early by %lld us.",
+                      mStateByTrackIndex.keyAt(i), -lateByUs);
+                break;
+            }
+        }
+    }
+
+    return OK;
+}
+
+status_t SimplePlayer::onOutputFormatChanged(
+        size_t trackIndex, CodecState *state) {
+    sp<AMessage> format;
+    status_t err = state->mCodec->getOutputFormat(&format);
+
+    if (err != OK) {
+        return err;
+    }
+
+    AString mime;
+    CHECK(format->findString("mime", &mime));
+
+    if (!strncasecmp(mime.c_str(), "audio/", 6)) {
+        int32_t channelCount;
+        int32_t sampleRate;
+        CHECK(format->findInt32("channel-count", &channelCount));
+        CHECK(format->findInt32("sample-rate", &sampleRate));
+
+        state->mAudioTrack = new AudioTrack(
+                AUDIO_STREAM_MUSIC,
+                sampleRate,
+                AUDIO_FORMAT_PCM_16_BIT,
+                (channelCount == 1)
+                    ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO,
+                0);
+
+        state->mNumFramesWritten = 0;
+    }
+
+    return OK;
+}
+
+void SimplePlayer::renderAudio(
+        CodecState *state, BufferInfo *info, const sp<ABuffer> &buffer) {
+    CHECK(state->mAudioTrack != NULL);
+
+    if (state->mAudioTrack->stopped()) {
+        state->mAudioTrack->start();
+    }
+
+    uint32_t numFramesPlayed;
+    CHECK_EQ(state->mAudioTrack->getPosition(&numFramesPlayed), (status_t)OK);
+
+    uint32_t numFramesAvailableToWrite =
+        state->mAudioTrack->frameCount()
+            - (state->mNumFramesWritten - numFramesPlayed);
+
+    size_t numBytesAvailableToWrite =
+        numFramesAvailableToWrite * state->mAudioTrack->frameSize();
+
+    size_t copy = info->mSize;
+    if (copy > numBytesAvailableToWrite) {
+        copy = numBytesAvailableToWrite;
+    }
+
+    if (copy == 0) {
+        return;
+    }
+
+    int64_t startTimeUs = ALooper::GetNowUs();
+
+    ssize_t nbytes = state->mAudioTrack->write(
+            buffer->base() + info->mOffset, copy);
+
+    CHECK_EQ(nbytes, (ssize_t)copy);
+
+    int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
+
+    uint32_t numFramesWritten = nbytes / state->mAudioTrack->frameSize();
+
+    if (delayUs > 2000ll) {
+        ALOGW("AudioTrack::write took %lld us, numFramesAvailableToWrite=%u, "
+              "numFramesWritten=%u",
+              delayUs, numFramesAvailableToWrite, numFramesWritten);
+    }
+
+    info->mOffset += nbytes;
+    info->mSize -= nbytes;
+
+    state->mNumFramesWritten += numFramesWritten;
+}
+
+}  // namespace android
diff --git a/cmds/stagefright/SimplePlayer.h b/cmds/stagefright/SimplePlayer.h
new file mode 100644
index 0000000..2548252
--- /dev/null
+++ b/cmds/stagefright/SimplePlayer.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+struct ABuffer;
+struct ALooper;
+struct AudioTrack;
+struct ISurfaceTexture;
+struct MediaCodec;
+struct NativeWindowWrapper;
+struct NuMediaExtractor;
+
+struct SimplePlayer : public AHandler {
+    SimplePlayer();
+
+    status_t setDataSource(const char *path);
+    status_t setSurface(const sp<ISurfaceTexture> &surfaceTexture);
+    status_t prepare();
+    status_t start();
+    status_t stop();
+    status_t reset();
+
+protected:
+    virtual ~SimplePlayer();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum State {
+        UNINITIALIZED,
+        UNPREPARED,
+        STOPPED,
+        STARTED
+    };
+
+    enum {
+        kWhatSetDataSource,
+        kWhatSetSurface,
+        kWhatPrepare,
+        kWhatStart,
+        kWhatStop,
+        kWhatReset,
+        kWhatDoMoreStuff,
+    };
+
+    struct BufferInfo {
+        size_t mIndex;
+        size_t mOffset;
+        size_t mSize;
+        int64_t mPresentationTimeUs;
+        uint32_t mFlags;
+    };
+
+    struct CodecState
+    {
+        sp<MediaCodec> mCodec;
+        Vector<sp<ABuffer> > mCSD;
+        Vector<sp<ABuffer> > mBuffers[2];
+
+        List<size_t> mAvailInputBufferIndices;
+        List<BufferInfo> mAvailOutputBufferInfos;
+
+        sp<AudioTrack> mAudioTrack;
+        uint32_t mNumFramesWritten;
+    };
+
+    State mState;
+    AString mPath;
+    sp<NativeWindowWrapper> mNativeWindow;
+
+    sp<NuMediaExtractor> mExtractor;
+    sp<ALooper> mCodecLooper;
+    KeyedVector<size_t, CodecState> mStateByTrackIndex;
+    int32_t mDoMoreStuffGeneration;
+
+    int64_t mStartTimeRealUs;
+
+    status_t onPrepare();
+    status_t onStart();
+    status_t onStop();
+    status_t onReset();
+    status_t onDoMoreStuff();
+    status_t onOutputFormatChanged(size_t trackIndex, CodecState *state);
+
+    void renderAudio(
+            CodecState *state, BufferInfo *info, const sp<ABuffer> &buffer);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SimplePlayer);
+};
+
+}  // namespace android
diff --git a/cmds/stagefright/SineSource.cpp b/cmds/stagefright/SineSource.cpp
index 021f636..14b4306 100644
--- a/cmds/stagefright/SineSource.cpp
+++ b/cmds/stagefright/SineSource.cpp
@@ -3,7 +3,7 @@
 #include <math.h>
 
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index 858681f..a6362a4 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -2,10 +2,10 @@
 
 #include <binder/ProcessState.h>
 #include <media/mediarecorder.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AMRWriter.h>
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/AudioSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXClient.h>
@@ -24,7 +24,7 @@
     android::ProcessState::self()->startThreadPool();
 
     OMXClient client;
-    CHECK_EQ(client.connect(), OK);
+    CHECK_EQ(client.connect(), (status_t)OK);
 
 #if 0
     sp<MediaSource> source = new SineSource(kSampleRate, kNumChannels);
@@ -82,7 +82,7 @@
     delete player;
     player = NULL;
 #elif 0
-    CHECK_EQ(decoder->start(), OK);
+    CHECK_EQ(decoder->start(), (status_t)OK);
 
     MediaBuffer *buffer;
     while (decoder->read(&buffer) == OK) {
@@ -95,7 +95,7 @@
         buffer = NULL;
     }
 
-    CHECK_EQ(decoder->stop(), OK);
+    CHECK_EQ(decoder->stop(), (status_t)OK);
 #endif
 #endif
 
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
new file mode 100644
index 0000000..1b01bd6
--- /dev/null
+++ b/cmds/stagefright/codec.cpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "codec"
+#include <utils/Log.h>
+
+#include "SimplePlayer.h"
+
+#include <binder/ProcessState.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/NuMediaExtractor.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+static void usage(const char *me) {
+    fprintf(stderr, "usage: %s [-a] use audio\n"
+                    "\t\t[-v] use video\n"
+                    "\t\t[-p] playback\n", me);
+
+    exit(1);
+}
+
+namespace android {
+
+struct CodecState {
+    sp<MediaCodec> mCodec;
+    Vector<sp<ABuffer> > mCSD;
+    size_t mCSDIndex;
+    Vector<sp<ABuffer> > mInBuffers;
+    Vector<sp<ABuffer> > mOutBuffers;
+    bool mSawOutputEOS;
+};
+
+}  // namespace android
+
+static int decode(
+        const android::sp<android::ALooper> &looper,
+        const char *path,
+        bool useAudio,
+        bool useVideo) {
+    using namespace android;
+
+    sp<NuMediaExtractor> extractor = new NuMediaExtractor;
+    if (extractor->setDataSource(path) != OK) {
+        fprintf(stderr, "unable to instantiate extractor.\n");
+        return 1;
+    }
+
+    KeyedVector<size_t, CodecState> stateByTrack;
+
+    bool haveAudio = false;
+    bool haveVideo = false;
+    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        sp<AMessage> format;
+        status_t err = extractor->getTrackFormat(i, &format);
+        CHECK_EQ(err, (status_t)OK);
+
+        AString mime;
+        CHECK(format->findString("mime", &mime));
+
+        if (useAudio && !haveAudio
+                && !strncasecmp(mime.c_str(), "audio/", 6)) {
+            haveAudio = true;
+        } else if (useVideo && !haveVideo
+                && !strncasecmp(mime.c_str(), "video/", 6)) {
+            haveVideo = true;
+        } else {
+            continue;
+        }
+
+        ALOGV("selecting track %d", i);
+
+        err = extractor->selectTrack(i);
+        CHECK_EQ(err, (status_t)OK);
+
+        CodecState *state =
+            &stateByTrack.editValueAt(stateByTrack.add(i, CodecState()));
+
+        state->mCodec = MediaCodec::CreateByType(
+                looper, mime.c_str(), false /* encoder */);
+
+        CHECK(state->mCodec != NULL);
+
+        err = state->mCodec->configure(
+                format, NULL /* surfaceTexture */, 0 /* flags */);
+
+        CHECK_EQ(err, (status_t)OK);
+
+        size_t j = 0;
+        sp<ABuffer> buffer;
+        while (format->findBuffer(StringPrintf("csd-%d", j).c_str(), &buffer)) {
+            state->mCSD.push_back(buffer);
+
+            ++j;
+        }
+
+        state->mCSDIndex = 0;
+        state->mSawOutputEOS = false;
+
+        ALOGV("got %d pieces of codec specific data.", state->mCSD.size());
+    }
+
+    CHECK(!stateByTrack.isEmpty());
+
+    for (size_t i = 0; i < stateByTrack.size(); ++i) {
+        CodecState *state = &stateByTrack.editValueAt(i);
+
+        sp<MediaCodec> codec = state->mCodec;
+
+        CHECK_EQ((status_t)OK, codec->start());
+
+        CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers));
+        CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers));
+
+        ALOGV("got %d input and %d output buffers",
+              state->mInBuffers.size(), state->mOutBuffers.size());
+
+        while (state->mCSDIndex < state->mCSD.size()) {
+            size_t index;
+            status_t err = codec->dequeueInputBuffer(&index);
+
+            if (err == -EAGAIN) {
+                usleep(10000);
+                continue;
+            }
+
+            CHECK_EQ(err, (status_t)OK);
+
+            const sp<ABuffer> &srcBuffer =
+                state->mCSD.itemAt(state->mCSDIndex++);
+
+            const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
+
+            memcpy(buffer->data(), srcBuffer->data(), srcBuffer->size());
+
+            err = codec->queueInputBuffer(
+                    index,
+                    0 /* offset */,
+                    srcBuffer->size(),
+                    0ll /* timeUs */,
+                    MediaCodec::BUFFER_FLAG_CODECCONFIG);
+
+            CHECK_EQ(err, (status_t)OK);
+        }
+    }
+
+    bool sawInputEOS = false;
+
+    for (;;) {
+        if (!sawInputEOS) {
+            size_t trackIndex;
+            status_t err = extractor->getSampleTrackIndex(&trackIndex);
+
+            if (err != OK) {
+                ALOGV("signalling EOS.");
+
+                for (size_t i = 0; i < stateByTrack.size(); ++i) {
+                    CodecState *state = &stateByTrack.editValueAt(i);
+
+                    for (;;) {
+                        size_t index;
+                        err = state->mCodec->dequeueInputBuffer(&index);
+
+                        if (err == -EAGAIN) {
+                            continue;
+                        }
+
+                        CHECK_EQ(err, (status_t)OK);
+
+                        err = state->mCodec->queueInputBuffer(
+                                index,
+                                0 /* offset */,
+                                0 /* size */,
+                                0ll /* timeUs */,
+                                MediaCodec::BUFFER_FLAG_EOS);
+
+                        CHECK_EQ(err, (status_t)OK);
+                        break;
+                    }
+                }
+
+                sawInputEOS = true;
+            } else {
+                CodecState *state = &stateByTrack.editValueFor(trackIndex);
+
+                size_t index;
+                err = state->mCodec->dequeueInputBuffer(&index);
+
+                if (err == OK) {
+                    ALOGV("filling input buffer %d", index);
+
+                    const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
+
+                    err = extractor->readSampleData(buffer);
+                    CHECK_EQ(err, (status_t)OK);
+
+                    int64_t timeUs;
+                    err = extractor->getSampleTime(&timeUs);
+                    CHECK_EQ(err, (status_t)OK);
+
+                    err = state->mCodec->queueInputBuffer(
+                            index,
+                            0 /* offset */,
+                            buffer->size(),
+                            timeUs,
+                            0 /* flags */);
+
+                    CHECK_EQ(err, (status_t)OK);
+
+                    extractor->advance();
+                } else {
+                    CHECK_EQ(err, -EAGAIN);
+                }
+            }
+        }
+
+        bool sawOutputEOSOnAllTracks = true;
+        for (size_t i = 0; i < stateByTrack.size(); ++i) {
+            CodecState *state = &stateByTrack.editValueAt(i);
+            if (!state->mSawOutputEOS) {
+                sawOutputEOSOnAllTracks = false;
+                break;
+            }
+        }
+
+        if (sawOutputEOSOnAllTracks) {
+            break;
+        }
+
+        for (size_t i = 0; i < stateByTrack.size(); ++i) {
+            CodecState *state = &stateByTrack.editValueAt(i);
+
+            if (state->mSawOutputEOS) {
+                continue;
+            }
+
+            size_t index;
+            size_t offset;
+            size_t size;
+            int64_t presentationTimeUs;
+            uint32_t flags;
+            status_t err = state->mCodec->dequeueOutputBuffer(
+                    &index, &offset, &size, &presentationTimeUs, &flags,
+                    10000ll);
+
+            if (err == OK) {
+                ALOGV("draining output buffer %d, time = %lld us",
+                      index, presentationTimeUs);
+
+                err = state->mCodec->releaseOutputBuffer(index);
+                CHECK_EQ(err, (status_t)OK);
+
+                if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+                    ALOGV("reached EOS on output.");
+
+                    state->mSawOutputEOS = true;
+                }
+            } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
+                ALOGV("INFO_OUTPUT_BUFFERS_CHANGED");
+                CHECK_EQ((status_t)OK,
+                         state->mCodec->getOutputBuffers(&state->mOutBuffers));
+
+                ALOGV("got %d output buffers", state->mOutBuffers.size());
+            } else if (err == INFO_FORMAT_CHANGED) {
+                sp<AMessage> format;
+                CHECK_EQ((status_t)OK, state->mCodec->getOutputFormat(&format));
+
+                ALOGV("INFO_FORMAT_CHANGED: %s", format->debugString().c_str());
+            } else {
+                CHECK_EQ(err, -EAGAIN);
+            }
+        }
+    }
+
+    for (size_t i = 0; i < stateByTrack.size(); ++i) {
+        CodecState *state = &stateByTrack.editValueAt(i);
+
+        CHECK_EQ((status_t)OK, state->mCodec->stop());
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv) {
+    using namespace android;
+
+    const char *me = argv[0];
+
+    bool useAudio = false;
+    bool useVideo = false;
+    bool playback = false;
+
+    int res;
+    while ((res = getopt(argc, argv, "havp")) >= 0) {
+        switch (res) {
+            case 'a':
+            {
+                useAudio = true;
+                break;
+            }
+
+            case 'v':
+            {
+                useVideo = true;
+                break;
+            }
+
+            case 'p':
+            {
+                playback = true;
+                break;
+            }
+
+            case '?':
+            case 'h':
+            default:
+            {
+                usage(me);
+            }
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc != 1) {
+        usage(me);
+    }
+
+    if (!useAudio && !useVideo) {
+        useAudio = useVideo = true;
+    }
+
+    ProcessState::self()->startThreadPool();
+
+    DataSource::RegisterDefaultSniffers();
+
+    sp<ALooper> looper = new ALooper;
+    looper->start();
+
+    if (playback) {
+        sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
+        CHECK_EQ(composerClient->initCheck(), (status_t)OK);
+
+        ssize_t displayWidth = composerClient->getDisplayWidth(0);
+        ssize_t displayHeight = composerClient->getDisplayHeight(0);
+
+        ALOGV("display is %ld x %ld\n", displayWidth, displayHeight);
+
+        sp<SurfaceControl> control =
+            composerClient->createSurface(
+                    String8("A Surface"),
+                    0,
+                    displayWidth,
+                    displayHeight,
+                    PIXEL_FORMAT_RGB_565,
+                    0);
+
+        CHECK(control != NULL);
+        CHECK(control->isValid());
+
+        SurfaceComposerClient::openGlobalTransaction();
+        CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
+        CHECK_EQ(control->show(), (status_t)OK);
+        SurfaceComposerClient::closeGlobalTransaction();
+
+        sp<Surface> surface = control->getSurface();
+        CHECK(surface != NULL);
+
+        sp<SimplePlayer> player = new SimplePlayer;
+        looper->registerHandler(player);
+
+        player->setDataSource(argv[0]);
+        player->setSurface(surface->getSurfaceTexture());
+        player->start();
+        sleep(60);
+        player->stop();
+        player->reset();
+
+        composerClient->dispose();
+    } else {
+        decode(looper, argv[0], useAudio, useVideo);
+    }
+
+    looper->stop();
+
+    return 0;
+}
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 7703058..45c3f7b 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -17,11 +17,11 @@
 #include "SineSource.h"
 
 #include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/CameraSource.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -183,7 +183,7 @@
         return 1;
     }
     OMXClient client;
-    CHECK_EQ(client.connect(), OK);
+    CHECK_EQ(client.connect(), (status_t)OK);
 
     status_t err = OK;
 
@@ -231,14 +231,14 @@
     sp<MPEG4Writer> writer = new MPEG4Writer("/sdcard/output.mp4");
     writer->addSource(encoder);
     writer->setMaxFileDuration(kDurationUs);
-    CHECK_EQ(OK, writer->start());
+    CHECK_EQ((status_t)OK, writer->start());
     while (!writer->reachedEOS()) {
         fprintf(stderr, ".");
         usleep(100000);
     }
     err = writer->stop();
 #else
-    CHECK_EQ(OK, encoder->start());
+    CHECK_EQ((status_t)OK, encoder->start());
 
     MediaBuffer *buffer;
     while (encoder->read(&buffer) == OK) {
@@ -272,7 +272,7 @@
     for (int i = 0; i < 100; ++i) {
         MediaBuffer *buffer;
         status_t err = source->read(&buffer);
-        CHECK_EQ(err, OK);
+        CHECK_EQ(err, (status_t)OK);
 
         printf("got a frame, data=%p, size=%d\n",
                buffer->data(), buffer->range_length());
@@ -299,7 +299,7 @@
     android::ProcessState::self()->startThreadPool();
 
     OMXClient client;
-    CHECK_EQ(client.connect(), OK);
+    CHECK_EQ(client.connect(), (status_t)OK);
 
     const int32_t kSampleRate = 22050;
     const int32_t kNumChannels = 2;
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
index c402286..3bd1fe2 100644
--- a/cmds/stagefright/recordvideo.cpp
+++ b/cmds/stagefright/recordvideo.cpp
@@ -17,9 +17,9 @@
 #include "SineSource.h"
 
 #include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/MPEG4Writer.h>
@@ -243,7 +243,7 @@
     }
 
     OMXClient client;
-    CHECK_EQ(client.connect(), OK);
+    CHECK_EQ(client.connect(), (status_t)OK);
 
     status_t err = OK;
     sp<MediaSource> source =
@@ -283,7 +283,7 @@
     sp<MPEG4Writer> writer = new MPEG4Writer(fileName);
     writer->addSource(encoder);
     int64_t start = systemTime();
-    CHECK_EQ(OK, writer->start());
+    CHECK_EQ((status_t)OK, writer->start());
     while (!writer->reachedEOS()) {
     }
     err = writer->stop();
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index ae80f88..6f0fb54 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -198,9 +198,7 @@
 
                     (new AMessage(kWhatSeek, id()))->post(5000000ll);
                 } else if (what == ACodec::kWhatOutputFormatChanged) {
-                } else {
-                    CHECK_EQ(what, (int32_t)ACodec::kWhatShutdownCompleted);
-
+                } else if (what == ACodec::kWhatShutdownCompleted) {
                     mDecodeLooper->unregisterHandler(mCodec->id());
 
                     if (mDecodeLooper != looper()) {
@@ -360,7 +358,7 @@
             buffer->meta()->setInt32("csd", true);
             mCSD.push(buffer);
 
-            msg->setObject("csd", buffer);
+            msg->setBuffer("csd", buffer);
         } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
             ESDS esds((const char *)data, size);
             CHECK_EQ(esds.InitCheck(), (status_t)OK);
@@ -410,9 +408,8 @@
             return;
         }
 
-        sp<RefBase> obj;
-        CHECK(msg->findObject("buffer", &obj));
-        sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+        sp<ABuffer> outBuffer;
+        CHECK(msg->findBuffer("buffer", &outBuffer));
 
         if (mCSDIndex < mCSD.size()) {
             outBuffer = mCSD.editItemAt(mCSDIndex++);
@@ -511,15 +508,14 @@
             }
         }
 
-        reply->setObject("buffer", outBuffer);
+        reply->setBuffer("buffer", outBuffer);
         reply->post();
     }
 
     void onDrainThisBuffer(const sp<AMessage> &msg) {
-        sp<RefBase> obj;
-        CHECK(msg->findObject("buffer", &obj));
+        sp<ABuffer> buffer;
+        CHECK(msg->findBuffer("buffer", &buffer));
 
-        sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
         mTotalBytesReceived += buffer->size();
 
         sp<AMessage> reply;
diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp
index 78b1007..6dbcf5c 100644
--- a/cmds/surfaceflinger/main_surfaceflinger.cpp
+++ b/cmds/surfaceflinger/main_surfaceflinger.cpp
@@ -20,6 +20,6 @@
 using namespace android;
 
 int main(int argc, char** argv) {
-    SurfaceFlinger::publishAndJoinThreadPool();
+    SurfaceFlinger::publishAndJoinThreadPool(true);
     return 0;
 }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index c9468eb..8d17325 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -30,36 +30,36 @@
     void setServiceInfo(in AccessibilityServiceInfo info);
 
     /**
-     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
+     * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} by accessibility id.
      *
      * @param accessibilityWindowId A unique window id. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
      *     to query the currently active window.
      * @param accessibilityNodeId A unique view id or virtual descendant id from
      *     where to start the search. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
      *     to start from the root.
      * @param interactionId The id of the interaction for matching with the callback result.
      * @param callback Callback which to receive the result.
      * @param threadId The id of the calling thread.
+     * @param prefetchFlags flags to guide prefetching.
      * @return The current window scale, where zero means a failure.
      */
     float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
         long accessibilityNodeId, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback, long threadId);
+        IAccessibilityInteractionConnectionCallback callback, long threadId, int prefetchFlags);
 
     /**
-     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
-     * insensitive containment. The search is performed in the window whose
-     * id is specified and starts from the node whose accessibility id is
-     * specified.
+     * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by View text.
+     * The match is case insensitive containment. The search is performed in the window
+     * whose id is specified and starts from the node whose accessibility id is specified.
      *
      * @param accessibilityWindowId A unique window id. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
      *     to query the currently active window.
      * @param accessibilityNodeId A unique view id or virtual descendant id from
      *     where to start the search. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
      *     to start from the root.
      * @param text The searched text.
      * @param interactionId The id of the interaction for matching with the callback result.
@@ -72,16 +72,16 @@
         long threadId);
 
     /**
-     * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
-     * the window whose id is specified and starts from the node whose accessibility
-     * id is specified.
+     * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} by View id. The search
+     * is performed in the window whose id is specified and starts from the node whose
+     * accessibility id is specified.
      *
      * @param accessibilityWindowId A unique window id. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
      *     to query the currently active window.
      * @param accessibilityNodeId A unique view id or virtual descendant id from
      *     where to start the search. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
      *     to start from the root.
      * @param id The id of the node.
      * @param interactionId The id of the interaction for matching with the callback result.
@@ -94,14 +94,15 @@
         long threadId);
 
     /**
-     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
+     * Performs an accessibility action on an
+     * {@link android.view.accessibility.AccessibilityNodeInfo}.
      *
      * @param accessibilityWindowId A unique window id. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
      *     to query the currently active window.
      * @param accessibilityNodeId A unique view id or virtual descendant id from
      *     where to start the search. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
      *     to start from the root.
      * @param action The action to perform.
      * @param interactionId The id of the interaction for matching with the callback result.
diff --git a/core/java/android/accessibilityservice/UiTestAutomationBridge.java b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
index 616b796..a898c3f 100644
--- a/core/java/android/accessibilityservice/UiTestAutomationBridge.java
+++ b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
@@ -49,12 +49,19 @@
 
     private static final String LOG_TAG = UiTestAutomationBridge.class.getSimpleName();
 
-    public static final int ACTIVE_WINDOW_ID = -1;
-
-    public static final long ROOT_NODE_ID = -1;
-
     private static final int TIMEOUT_REGISTER_SERVICE = 5000;
 
+    public static final int ACTIVE_WINDOW_ID = AccessibilityNodeInfo.ACTIVE_WINDOW_ID;
+
+    public static final long ROOT_NODE_ID = AccessibilityNodeInfo.ROOT_NODE_ID;
+
+    public static final int UNDEFINED = -1;
+
+    private static final int FIND_ACCESSIBILITY_NODE_INFO_PREFETCH_FLAGS =
+        AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS
+        | AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS
+        | AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
+
     private final Object mLock = new Object();
 
     private volatile int mConnectionId = AccessibilityInteractionClient.NO_ID;
@@ -63,12 +70,12 @@
 
     private AccessibilityEvent mLastEvent;
 
-    private AccessibilityEvent mLastWindowStateChangeEvent;
-
     private volatile boolean mWaitingForEventDelivery;
 
     private volatile boolean mUnprocessedEventAvailable;
 
+    private HandlerThread mHandlerThread;
+
     /**
      * Gets the last received {@link AccessibilityEvent}.
      *
@@ -121,9 +128,10 @@
         // is needed for making sure the binder calls are interleaved
         // with check for the expected event and also to make sure the
         // binder threads are allowed to proceed in the received order.
-        HandlerThread handlerThread = new HandlerThread("UiTestAutomationBridge");
-        handlerThread.start();
-        Looper looper = handlerThread.getLooper();
+        mHandlerThread = new HandlerThread("UiTestAutomationBridge");
+        mHandlerThread.setDaemon(true);
+        mHandlerThread.start();
+        Looper looper = mHandlerThread.getLooper();
 
         mListener = new IEventListenerWrapper(null, looper, new Callbacks() {
             @Override
@@ -141,17 +149,8 @@
                 synchronized (mLock) {
                     while (true) {
                         mLastEvent = AccessibilityEvent.obtain(event);
-
-                        final int eventType = event.getEventType();
-                        if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
-                                || eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
-                            if (mLastWindowStateChangeEvent != null) {
-                                mLastWindowStateChangeEvent.recycle();
-                            }
-                            mLastWindowStateChangeEvent = mLastEvent;
-                        }
-
                         if (!mWaitingForEventDelivery) {
+                            mLock.notifyAll();
                             break;
                         }
                         if (!mUnprocessedEventAvailable) {
@@ -221,6 +220,8 @@
             throw new IllegalStateException("Already disconnected.");
         }
 
+        mHandlerThread.quit();
+
         IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
               ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
 
@@ -295,6 +296,43 @@
     }
 
     /**
+     * Waits for the accessibility event stream to become idle, which is not to
+     * have received a new accessibility event within <code>idleTimeout</code>,
+     * and do so within a maximal global timeout as specified by
+     * <code>globalTimeout</code>.
+     *
+     * @param idleTimeout The timeout between two event to consider the device idle.
+     * @param globalTimeout The maximal global timeout in which to wait for idle.
+     */
+    public void waitForIdle(long idleTimeout, long globalTimeout) {
+        final long startTimeMillis = SystemClock.uptimeMillis();
+        long lastEventTime = (mLastEvent != null)
+                ? mLastEvent.getEventTime() : SystemClock.uptimeMillis();
+        synchronized (mLock) {
+            while (true) {
+                final long currentTimeMillis = SystemClock.uptimeMillis();
+                final long sinceLastEventTimeMillis = currentTimeMillis - lastEventTime;
+                if (sinceLastEventTimeMillis > idleTimeout) {
+                    return;
+                }
+                if (mLastEvent != null) {
+                    lastEventTime = mLastEvent.getEventTime();
+                }
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                final long remainingTimeMillis = globalTimeout - elapsedTimeMillis;
+                if (remainingTimeMillis <= 0) {
+                    return;
+                }
+                try {
+                     mLock.wait(idleTimeout);
+                } catch (InterruptedException e) {
+                     /* ignore */
+                }
+            }
+        }
+    }
+
+    /**
      * Finds an {@link AccessibilityNodeInfo} by accessibility id in the active
      * window. The search is performed from the root node.
      *
@@ -310,8 +348,8 @@
     /**
      * Finds an {@link AccessibilityNodeInfo} by accessibility id.
      *
-     * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID}
-     *     to query the currently active window.
+     * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} to query
+     *     the currently active window.
      * @param accessibilityNodeId A unique view id or virtual descendant id for
      *     which to search.
      * @return The current window scale, where zero means a failure.
@@ -323,13 +361,15 @@
         ensureValidConnection(connectionId);
         return AccessibilityInteractionClient.getInstance()
                 .findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
-                        accessibilityWindowId, accessibilityNodeId);
+                        accessibilityWindowId, accessibilityNodeId,
+                        FIND_ACCESSIBILITY_NODE_INFO_PREFETCH_FLAGS);
     }
 
     /**
      * Finds an {@link AccessibilityNodeInfo} by View id in the active
      * window. The search is performed from the root node.
      *
+     * @param viewId The id of a View.
      * @return The current window scale, where zero means a failure.
      */
     public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId) {
@@ -341,10 +381,11 @@
      * the window whose id is specified and starts from the node whose accessibility
      * id is specified.
      *
-     * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID}
-     *     to query the currently active window.
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link  #ACTIVE_WINDOW_ID} to query the currently active window.
      * @param accessibilityNodeId A unique view id or virtual descendant id from
-     *     where to start the search. Use {@link #ROOT_NODE_ID} to start from the root.
+     *     where to start the search. Use {@link  #ROOT_NODE_ID} to start from the root.
+     * @param viewId The id of a View.
      * @return The current window scale, where zero means a failure.
      */
     public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int accessibilityWindowId,
@@ -374,8 +415,8 @@
      * id is specified and starts from the node whose accessibility id is
      * specified.
      *
-     * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID}
-     *     to query the currently active window.
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link #ACTIVE_WINDOW_ID} to query the currently active window.
      * @param accessibilityNodeId A unique view id or virtual descendant id from
      *     where to start the search. Use {@link #ROOT_NODE_ID} to start from the root.
      * @param text The searched text.
@@ -406,8 +447,8 @@
     /**
      * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
      *
-     * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID}
-     *     to query the currently active window.
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link #ACTIVE_WINDOW_ID} to query the currently active window.
      * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
      * @param action The action to perform.
      * @return Whether the action was performed.
@@ -427,16 +468,16 @@
      * @return The root info.
      */
     public AccessibilityNodeInfo getRootAccessibilityNodeInfoInActiveWindow() {
-        synchronized (mLock) {
-            if (mLastWindowStateChangeEvent != null) {
-                return mLastWindowStateChangeEvent.getSource();
-            }
-        }
-        return null;
+        // Cache the id to avoid locking
+        final int connectionId = mConnectionId;
+        ensureValidConnection(connectionId);
+        return AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByAccessibilityId(connectionId, ACTIVE_WINDOW_ID,
+                        ROOT_NODE_ID, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
     }
 
     private void ensureValidConnection(int connectionId) {
-        if (connectionId == AccessibilityInteractionClient.NO_ID) {
+        if (connectionId == UNDEFINED) {
             throw new IllegalStateException("UiAutomationService not connected."
                     + " Did you call #register()?");
         }
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index cc1efb9..6fbeee3 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -522,8 +522,7 @@
      * animations possible.
      *
      */
-    private static class AnimationHandler extends Handler
-            implements Choreographer.OnAnimateListener {
+    private static class AnimationHandler extends Handler implements Runnable {
         // The per-thread list of all active animations
         private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
 
@@ -539,7 +538,7 @@
         private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
 
         private final Choreographer mChoreographer;
-        private boolean mIsChoreographed;
+        private boolean mAnimationScheduled;
 
         private AnimationHandler() {
             mChoreographer = Choreographer.getInstance();
@@ -644,22 +643,17 @@
 
             // If there are still active or delayed animations, schedule a future call to
             // onAnimate to process the next frame of the animations.
-            if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
-                if (!mIsChoreographed) {
-                    mIsChoreographed = true;
-                    mChoreographer.addOnAnimateListener(this);
-                }
-                mChoreographer.scheduleAnimation();
-            } else {
-                if (mIsChoreographed) {
-                    mIsChoreographed = false;
-                    mChoreographer.removeOnAnimateListener(this);
-                }
+            if (!mAnimationScheduled
+                    && (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())) {
+                mChoreographer.postAnimationCallback(this);
+                mAnimationScheduled = true;
             }
         }
 
+        // Called by the Choreographer.
         @Override
-        public void onAnimate() {
+        public void run() {
+            mAnimationScheduled = false;
             doAnimationFrame();
         }
     }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d80902d..24079a5d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -554,15 +554,6 @@
             return true;
         }
 
-        case FINISH_OTHER_INSTANCES_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            IBinder token = data.readStrongBinder();
-            ComponentName className = ComponentName.readFromParcel(data);
-            finishOtherInstances(token, className);
-            reply.writeNoException();
-            return true;
-        }
-
         case REPORT_THUMBNAIL_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
@@ -590,6 +581,21 @@
             return true;
         }
 
+        case GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String name = data.readString();
+            IBinder token = data.readStrongBinder();
+            ContentProviderHolder cph = getContentProviderExternal(name, token);
+            reply.writeNoException();
+            if (cph != null) {
+                reply.writeInt(1);
+                cph.writeToParcel(reply, 0);
+            } else {
+                reply.writeInt(0);
+            }
+            return true;
+        }
+
         case PUBLISH_CONTENT_PROVIDERS_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder b = data.readStrongBinder();
@@ -610,7 +616,16 @@
             reply.writeNoException();
             return true;
         }
-        
+
+        case REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String name = data.readString();
+            IBinder token = data.readStrongBinder();
+            removeContentProviderExternal(name, token);
+            reply.writeNoException();
+            return true;
+        }
+
         case GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             ComponentName comp = ComponentName.CREATOR.createFromParcel(data);
@@ -679,8 +694,9 @@
             String resolvedType = data.readString();
             b = data.readStrongBinder();
             int fl = data.readInt();
+            int userId = data.readInt();
             IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
-            int res = bindService(app, token, service, resolvedType, conn, fl);
+            int res = bindService(app, token, service, resolvedType, conn, fl, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -1192,22 +1208,6 @@
             return true;
         }
         
-        case REGISTER_ACTIVITY_WATCHER_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            IActivityWatcher watcher = IActivityWatcher.Stub.asInterface(
-                    data.readStrongBinder());
-            registerActivityWatcher(watcher);
-            return true;
-        }
-        
-        case UNREGISTER_ACTIVITY_WATCHER_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            IActivityWatcher watcher = IActivityWatcher.Stub.asInterface(
-                    data.readStrongBinder());
-            unregisterActivityWatcher(watcher);
-            return true;
-        }
-        
         case START_ACTIVITY_IN_PACKAGE_TRANSACTION:
         {
             data.enforceInterface(IActivityManager.descriptor);
@@ -2164,18 +2164,6 @@
         reply.recycle();
         return res;
     }
-    public void finishOtherInstances(IBinder token, ComponentName className) throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeStrongBinder(token);
-        ComponentName.writeToParcel(className, data);
-        mRemote.transact(FINISH_OTHER_INSTANCES_TRANSACTION, data, reply, 0);
-        reply.readException();
-        data.recycle();
-        reply.recycle();
-    }
     public void reportThumbnail(IBinder token,
                                 Bitmap thumbnail, CharSequence description) throws RemoteException
     {
@@ -2214,6 +2202,25 @@
         reply.recycle();
         return cph;
     }
+    public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
+            throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(name);
+        data.writeStrongBinder(token);
+        mRemote.transact(GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int res = reply.readInt();
+        ContentProviderHolder cph = null;
+        if (res != 0) {
+            cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
+        }
+        data.recycle();
+        reply.recycle();
+        return cph;
+    }
     public void publishContentProviders(IApplicationThread caller,
                                         List<ContentProviderHolder> providers) throws RemoteException
     {
@@ -2240,7 +2247,19 @@
         data.recycle();
         reply.recycle();
     }
-    
+
+    public void removeContentProviderExternal(String name, IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(name);
+        data.writeStrongBinder(token);
+        mRemote.transact(REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     public PendingIntent getRunningServiceControlPanel(ComponentName service)
             throws RemoteException
     {
@@ -2325,7 +2344,7 @@
     }
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType, IServiceConnection connection,
-            int flags) throws RemoteException {
+            int flags, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -2335,6 +2354,7 @@
         data.writeString(resolvedType);
         data.writeStrongBinder(connection.asBinder());
         data.writeInt(flags);
+        data.writeInt(userId);
         mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
@@ -3024,30 +3044,6 @@
         data.recycle();
     }
     
-    public void registerActivityWatcher(IActivityWatcher watcher)
-            throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
-        mRemote.transact(REGISTER_ACTIVITY_WATCHER_TRANSACTION, data, reply, 0);
-        reply.readException();
-        data.recycle();
-        reply.recycle();
-    }
-    
-    public void unregisterActivityWatcher(IActivityWatcher watcher)
-            throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
-        mRemote.transact(UNREGISTER_ACTIVITY_WATCHER_TRANSACTION, data, reply, 0);
-        reply.readException();
-        data.recycle();
-        reply.recycle();
-    }
-    
     public int startActivityInPackage(int uid,
             Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded)
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e4cfc99..bf632a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -134,7 +134,7 @@
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = true;
     private static final boolean DEBUG_CONFIGURATION = false;
-    private static final boolean DEBUG_SERVICE = true;
+    private static final boolean DEBUG_SERVICE = false;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
     private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
     private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@@ -3764,13 +3764,17 @@
     }
 
     private void setupGraphicsSupport(LoadedApk info) {
+        if (Process.isIsolated()) {
+            // Isolated processes aren't going to do UI.
+            return;
+        }
         try {
             int uid = Process.myUid();
             String[] packages = getPackageManager().getPackagesForUid(uid);
 
             // If there are several packages in this application we won't
             // initialize the graphics disk caches 
-            if (packages.length == 1) {
+            if (packages != null && packages.length == 1) {
                 ContextImpl appContext = new ContextImpl();
                 appContext.init(info, null, this);
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index db5113e..6d5cce5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -42,7 +42,9 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.hardware.ISerialManager;
 import android.hardware.SensorManager;
+import android.hardware.SerialManager;
 import android.hardware.usb.IUsbManager;
 import android.hardware.usb.UsbManager;
 import android.location.CountryDetector;
@@ -428,6 +430,12 @@
                     return new UsbManager(ctx, IUsbManager.Stub.asInterface(b));
                 }});
 
+        registerService(SERIAL_SERVICE, new ServiceFetcher() {
+                public Object createService(ContextImpl ctx) {
+                    IBinder b = ServiceManager.getService(SERIAL_SERVICE);
+                    return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));
+                }});
+
         registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
                     return new Vibrator();
@@ -903,6 +911,19 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public void sendBroadcast(Intent intent, int userId) {
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            intent.setAllowFds(false);
+            ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
+                    intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, false,
+                    userId);
+        } catch (RemoteException e) {
+        }
+    }
+
     @Override
     public void sendBroadcast(Intent intent, String receiverPermission) {
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
@@ -1117,6 +1138,12 @@
     @Override
     public boolean bindService(Intent service, ServiceConnection conn,
             int flags) {
+        return bindService(service, conn, flags, UserId.getUserId(Process.myUid()));
+    }
+
+    /** @hide */
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
         IServiceConnection sd;
         if (mPackageInfo != null) {
             sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
@@ -1135,7 +1162,7 @@
             int res = ActivityManagerNative.getDefault().bindService(
                 mMainThread.getApplicationThread(), getActivityToken(),
                 service, service.resolveTypeIfNeeded(getContentResolver()),
-                sd, flags);
+                sd, flags, userId);
             if (res < 0) {
                 throw new SecurityException(
                         "Not allowed to bind to service " + service);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 39817ac..53a71db 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -145,14 +145,16 @@
     public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
     public void moveTaskBackwards(int task) throws RemoteException;
     public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
-    public void finishOtherInstances(IBinder token, ComponentName className) throws RemoteException;
     /* oneway */
     public void reportThumbnail(IBinder token,
             Bitmap thumbnail, CharSequence description) throws RemoteException;
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
             String name) throws RemoteException;
+    public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
+            throws RemoteException;
     public void removeContentProvider(IApplicationThread caller,
             String name) throws RemoteException;
+    public void removeContentProviderExternal(String name, IBinder token) throws RemoteException;
     public void publishContentProviders(IApplicationThread caller,
             List<ContentProviderHolder> providers) throws RemoteException;
     public PendingIntent getRunningServiceControlPanel(ComponentName service)
@@ -167,7 +169,7 @@
             int id, Notification notification, boolean keepNotification) throws RemoteException;
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
-            IServiceConnection connection, int flags) throws RemoteException;
+            IServiceConnection connection, int flags, int userId) throws RemoteException;
     public boolean unbindService(IServiceConnection connection) throws RemoteException;
     public void publishService(IBinder token,
             Intent intent, IBinder service) throws RemoteException;
@@ -295,11 +297,6 @@
     public void stopAppSwitches() throws RemoteException;
     public void resumeAppSwitches() throws RemoteException;
     
-    public void registerActivityWatcher(IActivityWatcher watcher)
-            throws RemoteException;
-    public void unregisterActivityWatcher(IActivityWatcher watcher)
-            throws RemoteException;
-
     public int startActivityInPackage(int uid,
             Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded)
@@ -421,7 +418,7 @@
                 source.readStrongBinder());
             noReleaseNeeded = source.readInt() != 0;
         }
-    };
+    }
 
     /** Information returned after waiting for an activity start. */
     public static class WaitResult implements Parcelable {
@@ -505,7 +502,7 @@
     int BIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
     int UNBIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
     int PUBLISH_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
-    int FINISH_OTHER_INSTANCES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
+
     int GOING_TO_SLEEP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39;
     int WAKING_UP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40;
     int SET_DEBUG_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41;
@@ -559,8 +556,8 @@
     int START_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+89;
     int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90;
     int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91;
-    int REGISTER_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92;
-    int UNREGISTER_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93;
+
+
     int START_ACTIVITY_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
     int KILL_APPLICATION_WITH_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
     int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96;
@@ -607,4 +604,6 @@
     int SHOW_BOOT_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137;
     int DISMISS_KEYGUARD_ON_NEXT_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+138;
     int KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+139;
+    int GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+140;
+    int REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+141;
 }
diff --git a/core/java/android/app/IActivityWatcher.aidl b/core/java/android/app/IActivityWatcher.aidl
deleted file mode 100644
index 6737545..0000000
--- a/core/java/android/app/IActivityWatcher.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-package android.app;
-
-/**
- * Callback interface to watch the user's traversal through activities.
- * {@hide}
- */
-oneway interface IActivityWatcher {
-    void activityResuming(int activityId);
-    void closingSystemDialogs(String reason);
-}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 2420b84..4d5238c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -33,7 +33,6 @@
     void enqueueToast(String pkg, ITransientNotification callback, int duration);
     void cancelToast(String pkg, ITransientNotification callback);
     void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived);
-    void enqueueNotificationWithTagPriority(String pkg, String tag, int id, int priority, in Notification notification, inout int[] idReceived);
     void cancelNotificationWithTag(String pkg, String tag, int id);
 }
 
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index fcbcd81..d9bbb4a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -119,7 +119,7 @@
         final int myUid = Process.myUid();
         mResDir = aInfo.uid == myUid ? aInfo.sourceDir
                 : aInfo.publicSourceDir;
-        if (!UserId.isSameUser(aInfo.uid, myUid)) {
+        if (!UserId.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
             aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
                     mPackageName);
         }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d569e20..5325af0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -30,6 +31,7 @@
 import android.widget.RemoteViews;
 
 import java.text.NumberFormat;
+import java.util.ArrayList;
 
 /**
  * A class that represents how a persistent notification is to be presented to
@@ -51,36 +53,58 @@
      * Use all default values (where applicable).
      */
     public static final int DEFAULT_ALL = ~0;
-    
+
     /**
      * Use the default notification sound. This will ignore any given
      * {@link #sound}.
-     * 
+     *
+
      * @see #defaults
-     */ 
+     */
+
     public static final int DEFAULT_SOUND = 1;
 
     /**
      * Use the default notification vibrate. This will ignore any given
-     * {@link #vibrate}. Using phone vibration requires the 
+     * {@link #vibrate}. Using phone vibration requires the
      * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
-     * 
+     *
      * @see #defaults
-     */ 
+     */
+
     public static final int DEFAULT_VIBRATE = 2;
-    
+
     /**
      * Use the default notification lights. This will ignore the
      * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
      * {@link #ledOnMS}.
-     * 
+     *
      * @see #defaults
-     */ 
+     */
+
     public static final int DEFAULT_LIGHTS = 4;
-    
+
     /**
-     * The timestamp for the notification.  The icons and expanded views
-     * are sorted by this key.
+     * A timestamp related to this notification, in milliseconds since the epoch.
+     * 
+     * Default value: {@link System#currentTimeMillis() Now}.
+     *
+     * Choose a timestamp that will be most relevant to the user. For most finite events, this
+     * corresponds to the time the event happened (or will happen, in the case of events that have
+     * yet to occur but about which the user is being informed). Indefinite events should be
+     * timestamped according to when the activity began. 
+     * 
+     * Some examples:
+     * 
+     * <ul>
+     *   <li>Notification of a new chat message should be stamped when the message was received.</li>
+     *   <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
+     *   <li>Notification of a completed file download should be stamped when the download finished.</li>
+     *   <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
+     *   <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
+     *   <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
+     * </ul> 
+     * 
      */
     public long when;
 
@@ -100,10 +124,16 @@
     public int iconLevel;
 
     /**
-     * The number of events that this notification represents.  For example, in a new mail
-     * notification, this could be the number of unread messages.  This number is superimposed over
-     * the icon in the status bar.  If the number is 0 or negative, it is not shown in the status
-     * bar.
+     * The number of events that this notification represents. For example, in a new mail
+     * notification, this could be the number of unread messages.
+     * 
+     * The system may or may not use this field to modify the appearance of the notification. For
+     * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
+     * superimposed over the icon in the status bar. Starting with
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
+     * {@link Notification.Builder} has displayed the number in the expanded notification view.
+     * 
+     * If the number is 0 or negative, it is never shown.
      */
     public int number;
 
@@ -121,10 +151,11 @@
     public PendingIntent contentIntent;
 
     /**
-     * The intent to execute when the status entry is deleted by the user
-     * with the "Clear All Notifications" button. This probably shouldn't
-     * be launching an activity since several of those will be sent at the
-     * same time.
+     * The intent to execute when the notification is explicitly dismissed by the user, either with
+     * the "Clear All" button or by swiping it away individually.
+     *
+     * This probably shouldn't be launching an activity since several of those will be sent
+     * at the same time.
      */
     public PendingIntent deleteIntent;
 
@@ -139,11 +170,6 @@
      * Text to scroll across the screen when this item is added to
      * the status bar on large and smaller devices.
      *
-     * <p>This field is provided separately from the other ticker fields
-     * both for compatibility and to allow an application to choose different
-     * text for when the text scrolls in and when it is displayed all at once
-     * in conjunction with one or more icons.
-     *
      * @see #tickerView
      */
     public CharSequence tickerText;
@@ -166,9 +192,9 @@
 
     /**
      * The sound to play.
-     * 
+     *
      * <p>
-     * To play the default notification sound, see {@link #defaults}. 
+     * To play the default notification sound, see {@link #defaults}.
      * </p>
      */
     public Uri sound;
@@ -187,14 +213,13 @@
      */
     public int audioStreamType = STREAM_DEFAULT;
 
-    
     /**
-     * The pattern with which to vibrate. 
-     * 
+     * The pattern with which to vibrate.
+     *
      * <p>
      * To vibrate the default pattern, see {@link #defaults}.
      * </p>
-     * 
+     *
      * @see android.os.Vibrator#vibrate(long[],int)
      */
     public long[] vibrate;
@@ -235,7 +260,6 @@
      */
     public int defaults;
 
-
     /**
      * Bit to be bitwise-ored into the {@link #flags} field that should be
      * set if you want the LED on for this notification.
@@ -252,7 +276,7 @@
      * because they will be set to values that work on any given hardware.
      * <p>
      * The alpha channel must be set for forward compatibility.
-     * 
+     *
      */
     public static final int FLAG_SHOW_LIGHTS        = 0x00000001;
 
@@ -282,7 +306,8 @@
     /**
      * Bit to be bitwise-ored into the {@link #flags} field that should be
      * set if the notification should be canceled when it is clicked by the
-     * user.  On tablets, the 
+     * user.  On tablets, the
+
      */
     public static final int FLAG_AUTO_CANCEL        = 0x00000010;
 
@@ -301,22 +326,105 @@
     public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
 
     /**
-     * Bit to be bitwise-ored into the {@link #flags} field that should be set if this notification
-     * represents a high-priority event that may be shown to the user even if notifications are
-     * otherwise unavailable (that is, when the status bar is hidden). This flag is ideally used
-     * in conjunction with {@link #fullScreenIntent}.
+     * Obsolete flag indicating high-priority notifications; use the priority field instead.
+     * 
+     * @deprecated Use {@link #priority} with a positive value.
      */
-    public static final int FLAG_HIGH_PRIORITY = 0x00000080;
+    public static final int FLAG_HIGH_PRIORITY      = 0x00000080;
 
     public int flags;
 
     /**
-     * Constructs a Notification object with everything set to 0.
+     * Default notification {@link #priority}. If your application does not prioritize its own
+     * notifications, use this value for all notifications.
+     */
+    public static final int PRIORITY_DEFAULT = 0;
+
+    /**
+     * Lower {@link #priority}, for items that are less important. The UI may choose to show these
+     * items smaller, or at a different position in the list, compared with your app's
+     * {@link #PRIORITY_DEFAULT} items.
+     */
+    public static final int PRIORITY_LOW = -1;
+
+    /**
+     * Lowest {@link #priority}; these items might not be shown to the user except under special
+     * circumstances, such as detailed notification logs.
+     */
+    public static final int PRIORITY_MIN = -2;
+
+    /**
+     * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
+     * show these items larger, or at a different position in notification lists, compared with
+     * your app's {@link #PRIORITY_DEFAULT} items.
+     */
+    public static final int PRIORITY_HIGH = 1;
+
+    /**
+     * Highest {@link #priority}, for your application's most important items that require the
+     * user's prompt attention or input.
+     */
+    public static final int PRIORITY_MAX = 2;
+
+    /**
+     * Relative priority for this notification.
+     * 
+     * Priority is an indication of how much of the user's valuable attention should be consumed by
+     * this notification. Low-priority notifications may be hidden from the user in certain
+     * situations, while the user might be interrupted for a higher-priority notification. The
+     * system will make a determination about how to interpret notification priority as described in 
+     * MUMBLE MUMBLE.
+     */
+    public int priority;
+    
+    /**
+     * Notification type: incoming call (voice or video) or similar synchronous communication request.
+     */
+    public static final String KIND_CALL = "android.call";
+
+    /**
+     * Notification type: incoming direct message (SMS, instant message, etc.).
+     */
+    public static final String KIND_MESSAGE = "android.message";
+
+    /**
+     * Notification type: asynchronous bulk message (email).
+     */
+    public static final String KIND_EMAIL = "android.email";
+
+    /**
+     * Notification type: calendar event.
+     */
+    public static final String KIND_EVENT = "android.event";
+
+    /**
+     * Notification type: promotion or advertisement.
+     */
+    public static final String KIND_PROMO = "android.promo";
+
+    /**
+     * If this notification matches of one or more special types (see the <code>KIND_*</code>
+     * constants), add them here, best match first.
+     */
+    public String[] kind;
+
+    /**
+     * Extra key for people values (type TBD).
+     *
+     * @hide
+     */
+    public static final String EXTRA_PEOPLE = "android.people";
+
+    private Bundle extras;
+
+    /**
+     * Constructs a Notification object with default values.
      * You might want to consider using {@link Builder} instead.
      */
     public Notification()
     {
         this.when = System.currentTimeMillis();
+        this.priority = PRIORITY_DEFAULT;
     }
 
     /**
@@ -396,6 +504,14 @@
         if (parcel.readInt() != 0) {
             fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
         }
+
+        priority = parcel.readInt();
+        
+        kind = parcel.createStringArray(); // may set kind to null
+
+        if (parcel.readInt() != 0) {
+            extras = parcel.readBundle();
+        }
     }
 
     @Override
@@ -438,9 +554,23 @@
         that.ledOnMS = this.ledOnMS;
         that.ledOffMS = this.ledOffMS;
         that.defaults = this.defaults;
-        
+
         that.flags = this.flags;
 
+        that.priority = this.priority;
+        
+        final String[] thiskind = this.kind;
+        if (thiskind != null) {
+            final int N = thiskind.length;
+            final String[] thatkind = that.kind = new String[N];
+            System.arraycopy(thiskind, 0, thatkind, 0, N);
+        }
+
+        if (this.extras != null) {
+            that.extras = new Bundle(this.extras);
+
+        }
+
         return that;
     }
 
@@ -517,6 +647,17 @@
         } else {
             parcel.writeInt(0);
         }
+
+        parcel.writeInt(priority);
+        
+        parcel.writeStringArray(kind); // ok for null
+        
+        if (extras != null) {
+            parcel.writeInt(1);
+            extras.writeToParcel(parcel, 0);
+        } else {
+            parcel.writeInt(0);
+        }
     }
 
     /**
@@ -551,7 +692,7 @@
      * that you take care of task management as described in the
      * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
      * Stack</a> document.
-     * 
+     *
      * @deprecated Use {@link Builder} instead.
      */
     @Deprecated
@@ -579,7 +720,9 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("Notification(contentView=");
+        sb.append("Notification(pri=");
+        sb.append(priority);
+        sb.append(" contentView=");
         if (contentView != null) {
             sb.append(contentView.getPackage());
             sb.append("/0x");
@@ -587,6 +730,7 @@
         } else {
             sb.append("null");
         }
+        // TODO(dsandler): defaults take precedence over local values, so reorder the branches below
         sb.append(" vibrate=");
         if (this.vibrate != null) {
             int N = this.vibrate.length-1;
@@ -604,7 +748,7 @@
         } else {
             sb.append("null");
         }
-        sb.append(",sound=");
+        sb.append(" sound=");
         if (this.sound != null) {
             sb.append(this.sound.toString());
         } else if ((this.defaults & DEFAULT_SOUND) != 0) {
@@ -612,20 +756,39 @@
         } else {
             sb.append("null");
         }
-        sb.append(",defaults=0x");
+        sb.append(" defaults=0x");
         sb.append(Integer.toHexString(this.defaults));
-        sb.append(",flags=0x");
+        sb.append(" flags=0x");
         sb.append(Integer.toHexString(this.flags));
-        if ((this.flags & FLAG_HIGH_PRIORITY) != 0) {
-            sb.append("!!!1!one!");
+        sb.append(" kind=[");
+        if (this.kind == null) {
+            sb.append("null");
+        } else {
+            for (int i=0; i<this.kind.length; i++) {
+                if (i>0) sb.append(",");
+                sb.append(this.kind[i]);
+            }
         }
-        sb.append(")");
+        sb.append("])");
         return sb.toString();
     }
 
     /**
-     * Builder class for {@link Notification} objects.  Allows easier control over
-     * all the flags, as well as help constructing the typical notification layouts.
+     * Builder class for {@link Notification} objects.
+     * 
+     * Provides a convenient way to set the various fields of a {@link Notification} and generate
+     * content views using the platform's notification layout template. 
+     * 
+     * Example:
+     * 
+     * <pre class="prettyprint">
+     * Notification noti = new Notification.Builder()
+     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+     *         .setContentText(subject)
+     *         .setSmallIcon(R.drawable.new_mail)
+     *         .setLargeIcon(aBitmap)
+     *         .getNotification();
+     * </pre>
      */
     public static class Builder {
         private Context mContext;
@@ -655,16 +818,28 @@
         private int mProgressMax;
         private int mProgress;
         private boolean mProgressIndeterminate;
+        private ArrayList<String> mKindList = new ArrayList<String>(1);
+        private Bundle mExtras;
+        private int mPriority;
 
         /**
-         * Constructor.
+         * Constructs a new Builder with the defaults:
          *
-         * Automatically sets the when field to {@link System#currentTimeMillis()
-         * System.currentTimeMllis()} and the audio stream to the {@link #STREAM_DEFAULT}.
+
+         * <table>
+         * <tr><th align=right>priority</th>
+         *     <td>{@link #PRIORITY_DEFAULT}</td></tr>
+         * <tr><th align=right>when</th>
+         *     <td>now ({@link System#currentTimeMillis()})</td></tr>
+         * <tr><th align=right>audio stream</th>
+         *     <td>{@link #STREAM_DEFAULT}</td></tr>
+         * </table>
          *
-         * @param context A {@link Context} that will be used to construct the
-         *      RemoteViews. The Context will not be held past the lifetime of this
-         *      Builder object.
+
+         * @param context
+         *            A {@link Context} that will be used by the Builder to construct the
+         *            RemoteViews. The Context will not be held past the lifetime of this Builder
+         *            object.
          */
         public Builder(Context context) {
             mContext = context;
@@ -672,11 +847,14 @@
             // Set defaults to match the defaults of a Notification
             mWhen = System.currentTimeMillis();
             mAudioStreamType = STREAM_DEFAULT;
+            mPriority = PRIORITY_DEFAULT;
         }
 
         /**
-         * Set the time that the event occurred.  Notifications in the panel are
-         * sorted by this time.
+         * Add a timestamp pertaining to the notification (usually the time the event occurred).
+         *
+
+         * @see Notification#when
          */
         public Builder setWhen(long when) {
             mWhen = when;
@@ -684,11 +862,18 @@
         }
 
         /**
-         * Set the small icon to use in the notification layouts.  Different classes of devices
-         * may return different sizes.  See the UX guidelines for more information on how to
-         * design these icons.
+         * Set the small icon resource, which will be used to represent the notification in the
+         * status bar.
          *
-         * @param icon A resource ID in the application's package of the drawble to use.
+
+         * The platform template for the expanded view will draw this icon in the left, unless a
+         * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
+         * icon will be moved to the right-hand side.
+         *
+
+         * @param icon
+         *            A resource ID in the application's package of the drawable to use.
+         * @see Notification#icon
          */
         public Builder setSmallIcon(int icon) {
             mSmallIcon = icon;
@@ -700,10 +885,11 @@
          * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
          * LevelListDrawable}.
          *
-         * @param icon A resource ID in the application's package of the drawble to use.
+         * @param icon A resource ID in the application's package of the drawable to use.
          * @param level The level to use for the icon.
          *
-         * @see android.graphics.drawable.LevelListDrawable
+         * @see Notification#icon
+         * @see Notification#iconLevel
          */
         public Builder setSmallIcon(int icon, int level) {
             mSmallIcon = icon;
@@ -712,7 +898,7 @@
         }
 
         /**
-         * Set the title (first row) of the notification, in a standard notification.
+         * Set the first line of text in the platform notification template.
          */
         public Builder setContentTitle(CharSequence title) {
             mContentTitle = title;
@@ -720,7 +906,7 @@
         }
 
         /**
-         * Set the text (second row) of the notification, in a standard notification.
+         * Set the second line of text in the platform notification template.
          */
         public Builder setContentText(CharSequence text) {
             mContentText = text;
@@ -738,7 +924,11 @@
         }
 
         /**
-         * Set the large text at the right-hand side of the notification.
+         * A small piece of additional information pertaining to this notification.
+         *
+
+         * The platform template will draw this on the last line of the notification, at the far
+         * right (to the right of a smallIcon if it has been placed there).
          */
         public Builder setContentInfo(CharSequence info) {
             mContentInfo = info;
@@ -746,8 +936,10 @@
         }
 
         /**
-         * Set the progress this notification represents, which may be
-         * represented as a {@link ProgressBar}.
+         * Set the progress this notification represents.
+         *
+
+         * The platform template will represent this using a {@link ProgressBar}.
          */
         public Builder setProgress(int max, int progress, boolean indeterminate) {
             mProgressMax = max;
@@ -757,7 +949,10 @@
         }
 
         /**
-         * Supply a custom RemoteViews to use instead of the standard one.
+         * Supply a custom RemoteViews to use instead of the platform template.
+         *
+
+         * @see Notification#contentView
          */
         public Builder setContent(RemoteViews views) {
             mContentView = views;
@@ -765,12 +960,20 @@
         }
 
         /**
-         * Supply a {@link PendingIntent} to send when the notification is clicked.
-         * If you do not supply an intent, you can now add PendingIntents to individual
-         * views to be launched when clicked by calling {@link RemoteViews#setOnClickPendingIntent
-         * RemoteViews.setOnClickPendingIntent(int,PendingIntent)}.  Be sure to
-         * read {@link Notification#contentIntent Notification.contentIntent} for
-         * how to correctly use this.
+         * Supply a {@link PendingIntent} to be sent when the notification is clicked.
+         *
+
+         * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
+         * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
+         * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
+
+         * to assign PendingIntents to individual views in that custom layout (i.e., to create
+
+         * clickable buttons inside the
+         * notification view).
+         *
+
+         * @see Notification#contentIntent Notification.contentIntent
          */
         public Builder setContentIntent(PendingIntent intent) {
             mContentIntent = intent;
@@ -778,11 +981,10 @@
         }
 
         /**
-         * Supply a {@link PendingIntent} to send when the notification is cleared by the user
-         * directly from the notification panel.  For example, this intent is sent when the user
-         * clicks the "Clear all" button, or the individual "X" buttons on notifications.  This
-         * intent is not sent when the application calls {@link NotificationManager#cancel
-         * NotificationManager.cancel(int)}.
+         * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
+         *
+
+         * @see Notification#deleteIntent
          */
         public Builder setDeleteIntent(PendingIntent intent) {
             mDeleteIntent = intent;
@@ -801,6 +1003,8 @@
          * @param intent The pending intent to launch.
          * @param highPriority Passing true will cause this notification to be sent
          *          even if other notifications are suppressed.
+         *
+         * @see Notification#fullScreenIntent
          */
         public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
             mFullScreenIntent = intent;
@@ -809,8 +1013,11 @@
         }
 
         /**
-         * Set the text that is displayed in the status bar when the notification first
+         * Set the "ticker" text which is displayed in the status bar when the notification first
          * arrives.
+         *
+
+         * @see Notification#tickerText
          */
         public Builder setTicker(CharSequence tickerText) {
             mTickerText = tickerText;
@@ -821,6 +1028,9 @@
          * Set the text that is displayed in the status bar when the notification first
          * arrives, and also a RemoteViews object that may be displayed instead on some
          * devices.
+         *
+         * @see Notification#tickerText
+         * @see Notification#tickerView
          */
         public Builder setTicker(CharSequence tickerText, RemoteViews views) {
             mTickerText = tickerText;
@@ -829,7 +1039,12 @@
         }
 
         /**
-         * Set the large icon that is shown in the ticker and notification.
+         * Add a large icon to the notification (and the ticker on some devices).
+         *
+         * In the platform template, this image will be shown on the left of the notification view
+         * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side).
+         *
+         * @see Notification#largeIcon
          */
         public Builder setLargeIcon(Bitmap icon) {
             mLargeIcon = icon;
@@ -837,7 +1052,11 @@
         }
 
         /**
-         * Set the sound to play.  It will play on the default stream.
+         * Set the sound to play.
+         *
+         * It will be played on the {@link #STREAM_DEFAULT default stream} for notifications.
+         *
+         * @see Notification#sound
          */
         public Builder setSound(Uri sound) {
             mSound = sound;
@@ -846,10 +1065,11 @@
         }
 
         /**
-         * Set the sound to play.  It will play on the stream you supply.
+         * Set the sound to play, along with a specific stream on which to play it.
          *
-         * @see #STREAM_DEFAULT
-         * @see AudioManager for the <code>STREAM_</code> constants.
+         * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
+         *
+         * @see Notification#sound
          */
         public Builder setSound(Uri sound, int streamType) {
             mSound = sound;
@@ -860,8 +1080,12 @@
         /**
          * Set the vibration pattern to use.
          *
-         * @see android.os.Vibrator for a discussion of the <code>pattern</code>
-         * parameter.
+
+         * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
+         * <code>pattern</code> parameter.
+         *
+
+         * @see Notification#vibrate
          */
         public Builder setVibrate(long[] pattern) {
             mVibrate = pattern;
@@ -869,9 +1093,16 @@
         }
 
         /**
-         * Set the argb value that you would like the LED on the device to blnk, as well as the
-         * rate.  The rate is specified in terms of the number of milliseconds to be on
-         * and then the number of milliseconds to be off.
+         * Set the desired color for the indicator LED on the device, as well as the
+         * blink duty cycle (specified in milliseconds).
+         *
+
+         * Not all devices will honor all (or even any) of these values.
+         *
+
+         * @see Notification#ledARGB
+         * @see Notification#ledOnMS
+         * @see Notification#ledOffMS
          */
         public Builder setLights(int argb, int onMs, int offMs) {
             mLedArgb = argb;
@@ -881,15 +1112,20 @@
         }
 
         /**
-         * Set whether this is an ongoing notification.
+         * Set whether this is an "ongoing" notification.
          *
-         * <p>Ongoing notifications differ from regular notifications in the following ways:
-         * <ul>
-         *   <li>Ongoing notifications are sorted above the regular notifications in the
-         *   notification panel.</li>
-         *   <li>Ongoing notifications do not have an 'X' close button, and are not affected
-         *   by the "Clear all" button.
-         * </ul>
+
+         * Ongoing notifications cannot be dismissed by the user, so your application or service
+         * must take care of canceling them.
+         *
+
+         * They are typically used to indicate a background task that the user is actively engaged
+         * with (e.g., playing music) or is pending in some way and therefore occupying the device
+         * (e.g., a file download, sync operation, active network connection).
+         *
+
+         * @see Notification#FLAG_ONGOING_EVENT
+         * @see Service#setForeground(boolean)
          */
         public Builder setOngoing(boolean ongoing) {
             setFlag(FLAG_ONGOING_EVENT, ongoing);
@@ -899,6 +1135,8 @@
         /**
          * Set this flag if you would only like the sound, vibrate
          * and ticker to be played if the notification is not already showing.
+         *
+         * @see Notification#FLAG_ONLY_ALERT_ONCE
          */
         public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
             setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
@@ -906,10 +1144,10 @@
         }
 
         /**
-         * Setting this flag will make it so the notification is automatically
-         * canceled when the user clicks it in the panel.  The PendingIntent
-         * set with {@link #setDeleteIntent} will be broadcast when the notification
-         * is canceled.
+         * Make this notification automatically dismissed when the user touches it. The
+         * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
+         *
+         * @see Notification#FLAG_AUTO_CANCEL
          */
         public Builder setAutoCancel(boolean autoCancel) {
             setFlag(FLAG_AUTO_CANCEL, autoCancel);
@@ -917,7 +1155,7 @@
         }
 
         /**
-         * Set the default notification options that will be used.
+         * Set which notification properties will be inherited from system defaults.
          * <p>
          * The value should be one or more of the following fields combined with
          * bitwise-or:
@@ -930,6 +1168,41 @@
             return this;
         }
 
+        /**
+         * Set the priority of this notification.
+         *
+         * @see Notification#priority
+         */
+        public Builder setPriority(int pri) {
+            mPriority = pri;
+            return this;
+        }
+        
+        /**
+         * Add a kind (category) to this notification. Optional.
+         * 
+         * @see Notification#kind
+         */
+        public Builder addKind(String k) {
+            mKindList.add(k);
+            return this;
+        }
+
+        /**
+         * Add metadata to this notification.
+         *
+         * A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
+         * current contents are copied into the Notification each time {@link #getNotification()} is
+         * called.
+         *
+         * @see Notification#extras
+         * @hide
+         */
+        public Builder setExtras(Bundle bag) {
+            mExtras = bag;
+            return this;
+        }
+
         private void setFlag(int mask, boolean value) {
             if (value) {
                 mFlags |= mask;
@@ -1042,6 +1315,14 @@
             if ((mDefaults & DEFAULT_LIGHTS) != 0) {
                 n.flags |= FLAG_SHOW_LIGHTS;
             }
+            if (mKindList.size() > 0) {
+                n.kind = new String[mKindList.size()];
+                mKindList.toArray(n.kind);
+            } else {
+                n.kind = null;
+            }
+            n.priority = mPriority;
+            n.extras = mExtras != null ? new Bundle(mExtras) : null;
             return n;
         }
     }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index c1e28b0..c057d66 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -30,6 +30,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -433,7 +434,12 @@
      */
     public WallpaperInfo getWallpaperInfo() {
         try {
-            return sGlobals.mService.getWallpaperInfo();
+            if (sGlobals.mService == null) {
+                Log.w(TAG, "WallpaperService not running");
+                return null;
+            } else {
+                return sGlobals.mService.getWallpaperInfo();
+            }
         } catch (RemoteException e) {
             return null;
         }
@@ -451,6 +457,10 @@
      * wallpaper.
      */
     public void setResource(int resid) throws IOException {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            return;
+        }
         try {
             Resources resources = mContext.getResources();
             /* Set the wallpaper to the default values */
@@ -483,6 +493,10 @@
      * wallpaper.
      */
     public void setBitmap(Bitmap bitmap) throws IOException {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            return;
+        }
         try {
             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
             if (fd == null) {
@@ -515,6 +529,10 @@
      * wallpaper.
      */
     public void setStream(InputStream data) throws IOException {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            return;
+        }
         try {
             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
             if (fd == null) {
@@ -558,6 +576,10 @@
      * mandatory.
      */
     public int getDesiredMinimumWidth() {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            return 0;
+        }
         try {
             return sGlobals.mService.getWidthHint();
         } catch (RemoteException e) {
@@ -581,6 +603,10 @@
      * mandatory.
      */
     public int getDesiredMinimumHeight() {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            return 0;
+        }
         try {
             return sGlobals.mService.getHeightHint();
         } catch (RemoteException e) {
@@ -599,7 +625,11 @@
      */
     public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
         try {
-            sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
+            if (sGlobals.mService == null) {
+                Log.w(TAG, "WallpaperService not running");
+            } else {
+                sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
+            }
         } catch (RemoteException e) {
             // Ignore
         }
diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java
index 170171e..a74a268 100644
--- a/core/java/android/app/backup/WallpaperBackupHelper.java
+++ b/core/java/android/app/backup/WallpaperBackupHelper.java
@@ -38,15 +38,23 @@
     private static final boolean DEBUG = false;
 
     // This path must match what the WallpaperManagerService uses
-    private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper";
+    // TODO: Will need to change if backing up non-primary user's wallpaper
+    public static final String WALLPAPER_IMAGE = "/data/system/users/0/wallpaper";
+    public static final String WALLPAPER_INFO = "/data/system/users/0/wallpaper_info.xml";
+    // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
+    public static final String WALLPAPER_IMAGE_KEY =
+            "/data/data/com.android.settings/files/wallpaper";
+    public static final String WALLPAPER_INFO_KEY = "/data/system/wallpaper_info.xml";
 
     // Stage file - should be adjacent to the WALLPAPER_IMAGE location.  The wallpapers
     // will be saved to this file from the restore stream, then renamed to the proper
     // location if it's deemed suitable.
-    private static final String STAGE_FILE = "/data/data/com.android.settings/files/wallpaper-tmp";
+    // TODO: Will need to change if backing up non-primary user's wallpaper
+    private static final String STAGE_FILE = "/data/system/users/0/wallpaper-tmp";
 
     Context mContext;
     String[] mFiles;
+    String[] mKeys;
     double mDesiredMinWidth;
     double mDesiredMinHeight;
 
@@ -57,11 +65,12 @@
      * @param context
      * @param files
      */
-    public WallpaperBackupHelper(Context context, String... files) {
+    public WallpaperBackupHelper(Context context, String[] files, String[] keys) {
         super(context);
 
         mContext = context;
         mFiles = files;
+        mKeys = keys;
 
         WallpaperManager wpm;
         wpm = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
@@ -89,7 +98,7 @@
      */
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState) {
-        performBackup_checked(oldState, data, newState, mFiles, mFiles);
+        performBackup_checked(oldState, data, newState, mFiles, mKeys);
     }
 
     /**
@@ -99,8 +108,8 @@
      */
     public void restoreEntity(BackupDataInputStream data) {
         final String key = data.getKey();
-        if (isKeyInList(key, mFiles)) {
-            if (key.equals(WALLPAPER_IMAGE)) {
+        if (isKeyInList(key, mKeys)) {
+            if (key.equals(WALLPAPER_IMAGE_KEY)) {
                 // restore the file to the stage for inspection
                 File f = new File(STAGE_FILE);
                 if (writeFile(f, data)) {
@@ -135,9 +144,9 @@
                         f.delete();
                     }
                 }
-            } else {
-                // Some other normal file; just decode it to its destination
-                File f = new File(key);
+            } else if (key.equals(WALLPAPER_INFO_KEY)) {
+                // XML file containing wallpaper info
+                File f = new File(WALLPAPER_INFO);
                 writeFile(f, data);
             }
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bfbd0ac..a1198de 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -905,6 +905,16 @@
     public abstract void sendBroadcast(Intent intent);
 
     /**
+     * Same as #sendBroadcast(Intent intent), but for a specific user. Used by the system only.
+     * @param intent the intent to broadcast
+     * @param userId user to send the intent to
+     * @hide
+     */
+    public void sendBroadcast(Intent intent, int userId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Broadcast the given intent to all interested BroadcastReceivers, allowing
      * an optional required permission to be enforced.  This
      * call is asynchronous; it returns immediately, and you will continue
@@ -1307,6 +1317,15 @@
             int flags);
 
     /**
+     * Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userId
+     * argument for use by system server and other multi-user aware code.
+     * @hide
+     */
+    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Disconnect from an application service.  You will no longer receive
      * calls as the service is restarted, and the service is now allowed to
      * stop at any time.
@@ -1789,6 +1808,17 @@
     public static final String USB_SERVICE = "usb";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.hardware.SerialManager} for access to serial ports.
+     *
+     * @see #getSystemService
+     * @see android.harware.SerialManager
+     *
+     * @hide
+     */
+    public static final String SERIAL_SERVICE = "serial";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3928aaf..5ba9dcc 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -294,6 +294,12 @@
         mBase.sendBroadcast(intent);
     }
 
+    /** @hide */
+    @Override
+    public void sendBroadcast(Intent intent, int userId) {
+        mBase.sendBroadcast(intent, userId);
+    }
+
     @Override
     public void sendBroadcast(Intent intent, String receiverPermission) {
         mBase.sendBroadcast(intent, receiverPermission);
@@ -370,6 +376,12 @@
         return mBase.bindService(service, conn, flags);
     }
 
+    /** @hide */
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+        return mBase.bindService(service, conn, flags, userId);
+    }
+
     @Override
     public void unbindService(ServiceConnection conn) {
         mBase.unbindService(conn);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index faee873..8029bd5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2581,6 +2581,11 @@
                 false)) {
             s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
         }
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_isolatedProcess,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS;
+        }
 
         sa.recycle();
 
@@ -3375,7 +3380,8 @@
 
     public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
         if (s == null) return null;
-        if (!copyNeeded(flags, s.owner, s.metaData) && userId == 0) {
+        if (!copyNeeded(flags, s.owner, s.metaData)
+                && userId == UserId.getUserId(s.info.applicationInfo.uid)) {
             return s.info;
         }
         // Make shallow copies so we can store the metadata safely
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 612e345..7ee84ab 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -42,10 +42,17 @@
     public static final int FLAG_STOP_WITH_TASK = 0x0001;
 
     /**
+     * Bit in {@link #flags}: If set, the service will run in its own
+     * isolated process.  Set from the
+     * {@link android.R.attr#isolatedProcess} attribute.
+     */
+    public static final int FLAG_ISOLATED_PROCESS = 0x0002;
+
+    /**
      * Options that have been set in the service declaration in the
      * manifest.
      * These include:
-     * {@link #FLAG_STOP_WITH_TASK}
+     * {@link #FLAG_STOP_WITH_TASK}, {@link #FLAG_ISOLATED_PROCESS}.
      */
     public int flags;
 
diff --git a/libs/rs/rsFifo.cpp b/core/java/android/hardware/ISerialManager.aidl
similarity index 66%
copy from libs/rs/rsFifo.cpp
copy to core/java/android/hardware/ISerialManager.aidl
index 3d5d8c4..74d30f7 100644
--- a/libs/rs/rsFifo.cpp
+++ b/core/java/android/hardware/ISerialManager.aidl
@@ -14,18 +14,16 @@
  * limitations under the License.
  */
 
-#include "rsFifoSocket.h"
-#include "utils/Timers.h"
-#include "utils/StopWatch.h"
+package android.hardware;
 
-using namespace android;
-using namespace android::renderscript;
+import android.os.ParcelFileDescriptor;
 
-Fifo::Fifo() {
+/** @hide */
+interface ISerialManager
+{
+    /* Returns a list of all available serial ports */
+    String[] getSerialPorts();
 
+    /* Returns a file descriptor for the serial port. */
+    ParcelFileDescriptor openSerialPort(String name);
 }
-
-Fifo::~Fifo() {
-
-}
-
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 784bcc5..51a17c1 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -294,7 +294,7 @@
      * <li>X is defined as the vector product <b>Y.Z</b> (It is tangential to
      * the ground at the device's current location and roughly points East).</li>
      * <li>Y is tangential to the ground at the device's current location and
-     * points towards the magnetic North Pole.</li>
+     * points towards magnetic north.</li>
      * <li>Z points towards the sky and is perpendicular to the ground.</li>
      * </ul>
      *
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index aeb5d92..3fdf246 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -616,6 +616,7 @@
             Message msg = Message.obtain();
             msg.what = 0;
             msg.obj = t;
+            msg.setAsynchronous(true);
             mHandler.sendMessage(msg);
         }
     }
diff --git a/core/java/android/hardware/SerialManager.java b/core/java/android/hardware/SerialManager.java
new file mode 100644
index 0000000..c5e1c2b
--- /dev/null
+++ b/core/java/android/hardware/SerialManager.java
@@ -0,0 +1,88 @@
+/*
+ * 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 android.hardware;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * @hide
+ */
+public class SerialManager {
+    private static final String TAG = "SerialManager";
+
+    private final Context mContext;
+    private final ISerialManager mService;
+
+    /**
+     * {@hide}
+     */
+    public SerialManager(Context context, ISerialManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Returns a string array containing the names of available serial ports
+     *
+     * @return names of available serial ports
+     */
+    public String[] getSerialPorts() {
+        try {
+            return mService.getSerialPorts();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getSerialPorts", e);
+            return null;
+        }
+    }
+
+    /**
+     * Opens and returns the {@link android.hardware.SerialPort} with the given name.
+     * The speed of the serial port must be one of:
+     * 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600,
+     * 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000,
+     * 1500000, 2000000, 2500000, 3000000, 3500000 or 4000000
+     *
+     * @param name of the serial port
+     * @param speed at which to open the serial port
+     * @return the serial port
+     */
+    public SerialPort openSerialPort(String name, int speed) throws IOException {
+        try {
+            ParcelFileDescriptor pfd = mService.openSerialPort(name);
+            if (pfd != null) {
+                SerialPort port = new SerialPort(name);
+                port.open(pfd, speed);
+                return port;
+            } else {
+                throw new IOException("Could not open serial port " + name);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "exception in UsbManager.openDevice", e);
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/hardware/SerialPort.java b/core/java/android/hardware/SerialPort.java
new file mode 100644
index 0000000..5ef122b
--- /dev/null
+++ b/core/java/android/hardware/SerialPort.java
@@ -0,0 +1,130 @@
+/*
+ * 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 android.hardware;
+
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class SerialPort {
+
+    private static final String TAG = "SerialPort";
+
+    // used by the JNI code
+    private int mNativeContext;
+    private final String mName;
+    private ParcelFileDescriptor mFileDescriptor;
+
+    /**
+     * SerialPort should only be instantiated by SerialManager
+     * @hide
+     */
+    public SerialPort(String name) {
+        mName = name;
+    }
+
+    /**
+     * SerialPort should only be instantiated by SerialManager
+     * Speed must be one of 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600,
+     * 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000,
+     * 1500000, 2000000, 2500000, 3000000, 3500000, 4000000
+     *
+     * @hide
+     */
+    public void open(ParcelFileDescriptor pfd, int speed) throws IOException {
+        native_open(pfd.getFileDescriptor(), speed);
+        mFileDescriptor = pfd;
+    }
+
+    /**
+     * Closes the serial port
+     */
+    public void close() throws IOException {
+        if (mFileDescriptor != null) {
+            mFileDescriptor.close();
+            mFileDescriptor = null;
+        }
+        native_close();
+    }
+
+    /**
+     * Returns the name of the serial port
+     *
+     * @return the serial port's name
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Reads data into the provided buffer
+     *
+     * @param buffer to read into
+     * @return number of bytes read
+     */
+    public int read(ByteBuffer buffer) throws IOException {
+        if (buffer.isDirect()) {
+            return native_read_direct(buffer, buffer.remaining());
+        } else if (buffer.hasArray()) {
+            return native_read_array(buffer.array(), buffer.remaining());
+        } else {
+            throw new IllegalArgumentException("buffer is not direct and has no array");
+        }
+    }
+
+    /**
+     * Writes data from provided buffer
+     *
+     * @param buffer to write
+     * @param length number of bytes to write
+     */
+    public void write(ByteBuffer buffer, int length) throws IOException {
+        if (buffer.isDirect()) {
+            native_write_direct(buffer, length);
+        } else if (buffer.hasArray()) {
+            native_write_array(buffer.array(), length);
+        } else {
+            throw new IllegalArgumentException("buffer is not direct and has no array");
+        }
+    }
+
+    /**
+     * Sends a stream of zero valued bits for 0.25 to 0.5 seconds
+     */
+    public void sendBreak() {
+        native_send_break();
+    }
+
+    private native void native_open(FileDescriptor pfd, int speed) throws IOException;
+    private native void native_close();
+    private native int native_read_array(byte[] buffer, int length) throws IOException;
+    private native int native_read_direct(ByteBuffer buffer, int length) throws IOException;
+    private native void native_write_array(byte[] buffer, int length) throws IOException;
+    private native void native_write_direct(ByteBuffer buffer, int length) throws IOException;
+    private native void native_send_break();
+}
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 21ecc22..fb09ba5 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -49,6 +49,7 @@
     private LinkCapabilities mLinkCapabilities;
     private NetworkInfo mNetworkInfo;
     private InterfaceObserver mInterfaceObserver;
+    private String mHwAddr;
 
     /* For sending events to connectivity service handler */
     private Handler mCsHandler;
@@ -74,15 +75,13 @@
             if (mIface.equals(iface) && mLinkUp != up) {
                 Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down"));
                 mLinkUp = up;
+                mTracker.mNetworkInfo.setIsAvailable(up);
 
                 // use DHCP
                 if (up) {
                     mTracker.reconnect();
                 } else {
-                    NetworkUtils.stopDhcp(mIface);
-                    mTracker.mNetworkInfo.setIsAvailable(false);
-                    mTracker.mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED,
-                                                           null, null);
+                    mTracker.disconnect();
                 }
             }
         }
@@ -104,10 +103,6 @@
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, "");
         mLinkProperties = new LinkProperties();
         mLinkCapabilities = new LinkCapabilities();
-        mLinkUp = false;
-
-        mNetworkInfo.setIsAvailable(false);
-        setTeardownRequested(false);
     }
 
     private void interfaceAdded(String iface) {
@@ -116,7 +111,7 @@
 
         Log.d(TAG, "Adding " + iface);
 
-        synchronized(mIface) {
+        synchronized(this) {
             if(!mIface.isEmpty())
                 return;
             mIface = iface;
@@ -125,21 +120,15 @@
         mNetworkInfo.setIsAvailable(true);
         Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
         msg.sendToTarget();
-
-        runDhcp();
     }
 
-    private void interfaceRemoved(String iface) {
-        if (!iface.equals(mIface))
-            return;
-
-        Log.d(TAG, "Removing " + iface);
+    public void disconnect() {
 
         NetworkUtils.stopDhcp(mIface);
 
         mLinkProperties.clear();
         mNetworkInfo.setIsAvailable(false);
-        mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
+        mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
 
         Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
         msg.sendToTarget();
@@ -147,6 +136,21 @@
         msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
         msg.sendToTarget();
 
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+        try {
+            service.clearInterfaceAddresses(mIface);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to clear addresses or disable ipv6" + e);
+        }
+    }
+
+    private void interfaceRemoved(String iface) {
+        if (!iface.equals(mIface))
+            return;
+
+        Log.d(TAG, "Removing " + iface);
+        disconnect();
         mIface = "";
     }
 
@@ -161,7 +165,7 @@
                 mLinkProperties = dhcpInfoInternal.makeLinkProperties();
                 mLinkProperties.setInterfaceName(mIface);
 
-                mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+                mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
                 Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
                 msg.sendToTarget();
             }
@@ -208,8 +212,15 @@
             for (String iface : ifaces) {
                 if (iface.matches(sIfaceMatch)) {
                     mIface = iface;
+                    service.setInterfaceUp(iface);
                     InterfaceConfiguration config = service.getInterfaceConfig(iface);
                     mLinkUp = config.isActive();
+                    if (config != null && mHwAddr == null) {
+                        mHwAddr = config.getHardwareAddress();
+                        if (mHwAddr != null) {
+                            mNetworkInfo.setExtraInfo(mHwAddr);
+                        }
+                    }
                     reconnect();
                     break;
                 }
@@ -239,9 +250,11 @@
      * Re-enable connectivity to a network after a {@link #teardown()}.
      */
     public boolean reconnect() {
-        mTeardownRequested.set(false);
-        runDhcp();
-        return true;
+        if (mLinkUp) {
+            mTeardownRequested.set(false);
+            runDhcp();
+        }
+        return mLinkUp;
     }
 
     /**
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index a45ec54..31dc965a 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -21,5 +21,6 @@
 
     void onUidRulesChanged(int uid, int uidRules);
     void onMeteredIfacesChanged(in String[] meteredIfaces);
+    void onRestrictBackgroundChanged(boolean restrictBackground);
 
 }
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 7286f0d..2f43cb8 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -351,6 +351,18 @@
     }
 
     /**
+     * Set the extraInfo field.
+     * @param extraInfo an optional {@code String} providing addditional network state
+     * information passed up from the lower networking layers.
+     * @hide
+     */
+    public void setExtraInfo(String extraInfo) {
+        synchronized (this) {
+            this.mExtraInfo = extraInfo;
+        }
+    }
+
+    /**
      * Report the reason an attempt to establish connectivity failed,
      * if one is available.
      * @return the reason for failure, or null if not available
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index f94d320..06c6c6e 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -25,15 +25,17 @@
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509TrustManager;
 import org.apache.harmony.security.provider.cert.X509CertImpl;
 import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl;
+import org.apache.harmony.xnet.provider.jsse.TrustManagerImpl;
 
 /**
  * Class responsible for all server certificate validation functionality
  *
  * {@hide}
  */
-class CertificateChainValidator {
+public class CertificateChainValidator {
 
     /**
      * The singleton instance of the certificate chain validator
@@ -122,6 +124,18 @@
     }
 
     /**
+     * Handles updates to credential storage.
+     */
+    public static void handleTrustStorageUpdate() {
+
+        X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultTrustManager();
+        if( x509TrustManager instanceof TrustManagerImpl ) {
+            TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
+            trustManager.handleTrustStorageUpdate();
+        }
+    }
+
+    /**
      * Common code of doHandshakeAndValidateServerCertificates and verifyServerCertificates.
      * Calls DomainNamevalidator to verify the domain, and TrustManager to verify the certs.
      * @param chain the cert chain in X509 cert format.
diff --git a/core/java/android/net/http/HttpResponseCache.java b/core/java/android/net/http/HttpResponseCache.java
index 21736aa..73f3d7c 100644
--- a/core/java/android/net/http/HttpResponseCache.java
+++ b/core/java/android/net/http/HttpResponseCache.java
@@ -22,8 +22,10 @@
 import java.io.IOException;
 import java.net.CacheRequest;
 import java.net.CacheResponse;
+import java.net.ExtendedResponseCache;
 import java.net.HttpURLConnection;
 import java.net.ResponseCache;
+import java.net.ResponseSource;
 import java.net.URI;
 import java.net.URLConnection;
 import java.util.List;
@@ -149,7 +151,8 @@
  *       } catch (Exception httpResponseCacheNotAvailable) {
  *       }}</pre>
  */
-public final class HttpResponseCache extends ResponseCache implements Closeable {
+public final class HttpResponseCache extends ResponseCache
+        implements Closeable, ExtendedResponseCache {
 
     private final libcore.net.http.HttpResponseCache delegate;
 
@@ -260,6 +263,21 @@
         return delegate.getRequestCount();
     }
 
+    /** @hide */
+    @Override public void trackResponse(ResponseSource source) {
+        delegate.trackResponse(source);
+    }
+
+    /** @hide */
+    @Override public void trackConditionalCacheHit() {
+        delegate.trackConditionalCacheHit();
+    }
+
+    /** @hide */
+    @Override public void update(CacheResponse conditionalCacheHit, HttpURLConnection connection) {
+        delegate.update(conditionalCacheHit, connection);
+    }
+
     /**
      * Uninstalls the cache and releases any active resources. Stored contents
      * will remain on the filesystem.
diff --git a/core/java/android/os/CommonClock.java b/core/java/android/os/CommonClock.java
new file mode 100644
index 0000000..3a1da97
--- /dev/null
+++ b/core/java/android/os/CommonClock.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2012 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 android.os;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.util.NoSuchElementException;
+import static libcore.io.OsConstants.*;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.CommonTimeUtils;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * Used for accessing the android common time service's common clock and receiving notifications
+ * about common time synchronization status changes.
+ * @hide
+ */
+public class CommonClock {
+    /**
+     * Sentinel value returned by {@link #getTime()} and {@link #getEstimatedError()} when the
+     * common time service is not able to determine the current common time due to a lack of
+     * synchronization.
+     */
+    public static final long TIME_NOT_SYNCED = -1;
+
+    /**
+     * Sentinel value returned by {@link #getTimelineId()} when the common time service is not
+     * currently synced to any timeline.
+     */
+    public static final long INVALID_TIMELINE_ID = 0;
+
+    /**
+     * Sentinel value returned by {@link #getEstimatedError()} when the common time service is not
+     * currently synced to any timeline.
+     */
+    public static final int ERROR_ESTIMATE_UNKNOWN = 0x7FFFFFFF;
+
+    /**
+     * Value used by {@link #getState()} to indicate that there was an internal error while
+     * attempting to determine the state of the common time service.
+     */
+    public static final int STATE_INVALID = -1;
+
+    /**
+     * Value used by {@link #getState()} to indicate that the common time service is in its initial
+     * state and attempting to find the current timeline master, if any.  The service will
+     * transition to either {@link #STATE_CLIENT} if it finds an active master, or to
+     * {@link #STATE_MASTER} if no active master is found and this client becomes the master of a
+     * new timeline.
+     */
+    public static final int STATE_INITIAL = 0;
+
+    /**
+     * Value used by {@link #getState()} to indicate that the common time service is in its client
+     * state and is synchronizing its time to a different timeline master on the network.
+     */
+    public static final int STATE_CLIENT = 1;
+
+    /**
+     * Value used by {@link #getState()} to indicate that the common time service is in its master
+     * state and is serving as the timeline master for other common time service clients on the
+     * network.
+     */
+    public static final int STATE_MASTER = 2;
+
+    /**
+     * Value used by {@link #getState()} to indicate that the common time service is in its Ronin
+     * state.  Common time service instances in the client state enter the Ronin state after their
+     * timeline master becomes unreachable on the network.  Common time services who enter the Ronin
+     * state will begin a new master election for the timeline they were recently clients of.  As
+     * clients detect they are not the winner and drop out of the election, they will transition to
+     * the {@link #STATE_WAIT_FOR_ELECTION} state.  When there is only one client remaining in the
+     * election, it will assume ownership of the timeline and transition to the
+     * {@link #STATE_MASTER} state.  During the election, all clients will allow their timeline to
+     * drift without applying correction.
+     */
+    public static final int STATE_RONIN = 3;
+
+    /**
+     * Value used by {@link #getState()} to indicate that the common time service is waiting for a
+     * master election to conclude and for the new master to announce itself before transitioning to
+     * the {@link #STATE_CLIENT} state.  If no new master announces itself within the timeout
+     * threshold, the time service will transition back to the {@link #STATE_RONIN} state in order
+     * to restart the election.
+     */
+    public static final int STATE_WAIT_FOR_ELECTION = 4;
+
+    /**
+     * Name of the underlying native binder service
+     */
+    public static final String SERVICE_NAME = "common_time.clock";
+
+    /**
+     * Class constructor.
+     * @throws android.os.RemoteException
+     */
+    public CommonClock()
+    throws RemoteException {
+        mRemote = ServiceManager.getService(SERVICE_NAME);
+        if (null == mRemote)
+            throw new RemoteException();
+
+        mInterfaceDesc = mRemote.getInterfaceDescriptor();
+        mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc);
+        mRemote.linkToDeath(mDeathHandler, 0);
+        registerTimelineChangeListener();
+    }
+
+    /**
+     * Handy class factory method.
+     */
+    static public CommonClock create() {
+        CommonClock retVal;
+
+        try {
+            retVal = new CommonClock();
+        }
+        catch (RemoteException e) {
+            retVal = null;
+        }
+
+        return retVal;
+    }
+
+    /**
+     * Release all native resources held by this {@link android.os.CommonClock} instance.  Once
+     * resources have been released, the {@link android.os.CommonClock} instance is disconnected from
+     * the native service and will throw a {@link android.os.RemoteException} if any of its
+     * methods are called.  Clients should always call release on their client instances before
+     * releasing their last Java reference to the instance.  Failure to do this will cause
+     * non-deterministic native resource reclamation and may cause the common time service to remain
+     * active on the network for longer than it should.
+     */
+    public void release() {
+        unregisterTimelineChangeListener();
+        if (null != mRemote) {
+            try {
+                mRemote.unlinkToDeath(mDeathHandler, 0);
+            }
+            catch (NoSuchElementException e) { }
+            mRemote = null;
+        }
+        mUtils = null;
+    }
+
+    /**
+     * Gets the common clock's current time.
+     *
+     * @return a signed 64-bit value representing the current common time in microseconds, or the
+     * special value {@link #TIME_NOT_SYNCED} if the common time service is currently not
+     * synchronized.
+     * @throws android.os.RemoteException
+     */
+    public long getTime()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetLong(METHOD_GET_COMMON_TIME, TIME_NOT_SYNCED);
+    }
+
+    /**
+     * Gets the current estimation of common clock's synchronization accuracy from the common time
+     * service.
+     *
+     * @return a signed 32-bit value representing the common time service's estimation of
+     * synchronization accuracy in microseconds, or the special value
+     * {@link #ERROR_ESTIMATE_UNKNOWN} if the common time service is currently not synchronized.
+     * Negative values indicate that the local server estimates that the nominal common time is
+     * behind the local server's time (in other words, the local clock is running fast) Positive
+     * values indicate that the local server estimates that the nominal common time is ahead of the
+     * local server's time (in other words, the local clock is running slow)
+     * @throws android.os.RemoteException
+     */
+    public int getEstimatedError()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetInt(METHOD_GET_ESTIMATED_ERROR, ERROR_ESTIMATE_UNKNOWN);
+    }
+
+    /**
+     * Gets the ID of the timeline the common time service is currently synchronizing its clock to.
+     *
+     * @return a long representing the unique ID of the timeline the common time service is
+     * currently synchronizing with, or {@link #INVALID_TIMELINE_ID} if the common time service is
+     * currently not synchronized.
+     * @throws android.os.RemoteException
+     */
+    public long getTimelineId()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetLong(METHOD_GET_TIMELINE_ID, INVALID_TIMELINE_ID);
+    }
+
+    /**
+     * Gets the current state of this clock's common time service in the the master election
+     * algorithm.
+     *
+     * @return a integer indicating the current state of the this clock's common time service in the
+     * master election algorithm or {@link #STATE_INVALID} if there is an internal error.
+     * @throws android.os.RemoteException
+     */
+    public int getState()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetInt(METHOD_GET_STATE, STATE_INVALID);
+    }
+
+    /**
+     * Gets the IP address and UDP port of the current timeline master.
+     *
+     * @return an InetSocketAddress containing the IP address and UDP port of the current timeline
+     * master, or null if there is no current master.
+     * @throws android.os.RemoteException
+     */
+    public InetSocketAddress getMasterAddr()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ADDRESS);
+    }
+
+    /**
+     * The OnTimelineChangedListener interface defines a method called by the
+     * {@link android.os.CommonClock} instance to indicate that the time synchronization service has
+     * either synchronized with a new timeline, or is no longer a member of any timeline.  The
+     * client application can implement this interface and register the listener with the
+     * {@link #setTimelineChangedListener(OnTimelineChangedListener)} method.
+     */
+    public interface OnTimelineChangedListener  {
+        /**
+         * Method called when the time service's timeline has changed.
+         *
+         * @param newTimelineId a long which uniquely identifies the timeline the time
+         * synchronization service is now a member of, or {@link #INVALID_TIMELINE_ID} if the the
+         * service is not synchronized to any timeline.
+         */
+        void onTimelineChanged(long newTimelineId);
+    }
+
+    /**
+     * Registers an OnTimelineChangedListener interface.
+     * <p>Call this method with a null listener to stop receiving server death notifications.
+     */
+    public void setTimelineChangedListener(OnTimelineChangedListener listener) {
+        synchronized (mListenerLock) {
+            mTimelineChangedListener = listener;
+        }
+    }
+
+    /**
+     * The OnServerDiedListener interface defines a method called by the
+     * {@link android.os.CommonClock} instance to indicate that the connection to the native media
+     * server has been broken and that the {@link android.os.CommonClock} instance will need to be
+     * released and re-created.  The client application can implement this interface and register
+     * the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method.
+     */
+    public interface OnServerDiedListener  {
+        /**
+         * Method called when the native media server has died.  <p>If the native common time
+         * service encounters a fatal error and needs to restart, the binder connection from the
+         * {@link android.os.CommonClock} instance to the common time service will be broken.  To
+         * restore functionality, clients should {@link #release()} their old visualizer and create
+         * a new instance.
+         */
+        void onServerDied();
+    }
+
+    /**
+     * Registers an OnServerDiedListener interface.
+     * <p>Call this method with a null listener to stop receiving server death notifications.
+     */
+    public void setServerDiedListener(OnServerDiedListener listener) {
+        synchronized (mListenerLock) {
+            mServerDiedListener = listener;
+        }
+    }
+
+    protected void finalize() throws Throwable { release(); }
+
+    private void throwOnDeadServer() throws RemoteException {
+        if ((null == mRemote) || (null == mUtils))
+            throw new RemoteException();
+    }
+
+    private final Object mListenerLock = new Object();
+    private OnTimelineChangedListener mTimelineChangedListener = null;
+    private OnServerDiedListener mServerDiedListener = null;
+
+    private IBinder mRemote = null;
+    private String mInterfaceDesc = "";
+    private CommonTimeUtils mUtils;
+
+    private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() {
+        public void binderDied() {
+            synchronized (mListenerLock) {
+                if (null != mServerDiedListener)
+                    mServerDiedListener.onServerDied();
+            }
+        }
+    };
+
+    private class TimelineChangedListener extends Binder {
+        @Override
+        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+        throws RemoteException {
+            switch (code) {
+                case METHOD_CBK_ON_TIMELINE_CHANGED:
+                    data.enforceInterface(DESCRIPTOR);
+                    long timelineId = data.readLong();
+                    synchronized (mListenerLock) {
+                        if (null != mTimelineChangedListener)
+                            mTimelineChangedListener.onTimelineChanged(timelineId);
+                    }
+                    return true;
+            }
+
+            return super.onTransact(code, data, reply, flags);
+        }
+
+        private static final String DESCRIPTOR = "android.os.ICommonClockListener";
+    };
+
+    private TimelineChangedListener mCallbackTgt = null;
+
+    private void registerTimelineChangeListener() throws RemoteException {
+        if (null != mCallbackTgt)
+            return;
+
+        boolean success = false;
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+        mCallbackTgt = new TimelineChangedListener();
+
+        try {
+            data.writeInterfaceToken(mInterfaceDesc);
+            data.writeStrongBinder(mCallbackTgt);
+            mRemote.transact(METHOD_REGISTER_LISTENER, data, reply, 0);
+            success = (0 == reply.readInt());
+        }
+        catch (RemoteException e) {
+            success = false;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+
+        // Did we catch a remote exception or fail to register our callback target?  If so, our
+        // object must already be dead (or be as good as dead).  Clear out all of our state so that
+        // our other methods will properly indicate a dead object.
+        if (!success) {
+            mCallbackTgt = null;
+            mRemote = null;
+            mUtils = null;
+        }
+    }
+
+    private void unregisterTimelineChangeListener() {
+        if (null == mCallbackTgt)
+            return;
+
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+
+        try {
+            data.writeInterfaceToken(mInterfaceDesc);
+            data.writeStrongBinder(mCallbackTgt);
+            mRemote.transact(METHOD_UNREGISTER_LISTENER, data, reply, 0);
+        }
+        catch (RemoteException e) { }
+        finally {
+            reply.recycle();
+            data.recycle();
+            mCallbackTgt = null;
+        }
+    }
+
+    private static final int METHOD_IS_COMMON_TIME_VALID = IBinder.FIRST_CALL_TRANSACTION;
+    private static final int METHOD_COMMON_TIME_TO_LOCAL_TIME = METHOD_IS_COMMON_TIME_VALID + 1;
+    private static final int METHOD_LOCAL_TIME_TO_COMMON_TIME = METHOD_COMMON_TIME_TO_LOCAL_TIME + 1;
+    private static final int METHOD_GET_COMMON_TIME = METHOD_LOCAL_TIME_TO_COMMON_TIME + 1;
+    private static final int METHOD_GET_COMMON_FREQ = METHOD_GET_COMMON_TIME + 1;
+    private static final int METHOD_GET_LOCAL_TIME = METHOD_GET_COMMON_FREQ + 1;
+    private static final int METHOD_GET_LOCAL_FREQ = METHOD_GET_LOCAL_TIME + 1;
+    private static final int METHOD_GET_ESTIMATED_ERROR = METHOD_GET_LOCAL_FREQ + 1;
+    private static final int METHOD_GET_TIMELINE_ID = METHOD_GET_ESTIMATED_ERROR + 1;
+    private static final int METHOD_GET_STATE = METHOD_GET_TIMELINE_ID + 1;
+    private static final int METHOD_GET_MASTER_ADDRESS = METHOD_GET_STATE + 1;
+    private static final int METHOD_REGISTER_LISTENER = METHOD_GET_MASTER_ADDRESS + 1;
+    private static final int METHOD_UNREGISTER_LISTENER = METHOD_REGISTER_LISTENER + 1;
+
+    private static final int METHOD_CBK_ON_TIMELINE_CHANGED = IBinder.FIRST_CALL_TRANSACTION;
+}
diff --git a/core/java/android/os/CommonTimeConfig.java b/core/java/android/os/CommonTimeConfig.java
new file mode 100644
index 0000000..3355ee3
--- /dev/null
+++ b/core/java/android/os/CommonTimeConfig.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2012 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 android.os;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.NoSuchElementException;
+
+import android.os.CommonTimeUtils;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * Used for configuring and controlling the status of the android common time service.
+ * @hide
+ */
+public class CommonTimeConfig {
+    /**
+     * Successful operation.
+     */
+    public static final int SUCCESS = 0;
+    /**
+     * Unspecified error.
+     */
+    public static final int ERROR = -1;
+    /**
+     * Operation failed due to bad parameter value.
+     */
+    public static final int ERROR_BAD_VALUE = -4;
+    /**
+     * Operation failed due to dead remote object.
+     */
+    public static final int ERROR_DEAD_OBJECT = -7;
+
+    /**
+     * Sentinel value returned by {@link #getMasterElectionGroupId()} when an error occurs trying to
+     * fetch the master election group.
+     */
+    public static final long INVALID_GROUP_ID = -1;
+
+    /**
+     * Name of the underlying native binder service
+     */
+    public static final String SERVICE_NAME = "common_time.config";
+
+    /**
+     * Class constructor.
+     * @throws android.os.RemoteException
+     */
+    public CommonTimeConfig()
+    throws RemoteException {
+        mRemote = ServiceManager.getService(SERVICE_NAME);
+        if (null == mRemote)
+            throw new RemoteException();
+
+        mInterfaceDesc = mRemote.getInterfaceDescriptor();
+        mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc);
+        mRemote.linkToDeath(mDeathHandler, 0);
+    }
+
+    /**
+     * Handy class factory method.
+     */
+    static public CommonTimeConfig create() {
+        CommonTimeConfig retVal;
+
+        try {
+            retVal = new CommonTimeConfig();
+        }
+        catch (RemoteException e) {
+            retVal = null;
+        }
+
+        return retVal;
+    }
+
+    /**
+     * Release all native resources held by this {@link android.os.CommonTimeConfig} instance.  Once
+     * resources have been released, the {@link android.os.CommonTimeConfig} instance is
+     * disconnected from the native service and will throw a {@link android.os.RemoteException} if
+     * any of its methods are called.  Clients should always call release on their client instances
+     * before releasing their last Java reference to the instance.  Failure to do this will cause
+     * non-deterministic native resource reclamation and may cause the common time service to remain
+     * active on the network for longer than it should.
+     */
+    public void release() {
+        if (null != mRemote) {
+            try {
+                mRemote.unlinkToDeath(mDeathHandler, 0);
+            }
+            catch (NoSuchElementException e) { }
+            mRemote = null;
+        }
+        mUtils = null;
+    }
+
+    /**
+     * Gets the current priority of the common time service used in the master election protocol.
+     *
+     * @return an 8 bit value indicating the priority of this common time service relative to other
+     * common time services operating in the same domain.
+     * @throws android.os.RemoteException
+     */
+    public byte getMasterElectionPriority()
+    throws RemoteException {
+        throwOnDeadServer();
+        return (byte)mUtils.transactGetInt(METHOD_GET_MASTER_ELECTION_PRIORITY, -1);
+    }
+
+    /**
+     * Sets the current priority of the common time service used in the master election protocol.
+     *
+     * @param priority priority of the common time service used in the master election protocol.
+     * Lower numbers are lower priority.
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int setMasterElectionPriority(byte priority) {
+        if (checkDeadServer())
+            return ERROR_DEAD_OBJECT;
+        return mUtils.transactSetInt(METHOD_SET_MASTER_ELECTION_PRIORITY, priority);
+    }
+
+    /**
+     * Gets the IP endpoint used by the time service to participate in the master election protocol.
+     *
+     * @return an InetSocketAddress containing the IP address and UDP port being used by the
+     * system's common time service to participate in the master election protocol.
+     * @throws android.os.RemoteException
+     */
+    public InetSocketAddress getMasterElectionEndpoint()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ELECTION_ENDPOINT);
+    }
+
+    /**
+     * Sets the IP endpoint used by the common time service to participate in the master election
+     * protocol.
+     *
+     * @param ep The IP address and UDP port to be used by the common time service to participate in
+     * the master election protocol.  The supplied IP address must be either the broadcast or
+     * multicast address, unicast addresses are considered to be illegal values.
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int setMasterElectionEndpoint(InetSocketAddress ep) {
+        if (checkDeadServer())
+            return ERROR_DEAD_OBJECT;
+        return mUtils.transactSetSockaddr(METHOD_SET_MASTER_ELECTION_ENDPOINT, ep);
+    }
+
+    /**
+     * Gets the current group ID used by the common time service in the master election protocol.
+     *
+     * @return The 64-bit group ID of the common time service.
+     * @throws android.os.RemoteException
+     */
+    public long getMasterElectionGroupId()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetLong(METHOD_GET_MASTER_ELECTION_GROUP_ID, INVALID_GROUP_ID);
+    }
+
+    /**
+     * Sets the current group ID used by the common time service in the master election protocol.
+     *
+     * @param id The 64-bit group ID of the common time service.
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int setMasterElectionGroupId(long id) {
+        if (checkDeadServer())
+            return ERROR_DEAD_OBJECT;
+        return mUtils.transactSetLong(METHOD_SET_MASTER_ELECTION_GROUP_ID, id);
+    }
+
+    /**
+     * Gets the name of the network interface which the common time service attempts to bind to.
+     *
+     * @return a string with the network interface name which the common time service is bound to,
+     * or null if the service is currently unbound.  Examples of interface names are things like
+     * "eth0", or "wlan0".
+     * @throws android.os.RemoteException
+     */
+    public String getInterfaceBinding()
+    throws RemoteException {
+        throwOnDeadServer();
+
+        String ifaceName = mUtils.transactGetString(METHOD_GET_INTERFACE_BINDING, null);
+
+        if ((null != ifaceName) && (0 == ifaceName.length()))
+                return null;
+
+        return ifaceName;
+    }
+
+    /**
+     * Sets the name of the network interface which the common time service should attempt to bind
+     * to.
+     *
+     * @param ifaceName The name of the network interface ("eth0", "wlan0", etc...) wich the common
+     * time service should attempt to bind to, or null to force the common time service to unbind
+     * from the network and run in networkless mode.
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int setNetworkBinding(String ifaceName) {
+        if (checkDeadServer())
+            return ERROR_DEAD_OBJECT;
+
+        return mUtils.transactSetString(METHOD_SET_INTERFACE_BINDING,
+                                       (null == ifaceName) ? "" : ifaceName);
+    }
+
+    /**
+     * Gets the amount of time the common time service will wait between master announcements when
+     * it is the timeline master.
+     *
+     * @return The time (in milliseconds) between master announcements.
+     * @throws android.os.RemoteException
+     */
+    public int getMasterAnnounceInterval()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetInt(METHOD_GET_MASTER_ANNOUNCE_INTERVAL, -1);
+    }
+
+    /**
+     * Sets the amount of time the common time service will wait between master announcements when
+     * it is the timeline master.
+     *
+     * @param interval The time (in milliseconds) between master announcements.
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int setMasterAnnounceInterval(int interval) {
+        if (checkDeadServer())
+            return ERROR_DEAD_OBJECT;
+        return mUtils.transactSetInt(METHOD_SET_MASTER_ANNOUNCE_INTERVAL, interval);
+    }
+
+    /**
+     * Gets the amount of time the common time service will wait between time synchronization
+     * requests when it is the client of another common time service on the network.
+     *
+     * @return The time (in milliseconds) between time sync requests.
+     * @throws android.os.RemoteException
+     */
+    public int getClientSyncInterval()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetInt(METHOD_GET_CLIENT_SYNC_INTERVAL, -1);
+    }
+
+    /**
+     * Sets the amount of time the common time service will wait between time synchronization
+     * requests when it is the client of another common time service on the network.
+     *
+     * @param interval The time (in milliseconds) between time sync requests.
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int setClientSyncInterval(int interval) {
+        if (checkDeadServer())
+            return ERROR_DEAD_OBJECT;
+        return mUtils.transactSetInt(METHOD_SET_CLIENT_SYNC_INTERVAL, interval);
+    }
+
+    /**
+     * Gets the panic threshold for the estimated error level of the common time service.  When the
+     * common time service's estimated error rises above this level, the service will panic and
+     * reset, causing a discontinuity in the currently synchronized timeline.
+     *
+     * @return The threshold (in microseconds) past which the common time service will panic.
+     * @throws android.os.RemoteException
+     */
+    public int getPanicThreshold()
+    throws RemoteException {
+        throwOnDeadServer();
+        return mUtils.transactGetInt(METHOD_GET_PANIC_THRESHOLD, -1);
+    }
+
+    /**
+     * Sets the panic threshold for the estimated error level of the common time service.  When the
+     * common time service's estimated error rises above this level, the service will panic and
+     * reset, causing a discontinuity in the currently synchronized timeline.
+     *
+     * @param threshold The threshold (in microseconds) past which the common time service will
+     * panic.
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int setPanicThreshold(int threshold) {
+        if (checkDeadServer())
+            return ERROR_DEAD_OBJECT;
+        return mUtils.transactSetInt(METHOD_SET_PANIC_THRESHOLD, threshold);
+    }
+
+    /**
+     * Gets the current state of the common time service's auto disable flag.
+     *
+     * @return The current state of the common time service's auto disable flag.
+     * @throws android.os.RemoteException
+     */
+    public boolean getAutoDisable()
+    throws RemoteException {
+        throwOnDeadServer();
+        return (1 == mUtils.transactGetInt(METHOD_GET_AUTO_DISABLE, 1));
+    }
+
+    /**
+     * Sets the current state of the common time service's auto disable flag.  When the time
+     * service's auto disable flag is set, it will automatically cease all network activity when
+     * it has no active local clients, resuming activity the next time the service has interested
+     * local clients.  When the auto disabled flag is cleared, the common time service will continue
+     * to participate the time synchronization group even when it has no active local clients.
+     *
+     * @param autoDisable The desired state of the common time service's auto disable flag.
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int setAutoDisable(boolean autoDisable) {
+        if (checkDeadServer())
+            return ERROR_DEAD_OBJECT;
+
+        return mUtils.transactSetInt(METHOD_SET_AUTO_DISABLE, autoDisable ? 1 : 0);
+    }
+
+    /**
+     * At startup, the time service enters the initial state and remains there until it is given a
+     * network interface to bind to.  Common time will be unavailable to clients of the common time
+     * service until the service joins a network (even an empty network).  Devices may use the
+     * {@link #forceNetworklessMasterMode()} method to force a time service in the INITIAL state
+     * with no network configuration to assume MASTER status for a brand new timeline in order to
+     * allow clients of the common time service to operate, even though the device is isolated and
+     * not on any network.  When a networkless master does join a network, it will defer to any
+     * masters already on the network, or continue to maintain the timeline it made up during its
+     * networkless state if no other masters are detected.  Attempting to force a client into master
+     * mode while it is actively bound to a network will fail with the status code {@link #ERROR}
+     *
+     * @return {@link #SUCCESS} in case of success,
+     * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+     */
+    public int forceNetworklessMasterMode() {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+
+        try {
+            data.writeInterfaceToken(mInterfaceDesc);
+            mRemote.transact(METHOD_FORCE_NETWORKLESS_MASTER_MODE, data, reply, 0);
+
+            return reply.readInt();
+        }
+        catch (RemoteException e) {
+            return ERROR_DEAD_OBJECT;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+    }
+
+    /**
+     * The OnServerDiedListener interface defines a method called by the
+     * {@link android.os.CommonTimeConfig} instance to indicate that the connection to the native
+     * media server has been broken and that the {@link android.os.CommonTimeConfig} instance will
+     * need to be released and re-created.  The client application can implement this interface and
+     * register the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method.
+     */
+    public interface OnServerDiedListener  {
+        /**
+         * Method called when the native common time service has died.  <p>If the native common time
+         * service encounters a fatal error and needs to restart, the binder connection from the
+         * {@link android.os.CommonTimeConfig} instance to the common time service will be broken.
+         */
+        void onServerDied();
+    }
+
+    /**
+     * Registers an OnServerDiedListener interface.
+     * <p>Call this method with a null listener to stop receiving server death notifications.
+     */
+    public void setServerDiedListener(OnServerDiedListener listener) {
+        synchronized (mListenerLock) {
+            mServerDiedListener = listener;
+        }
+    }
+
+    protected void finalize() throws Throwable { release(); }
+
+    private boolean checkDeadServer() {
+        return ((null == mRemote) || (null == mUtils));
+    }
+
+    private void throwOnDeadServer() throws RemoteException {
+        if (checkDeadServer())
+            throw new RemoteException();
+    }
+
+    private final Object mListenerLock = new Object();
+    private OnServerDiedListener mServerDiedListener = null;
+
+    private IBinder mRemote = null;
+    private String mInterfaceDesc = "";
+    private CommonTimeUtils mUtils;
+
+    private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() {
+        public void binderDied() {
+            synchronized (mListenerLock) {
+                if (null != mServerDiedListener)
+                    mServerDiedListener.onServerDied();
+            }
+        }
+    };
+
+    private static final int METHOD_GET_MASTER_ELECTION_PRIORITY = IBinder.FIRST_CALL_TRANSACTION;
+    private static final int METHOD_SET_MASTER_ELECTION_PRIORITY = METHOD_GET_MASTER_ELECTION_PRIORITY + 1;
+    private static final int METHOD_GET_MASTER_ELECTION_ENDPOINT = METHOD_SET_MASTER_ELECTION_PRIORITY + 1;
+    private static final int METHOD_SET_MASTER_ELECTION_ENDPOINT = METHOD_GET_MASTER_ELECTION_ENDPOINT + 1;
+    private static final int METHOD_GET_MASTER_ELECTION_GROUP_ID = METHOD_SET_MASTER_ELECTION_ENDPOINT + 1;
+    private static final int METHOD_SET_MASTER_ELECTION_GROUP_ID = METHOD_GET_MASTER_ELECTION_GROUP_ID + 1;
+    private static final int METHOD_GET_INTERFACE_BINDING = METHOD_SET_MASTER_ELECTION_GROUP_ID + 1;
+    private static final int METHOD_SET_INTERFACE_BINDING = METHOD_GET_INTERFACE_BINDING + 1;
+    private static final int METHOD_GET_MASTER_ANNOUNCE_INTERVAL = METHOD_SET_INTERFACE_BINDING + 1;
+    private static final int METHOD_SET_MASTER_ANNOUNCE_INTERVAL = METHOD_GET_MASTER_ANNOUNCE_INTERVAL + 1;
+    private static final int METHOD_GET_CLIENT_SYNC_INTERVAL = METHOD_SET_MASTER_ANNOUNCE_INTERVAL + 1;
+    private static final int METHOD_SET_CLIENT_SYNC_INTERVAL = METHOD_GET_CLIENT_SYNC_INTERVAL + 1;
+    private static final int METHOD_GET_PANIC_THRESHOLD = METHOD_SET_CLIENT_SYNC_INTERVAL + 1;
+    private static final int METHOD_SET_PANIC_THRESHOLD = METHOD_GET_PANIC_THRESHOLD + 1;
+    private static final int METHOD_GET_AUTO_DISABLE = METHOD_SET_PANIC_THRESHOLD + 1;
+    private static final int METHOD_SET_AUTO_DISABLE = METHOD_GET_AUTO_DISABLE + 1;
+    private static final int METHOD_FORCE_NETWORKLESS_MASTER_MODE = METHOD_SET_AUTO_DISABLE + 1;
+}
diff --git a/core/java/android/os/CommonTimeUtils.java b/core/java/android/os/CommonTimeUtils.java
new file mode 100644
index 0000000..9081ee4
--- /dev/null
+++ b/core/java/android/os/CommonTimeUtils.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2012 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 android.os;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import static libcore.io.OsConstants.*;
+
+class CommonTimeUtils {
+    /**
+     * Successful operation.
+     */
+    public static final int SUCCESS = 0;
+    /**
+     * Unspecified error.
+     */
+    public static final int ERROR = -1;
+    /**
+     * Operation failed due to bad parameter value.
+     */
+    public static final int ERROR_BAD_VALUE = -4;
+    /**
+     * Operation failed due to dead remote object.
+     */
+    public static final int ERROR_DEAD_OBJECT = -7;
+
+    public CommonTimeUtils(IBinder remote, String interfaceDesc) {
+        mRemote = remote;
+        mInterfaceDesc = interfaceDesc;
+    }
+
+    public int transactGetInt(int method_code, int error_ret_val)
+    throws RemoteException {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+        int ret_val;
+
+        try {
+            int res;
+            data.writeInterfaceToken(mInterfaceDesc);
+            mRemote.transact(method_code, data, reply, 0);
+
+            res = reply.readInt();
+            ret_val = (0 == res) ? reply.readInt() : error_ret_val;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+
+        return ret_val;
+    }
+
+    public int transactSetInt(int method_code, int val) {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+
+        try {
+            data.writeInterfaceToken(mInterfaceDesc);
+            data.writeInt(val);
+            mRemote.transact(method_code, data, reply, 0);
+
+            return reply.readInt();
+        }
+        catch (RemoteException e) {
+            return ERROR_DEAD_OBJECT;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+    }
+
+    public long transactGetLong(int method_code, long error_ret_val)
+    throws RemoteException {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+        long ret_val;
+
+        try {
+            int res;
+            data.writeInterfaceToken(mInterfaceDesc);
+            mRemote.transact(method_code, data, reply, 0);
+
+            res = reply.readInt();
+            ret_val = (0 == res) ? reply.readLong() : error_ret_val;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+
+        return ret_val;
+    }
+
+    public int transactSetLong(int method_code, long val) {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+
+        try {
+            data.writeInterfaceToken(mInterfaceDesc);
+            data.writeLong(val);
+            mRemote.transact(method_code, data, reply, 0);
+
+            return reply.readInt();
+        }
+        catch (RemoteException e) {
+            return ERROR_DEAD_OBJECT;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+    }
+
+    public String transactGetString(int method_code, String error_ret_val)
+    throws RemoteException {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+        String ret_val;
+
+        try {
+            int res;
+            data.writeInterfaceToken(mInterfaceDesc);
+            mRemote.transact(method_code, data, reply, 0);
+
+            res = reply.readInt();
+            ret_val = (0 == res) ? reply.readString() : error_ret_val;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+
+        return ret_val;
+    }
+
+    public int transactSetString(int method_code, String val) {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+
+        try {
+            data.writeInterfaceToken(mInterfaceDesc);
+            data.writeString(val);
+            mRemote.transact(method_code, data, reply, 0);
+
+            return reply.readInt();
+        }
+        catch (RemoteException e) {
+            return ERROR_DEAD_OBJECT;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+    }
+
+    public InetSocketAddress transactGetSockaddr(int method_code)
+    throws RemoteException {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+        InetSocketAddress ret_val = null;
+
+        try {
+            int res;
+            data.writeInterfaceToken(mInterfaceDesc);
+            mRemote.transact(method_code, data, reply, 0);
+
+            res = reply.readInt();
+            if (0 == res) {
+                int type;
+                int port = 0;
+                String addrStr = null;
+
+                type = reply.readInt();
+
+                if (AF_INET == type) {
+                    int addr = reply.readInt();
+                    port = reply.readInt();
+                    addrStr = String.format("%d.%d.%d.%d", (addr >> 24) & 0xFF,
+                                                           (addr >> 16) & 0xFF,
+                                                           (addr >>  8) & 0xFF,
+                                                            addr        & 0xFF);
+                } else if (AF_INET6 == type) {
+                    int addr1 = reply.readInt();
+                    int addr2 = reply.readInt();
+                    int addr3 = reply.readInt();
+                    int addr4 = reply.readInt();
+
+                    port = reply.readInt();
+
+                    int flowinfo = reply.readInt();
+                    int scope_id = reply.readInt();
+
+                    addrStr = String.format("[%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X]",
+                                            (addr1 >> 16) & 0xFFFF, addr1 & 0xFFFF,
+                                            (addr2 >> 16) & 0xFFFF, addr2 & 0xFFFF,
+                                            (addr3 >> 16) & 0xFFFF, addr3 & 0xFFFF,
+                                            (addr4 >> 16) & 0xFFFF, addr4 & 0xFFFF);
+                }
+
+                if (null != addrStr) {
+                    ret_val = new InetSocketAddress(addrStr, port);
+                }
+            }
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+
+        return ret_val;
+    }
+
+    public int transactSetSockaddr(int method_code, InetSocketAddress addr) {
+        android.os.Parcel data  = android.os.Parcel.obtain();
+        android.os.Parcel reply = android.os.Parcel.obtain();
+        int ret_val = ERROR;
+
+        try {
+            data.writeInterfaceToken(mInterfaceDesc);
+
+            if (null == addr) {
+                data.writeInt(0);
+            } else {
+                data.writeInt(1);
+                final InetAddress a = addr.getAddress();
+                final byte[]      b = a.getAddress();
+                final int         p = addr.getPort();
+
+                if (a instanceof Inet4Address) {
+                    int v4addr = (((int)b[0] & 0xFF) << 24) |
+                                 (((int)b[1] & 0xFF) << 16) |
+                                 (((int)b[2] & 0xFF) << 8) |
+                                  ((int)b[3] & 0xFF);
+
+                    data.writeInt(AF_INET);
+                    data.writeInt(v4addr);
+                    data.writeInt(p);
+                } else
+                if (a instanceof Inet6Address) {
+                    int i;
+                    Inet6Address v6 = (Inet6Address)a;
+                    data.writeInt(AF_INET6);
+                    for (i = 0; i < 4; ++i) {
+                        int aword = (((int)b[(i*4) + 0] & 0xFF) << 24) |
+                                    (((int)b[(i*4) + 1] & 0xFF) << 16) |
+                                    (((int)b[(i*4) + 2] & 0xFF) << 8) |
+                                     ((int)b[(i*4) + 3] & 0xFF);
+                        data.writeInt(aword);
+                    }
+                    data.writeInt(p);
+                    data.writeInt(0);   // flow info
+                    data.writeInt(v6.getScopeId());
+                } else {
+                    return ERROR_BAD_VALUE;
+                }
+            }
+
+            mRemote.transact(method_code, data, reply, 0);
+            ret_val = reply.readInt();
+        }
+        catch (RemoteException e) {
+            ret_val = ERROR_DEAD_OBJECT;
+        }
+        finally {
+            reply.recycle();
+            data.recycle();
+        }
+
+        return ret_val;
+    }
+
+    private IBinder mRemote;
+    private String mInterfaceDesc;
+};
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index af2fa9b..610b3550 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -513,7 +513,7 @@
      * message queue.
      */
     public final void removeMessages(int what) {
-        mQueue.removeMessages(this, what, null, true);
+        mQueue.removeMessages(this, what, null);
     }
 
     /**
@@ -522,7 +522,7 @@
      * all messages will be removed.
      */
     public final void removeMessages(int what, Object object) {
-        mQueue.removeMessages(this, what, object, true);
+        mQueue.removeMessages(this, what, object);
     }
 
     /**
@@ -539,7 +539,7 @@
      * the message queue.
      */
     public final boolean hasMessages(int what) {
-        return mQueue.removeMessages(this, what, null, false);
+        return mQueue.hasMessages(this, what, null);
     }
 
     /**
@@ -547,7 +547,7 @@
      * whose obj is 'object' in the message queue.
      */
     public final boolean hasMessages(int what, Object object) {
-        return mQueue.removeMessages(this, what, object, false);
+        return mQueue.hasMessages(this, what, object);
     }
 
     // if we can get rid of this method, the handler need not remember its loop
diff --git a/core/java/android/os/IServiceManager.java b/core/java/android/os/IServiceManager.java
index 9a5ff47..7b11c28 100644
--- a/core/java/android/os/IServiceManager.java
+++ b/core/java/android/os/IServiceManager.java
@@ -45,7 +45,8 @@
      * Place a new @a service called @a name into the service
      * manager.
      */
-    public void addService(String name, IBinder service) throws RemoteException;
+    public void addService(String name, IBinder service, boolean allowIsolated)
+                throws RemoteException;
 
     /**
      * Return a list of all currently running services.
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 5607f7f..a06aadb6 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -55,13 +55,13 @@
 
     // sThreadLocal.get() will return null unless you've called prepare().
     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
+    private static Looper sMainLooper;  // guarded by Looper.class
 
     final MessageQueue mQueue;
     final Thread mThread;
     volatile boolean mRun;
 
-    private Printer mLogging = null;
-    private static Looper mMainLooper = null;  // guarded by Looper.class
+    private Printer mLogging;
 
      /** Initialize the current thread as a looper.
       * This gives you a chance to create handlers that then reference
@@ -70,10 +70,14 @@
       * {@link #quit()}.
       */
     public static void prepare() {
+        prepare(true);
+    }
+
+    private static void prepare(boolean quitAllowed) {
         if (sThreadLocal.get() != null) {
             throw new RuntimeException("Only one Looper may be created per thread");
         }
-        sThreadLocal.set(new Looper());
+        sThreadLocal.set(new Looper(quitAllowed));
     }
 
     /**
@@ -83,19 +87,21 @@
      * to call this function yourself.  See also: {@link #prepare()}
      */
     public static void prepareMainLooper() {
-        prepare();
-        setMainLooper(myLooper());
-        myLooper().mQueue.mQuitAllowed = false;
-    }
-
-    private synchronized static void setMainLooper(Looper looper) {
-        mMainLooper = looper;
+        prepare(false);
+        synchronized (Looper.class) {
+            if (sMainLooper != null) {
+                throw new IllegalStateException("The main Looper has already been prepared.");
+            }
+            sMainLooper = myLooper();
+        }
     }
 
     /** Returns the application's main looper, which lives in the main thread of the application.
      */
-    public synchronized static Looper getMainLooper() {
-        return mMainLooper;
+    public static Looper getMainLooper() {
+        synchronized (Looper.class) {
+            return sMainLooper;
+        }
     }
 
     /**
@@ -103,63 +109,61 @@
      * {@link #quit()} to end the loop.
      */
     public static void loop() {
-        Looper me = myLooper();
+        final Looper me = myLooper();
         if (me == null) {
             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
         }
-        MessageQueue queue = me.mQueue;
-        
+        final MessageQueue queue = me.mQueue;
+
         // Make sure the identity of this thread is that of the local process,
         // and keep track of what that identity token actually is.
         Binder.clearCallingIdentity();
         final long ident = Binder.clearCallingIdentity();
-        
-        while (true) {
+
+        for (;;) {
             Message msg = queue.next(); // might block
-            if (msg != null) {
-                if (msg.target == null) {
-                    // No target is a magic identifier for the quit message.
-                    return;
-                }
-
-                long wallStart = 0;
-                long threadStart = 0;
-
-                // This must be in a local variable, in case a UI event sets the logger
-                Printer logging = me.mLogging;
-                if (logging != null) {
-                    logging.println(">>>>> Dispatching to " + msg.target + " " +
-                            msg.callback + ": " + msg.what);
-                    wallStart = SystemClock.currentTimeMicro();
-                    threadStart = SystemClock.currentThreadTimeMicro();
-                }
-
-                msg.target.dispatchMessage(msg);
-
-                if (logging != null) {
-                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
-                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
-
-                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
-                    if (logging instanceof Profiler) {
-                        ((Profiler) logging).profile(msg, wallStart, wallTime,
-                                threadStart, threadTime);
-                    }
-                }
-
-                // Make sure that during the course of dispatching the
-                // identity of the thread wasn't corrupted.
-                final long newIdent = Binder.clearCallingIdentity();
-                if (ident != newIdent) {
-                    Log.wtf(TAG, "Thread identity changed from 0x"
-                            + Long.toHexString(ident) + " to 0x"
-                            + Long.toHexString(newIdent) + " while dispatching to "
-                            + msg.target.getClass().getName() + " "
-                            + msg.callback + " what=" + msg.what);
-                }
-                
-                msg.recycle();
+            if (msg == null) {
+                // No message indicates that the message queue is quitting.
+                return;
             }
+
+            long wallStart = 0;
+            long threadStart = 0;
+
+            // This must be in a local variable, in case a UI event sets the logger
+            Printer logging = me.mLogging;
+            if (logging != null) {
+                logging.println(">>>>> Dispatching to " + msg.target + " " +
+                        msg.callback + ": " + msg.what);
+                wallStart = SystemClock.currentTimeMicro();
+                threadStart = SystemClock.currentThreadTimeMicro();
+            }
+
+            msg.target.dispatchMessage(msg);
+
+            if (logging != null) {
+                long wallTime = SystemClock.currentTimeMicro() - wallStart;
+                long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
+
+                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
+                if (logging instanceof Profiler) {
+                    ((Profiler) logging).profile(msg, wallStart, wallTime,
+                            threadStart, threadTime);
+                }
+            }
+
+            // Make sure that during the course of dispatching the
+            // identity of the thread wasn't corrupted.
+            final long newIdent = Binder.clearCallingIdentity();
+            if (ident != newIdent) {
+                Log.wtf(TAG, "Thread identity changed from 0x"
+                        + Long.toHexString(ident) + " to 0x"
+                        + Long.toHexString(newIdent) + " while dispatching to "
+                        + msg.target.getClass().getName() + " "
+                        + msg.callback + " what=" + msg.what);
+            }
+
+            msg.recycle();
         }
     }
 
@@ -193,18 +197,61 @@
         return myLooper().mQueue;
     }
 
-    private Looper() {
-        mQueue = new MessageQueue();
+    private Looper(boolean quitAllowed) {
+        mQueue = new MessageQueue(quitAllowed);
         mRun = true;
         mThread = Thread.currentThread();
     }
 
+    /**
+     * Quits the looper.
+     *
+     * Causes the {@link #loop} method to terminate as soon as possible.
+     */
     public void quit() {
-        Message msg = Message.obtain();
-        // NOTE: By enqueueing directly into the message queue, the
-        // message is left with a null target.  This is how we know it is
-        // a quit message.
-        mQueue.enqueueMessage(msg, 0);
+        mQueue.quit();
+    }
+
+    /**
+     * Posts a synchronization barrier to the Looper's message queue.
+     *
+     * Message processing occurs as usual until the message queue encounters the
+     * synchronization barrier that has been posted.  When the barrier is encountered,
+     * later synchronous messages in the queue are stalled (prevented from being executed)
+     * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
+     * the token that identifies the synchronization barrier.
+     *
+     * This method is used to immediately postpone execution of all subsequently posted
+     * synchronous messages until a condition is met that releases the barrier.
+     * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
+     * and continue to be processed as usual.
+     *
+     * This call must be always matched by a call to {@link #removeSyncBarrier} with
+     * the same token to ensure that the message queue resumes normal operation.
+     * Otherwise the application will probably hang!
+     *
+     * @return A token that uniquely identifies the barrier.  This token must be
+     * passed to {@link #removeSyncBarrier} to release the barrier.
+     *
+     * @hide
+     */
+    public final int postSyncBarrier() {
+        return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
+    }
+
+
+    /**
+     * Removes a synchronization barrier.
+     *
+     * @param token The synchronization barrier token that was returned by
+     * {@link #postSyncBarrier}.
+     *
+     * @throws IllegalStateException if the barrier was not found.
+     *
+     * @hide
+     */
+    public final void removeSyncBarrier(int token) {
+        mQueue.removeSyncBarrier(token);
     }
 
     /**
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 844ed6a..b816b11 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -75,13 +75,13 @@
     public Messenger replyTo;
 
     /** If set message is in use */
-    /*package*/ static final int FLAG_IN_USE = 1;
+    /*package*/ static final int FLAG_IN_USE = 1 << 0;
 
-    /** Flags reserved for future use (All are reserved for now) */
-    /*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE;
+    /** If set message is asynchronous */
+    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
 
     /** Flags to clear in the copyFrom method */
-    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE;
+    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
 
     /*package*/ int flags;
 
@@ -363,6 +363,48 @@
         target.sendMessage(this);
     }
 
+    /**
+     * Returns true if the message is asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with represent to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#acquireSyncBarrier()}.
+     *
+     * @return True if the message is asynchronous.
+     *
+     * @see #setAsynchronous(boolean)
+     * @see MessageQueue#acquireSyncBarrier()
+     * @see MessageQueue#releaseSyncBarrier()
+     *
+     * @hide
+     */
+    public boolean isAsynchronous() {
+        return (flags & FLAG_ASYNCHRONOUS) != 0;
+    }
+
+    /**
+     * Sets whether the message is asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with represent to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#acquireSyncBarrier()}.
+     *
+     * @param async True if the message is asynchronous.
+     *
+     * @see #isAsynchronous()
+     * @see MessageQueue#acquireSyncBarrier()
+     * @see MessageQueue#releaseSyncBarrier()
+     *
+     * @hide
+     */
+    public void setAsynchronous(boolean async) {
+        if (async) {
+            flags |= FLAG_ASYNCHRONOUS;
+        } else {
+            flags &= ~FLAG_ASYNCHRONOUS;
+        }
+    }
+
     /*package*/ void clearForRecycle() {
         flags = 0;
         what = 0;
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index a658fc4..64027ef 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -30,18 +30,24 @@
  * {@link Looper#myQueue() Looper.myQueue()}.
  */
 public class MessageQueue {
+    // True if the message queue can be quit.
+    private final boolean mQuitAllowed;
+
+    @SuppressWarnings("unused")
+    private int mPtr; // used by native code
+
     Message mMessages;
     private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
     private IdleHandler[] mPendingIdleHandlers;
     private boolean mQuiting;
-    boolean mQuitAllowed = true;
 
     // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
     private boolean mBlocked;
 
-    @SuppressWarnings("unused")
-    private int mPtr; // used by native code
-    
+    // The next barrier token.
+    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
+    private int mNextBarrierToken;
+
     private native void nativeInit();
     private native void nativeDestroy();
     private native void nativePollOnce(int ptr, int timeoutMillis);
@@ -93,11 +99,12 @@
             mIdleHandlers.remove(handler);
         }
     }
-    
-    MessageQueue() {
+
+    MessageQueue(boolean quitAllowed) {
+        mQuitAllowed = quitAllowed;
         nativeInit();
     }
-    
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -118,30 +125,51 @@
             nativePollOnce(mPtr, nextPollTimeoutMillis);
 
             synchronized (this) {
+                if (mQuiting) {
+                    return null;
+                }
+
                 // Try to retrieve the next message.  Return if found.
                 final long now = SystemClock.uptimeMillis();
-                final Message msg = mMessages;
+                Message prevMsg = null;
+                Message msg = mMessages;
+                if (msg != null && msg.target == null) {
+                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
+                    do {
+                        prevMsg = msg;
+                        msg = msg.next;
+                    } while (msg != null && !msg.isAsynchronous());
+                }
                 if (msg != null) {
-                    final long when = msg.when;
-                    if (now >= when) {
+                    if (now < msg.when) {
+                        // Next message is not ready.  Set a timeout to wake up when it is ready.
+                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
+                    } else {
+                        // Got a message.
                         mBlocked = false;
-                        mMessages = msg.next;
+                        if (prevMsg != null) {
+                            prevMsg.next = msg.next;
+                        } else {
+                            mMessages = msg.next;
+                        }
                         msg.next = null;
                         if (false) Log.v("MessageQueue", "Returning message: " + msg);
                         msg.markInUse();
                         return msg;
-                    } else {
-                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                     }
                 } else {
+                    // No more messages.
                     nextPollTimeoutMillis = -1;
                 }
 
-                // If first time, then get the number of idlers to run.
-                if (pendingIdleHandlerCount < 0) {
+                // If first time idle, then get the number of idlers to run.
+                // Idle handles only run if the queue is empty or if the first message
+                // in the queue (possibly a barrier) is due to be handled in the future.
+                if (pendingIdleHandlerCount < 0
+                        && (mMessages == null || now < mMessages.when)) {
                     pendingIdleHandlerCount = mIdleHandlers.size();
                 }
-                if (pendingIdleHandlerCount == 0) {
+                if (pendingIdleHandlerCount <= 0) {
                     // No idle handlers to run.  Loop and wait some more.
                     mBlocked = true;
                     continue;
@@ -182,41 +210,118 @@
         }
     }
 
-    final boolean enqueueMessage(Message msg, long when) {
-        if (msg.isInUse()) {
-            throw new AndroidRuntimeException(msg
-                    + " This message is already in use.");
+    final void quit() {
+        if (!mQuitAllowed) {
+            throw new RuntimeException("Main thread not allowed to quit.");
         }
-        if (msg.target == null && !mQuitAllowed) {
-            throw new RuntimeException("Main thread not allowed to quit");
-        }
-        final boolean needWake;
+
         synchronized (this) {
             if (mQuiting) {
-                RuntimeException e = new RuntimeException(
-                    msg.target + " sending message to a Handler on a dead thread");
-                Log.w("MessageQueue", e.getMessage(), e);
-                return false;
-            } else if (msg.target == null) {
-                mQuiting = true;
+                return;
             }
+            mQuiting = true;
+        }
+        nativeWake(mPtr);
+    }
 
-            msg.when = when;
-            //Log.d("MessageQueue", "Enqueing: " + msg);
+    final int enqueueSyncBarrier(long when) {
+        // Enqueue a new sync barrier token.
+        // We don't need to wake the queue because the purpose of a barrier is to stall it.
+        synchronized (this) {
+            final int token = mNextBarrierToken++;
+            final Message msg = Message.obtain();
+            msg.arg1 = token;
+
+            Message prev = null;
             Message p = mMessages;
-            if (p == null || when == 0 || when < p.when) {
-                msg.next = p;
-                mMessages = msg;
-                needWake = mBlocked; // new head, might need to wake up
-            } else {
-                Message prev = null;
+            if (when != 0) {
                 while (p != null && p.when <= when) {
                     prev = p;
                     p = p.next;
                 }
-                msg.next = prev.next;
+            }
+            if (prev != null) { // invariant: p == prev.next
+                msg.next = p;
                 prev.next = msg;
-                needWake = false; // still waiting on head, no need to wake up
+            } else {
+                msg.next = p;
+                mMessages = msg;
+            }
+            return token;
+        }
+    }
+
+    final void removeSyncBarrier(int token) {
+        // Remove a sync barrier token from the queue.
+        // If the queue is no longer stalled by a barrier then wake it.
+        final boolean needWake;
+        synchronized (this) {
+            Message prev = null;
+            Message p = mMessages;
+            while (p != null && (p.target != null || p.arg1 != token)) {
+                prev = p;
+                p = p.next;
+            }
+            if (p == null) {
+                throw new IllegalStateException("The specified message queue synchronization "
+                        + " barrier token has not been posted or has already been removed.");
+            }
+            if (prev != null) {
+                prev.next = p.next;
+                needWake = false;
+            } else {
+                mMessages = p.next;
+                needWake = mMessages == null || mMessages.target != null;
+            }
+            p.recycle();
+        }
+        if (needWake) {
+            nativeWake(mPtr);
+        }
+    }
+
+    final boolean enqueueMessage(Message msg, long when) {
+        if (msg.isInUse()) {
+            throw new AndroidRuntimeException(msg + " This message is already in use.");
+        }
+        if (msg.target == null) {
+            throw new AndroidRuntimeException("Message must have a target.");
+        }
+
+        boolean needWake;
+        synchronized (this) {
+            if (mQuiting) {
+                RuntimeException e = new RuntimeException(
+                        msg.target + " sending message to a Handler on a dead thread");
+                Log.w("MessageQueue", e.getMessage(), e);
+                return false;
+            }
+
+            msg.when = when;
+            Message p = mMessages;
+            if (p == null || when == 0 || when < p.when) {
+                // New head, wake up the event queue if blocked.
+                msg.next = p;
+                mMessages = msg;
+                needWake = mBlocked;
+            } else {
+                // Inserted within the middle of the queue.  Usually we don't have to wake
+                // up the event queue unless there is a barrier at the head of the queue
+                // and the message is the earliest asynchronous message in the queue.
+                needWake = mBlocked && p.target == null && msg.isAsynchronous();
+                Message prev;
+                for (;;) {
+                    prev = p;
+                    p = p.next;
+                    if (p == null || when < p.when) {
+                        break;
+                    }
+                    if (needWake && p.isAsynchronous()) {
+                        needWake = false;
+                    }
+                }
+                msg.next = p; // invariant: p == prev.next
+                prev.next = msg;
             }
         }
         if (needWake) {
@@ -225,17 +330,34 @@
         return true;
     }
 
-    final boolean removeMessages(Handler h, int what, Object object,
-            boolean doRemove) {
+    final boolean hasMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+
         synchronized (this) {
             Message p = mMessages;
-            boolean found = false;
+            while (p != null) {
+                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    final void removeMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
 
             // Remove all messages at front.
             while (p != null && p.target == h && p.what == what
                    && (object == null || p.obj == object)) {
-                if (!doRemove) return true;
-                found = true;
                 Message n = p.next;
                 mMessages = n;
                 p.recycle();
@@ -248,8 +370,6 @@
                 if (n != null) {
                     if (n.target == h && n.what == what
                         && (object == null || n.obj == object)) {
-                        if (!doRemove) return true;
-                        found = true;
                         Message nn = n.next;
                         n.recycle();
                         p.next = nn;
@@ -258,13 +378,11 @@
                 }
                 p = n;
             }
-            
-            return found;
         }
     }
 
     final void removeMessages(Handler h, Runnable r, Object object) {
-        if (r == null) {
+        if (h == null || r == null) {
             return;
         }
 
@@ -298,6 +416,10 @@
     }
 
     final void removeCallbacksAndMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
         synchronized (this) {
             Message p = mMessages;
 
@@ -325,16 +447,4 @@
             }
         }
     }
-
-    /*
-    private void dumpQueue_l()
-    {
-        Message p = mMessages;
-        System.out.println(this + "  queue is:");
-        while (p != null) {
-            System.out.println("            " + p);
-            p = p.next;
-        }
-    }
-    */
 }
diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java
index 5a79215..58df940 100644
--- a/core/java/android/os/Power.java
+++ b/core/java/android/os/Power.java
@@ -104,4 +104,6 @@
     }
 
     private static native void rebootNative(String reason) throws IOException ;
+
+    public static native int powerInitNative();
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index cdf235d..6139296 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -119,7 +119,19 @@
      * Last of application-specific UIDs starting at
      * {@link #FIRST_APPLICATION_UID}.
      */
-    public static final int LAST_APPLICATION_UID = 99999;
+    public static final int LAST_APPLICATION_UID = 19999;
+
+    /**
+     * First uid used for fully isolated sandboxed processes (with no permissions of their own)
+     * @hide
+     */
+    public static final int FIRST_ISOLATED_UID = 99000;
+
+    /**
+     * Last uid used for fully isolated sandboxed processes (with no permissions of their own)
+     * @hide
+     */
+    public static final int LAST_ISOLATED_UID = 99999;
 
     /**
      * Defines a secondary group id for access to the bluetooth hardware.
@@ -576,6 +588,15 @@
     public static final native int myUid();
 
     /**
+     * Returns whether the current process is in an isolated sandbox.
+     * @hide
+     */
+    public static final boolean isIsolated() {
+        int uid = UserId.getAppId(myUid());
+        return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
+    }
+
+    /**
      * Returns the UID assigned to a particular user name, or -1 if there is
      * none.  If the given string consists of only numbers, it is converted
      * directly to a uid.
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 1af24f4a..13b8b66 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -69,7 +69,24 @@
      */
     public static void addService(String name, IBinder service) {
         try {
-            getIServiceManager().addService(name, service);
+            getIServiceManager().addService(name, service, false);
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in addService", e);
+        }
+    }
+
+    /**
+     * Place a new @a service called @a name into the service
+     * manager.
+     * 
+     * @param name the name of the new service
+     * @param service the service object
+     * @param allowIsolated set to true to allow isolated sandboxed processes
+     * to access this service
+     */
+    public static void addService(String name, IBinder service, boolean allowIsolated) {
+        try {
+            getIServiceManager().addService(name, service, allowIsolated);
         } catch (RemoteException e) {
             Log.e(TAG, "error in addService", e);
         }
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 2aab0e6..43b5128 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -71,7 +71,8 @@
                 data.enforceInterface(IServiceManager.descriptor);
                 String name = data.readString();
                 IBinder service = data.readStrongBinder();
-                addService(name, service);
+                boolean allowIsolated = data.readInt() != 0;
+                addService(name, service, allowIsolated);
                 return true;
             }
     
@@ -136,13 +137,14 @@
         return binder;
     }
 
-    public void addService(String name, IBinder service)
+    public void addService(String name, IBinder service, boolean allowIsolated)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IServiceManager.descriptor);
         data.writeString(name);
         data.writeStrongBinder(service);
+        data.writeInt(allowIsolated ? 1 : 0);
         mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
         reply.recycle();
         data.recycle();
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
index 4124d51..0da67d63 100644
--- a/core/java/android/os/UserId.java
+++ b/core/java/android/os/UserId.java
@@ -56,6 +56,11 @@
         return getAppId(uid1) == getAppId(uid2);
     }
 
+    public static final boolean isIsolated(int uid) {
+        uid = getAppId(uid);
+        return uid >= Process.FIRST_ISOLATED_UID && uid <= Process.LAST_ISOLATED_UID;
+    }
+
     /**
      * Returns the user id for a given uid.
      * @hide
@@ -68,6 +73,10 @@
         }
     }
 
+    public static final int getCallingUserId() {
+        return getUserId(Binder.getCallingUid());
+    }
+
     /**
      * Returns the uid that is composed from the userId and the appId.
      * @hide
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index cdb622c..fbf512c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -16,6 +16,7 @@
 
 package android.os.storage;
 
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -534,6 +535,7 @@
      * @hide
      */
     public String getVolumeState(String mountPoint) {
+         if (mMountService == null) return Environment.MEDIA_REMOVED;
         try {
             return mMountService.getVolumeState(mountPoint);
         } catch (RemoteException e) {
@@ -547,6 +549,7 @@
      * @hide
      */
     public StorageVolume[] getVolumeList() {
+        if (mMountService == null) return new StorageVolume[0];
         try {
             Parcelable[] list = mMountService.getVolumeList();
             if (list == null) return new StorageVolume[0];
diff --git a/core/java/android/pim/ContactsAsyncHelper.java b/core/java/android/pim/ContactsAsyncHelper.java
deleted file mode 100644
index 21fc594..0000000
--- a/core/java/android/pim/ContactsAsyncHelper.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.pim;
-
-import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.Connection;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.ContactsContract.Contacts;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-
-import java.io.InputStream;
-
-/**
- * Helper class for async access of images.
- */
-public class ContactsAsyncHelper extends Handler {
-
-    private static final boolean DBG = false;
-    private static final String LOG_TAG = "ContactsAsyncHelper";
-
-    /**
-     * Interface for a WorkerHandler result return.
-     */
-    public interface OnImageLoadCompleteListener {
-        /**
-         * Called when the image load is complete.
-         *
-         * @param imagePresent true if an image was found
-         */
-        public void onImageLoadComplete(int token, Object cookie, ImageView iView,
-                boolean imagePresent);
-    }
-
-    // constants
-    private static final int EVENT_LOAD_IMAGE = 1;
-    private static final int DEFAULT_TOKEN = -1;
-
-    // static objects
-    private static Handler sThreadHandler;
-    private static ContactsAsyncHelper sInstance;
-
-    static {
-        sInstance = new ContactsAsyncHelper();
-    }
-
-    private static final class WorkerArgs {
-        public Context context;
-        public ImageView view;
-        public Uri uri;
-        public int defaultResource;
-        public Object result;
-        public Object cookie;
-        public OnImageLoadCompleteListener listener;
-        public CallerInfo info;
-    }
-
-    /**
-     * public inner class to help out the ContactsAsyncHelper callers
-     * with tracking the state of the CallerInfo Queries and image
-     * loading.
-     *
-     * Logic contained herein is used to remove the race conditions
-     * that exist as the CallerInfo queries run and mix with the image
-     * loads, which then mix with the Phone state changes.
-     */
-    public static class ImageTracker {
-
-        // Image display states
-        public static final int DISPLAY_UNDEFINED = 0;
-        public static final int DISPLAY_IMAGE = -1;
-        public static final int DISPLAY_DEFAULT = -2;
-
-        // State of the image on the imageview.
-        private CallerInfo mCurrentCallerInfo;
-        private int displayMode;
-
-        public ImageTracker() {
-            mCurrentCallerInfo = null;
-            displayMode = DISPLAY_UNDEFINED;
-        }
-
-        /**
-         * Used to see if the requested call / connection has a
-         * different caller attached to it than the one we currently
-         * have in the CallCard.
-         */
-        public boolean isDifferentImageRequest(CallerInfo ci) {
-            // note, since the connections are around for the lifetime of the
-            // call, and the CallerInfo-related items as well, we can
-            // definitely use a simple != comparison.
-            return (mCurrentCallerInfo != ci);
-        }
-
-        public boolean isDifferentImageRequest(Connection connection) {
-            // if the connection does not exist, see if the
-            // mCurrentCallerInfo is also null to match.
-            if (connection == null) {
-                if (DBG) Log.d(LOG_TAG, "isDifferentImageRequest: connection is null");
-                return (mCurrentCallerInfo != null);
-            }
-            Object o = connection.getUserData();
-
-            // if the call does NOT have a callerInfo attached
-            // then it is ok to query.
-            boolean runQuery = true;
-            if (o instanceof CallerInfo) {
-                runQuery = isDifferentImageRequest((CallerInfo) o);
-            }
-            return runQuery;
-        }
-
-        /**
-         * Simple setter for the CallerInfo object.
-         */
-        public void setPhotoRequest(CallerInfo ci) {
-            mCurrentCallerInfo = ci;
-        }
-
-        /**
-         * Convenience method used to retrieve the URI
-         * representing the Photo file recorded in the attached
-         * CallerInfo Object.
-         */
-        public Uri getPhotoUri() {
-            if (mCurrentCallerInfo != null) {
-                return ContentUris.withAppendedId(Contacts.CONTENT_URI,
-                        mCurrentCallerInfo.person_id);
-            }
-            return null;
-        }
-
-        /**
-         * Simple setter for the Photo state.
-         */
-        public void setPhotoState(int state) {
-            displayMode = state;
-        }
-
-        /**
-         * Simple getter for the Photo state.
-         */
-        public int getPhotoState() {
-            return displayMode;
-        }
-    }
-
-    /**
-     * Thread worker class that handles the task of opening the stream and loading
-     * the images.
-     */
-    private class WorkerHandler extends Handler {
-        public WorkerHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            WorkerArgs args = (WorkerArgs) msg.obj;
-
-            switch (msg.arg1) {
-                case EVENT_LOAD_IMAGE:
-                    InputStream inputStream = null;
-                    try {
-                        inputStream = Contacts.openContactPhotoInputStream(
-                                args.context.getContentResolver(), args.uri, true);
-                    } catch (Exception e) {
-                        Log.e(LOG_TAG, "Error opening photo input stream", e);
-                    }
-
-                    if (inputStream != null) {
-                        args.result = Drawable.createFromStream(inputStream, args.uri.toString());
-
-                        if (DBG) Log.d(LOG_TAG, "Loading image: " + msg.arg1 +
-                                " token: " + msg.what + " image URI: " + args.uri);
-                    } else {
-                        args.result = null;
-                        if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 +
-                                " token: " + msg.what + " image URI: " + args.uri +
-                                ", using default image.");
-                    }
-                    break;
-                default:
-            }
-
-            // send the reply to the enclosing class.
-            Message reply = ContactsAsyncHelper.this.obtainMessage(msg.what);
-            reply.arg1 = msg.arg1;
-            reply.obj = msg.obj;
-            reply.sendToTarget();
-        }
-    }
-
-    /**
-     * Private constructor for static class
-     */
-    private ContactsAsyncHelper() {
-        HandlerThread thread = new HandlerThread("ContactsAsyncWorker");
-        thread.start();
-        sThreadHandler = new WorkerHandler(thread.getLooper());
-    }
-
-    /**
-     * Convenience method for calls that do not want to deal with listeners and tokens.
-     */
-    public static final void updateImageViewWithContactPhotoAsync(Context context,
-            ImageView imageView, Uri person, int placeholderImageResource) {
-        // Added additional Cookie field in the callee.
-        updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context,
-                imageView, person, placeholderImageResource);
-    }
-
-    /**
-     * Convenience method for calls that do not want to deal with listeners and tokens, but have
-     * a CallerInfo object to cache the image to.
-     */
-    public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context,
-            ImageView imageView, Uri person, int placeholderImageResource) {
-        // Added additional Cookie field in the callee.
-        updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context,
-                imageView, person, placeholderImageResource);
-    }
-
-
-    /**
-     * Start an image load, attach the result to the specified CallerInfo object.
-     * Note, when the query is started, we make the ImageView INVISIBLE if the
-     * placeholderImageResource value is -1.  When we're given a valid (!= -1)
-     * placeholderImageResource value, we make sure the image is visible.
-     */
-    public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token,
-            OnImageLoadCompleteListener listener, Object cookie, Context context,
-            ImageView imageView, Uri person, int placeholderImageResource) {
-
-        // in case the source caller info is null, the URI will be null as well.
-        // just update using the placeholder image in this case.
-        if (person == null) {
-            if (DBG) Log.d(LOG_TAG, "target image is null, just display placeholder.");
-            imageView.setVisibility(View.VISIBLE);
-            imageView.setImageResource(placeholderImageResource);
-            return;
-        }
-
-        // Added additional Cookie field in the callee to handle arguments
-        // sent to the callback function.
-
-        // setup arguments
-        WorkerArgs args = new WorkerArgs();
-        args.cookie = cookie;
-        args.context = context;
-        args.view = imageView;
-        args.uri = person;
-        args.defaultResource = placeholderImageResource;
-        args.listener = listener;
-        args.info = info;
-
-        // setup message arguments
-        Message msg = sThreadHandler.obtainMessage(token);
-        msg.arg1 = EVENT_LOAD_IMAGE;
-        msg.obj = args;
-
-        if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri +
-                ", displaying default image for now.");
-
-        // set the default image first, when the query is complete, we will
-        // replace the image with the correct one.
-        if (placeholderImageResource != -1) {
-            imageView.setVisibility(View.VISIBLE);
-            imageView.setImageResource(placeholderImageResource);
-        } else {
-            imageView.setVisibility(View.INVISIBLE);
-        }
-
-        // notify the thread to begin working
-        sThreadHandler.sendMessage(msg);
-    }
-
-    /**
-     * Called when loading is done.
-     */
-    @Override
-    public void handleMessage(Message msg) {
-        WorkerArgs args = (WorkerArgs) msg.obj;
-        switch (msg.arg1) {
-            case EVENT_LOAD_IMAGE:
-                boolean imagePresent = false;
-
-                // if the image has been loaded then display it, otherwise set default.
-                // in either case, make sure the image is visible.
-                if (args.result != null) {
-                    args.view.setVisibility(View.VISIBLE);
-                    args.view.setImageDrawable((Drawable) args.result);
-                    // make sure the cached photo data is updated.
-                    if (args.info != null) {
-                        args.info.cachedPhoto = (Drawable) args.result;
-                    }
-                    imagePresent = true;
-                } else if (args.defaultResource != -1) {
-                    args.view.setVisibility(View.VISIBLE);
-                    args.view.setImageResource(args.defaultResource);
-                }
-
-                // Note that the data is cached.
-                if (args.info != null) {
-                    args.info.isCachedPhotoCurrent = true;
-                }
-
-                // notify the listener if it is there.
-                if (args.listener != null) {
-                    if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() +
-                            " image: " + args.uri + " completed");
-                    args.listener.onImageLoadComplete(msg.what, args.cookie, args.view,
-                            imagePresent);
-                }
-                break;
-            default:
-        }
-    }
-}
diff --git a/core/java/android/pim/package.html b/core/java/android/pim/package.html
deleted file mode 100644
index 75237c9..0000000
--- a/core/java/android/pim/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<HTML>
-<BODY>
-{@hide}
-Provides helpers for working with PIM (Personal Information Manager) data used
-by contact lists and calendars.
-</BODY>
-</HTML>
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 375e5e4..0aad64a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1412,7 +1412,7 @@
         public static final int SCREEN_BRIGHTNESS_MODE_MANUAL = 0;
 
         /**
-         * SCREEN_BRIGHTNESS_MODE value for manual mode.
+         * SCREEN_BRIGHTNESS_MODE value for automatic mode.
          */
         public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
 
@@ -1499,6 +1499,12 @@
         public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco";
 
         /**
+         * Master volume (float in the range 0.0f to 1.0f).
+         * @hide
+         */
+        public static final String VOLUME_MASTER = "volume_master";
+
+        /**
          * Whether the notifications should use the ring volume (value of 1) or a separate
          * notification volume (value of 0). In most cases, users will have this enabled so the
          * notification and ringer volumes will be the same. However, power users can disable this
@@ -1830,6 +1836,12 @@
         public static final String LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled";
 
         /**
+         * Whether the lockscreen should be completely disabled.
+         * @hide
+         */
+        public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled";
+
+        /**
          * URI for the low battery sound file.
          * @hide
          */
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 2b8a458..cac449d 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -27,6 +27,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.textservice.SentenceSuggestionsInfo;
 import android.view.textservice.SuggestionsInfo;
 import android.view.textservice.TextInfo;
 
@@ -140,19 +141,21 @@
 
         /**
          * @hide
-         * The default implementation returns an array of SuggestionsInfo by simply calling
+         * The default implementation returns an array of SentenceSuggestionsInfo by simply calling
          * onGetSuggestions().
          * When you override this method, make sure that suggestionsLimit is applied to suggestions
          * that share the same start position and length.
          */
-        public SuggestionsInfo[] onGetSuggestionsMultipleForSentence(TextInfo[] textInfos,
+        public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
                 int suggestionsLimit) {
             final int length = textInfos.length;
-            final SuggestionsInfo[] retval = new SuggestionsInfo[length];
+            final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length];
             for (int i = 0; i < length; ++i) {
-                retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
-                retval[i].setCookieAndSequence(
-                        textInfos[i].getCookie(), textInfos[i].getSequence());
+                final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit);
+                si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence());
+                final int N = textInfos[i].getText().length();
+                retval[i] = new SentenceSuggestionsInfo(
+                        new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N});
             }
             return retval;
         }
@@ -220,11 +223,10 @@
         }
 
         @Override
-        public void onGetSuggestionsMultipleForSentence(
-                TextInfo[] textInfos, int suggestionsLimit) {
+        public void onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) {
             try {
-                mListener.onGetSuggestionsForSentence(
-                        mSession.onGetSuggestionsMultipleForSentence(textInfos, suggestionsLimit));
+                mListener.onGetSentenceSuggestions(
+                        mSession.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit));
             } catch (RemoteException e) {
             }
         }
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index e5c1e5b..ae41eab 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -17,13 +17,10 @@
 package android.text;
 
 
-import java.util.Locale;
-
 import android.util.LocaleUtil;
 
 /**
  * Some objects that implement TextDirectionHeuristic.
- * @hide
  */
 public class TextDirectionHeuristics {
 
@@ -74,9 +71,8 @@
      * Computes the text direction based on an algorithm.  Subclasses implement
      * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
      * direction from the text alone.
-     * @hide
      */
-    public static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
+    private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
         private final TextDirectionAlgorithm mAlgorithm;
 
         public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
@@ -157,13 +153,11 @@
     /**
      * Interface for an algorithm to guess the direction of a paragraph of text.
      *
-     * @hide
      */
-    public static interface TextDirectionAlgorithm {
+    private static interface TextDirectionAlgorithm {
         /**
          * Returns whether the range of text is RTL according to the algorithm.
          *
-         * @hide
          */
         TriState checkRtl(char[] text, int start, int count);
     }
@@ -173,9 +167,8 @@
      * the paragraph direction.  This is the standard Unicode Bidirectional
      * algorithm.
      *
-     * @hide
      */
-    public static class FirstStrong implements TextDirectionAlgorithm {
+    private static class FirstStrong implements TextDirectionAlgorithm {
         @Override
         public TriState checkRtl(char[] text, int start, int count) {
             TriState result = TriState.UNKNOWN;
@@ -196,9 +189,8 @@
      * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
      * direction of text.
      *
-     * @hide
      */
-    public static class AnyStrong implements TextDirectionAlgorithm {
+    private static class AnyStrong implements TextDirectionAlgorithm {
         private final boolean mLookForRtl;
 
         @Override
@@ -239,7 +231,7 @@
     /**
      * Algorithm that uses the Locale direction to force the direction of a paragraph.
      */
-    public static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
+    private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
 
         public TextDirectionHeuristicLocale() {
             super(null);
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 95a3cdc..43dfc81 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1325,7 +1325,11 @@
                 sb.append("&amp;"); //$NON-NLS-1$
                 break;
             case '\'':
-                sb.append("&apos;"); //$NON-NLS-1$
+                //http://www.w3.org/TR/xhtml1
+                // The named character reference &apos; (the apostrophe, U+0027) was introduced in
+                // XML 1.0 but does not appear in HTML. Authors should therefore use &#39; instead
+                // of &apos; to work as expected in HTML 4 user agents.
+                sb.append("&#39;"); //$NON-NLS-1$
                 break;
             case '"':
                 sb.append("&quot;"); //$NON-NLS-1$
diff --git a/core/java/android/text/method/KeyListener.java b/core/java/android/text/method/KeyListener.java
index 8594852..318149a 100644
--- a/core/java/android/text/method/KeyListener.java
+++ b/core/java/android/text/method/KeyListener.java
@@ -22,7 +22,7 @@
 
 /**
  * Interface for converting text key events into edit operations on an
- * Editable class.  Note that for must cases this interface has been
+ * Editable class.  Note that for most cases this interface has been
  * superceded by general soft input methods as defined by
  * {@link android.view.inputmethod.InputMethod}; it should only be used
  * for cases where an application has its own on-screen keypad and also wants
diff --git a/core/java/android/text/method/TransformationMethod.java b/core/java/android/text/method/TransformationMethod.java
index 9f51c2a..b542109 100644
--- a/core/java/android/text/method/TransformationMethod.java
+++ b/core/java/android/text/method/TransformationMethod.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Rect;
 import android.view.View;
-import android.widget.TextView;
 
 /**
  * TextView uses TransformationMethods to do things like replacing the
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index 1ec8be6..630e5f3 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -19,56 +19,50 @@
 import com.android.internal.util.ArrayUtils;
 
 /**
- * SparseArrays map longs to Objects.  Unlike a normal array of Objects,
+ * SparseArray mapping longs to Objects.  Unlike a normal array of Objects,
  * there can be gaps in the indices.  It is intended to be more efficient
  * than using a HashMap to map Longs to Objects.
- *
- * @hide
  */
-public class LongSparseArray<E> {
+public class LongSparseArray<E> implements Cloneable {
     private static final Object DELETED = new Object();
     private boolean mGarbage = false;
 
+    private long[] mKeys;
+    private Object[] mValues;
+    private int mSize;
+
     /**
-     * Creates a new SparseArray containing no mappings.
+     * Creates a new LongSparseArray containing no mappings.
      */
     public LongSparseArray() {
         this(10);
     }
 
     /**
-     * Creates a new SparseArray containing no mappings that will not
+     * Creates a new LongSparseArray containing no mappings that will not
      * require any additional memory allocation to store the specified
      * number of mappings.
      */
     public LongSparseArray(int initialCapacity) {
-        initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
+        initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
 
         mKeys = new long[initialCapacity];
         mValues = new Object[initialCapacity];
         mSize = 0;
     }
-    
-    /**
-     * @return A copy of all keys contained in the sparse array.
-     */
-    public long[] getKeys() {
-        int length = mKeys.length;
-        long[] result = new long[length];
-        System.arraycopy(mKeys, 0, result, 0, length);
-        return result;
-    }
-    
-    /**
-     * Sets all supplied keys to the given unique value.
-     * @param keys Keys to set
-     * @param uniqueValue Value to set all supplied keys to
-     */
-    public void setValues(long[] keys, E uniqueValue) {
-        int length = keys.length;
-        for (int i = 0; i < length; i++) {
-            put(keys[i], uniqueValue);
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public LongSparseArray<E> clone() {
+        LongSparseArray<E> clone = null;
+        try {
+            clone = (LongSparseArray<E>) super.clone();
+            clone.mKeys = mKeys.clone();
+            clone.mValues = mValues.clone();
+        } catch (CloneNotSupportedException cnse) {
+            /* ignore */
         }
+        return clone;
     }
 
     /**
@@ -83,6 +77,7 @@
      * Gets the Object mapped from the specified key, or the specified Object
      * if no such mapping has been made.
      */
+    @SuppressWarnings("unchecked")
     public E get(long key, E valueIfKeyNotFound) {
         int i = binarySearch(mKeys, 0, mSize, key);
 
@@ -114,6 +109,16 @@
         delete(key);
     }
 
+    /**
+     * Removes the mapping at the specified index.
+     */
+    public void removeAt(int index) {
+        if (mValues[index] != DELETED) {
+            mValues[index] = DELETED;
+            mGarbage = true;
+        }
+    }
+
     private void gc() {
         // Log.e("SparseArray", "gc start with " + mSize);
 
@@ -129,6 +134,7 @@
                 if (i != o) {
                     keys[o] = keys[i];
                     values[o] = val;
+                    values[i] = null;
                 }
 
                 o++;
@@ -168,7 +174,7 @@
             }
 
             if (mSize >= mKeys.length) {
-                int n = ArrayUtils.idealIntArraySize(mSize + 1);
+                int n = ArrayUtils.idealLongArraySize(mSize + 1);
 
                 long[] nkeys = new long[n];
                 Object[] nvalues = new Object[n];
@@ -194,7 +200,7 @@
     }
 
     /**
-     * Returns the number of key-value mappings that this SparseArray
+     * Returns the number of key-value mappings that this LongSparseArray
      * currently stores.
      */
     public int size() {
@@ -208,7 +214,7 @@
     /**
      * Given an index in the range <code>0...size()-1</code>, returns
      * the key from the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
+     * LongSparseArray stores.
      */
     public long keyAt(int index) {
         if (mGarbage) {
@@ -221,8 +227,9 @@
     /**
      * Given an index in the range <code>0...size()-1</code>, returns
      * the value from the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
+     * LongSparseArray stores.
      */
+    @SuppressWarnings("unchecked")
     public E valueAt(int index) {
         if (mGarbage) {
             gc();
@@ -234,7 +241,7 @@
     /**
      * Given an index in the range <code>0...size()-1</code>, sets a new
      * value for the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
+     * LongSparseArray stores.
      */
     public void setValueAt(int index, E value) {
         if (mGarbage) {
@@ -278,7 +285,7 @@
     }
 
     /**
-     * Removes all key-value mappings from this SparseArray.
+     * Removes all key-value mappings from this LongSparseArray.
      */
     public void clear() {
         int n = mSize;
@@ -308,7 +315,7 @@
 
         int pos = mSize;
         if (pos >= mKeys.length) {
-            int n = ArrayUtils.idealIntArraySize(pos + 1);
+            int n = ArrayUtils.idealLongArraySize(pos + 1);
 
             long[] nkeys = new long[n];
             Object[] nvalues = new Object[n];
@@ -345,20 +352,4 @@
         else
             return ~high;
     }
-
-    private void checkIntegrity() {
-        for (int i = 1; i < mSize; i++) {
-            if (mKeys[i] <= mKeys[i - 1]) {
-                for (int j = 0; j < mSize; j++) {
-                    Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]);
-                }
-
-                throw new RuntimeException();
-            }
-        }
-    }
-
-    private long[] mKeys;
-    private Object[] mValues;
-    private int mSize;
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
index 0349a2b..c1c7fe2 100644
--- a/core/java/android/view/ActionMode.java
+++ b/core/java/android/view/ActionMode.java
@@ -24,7 +24,7 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For information about how to provide contextual actions with {@code ActionMode},
- * read the <a href="{@docRoot}guide/topics/ui/menu.html#context-menu">Menus</a>
+ * read the <a href="{@docRoot}guide/topics/ui/menus.html#context-menu">Menus</a>
  * developer guide.</p>
  * </div>
  */
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 3ee275c..42c3913 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -16,8 +16,6 @@
 
 package android.view;
 
-import com.android.internal.util.ArrayUtils;
-
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -28,12 +26,16 @@
 /**
  * Coordinates animations and drawing for UI on a particular thread.
  *
- * This object is thread-safe.  Other threads can add and remove listeners
- * or schedule work to occur at a later time on the UI thread.
+ * This object is thread-safe.  Other threads can post callbacks to run at a later time
+ * on the UI thread.
+ *
+ * Ensuring thread-safety is a little tricky because the {@link DisplayEventReceiver}
+ * can only be accessed from the UI thread so operations that touch the event receiver
+ * are posted to the UI thread if needed.
  *
  * @hide
  */
-public final class Choreographer extends Handler {
+public final class Choreographer {
     private static final String TAG = "Choreographer";
     private static final boolean DEBUG = false;
 
@@ -78,23 +80,28 @@
 
     private static final int MSG_DO_ANIMATION = 0;
     private static final int MSG_DO_DRAW = 1;
+    private static final int MSG_DO_SCHEDULE_VSYNC = 2;
 
     private final Object mLock = new Object();
 
     private final Looper mLooper;
+    private final FrameHandler mHandler;
+    private final FrameDisplayEventReceiver mDisplayEventReceiver;
 
-    private OnAnimateListener[] mOnAnimateListeners;
-    private OnDrawListener[] mOnDrawListeners;
+    private Callback mCallbackPool;
+
+    private Callback mAnimationCallbacks;
+    private Callback mDrawCallbacks;
 
     private boolean mAnimationScheduled;
     private boolean mDrawScheduled;
-    private FrameDisplayEventReceiver mFrameDisplayEventReceiver;
     private long mLastAnimationTime;
     private long mLastDrawTime;
 
     private Choreographer(Looper looper) {
-        super(looper);
         mLooper = looper;
+        mHandler = new FrameHandler(looper);
+        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
         mLastAnimationTime = Long.MIN_VALUE;
         mLastDrawTime = Long.MIN_VALUE;
     }
@@ -143,14 +150,77 @@
     }
 
     /**
-     * Schedules animation (and drawing) to occur on the next frame synchronization boundary.
+     * Posts a callback to run on the next animation cycle and schedules an animation cycle.
+     * The callback only runs once and then is automatically removed.
+     *
+     * @param runnable The callback to run during the next animation cycle.
+     *
+     * @see #removeAnimationCallback
      */
-    public void scheduleAnimation() {
+    public void postAnimationCallback(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
         synchronized (mLock) {
+            mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, runnable);
             scheduleAnimationLocked();
         }
     }
 
+    /**
+     * Removes an animation callback.
+     * Does nothing if the specified animation callback has not been posted or has already
+     * been removed.
+     *
+     * @param runnable The animation callback to remove.
+     *
+     * @see #postAnimationCallback
+     */
+    public void removeAnimationCallback(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
+        synchronized (mLock) {
+            mAnimationCallbacks = removeCallbackLocked(mAnimationCallbacks, runnable);
+        }
+    }
+
+    /**
+     * Posts a callback to run on the next draw cycle and schedules a draw cycle.
+     * The callback only runs once and then is automatically removed.
+     *
+     * @param runnable The callback to run during the next draw cycle.
+     *
+     * @see #removeDrawCallback
+     */
+    public void postDrawCallback(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
+        synchronized (mLock) {
+            mDrawCallbacks = addCallbackLocked(mDrawCallbacks, runnable);
+            scheduleDrawLocked();
+        }
+    }
+
+    /**
+     * Removes a draw callback.
+     * Does nothing if the specified draw callback has not been posted or has already
+     * been removed.
+     *
+     * @param runnable The draw callback to remove.
+     *
+     * @see #postDrawCallback
+     */
+    public void removeDrawCallback(Runnable runnable) {
+        if (runnable == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
+        synchronized (mLock) {
+            mDrawCallbacks = removeCallbackLocked(mDrawCallbacks, runnable);
+        }
+    }
+
     private void scheduleAnimationLocked() {
         if (!mAnimationScheduled) {
             mAnimationScheduled = true;
@@ -158,76 +228,47 @@
                 if (DEBUG) {
                     Log.d(TAG, "Scheduling vsync for animation.");
                 }
-                if (mFrameDisplayEventReceiver == null) {
-                    mFrameDisplayEventReceiver = new FrameDisplayEventReceiver(mLooper);
+
+                // If running on the Looper thread, then schedule the vsync immediately,
+                // otherwise post a message to schedule the vsync from the UI thread
+                // as soon as possible.
+                if (isRunningOnLooperThreadLocked()) {
+                    doScheduleVsyncLocked();
+                } else {
+                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
+                    msg.setAsynchronous(true);
+                    mHandler.sendMessageAtFrontOfQueue(msg);
                 }
-                mFrameDisplayEventReceiver.scheduleVsync();
             } else {
                 final long now = SystemClock.uptimeMillis();
                 final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now);
                 if (DEBUG) {
                     Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
                 }
-                sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime);
+                Message msg = mHandler.obtainMessage(MSG_DO_ANIMATION);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageAtTime(msg, nextAnimationTime);
             }
         }
     }
 
-    /**
-     * Returns true if {@link #scheduleAnimation()} has been called but
-     * {@link OnAnimateListener#onAnimate() OnAnimateListener.onAnimate()} has
-     * not yet been called.
-     */
-    public boolean isAnimationScheduled() {
-        synchronized (mLock) {
-            return mAnimationScheduled;
-        }
-    }
-
-    /**
-     * Schedules drawing to occur on the next frame synchronization boundary.
-     * Must be called on the UI thread.
-     */
-    public void scheduleDraw() {
-        synchronized (mLock) {
-            if (!mDrawScheduled) {
-                mDrawScheduled = true;
-                if (USE_ANIMATION_TIMER_FOR_DRAW) {
-                    scheduleAnimationLocked();
-                } else {
-                    if (DEBUG) {
-                        Log.d(TAG, "Scheduling draw immediately.");
-                    }
-                    sendEmptyMessage(MSG_DO_DRAW);
+    private void scheduleDrawLocked() {
+        if (!mDrawScheduled) {
+            mDrawScheduled = true;
+            if (USE_ANIMATION_TIMER_FOR_DRAW) {
+                scheduleAnimationLocked();
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "Scheduling draw immediately.");
                 }
+                Message msg = mHandler.obtainMessage(MSG_DO_DRAW);
+                msg.setAsynchronous(true);
+                mHandler.sendMessage(msg);
             }
         }
     }
 
-    /**
-     * Returns true if {@link #scheduleDraw()} has been called but
-     * {@link OnDrawListener#onDraw() OnDrawListener.onDraw()} has
-     * not yet been called.
-     */
-    public boolean isDrawScheduled() {
-        synchronized (mLock) {
-            return mDrawScheduled;
-        }
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case MSG_DO_ANIMATION:
-                doAnimation();
-                break;
-            case MSG_DO_DRAW:
-                doDraw();
-                break;
-        }
-    }
-
-    private void doAnimation() {
+    void doAnimation() {
         doAnimationInner();
 
         if (USE_ANIMATION_TIMER_FOR_DRAW) {
@@ -235,9 +276,9 @@
         }
     }
 
-    private void doAnimationInner() {
+    void doAnimationInner() {
         final long start;
-        final OnAnimateListener[] listeners;
+        final Callback callbacks;
         synchronized (mLock) {
             if (!mAnimationScheduled) {
                 return; // no work to do
@@ -251,12 +292,14 @@
             }
             mLastAnimationTime = start;
 
-            listeners = mOnAnimateListeners;
+            callbacks = mAnimationCallbacks;
+            mAnimationCallbacks = null;
         }
 
-        if (listeners != null) {
-            for (int i = 0; i < listeners.length; i++) {
-                listeners[i].onAnimate();
+        if (callbacks != null) {
+            runCallbacks(callbacks);
+            synchronized (mLock) {
+                recycleCallbacksLocked(callbacks);
             }
         }
 
@@ -265,9 +308,9 @@
         }
     }
 
-    private void doDraw() {
+    void doDraw() {
         final long start;
-        final OnDrawListener[] listeners;
+        final Callback callbacks;
         synchronized (mLock) {
             if (!mDrawScheduled) {
                 return; // no work to do
@@ -281,12 +324,14 @@
             }
             mLastDrawTime = start;
 
-            listeners = mOnDrawListeners;
+            callbacks = mDrawCallbacks;
+            mDrawCallbacks = null;
         }
 
-        if (listeners != null) {
-            for (int i = 0; i < listeners.length; i++) {
-                listeners[i].onDraw();
+        if (callbacks != null) {
+            runCallbacks(callbacks);
+            synchronized (mLock) {
+                recycleCallbacksLocked(callbacks);
             }
         }
 
@@ -295,134 +340,106 @@
         }
     }
 
-    /**
-     * Adds an animation listener.
-     *
-     * @param listener The listener to add.
-     */
-    public void addOnAnimateListener(OnAnimateListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener must not be null");
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "Adding onAnimate listener: " + listener);
-        }
-
+    void doScheduleVsync() {
         synchronized (mLock) {
-            mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
-                    mOnAnimateListeners, listener);
+            doScheduleVsyncLocked();
         }
     }
 
-    /**
-     * Removes an animation listener.
-     *
-     * @param listener The listener to remove.
-     */
-    public void removeOnAnimateListener(OnAnimateListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener must not be null");
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "Removing onAnimate listener: " + listener);
-        }
-
-        synchronized (mLock) {
-            mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
-                    mOnAnimateListeners, listener);
-            stopTimingLoopIfNoListenersLocked();
+    private void doScheduleVsyncLocked() {
+        if (mAnimationScheduled) {
+            mDisplayEventReceiver.scheduleVsync();
         }
     }
 
-    /**
-     * Adds a draw listener.
-     *
-     * @param listener The listener to add.
-     */
-    public void addOnDrawListener(OnDrawListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener must not be null");
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "Adding onDraw listener: " + listener);
-        }
-
-        synchronized (mLock) {
-            mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class,
-                    mOnDrawListeners, listener);
-        }
+    private boolean isRunningOnLooperThreadLocked() {
+        return Looper.myLooper() == mLooper;
     }
 
-    /**
-     * Removes a draw listener.
-     * Must be called on the UI thread.
-     *
-     * @param listener The listener to remove.
-     */
-    public void removeOnDrawListener(OnDrawListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener must not be null");
+    private Callback addCallbackLocked(Callback head, Runnable runnable) {
+        Callback callback = obtainCallbackLocked(runnable);
+        if (head == null) {
+            return callback;
         }
-
-        if (DEBUG) {
-            Log.d(TAG, "Removing onDraw listener: " + listener);
+        Callback tail = head;
+        while (tail.next != null) {
+            tail = tail.next;
         }
-
-        synchronized (mLock) {
-            mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
-                    mOnDrawListeners, listener);
-            stopTimingLoopIfNoListenersLocked();
-        }
+        tail.next = callback;
+        return head;
     }
 
-    private void stopTimingLoopIfNoListenersLocked() {
-        if (mOnDrawListeners == null && mOnAnimateListeners == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Stopping timing loop.");
-            }
-
-            if (mAnimationScheduled) {
-                mAnimationScheduled = false;
-                if (!USE_VSYNC) {
-                    removeMessages(MSG_DO_ANIMATION);
+    private Callback removeCallbackLocked(Callback head, Runnable runnable) {
+        Callback predecessor = null;
+        for (Callback callback = head; callback != null;) {
+            final Callback next = callback.next;
+            if (callback.runnable == runnable) {
+                if (predecessor != null) {
+                    predecessor.next = next;
+                } else {
+                    head = next;
                 }
+                recycleCallbackLocked(callback);
+            } else {
+                predecessor = callback;
             }
+            callback = next;
+        }
+        return head;
+    }
 
-            if (mDrawScheduled) {
-                mDrawScheduled = false;
-                if (!USE_ANIMATION_TIMER_FOR_DRAW) {
-                    removeMessages(MSG_DO_DRAW);
-                }
-            }
-
-            if (mFrameDisplayEventReceiver != null) {
-                mFrameDisplayEventReceiver.dispose();
-                mFrameDisplayEventReceiver = null;
-            }
+    private void runCallbacks(Callback head) {
+        while (head != null) {
+            head.runnable.run();
+            head = head.next;
         }
     }
 
-    /**
-     * Listens for animation frame timing events.
-     */
-    public static interface OnAnimateListener {
-        /**
-         * Called to animate properties before drawing the frame.
-         */
-        public void onAnimate();
+    private void recycleCallbacksLocked(Callback head) {
+        while (head != null) {
+            final Callback next = head.next;
+            recycleCallbackLocked(head);
+            head = next;
+        }
     }
 
-    /**
-     * Listens for draw frame timing events.
-     */
-    public static interface OnDrawListener {
-        /**
-         * Called to draw the frame.
-         */
-        public void onDraw();
+    private Callback obtainCallbackLocked(Runnable runnable) {
+        Callback callback = mCallbackPool;
+        if (callback == null) {
+            callback = new Callback();
+        } else {
+            mCallbackPool = callback.next;
+            callback.next = null;
+        }
+        callback.runnable = runnable;
+        return callback;
+    }
+
+    private void recycleCallbackLocked(Callback callback) {
+        callback.runnable = null;
+        callback.next = mCallbackPool;
+        mCallbackPool = callback;
+    }
+
+    private final class FrameHandler extends Handler {
+        public FrameHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_DO_ANIMATION:
+                    doAnimation();
+                    break;
+                case MSG_DO_DRAW:
+                    doDraw();
+                    break;
+                case MSG_DO_SCHEDULE_VSYNC:
+                    doScheduleVsync();
+                    break;
+            }
+        }
     }
 
     private final class FrameDisplayEventReceiver extends DisplayEventReceiver {
@@ -435,4 +452,9 @@
             doAnimation();
         }
     }
+
+    private static final class Callback {
+        public Callback next;
+        public Runnable runnable;
+    }
 }
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index d6711ee..6c2e540 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -25,6 +25,10 @@
 /**
  * Provides a low-level mechanism for an application to receive display events
  * such as vertical sync.
+ *
+ * The display event receive is NOT thread safe.  Moreover, its methods must only
+ * be called on the Looper thread to which it is attached.
+ *
  * @hide
  */
 public abstract class DisplayEventReceiver {
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index fec0d4b..f60c8f0 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -27,6 +27,15 @@
  */
 public abstract class DisplayList {
     /**
+     * Flag used when calling
+     * {@link HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)}.
+     * When this flag is set, draw operations lying outside of the bounds of the
+     * display list will be culled early. It is recommeneded to always set this
+     * flag.
+     */
+    public static final int FLAG_CLIP_CHILDREN = 0x1;
+
+    /**
      * Starts recording the display list. All operations performed on the
      * returned canvas are recorded and stored in this display list.
      * 
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index fa4dd25..f5fc708 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -385,13 +385,14 @@
     private static native void nSetDisplayListName(int displayList, String name);
 
     @Override
-    public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) {
-        return nDrawDisplayList(mRenderer,
-                ((GLES20DisplayList) displayList).getNativeDisplayList(), width, height, dirty);
+    public boolean drawDisplayList(DisplayList displayList, int width, int height,
+            Rect dirty, int flags) {
+        return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList(),
+                width, height, dirty, flags);
     }
 
     private static native boolean nDrawDisplayList(int renderer, int displayList,
-            int width, int height, Rect dirty);
+            int width, int height, Rect dirty, int flags);
 
     @Override
     void outputDisplayList(DisplayList displayList) {
@@ -407,9 +408,12 @@
     void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
         final GLES20Layer glLayer = (GLES20Layer) layer;
         int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawLayer(mRenderer, glLayer.getLayer(), x, y, nativePaint);
-        if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        try {
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+            nDrawLayer(mRenderer, glLayer.getLayer(), x, y, nativePaint);
+        } finally {
+            if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        }
     }
 
     private static native void nDrawLayer(int renderer, int layer, float x, float y, int paint);
@@ -607,10 +611,14 @@
             return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
         }
 
+        int count;
         int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        int count = nSaveLayer(mRenderer, nativePaint, saveFlags);
-        if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        try {
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+            count = nSaveLayer(mRenderer, nativePaint, saveFlags);
+        } finally {
+            if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        }
         return count;
     }
 
@@ -620,10 +628,14 @@
     public int saveLayer(float left, float top, float right, float bottom, Paint paint,
             int saveFlags) {
         if (left < right && top < bottom) {
+            int count;
             int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
-            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-            int count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
-            if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+            try {
+                final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+                count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
+            } finally {
+                if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+            }
             return count;
         }
         return save(saveFlags);
@@ -707,9 +719,12 @@
     public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
             Paint paint) {
         int modifiers = setupModifiers(paint);
-        nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle,
-                useCenter, paint.mNativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        try {
+            nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
+                    startAngle, sweepAngle, useCenter, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawArc(int renderer, float left, float top,
@@ -723,12 +738,16 @@
 
     @Override
     public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+        if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
         // Shaders are ignored when drawing patches
         int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
-                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
-        if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        try {
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+            nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
+                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
+        } finally {
+            if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        }
     }
 
     private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
@@ -736,11 +755,15 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
+        if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
         // Shaders are ignored when drawing bitmaps
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        try {
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawBitmap(
@@ -748,12 +771,16 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
+        if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
         // Shaders are ignored when drawing bitmaps
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
-                matrix.native_instance, nativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        try {
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
+                    matrix.native_instance, nativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff,
@@ -761,48 +788,56 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
+        if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
         // Shaders are ignored when drawing bitmaps
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        try {
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
 
-        int left, top, right, bottom;
-        if (src == null) {
-            left = top = 0;
-            right = bitmap.getWidth();
-            bottom = bitmap.getHeight();
-        } else {
-            left = src.left;
-            right = src.right;
-            top = src.top;
-            bottom = src.bottom;
+            int left, top, right, bottom;
+            if (src == null) {
+                left = top = 0;
+                right = bitmap.getWidth();
+                bottom = bitmap.getHeight();
+            } else {
+                left = src.left;
+                right = src.right;
+                top = src.top;
+                bottom = src.bottom;
+            }
+
+            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
         }
-
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
-                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
     }
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
+        if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
         // Shaders are ignored when drawing bitmaps
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-
-        float left, top, right, bottom;
-        if (src == null) {
-            left = top = 0;
-            right = bitmap.getWidth();
-            bottom = bitmap.getHeight();
-        } else {
-            left = src.left;
-            right = src.right;
-            top = src.top;
-            bottom = src.bottom;
+        try {
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+    
+            float left, top, right, bottom;
+            if (src == null) {
+                left = top = 0;
+                right = bitmap.getWidth();
+                bottom = bitmap.getHeight();
+            } else {
+                left = src.left;
+                right = src.right;
+                top = src.top;
+                bottom = src.bottom;
+            }
+    
+            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
         }
-
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
-                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
     }
 
     private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
@@ -814,12 +849,15 @@
             int width, int height, boolean hasAlpha, Paint paint) {
         // Shaders are ignored when drawing bitmaps
         int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
-        final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
-        final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, b.mNativeBitmap, b.mBuffer, x, y, nativePaint);
-        b.recycle();
-        if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        try {
+            final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
+            final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+            nDrawBitmap(mRenderer, b.mNativeBitmap, b.mBuffer, x, y, nativePaint);
+            b.recycle();
+        } finally {
+            if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        }
     }
 
     @Override
@@ -832,6 +870,7 @@
     @Override
     public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
             int vertOffset, int[] colors, int colorOffset, Paint paint) {
+        if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
         if (meshWidth < 0 || meshHeight < 0 || vertOffset < 0 || colorOffset < 0) {
             throw new ArrayIndexOutOfBoundsException();
         }
@@ -848,10 +887,13 @@
         colorOffset = 0;
 
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;        
-        nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
-                verts, vertOffset, colors, colorOffset, nativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        try {
+            final int nativePaint = paint == null ? 0 : paint.mNativePaint;        
+            nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
+                    verts, vertOffset, colors, colorOffset, nativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawBitmapMesh(int renderer, int bitmap, byte[] buffer,
@@ -861,8 +903,11 @@
     @Override
     public void drawCircle(float cx, float cy, float radius, Paint paint) {
         int modifiers = setupModifiers(paint);
-        nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);        
+        try {
+            nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawCircle(int renderer, float cx, float cy,
@@ -895,8 +940,11 @@
             throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
         }
         int modifiers = setupModifiers(paint);
-        nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        try {
+            nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawLines(int renderer, float[] points,
@@ -910,8 +958,11 @@
     @Override
     public void drawOval(RectF oval, Paint paint) {
         int modifiers = setupModifiers(paint);
-        nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); 
+        try {
+            nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawOval(int renderer, float left, float top,
@@ -927,14 +978,17 @@
     @Override
     public void drawPath(Path path, Paint paint) {
         int modifiers = setupModifiers(paint);
-        if (path.isSimplePath) {
-            if (path.rects != null) {
-                nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
+        try {
+            if (path.isSimplePath) {
+                if (path.rects != null) {
+                    nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
+                }
+            } else {
+                nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
             }
-        } else {
-            nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
         }
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
     }
 
     private static native void nDrawPath(int renderer, int path, int paint);
@@ -995,8 +1049,11 @@
     @Override
     public void drawPoints(float[] pts, int offset, int count, Paint paint) {
         int modifiers = setupModifiers(paint);
-        nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        try {
+            nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawPoints(int renderer, float[] points,
@@ -1041,8 +1098,11 @@
     @Override
     public void drawRect(float left, float top, float right, float bottom, Paint paint) {
         int modifiers = setupModifiers(paint);
-        nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        try {
+            nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawRect(int renderer, float left, float top,
@@ -1066,9 +1126,12 @@
     @Override
     public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
         int modifiers = setupModifiers(paint);
-        nDrawRoundRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
-                rx, ry, paint.mNativePaint);
-        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);        
+        try {
+            nDrawRoundRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
+                    rx, ry, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
     private static native void nDrawRoundRect(int renderer, float left, float top,
@@ -1217,17 +1280,17 @@
     }
 
     private int setupModifiers(Bitmap b, Paint paint) {
-        if (b.getConfig() == Bitmap.Config.ALPHA_8) {
+        if (b.getConfig() != Bitmap.Config.ALPHA_8) {
+            final ColorFilter filter = paint.getColorFilter();
+            if (filter != null) {
+                nSetupColorFilter(mRenderer, filter.nativeColorFilter);
+                return MODIFIER_COLOR_FILTER;
+            }
+
+            return MODIFIER_NONE;
+        } else {
             return setupModifiers(paint);
         }
-
-        final ColorFilter filter = paint.getColorFilter();
-        if (filter != null) {
-            nSetupColorFilter(mRenderer, filter.nativeColorFilter);
-            return MODIFIER_COLOR_FILTER;
-        }
-
-        return MODIFIER_NONE;
     }
 
     private int setupModifiers(Paint paint) {
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index c987f48..c9ba65f 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -143,8 +143,8 @@
     @Override
     public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
             int vertOffset, int[] colors, int colorOffset, Paint paint) {
-        super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset,
-                paint);
+        super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset,
+                colors, colorOffset, paint);
         mDisplayList.mBitmaps.add(bitmap);
         // Shaders in the Paint are ignored when drawing a Bitmap
     }
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index cbdbbde..838c03c 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -57,11 +57,14 @@
      * @param height The height of the display list.
      * @param dirty The dirty region to redraw in the next pass, matters only
      *        if this method returns true, can be null.
+     * @param flags Optional flags about drawing, see {@link DisplayList} for
+     *              the possible flags.
      * 
      * @return True if the content of the display list requires another
      *         drawing pass (invalidate()), false otherwise
      */
-    public abstract boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty);
+    public abstract boolean drawDisplayList(DisplayList displayList, int width, int height,
+            Rect dirty, int flags);
 
     /**
      * Outputs the specified display list to the log. This method exists for use by
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 9e8a228..ec95863 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -39,6 +39,7 @@
 import javax.microedition.khronos.opengles.GL;
 
 import java.io.File;
+import java.io.PrintWriter;
 
 import static javax.microedition.khronos.egl.EGL10.*;
 
@@ -84,18 +85,36 @@
     static final String DISABLE_VSYNC_PROPERTY = "hwui.disable_vsync";
 
     /**
+     * System property used to enable or disable hardware rendering profiling.
+     * The default value of this property is assumed to be false.
+     * 
+     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+     * output extra information about the time taken to execute by the last
+     * frames.
+     *
+     * Possible values:
+     * "true", to enable profiling
+     * "false", to disable profiling
+     */
+    static final String PROFILE_PROPERTY = "hwui.profile";
+
+    /**
      * System property used to debug EGL configuration choice.
      * 
      * Possible values:
      * "choice", print the chosen configuration only
      * "all", print all possible configurations
      */
-    static final String PRINT_CONFIG_PROPERTY = "hwui.print_config";    
+    static final String PRINT_CONFIG_PROPERTY = "hwui.print_config";
 
     /**
      * Turn on to draw dirty regions every other frame.
+     *
+     * Possible values:
+     * "true", to enable dirty regions debugging
+     * "false", to disable dirty regions debugging
      */
-    private static final boolean DEBUG_DIRTY_REGION = false;
+    static final String DEBUG_DIRTY_REGIONS_PROPERTY = "hwui.debug_dirty_regions";
     
     /**
      * A process can set this flag to false to prevent the use of hardware
@@ -112,6 +131,16 @@
      */
     public static boolean sSystemRendererDisabled = false;
 
+    /**
+     * Number of frames to profile.
+     */
+    private static final int PROFILE_MAX_FRAMES = 64;
+
+    /**
+     * Number of floats per profiled frame.
+     */
+    private static final int PROFILE_FRAME_DATA_COUNT = 3;
+
     private boolean mEnabled;
     private boolean mRequested = true;
 
@@ -226,6 +255,12 @@
     abstract HardwareCanvas getCanvas();
 
     /**
+     * Outputs extra debugging information in the specified file descriptor.
+     * @param pw
+     */
+    abstract void dumpGfxInfo(PrintWriter pw);
+    
+    /**
      * Sets the directory to use as a persistent storage for hardware rendering
      * resources.
      * 
@@ -439,6 +474,7 @@
         
         GL mGl;
         HardwareCanvas mCanvas;
+
         int mFrameCount;
         Paint mDebugPaint;
 
@@ -456,6 +492,12 @@
 
         final boolean mVsyncDisabled;
 
+        final boolean mProfileEnabled;
+        final float[] mProfileData;
+        int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
+        
+        final boolean mDebugDirtyRegions;
+
         final int mGlVersion;
         final boolean mTranslucent;
 
@@ -466,12 +508,48 @@
         GlRenderer(int glVersion, boolean translucent) {
             mGlVersion = glVersion;
             mTranslucent = translucent;
+            
+            String property;
 
-            final String vsyncProperty = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false");
-            mVsyncDisabled = "true".equalsIgnoreCase(vsyncProperty);
+            property = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false");
+            mVsyncDisabled = "true".equalsIgnoreCase(property);
             if (mVsyncDisabled) {
                 Log.d(LOG_TAG, "Disabling v-sync");
             }
+
+            //noinspection PointlessBooleanExpression,ConstantConditions
+            if (!ViewDebug.DEBUG_LATENCY) {
+                property = SystemProperties.get(PROFILE_PROPERTY, "false");
+                mProfileEnabled = "true".equalsIgnoreCase(property);
+                if (mProfileEnabled) {
+                    Log.d(LOG_TAG, "Profiling hardware renderer");
+                }
+            } else {
+                mProfileEnabled = true;
+            }
+
+            if (mProfileEnabled) {
+                mProfileData = new float[PROFILE_MAX_FRAMES * PROFILE_FRAME_DATA_COUNT];
+            } else {
+                mProfileData = null;
+            }
+
+            property = SystemProperties.get(DEBUG_DIRTY_REGIONS_PROPERTY, "false");
+            mDebugDirtyRegions = "true".equalsIgnoreCase(property);
+            if (mDebugDirtyRegions) {
+                Log.d(LOG_TAG, "Debugging dirty regions");
+            }
+        }
+
+        @Override
+        void dumpGfxInfo(PrintWriter pw) {
+            if (mProfileEnabled) {
+                pw.printf("\n\tDraw\tProcess\tExecute\n");
+                for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+                    pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
+                            mProfileData[i + 2]);
+                }
+            }
         }
 
         /**
@@ -860,34 +938,50 @@
                                 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED;
                         view.mPrivateFlags &= ~View.INVALIDATED;
 
-                        final long getDisplayListStartTime;
-                        if (ViewDebug.DEBUG_LATENCY) {
+                        long getDisplayListStartTime = 0;
+                        if (mProfileEnabled) {
+                            mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
+                            if (mProfileCurrentFrame >= mProfileData.length) {
+                                mProfileCurrentFrame = 0;
+                            }
+
                             getDisplayListStartTime = System.nanoTime();
                         }
 
                         DisplayList displayList = view.getDisplayList();
 
-                        if (ViewDebug.DEBUG_LATENCY) {
+                        if (mProfileEnabled) {
                             long now = System.nanoTime();
-                            Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took "
-                                    + ((now - getDisplayListStartTime) * 0.000001f) + "ms");
+                            float total = (now - getDisplayListStartTime) * 0.000001f;
+                            //noinspection PointlessArithmeticExpression
+                            mProfileData[mProfileCurrentFrame] = total;
+
+                            if (ViewDebug.DEBUG_LATENCY) {
+                                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took " +
+                                        total + "ms");
+                            }
                         }
 
                         if (displayList != null) {
-                            final long drawDisplayListStartTime;
-                            if (ViewDebug.DEBUG_LATENCY) {
+                            long drawDisplayListStartTime = 0;
+                            if (mProfileEnabled) {
                                 drawDisplayListStartTime = System.nanoTime();
                             }
 
-                            boolean invalidateNeeded = canvas.drawDisplayList(
-                                    displayList, view.getWidth(),
-                                    view.getHeight(), mRedrawClip);
+                            boolean invalidateNeeded = canvas.drawDisplayList(displayList,
+                                    view.getWidth(), view.getHeight(), mRedrawClip,
+                                    DisplayList.FLAG_CLIP_CHILDREN);
 
-                            if (ViewDebug.DEBUG_LATENCY) {
+                            if (mProfileEnabled) {
                                 long now = System.nanoTime();
-                                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took "
-                                        + ((now - drawDisplayListStartTime) * 0.000001f)
-                                        + "ms, invalidateNeeded=" + invalidateNeeded + ".");
+                                float total = (now - drawDisplayListStartTime) * 0.000001f;
+                                mProfileData[mProfileCurrentFrame + 1] = total;
+
+                                if (ViewDebug.DEBUG_LATENCY) {
+                                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " +
+                                            total + "ms, invalidateNeeded=" +
+                                            invalidateNeeded + ".");
+                                }
                             }
 
                             if (invalidateNeeded) {
@@ -902,8 +996,12 @@
                             // Shouldn't reach here
                             view.draw(canvas);
                         }
+                    } finally {
+                        callbacks.onHardwarePostDraw(canvas);
+                        canvas.restoreToCount(saveCount);
+                        view.mRecreateDisplayList = false;
 
-                        if (DEBUG_DIRTY_REGION) {
+                        if (mDebugDirtyRegions) {
                             if (mDebugPaint == null) {
                                 mDebugPaint = new Paint();
                                 mDebugPaint.setColor(0x7fff0000);
@@ -912,27 +1010,28 @@
                                 canvas.drawRect(dirty, mDebugPaint);
                             }
                         }
-                    } finally {
-                        callbacks.onHardwarePostDraw(canvas);
-                        canvas.restoreToCount(saveCount);
-                        view.mRecreateDisplayList = false;
                     }
 
                     onPostDraw();
 
                     attachInfo.mIgnoreDirtyState = false;
 
-                    final long eglSwapBuffersStartTime;
-                    if (ViewDebug.DEBUG_LATENCY) {
+                    long eglSwapBuffersStartTime = 0;
+                    if (mProfileEnabled) {
                         eglSwapBuffersStartTime = System.nanoTime();
                     }
 
                     sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
 
-                    if (ViewDebug.DEBUG_LATENCY) {
+                    if (mProfileEnabled) {
                         long now = System.nanoTime();
-                        Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- eglSwapBuffers() took "
-                                + ((now - eglSwapBuffersStartTime) * 0.000001f) + "ms");
+                        float total = (now - eglSwapBuffersStartTime) * 0.000001f;
+                        mProfileData[mProfileCurrentFrame + 2] = total;
+
+                        if (ViewDebug.DEBUG_LATENCY) {
+                            Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- eglSwapBuffers() took " +
+                                    total + "ms");
+                        }
                     }
 
                     checkEglErrors();
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 764d8dc..6a457ec 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -21,6 +21,7 @@
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.util.Log;
+import android.util.SparseIntArray;
 
 /**
  * Provides a low-level mechanism for an application to receive input events.
@@ -38,13 +39,14 @@
     private InputChannel mInputChannel;
     private MessageQueue mMessageQueue;
 
-    // The sequence number of the event that is in progress.
-    private int mEventSequenceNumberInProgress = -1;
+    // Map from InputEvent sequence numbers to dispatcher sequence numbers.
+    private final SparseIntArray mSeqMap = new SparseIntArray();
 
     private static native int nativeInit(InputEventReceiver receiver,
             InputChannel inputChannel, MessageQueue messageQueue);
     private static native void nativeDispose(int receiverPtr);
-    private static native void nativeFinishInputEvent(int receiverPtr, boolean handled);
+    private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled);
+    private static native void nativeConsumeBatchedInputEvents(int receiverPtr);
 
     /**
      * Creates an input event receiver bound to the specified input channel.
@@ -104,12 +106,25 @@
     }
 
     /**
+     * Called when a batched input event is pending.
+     *
+     * The batched input event will continue to accumulate additional movement
+     * samples until the recipient calls {@link #consumeBatchedInputEvents} or
+     * an event is received that ends the batch and causes it to be consumed
+     * immediately (such as a pointer up event).
+     */
+    public void onBatchedInputEventPending() {
+        consumeBatchedInputEvents();
+    }
+
+    /**
      * Finishes an input event and indicates whether it was handled.
+     * Must be called on the same Looper thread to which the receiver is attached.
      *
      * @param event The input event that was finished.
      * @param handled True if the event was handled.
      */
-    public void finishInputEvent(InputEvent event, boolean handled) {
+    public final void finishInputEvent(InputEvent event, boolean handled) {
         if (event == null) {
             throw new IllegalArgumentException("event must not be null");
         }
@@ -117,23 +132,47 @@
             Log.w(TAG, "Attempted to finish an input event but the input event "
                     + "receiver has already been disposed.");
         } else {
-            if (event.getSequenceNumber() != mEventSequenceNumberInProgress) {
+            int index = mSeqMap.indexOfKey(event.getSequenceNumber());
+            if (index < 0) {
                 Log.w(TAG, "Attempted to finish an input event that is not in progress.");
             } else {
-                mEventSequenceNumberInProgress = -1;
-                nativeFinishInputEvent(mReceiverPtr, handled);
+                int seq = mSeqMap.valueAt(index);
+                mSeqMap.removeAt(index);
+                nativeFinishInputEvent(mReceiverPtr, seq, handled);
             }
         }
         event.recycleIfNeededAfterDispatch();
     }
 
+    /**
+     * Consumes all pending batched input events.
+     * Must be called on the same Looper thread to which the receiver is attached.
+     *
+     * This method forces all batched input events to be delivered immediately.
+     * Should be called just before animating or drawing a new frame in the UI.
+     */
+    public final void consumeBatchedInputEvents() {
+        if (mReceiverPtr == 0) {
+            Log.w(TAG, "Attempted to consume batched input events but the input event "
+                    + "receiver has already been disposed.");
+        } else {
+            nativeConsumeBatchedInputEvents(mReceiverPtr);
+        }
+    }
+
     // Called from native code.
     @SuppressWarnings("unused")
-    private void dispatchInputEvent(InputEvent event) {
-        mEventSequenceNumberInProgress = event.getSequenceNumber();
+    private void dispatchInputEvent(int seq, InputEvent event) {
+        mSeqMap.put(event.getSequenceNumber(), seq);
         onInputEvent(event);
     }
 
+    // Called from native code.
+    @SuppressWarnings("unused")
+    private void dispatchBatchedInputEventPending() {
+        onBatchedInputEventPending();
+    }
+
     public static interface Factory {
         public InputEventReceiver createInputEventReceiver(
                 InputChannel inputChannel, Looper looper);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6726c56e..c658a80 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -384,7 +384,7 @@
         if (!mHaveFrame) {
             return;
         }
-        ViewRootImpl viewRoot = (ViewRootImpl) getRootView().getParent();
+        ViewRootImpl viewRoot = getViewRootImpl();
         if (viewRoot != null) {
             mTranslator = viewRoot.mTranslator;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8d32edc..0675a74 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -65,6 +65,7 @@
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
+import android.view.animation.Transformation;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
@@ -600,6 +601,8 @@
  * @attr ref android.R.styleable#View_paddingLeft
  * @attr ref android.R.styleable#View_paddingRight
  * @attr ref android.R.styleable#View_paddingTop
+ * @attr ref android.R.styleable#View_paddingStart
+ * @attr ref android.R.styleable#View_paddingEnd
  * @attr ref android.R.styleable#View_saveEnabled
  * @attr ref android.R.styleable#View_rotation
  * @attr ref android.R.styleable#View_rotationX
@@ -1756,6 +1759,16 @@
     static final int LAYOUT_DIRECTION_RESOLVED = 0x00000008;
 
 
+    /**
+     * Indicates that the view is tracking some sort of transient state
+     * that the app should not need to be aware of, but that the framework
+     * should take special care to preserve.
+     *
+     * @hide
+     */
+    static final int HAS_TRANSIENT_STATE = 0x00000010;
+
+
     /* End of masks for mPrivateFlags2 */
 
     static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED;
@@ -2617,7 +2630,6 @@
 
     /**
      * Text direction is inherited thru {@link ViewGroup}
-     * @hide
      */
     public static final int TEXT_DIRECTION_INHERIT = 0;
 
@@ -2626,7 +2638,6 @@
      * determines the paragraph direction. If there is no strong directional character, the
      * paragraph direction is the view's resolved layout direction.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_FIRST_STRONG = 1;
 
@@ -2635,42 +2646,36 @@
      * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters.
      * If there are neither, the paragraph direction is the view's resolved layout direction.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_ANY_RTL = 2;
 
     /**
      * Text direction is forced to LTR.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_LTR = 3;
 
     /**
      * Text direction is forced to RTL.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_RTL = 4;
 
     /**
      * Text direction is coming from the system Locale.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_LOCALE = 5;
 
     /**
      * Default text direction is inherited
      *
-     * @hide
      */
     protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
 
     /**
      * The text direction that has been defined by {@link #setTextDirection(int)}.
      *
-     * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -2688,7 +2693,6 @@
      * not TEXT_DIRECTION_INHERIT, otherwise resolution proceeds up the parent
      * chain of the view.
      *
-     * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -3627,7 +3631,9 @@
      * @see ActionMode
      */
     public ActionMode startActionMode(ActionMode.Callback callback) {
-        return getParent().startActionModeForChild(this, callback);
+        ViewParent parent = getParent();
+        if (parent == null) return null;
+        return parent.startActionModeForChild(this, callback);
     }
 
     /**
@@ -3774,14 +3780,6 @@
         }
 
         if ((mPrivateFlags & FOCUSED) != 0) {
-            // If this is the first focusable do not clear focus since the we
-            // try to give it focus every time a view clears its focus. Hence,
-            // the view that would gain focus already has it.
-            View firstFocusable = getFirstFocusable();
-            if (firstFocusable == this) {
-                return;
-            }
-
             mPrivateFlags &= ~FOCUSED;
 
             if (mParent != null) {
@@ -3790,24 +3788,9 @@
 
             onFocusChanged(false, 0, null);
             refreshDrawableState();
-
-            // The view cleared focus and invoked the callbacks, so  now is the
-            // time to give focus to the the first focusable to ensure that the
-            // gain focus is announced after clear focus.
-            if (firstFocusable != null) {
-                firstFocusable.requestFocus(FOCUS_FORWARD);
-            }
         }
     }
 
-    private View getFirstFocusable() {
-        ViewRootImpl viewRoot = getViewRootImpl();
-        if (viewRoot != null) {
-            return viewRoot.focusSearch(null, FOCUS_FORWARD);
-        }
-        return null;
-    }
-
     /**
      * Called to clear the focus of a view that is about to be removed.
      * Doesn't call clearChildFocus, which prevents this view from taking
@@ -3819,6 +3802,11 @@
 
             onFocusChanged(false, 0, null);
             refreshDrawableState();
+
+            // The view cleared focus and invoked the callbacks, so  now is the
+            // time to give focus to the the first focusable from the top to
+            // ensure that the gain focus is announced after clear focus.
+            getRootView().requestFocus(FOCUS_FORWARD);
         }
     }
 
@@ -4913,6 +4901,43 @@
     }
 
     /**
+     * Indicates whether the view is currently tracking transient state that the
+     * app should not need to concern itself with saving and restoring, but that
+     * the framework should take special note to preserve when possible.
+     *
+     * @return true if the view has transient state
+     *
+     * @hide
+     */
+    @ViewDebug.ExportedProperty(category = "layout")
+    public boolean hasTransientState() {
+        return (mPrivateFlags2 & HAS_TRANSIENT_STATE) == HAS_TRANSIENT_STATE;
+    }
+
+    /**
+     * Set whether this view is currently tracking transient state that the
+     * framework should attempt to preserve when possible.
+     *
+     * @param hasTransientState true if this view has transient state
+     *
+     * @hide
+     */
+    public void setHasTransientState(boolean hasTransientState) {
+        if (hasTransientState() == hasTransientState) return;
+
+        mPrivateFlags2 = (mPrivateFlags2 & ~HAS_TRANSIENT_STATE) |
+                (hasTransientState ? HAS_TRANSIENT_STATE : 0);
+        if (mParent != null) {
+            try {
+                mParent.childHasTransientStateChanged(this, hasTransientState);
+            } catch (AbstractMethodError e) {
+                Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                        " does not fully implement ViewParent", e);
+            }
+        }
+    }
+
+    /**
      * If this view doesn't do any drawing on its own, set this flag to
      * allow further optimizations. By default, this flag is not set on
      * View, but could be set on some View subclasses such as ViewGroup.
@@ -5023,6 +5048,10 @@
      *        the View's internal state from a previously set "pressed" state.
      */
     public void setPressed(boolean pressed) {
+        if (pressed == ((mPrivateFlags & PRESSED) == PRESSED)) {
+            return;
+        }
+
         if (pressed) {
             mPrivateFlags |= PRESSED;
         } else {
@@ -5466,12 +5495,6 @@
         return true;
     }
 
-    /** Gets the ViewAncestor, or null if not attached. */
-    /*package*/ ViewRootImpl getViewRootImpl() {
-        View root = getRootView();
-        return root != null ? (ViewRootImpl)root.getParent() : null;
-    }
-
     /**
      * Call this to try to give focus to a specific view or to one of its descendants. This is a
      * special variant of {@link #requestFocus() } that will allow views that are not focuable in
@@ -6531,8 +6554,7 @@
 
         if ((viewFlags & ENABLED_MASK) == DISABLED) {
             if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PRESSED) != 0) {
-                mPrivateFlags &= ~PRESSED;
-                refreshDrawableState();
+                setPressed(false);
             }
             // A disabled view that is clickable still consumes the touch
             // events, it just doesn't respond to them.
@@ -6564,8 +6586,7 @@
                             // showed it as pressed.  Make it show the pressed
                             // state now (before scheduling the click) to ensure
                             // the user sees it.
-                            mPrivateFlags |= PRESSED;
-                            refreshDrawableState();
+                            setPressed(true);
                        }
 
                         if (!mHasPerformedLongPress) {
@@ -6621,15 +6642,13 @@
                         postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                     } else {
                         // Not inside a scrolling container, so show the feedback right away
-                        mPrivateFlags |= PRESSED;
-                        refreshDrawableState();
+                        setPressed(true);
                         checkForLongClick(0);
                     }
                     break;
 
                 case MotionEvent.ACTION_CANCEL:
-                    mPrivateFlags &= ~PRESSED;
-                    refreshDrawableState();
+                    setPressed(false);
                     removeTapCallback();
                     break;
 
@@ -6645,9 +6664,7 @@
                             // Remove any future long press/tap checks
                             removeLongPressCallback();
 
-                            // Need to switch from pressed to not pressed
-                            mPrivateFlags &= ~PRESSED;
-                            refreshDrawableState();
+                            setPressed(false);
                         }
                     }
                     break;
@@ -6849,8 +6866,8 @@
 
         if ((changed & VISIBILITY_MASK) != 0) {
             if (mParent instanceof ViewGroup) {
-                ((ViewGroup) mParent).onChildVisibilityChanged(this, (changed & VISIBILITY_MASK),
-                        (flags & VISIBILITY_MASK));
+                ((ViewGroup) mParent).onChildVisibilityChanged(this,
+                        (changed & VISIBILITY_MASK), (flags & VISIBILITY_MASK));
                 ((View) mParent).invalidate(true);
             } else if (mParent != null) {
                 mParent.invalidateChild(this, null);
@@ -8252,6 +8269,9 @@
             throw new NullPointerException("Layout parameters cannot be null");
         }
         mLayoutParams = params;
+        if (mParent instanceof ViewGroup) {
+            ((ViewGroup) mParent).onSetLayoutParams(this, params);
+        }
         requestLayout();
     }
 
@@ -8706,6 +8726,18 @@
     }
 
     /**
+     * Gets the view root associated with the View.
+     * @return The view root, or null if none.
+     * @hide
+     */
+    public ViewRootImpl getViewRootImpl() {
+        if (mAttachInfo != null) {
+            return mAttachInfo.mViewRootImpl;
+        }
+        return null;
+    }
+
+    /**
      * <p>Causes the Runnable to be added to the message queue.
      * The runnable will be run on the user interface thread.</p>
      * 
@@ -8719,17 +8751,13 @@
      *         looper processing the message queue is exiting.
      */
     public boolean post(Runnable action) {
-        Handler handler;
-        AttachInfo attachInfo = mAttachInfo;
+        final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            handler = attachInfo.mHandler;
-        } else {
-            // Assume that post will succeed later
-            ViewRootImpl.getRunQueue().post(action);
-            return true;
+            return attachInfo.mHandler.post(action);
         }
-
-        return handler.post(action);
+        // Assume that post will succeed later
+        ViewRootImpl.getRunQueue().post(action);
+        return true;
     }
 
     /**
@@ -8752,17 +8780,13 @@
      *         occurs then the message will be dropped.
      */
     public boolean postDelayed(Runnable action, long delayMillis) {
-        Handler handler;
-        AttachInfo attachInfo = mAttachInfo;
+        final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            handler = attachInfo.mHandler;
-        } else {
-            // Assume that post will succeed later
-            ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
-            return true;
+            return attachInfo.mHandler.postDelayed(action, delayMillis);
         }
-
-        return handler.postDelayed(action, delayMillis);
+        // Assume that post will succeed later
+        ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
+        return true;
     }
 
     /**
@@ -8779,17 +8803,13 @@
      *         (for instance, if the Runnable was not in the queue already.)
      */
     public boolean removeCallbacks(Runnable action) {
-        Handler handler;
-        AttachInfo attachInfo = mAttachInfo;
+        final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            handler = attachInfo.mHandler;
+            attachInfo.mHandler.removeCallbacks(action);
         } else {
             // Assume that post will succeed later
             ViewRootImpl.getRunQueue().removeCallbacks(action);
-            return true;
         }
-
-        handler.removeCallbacks(action);
         return true;
     }
 
@@ -8838,12 +8858,9 @@
     public void postInvalidateDelayed(long delayMilliseconds) {
         // We try only with the AttachInfo because there's no point in invalidating
         // if we are not attached to our window
-        AttachInfo attachInfo = mAttachInfo;
+        final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            Message msg = Message.obtain();
-            msg.what = AttachInfo.INVALIDATE_MSG;
-            msg.obj = this;
-            attachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
+            attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
         }
     }
 
@@ -8866,7 +8883,7 @@
 
         // We try only with the AttachInfo because there's no point in invalidating
         // if we are not attached to our window
-        AttachInfo attachInfo = mAttachInfo;
+        final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
             final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire();
             info.target = this;
@@ -8875,10 +8892,7 @@
             info.right = right;
             info.bottom = bottom;
 
-            final Message msg = Message.obtain();
-            msg.what = AttachInfo.INVALIDATE_RECT_MSG;
-            msg.obj = info;
-            attachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
+            attachInfo.mViewRootImpl.dispatchInvalidateRectDelayed(info, delayMilliseconds);
         }
     }
 
@@ -9563,10 +9577,6 @@
         // Clear any previous layout direction resolution
         mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_RTL;
 
-        // Reset also TextDirection as a change into LayoutDirection may impact the selected
-        // TextDirectionHeuristic
-        resetResolvedTextDirection();
-
         // Set resolved depending on layout direction
         switch (getLayoutDirection()) {
             case LAYOUT_DIRECTION_INHERIT:
@@ -9603,14 +9613,15 @@
     }
 
     /**
-     * @hide
+     * Force padding depending on layout direction.
      */
-    protected void resolvePadding() {
+    public void resolvePadding() {
         // If the user specified the absolute padding (either with android:padding or
         // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise
         // use the default padding or the padding from the background drawable
         // (stored at this point in mPadding*)
-        switch (getResolvedLayoutDirection()) {
+        int resolvedLayoutDirection = getResolvedLayoutDirection();
+        switch (resolvedLayoutDirection) {
             case LAYOUT_DIRECTION_RTL:
                 // Start user padding override Right user padding. Otherwise, if Right user
                 // padding is not defined, use the default Right padding. If Right user padding
@@ -9646,6 +9657,18 @@
         mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
 
         recomputePadding();
+        onResolvePadding(resolvedLayoutDirection);
+    }
+
+    /**
+     * Resolve padding depending on the layout direction. Subclasses that care about
+     * padding resolution should override this method. The default implementation does
+     * nothing.
+     *
+     * @param layoutDirection the direction of the layout
+     *
+     */
+    public void onResolvePadding(int layoutDirection) {
     }
 
     /**
@@ -9672,8 +9695,10 @@
      * @hide
      */
     protected void resetResolvedLayoutDirection() {
-        // Reset the current View resolution
+        // Reset the layout direction resolution
         mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED;
+        // Reset also the text direction
+        resetResolvedTextDirection();
     }
 
     /**
@@ -9712,13 +9737,12 @@
         }
 
         if (mAttachInfo != null) {
-            mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_MSG, this);
+            mAttachInfo.mViewRootImpl.cancelInvalidate(this);
         }
 
         mCurrentAnimation = null;
 
         resetResolvedLayoutDirection();
-        resetResolvedTextDirection();
     }
 
     /**
@@ -10947,6 +10971,346 @@
     }
 
     /**
+     * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common
+     * case of an active Animation being run on the view.
+     */
+    private boolean drawAnimation(ViewGroup parent, long drawingTime,
+            Animation a, boolean scalingRequired) {
+        Transformation invalidationTransform;
+        final int flags = parent.mGroupFlags;
+        final boolean initialized = a.isInitialized();
+        if (!initialized) {
+            a.initialize(mRight - mLeft, mBottom - mTop, getWidth(), getHeight());
+            a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
+            onAnimationStart();
+        }
+
+        boolean more = a.getTransformation(drawingTime, parent.mChildTransformation, 1f);
+        if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
+            if (parent.mInvalidationTransformation == null) {
+                parent.mInvalidationTransformation = new Transformation();
+            }
+            invalidationTransform = parent.mInvalidationTransformation;
+            a.getTransformation(drawingTime, invalidationTransform, 1f);
+        } else {
+            invalidationTransform = parent.mChildTransformation;
+        }
+        if (more) {
+            if (!a.willChangeBounds()) {
+                if ((flags & (parent.FLAG_OPTIMIZE_INVALIDATE | parent.FLAG_ANIMATION_DONE)) ==
+                        parent.FLAG_OPTIMIZE_INVALIDATE) {
+                    parent.mGroupFlags |= parent.FLAG_INVALIDATE_REQUIRED;
+                } else if ((flags & parent.FLAG_INVALIDATE_REQUIRED) == 0) {
+                    // The child need to draw an animation, potentially offscreen, so
+                    // make sure we do not cancel invalidate requests
+                    parent.mPrivateFlags |= DRAW_ANIMATION;
+                    parent.invalidate(mLeft, mTop, mRight, mBottom);
+                }
+            } else {
+                if (parent.mInvalidateRegion == null) {
+                    parent.mInvalidateRegion = new RectF();
+                }
+                final RectF region = parent.mInvalidateRegion;
+                a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
+                        invalidationTransform);
+
+                // The child need to draw an animation, potentially offscreen, so
+                // make sure we do not cancel invalidate requests
+                parent.mPrivateFlags |= DRAW_ANIMATION;
+
+                final int left = mLeft + (int) region.left;
+                final int top = mTop + (int) region.top;
+                parent.invalidate(left, top, left + (int) (region.width() + .5f),
+                        top + (int) (region.height() + .5f));
+            }
+        }
+        return more;
+    }
+
+    /**
+     * This method is called by ViewGroup.drawChild() to have each child view draw itself.
+     * This draw() method is an implementation detail and is not intended to be overridden or
+     * to be called from anywhere else other than ViewGroup.drawChild().
+     */
+    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
+        boolean more = false;
+        final boolean childHasIdentityMatrix = hasIdentityMatrix();
+        final int flags = parent.mGroupFlags;
+
+        if ((flags & parent.FLAG_CLEAR_TRANSFORMATION) == parent.FLAG_CLEAR_TRANSFORMATION) {
+            parent.mChildTransformation.clear();
+            parent.mGroupFlags &= ~parent.FLAG_CLEAR_TRANSFORMATION;
+        }
+
+        Transformation transformToApply = null;
+        boolean concatMatrix = false;
+
+        boolean scalingRequired = false;
+        boolean caching;
+        int layerType = parent.mDrawLayers ? getLayerType() : LAYER_TYPE_NONE;
+
+        final boolean hardwareAccelerated = canvas.isHardwareAccelerated();
+        if ((flags & parent.FLAG_CHILDREN_DRAWN_WITH_CACHE) == parent.FLAG_CHILDREN_DRAWN_WITH_CACHE ||
+                (flags & parent.FLAG_ALWAYS_DRAWN_WITH_CACHE) == parent.FLAG_ALWAYS_DRAWN_WITH_CACHE) {
+            caching = true;
+            if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
+        } else {
+            caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
+        }
+
+        final Animation a = getAnimation();
+        if (a != null) {
+            more = drawAnimation(parent, drawingTime, a, scalingRequired);
+            concatMatrix = a.willChangeTransformationMatrix();
+            transformToApply = parent.mChildTransformation;
+        } else if ((flags & parent.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
+                parent.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
+            final boolean hasTransform =
+                    parent.getChildStaticTransformation(this, parent.mChildTransformation);
+            if (hasTransform) {
+                final int transformType = parent.mChildTransformation.getTransformationType();
+                transformToApply = transformType != Transformation.TYPE_IDENTITY ?
+                        parent.mChildTransformation : null;
+                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
+            }
+        }
+
+        concatMatrix |= !childHasIdentityMatrix;
+
+        // Sets the flag as early as possible to allow draw() implementations
+        // to call invalidate() successfully when doing animations
+        mPrivateFlags |= DRAWN;
+
+        if (!concatMatrix && canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
+                (mPrivateFlags & DRAW_ANIMATION) == 0) {
+            return more;
+        }
+
+        if (hardwareAccelerated) {
+            // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
+            // retain the flag's value temporarily in the mRecreateDisplayList flag
+            mRecreateDisplayList = (mPrivateFlags & INVALIDATED) == INVALIDATED;
+            mPrivateFlags &= ~INVALIDATED;
+        }
+
+        computeScroll();
+
+        final int sx = mScrollX;
+        final int sy = mScrollY;
+
+        DisplayList displayList = null;
+        Bitmap cache = null;
+        boolean hasDisplayList = false;
+        if (caching) {
+            if (!hardwareAccelerated) {
+                if (layerType != LAYER_TYPE_NONE) {
+                    layerType = LAYER_TYPE_SOFTWARE;
+                    buildDrawingCache(true);
+                }
+                cache = getDrawingCache(true);
+            } else {
+                switch (layerType) {
+                    case LAYER_TYPE_SOFTWARE:
+                        buildDrawingCache(true);
+                        cache = getDrawingCache(true);
+                        break;
+                    case LAYER_TYPE_NONE:
+                        // Delay getting the display list until animation-driven alpha values are
+                        // set up and possibly passed on to the view
+                        hasDisplayList = canHaveDisplayList();
+                        break;
+                }
+            }
+        }
+
+        final boolean hasNoCache = cache == null || hasDisplayList;
+        final boolean offsetForScroll = cache == null && !hasDisplayList &&
+                layerType != LAYER_TYPE_HARDWARE;
+
+        final int restoreTo = canvas.save();
+        if (offsetForScroll) {
+            canvas.translate(mLeft - sx, mTop - sy);
+        } else {
+            canvas.translate(mLeft, mTop);
+            if (scalingRequired) {
+                // mAttachInfo cannot be null, otherwise scalingRequired == false
+                final float scale = 1.0f / mAttachInfo.mApplicationScale;
+                canvas.scale(scale, scale);
+            }
+        }
+
+        float alpha = getAlpha();
+        if (transformToApply != null || alpha < 1.0f || !hasIdentityMatrix()) {
+            if (transformToApply != null || !childHasIdentityMatrix) {
+                int transX = 0;
+                int transY = 0;
+
+                if (offsetForScroll) {
+                    transX = -sx;
+                    transY = -sy;
+                }
+
+                if (transformToApply != null) {
+                    if (concatMatrix) {
+                        // Undo the scroll translation, apply the transformation matrix,
+                        // then redo the scroll translate to get the correct result.
+                        canvas.translate(-transX, -transY);
+                        canvas.concat(transformToApply.getMatrix());
+                        canvas.translate(transX, transY);
+                        parent.mGroupFlags |= parent.FLAG_CLEAR_TRANSFORMATION;
+                    }
+
+                    float transformAlpha = transformToApply.getAlpha();
+                    if (transformAlpha < 1.0f) {
+                        alpha *= transformToApply.getAlpha();
+                        parent.mGroupFlags |= parent.FLAG_CLEAR_TRANSFORMATION;
+                    }
+                }
+
+                if (!childHasIdentityMatrix) {
+                    canvas.translate(-transX, -transY);
+                    canvas.concat(getMatrix());
+                    canvas.translate(transX, transY);
+                }
+            }
+
+            if (alpha < 1.0f) {
+                parent.mGroupFlags |= parent.FLAG_CLEAR_TRANSFORMATION;
+                if (hasNoCache) {
+                    final int multipliedAlpha = (int) (255 * alpha);
+                    if (!onSetAlpha(multipliedAlpha)) {
+                        int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
+                        if ((flags & parent.FLAG_CLIP_CHILDREN) == parent.FLAG_CLIP_CHILDREN ||
+                                layerType != LAYER_TYPE_NONE) {
+                            layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
+                        }
+                        if (layerType == LAYER_TYPE_NONE) {
+                            final int scrollX = hasDisplayList ? 0 : sx;
+                            final int scrollY = hasDisplayList ? 0 : sy;
+                            canvas.saveLayerAlpha(scrollX, scrollY, scrollX + mRight - mLeft,
+                                    scrollY + mBottom - mTop, multipliedAlpha, layerFlags);
+                        }
+                    } else {
+                        // Alpha is handled by the child directly, clobber the layer's alpha
+                        mPrivateFlags |= ALPHA_SET;
+                    }
+                }
+            }
+        } else if ((mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
+            onSetAlpha(255);
+            mPrivateFlags &= ~ALPHA_SET;
+        }
+
+        if ((flags & parent.FLAG_CLIP_CHILDREN) == parent.FLAG_CLIP_CHILDREN) {
+            if (offsetForScroll) {
+                canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop));
+            } else {
+                if (!scalingRequired || cache == null) {
+                    canvas.clipRect(0, 0, mRight - mLeft, mBottom - mTop);
+                } else {
+                    canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
+                }
+            }
+        }
+
+        if (hasDisplayList) {
+            displayList = getDisplayList();
+            if (!displayList.isValid()) {
+                // Uncommon, but possible. If a view is removed from the hierarchy during the call
+                // to getDisplayList(), the display list will be marked invalid and we should not
+                // try to use it again.
+                displayList = null;
+                hasDisplayList = false;
+            }
+        }
+
+        if (hasNoCache) {
+            boolean layerRendered = false;
+            if (layerType == LAYER_TYPE_HARDWARE) {
+                final HardwareLayer layer = getHardwareLayer();
+                if (layer != null && layer.isValid()) {
+                    mLayerPaint.setAlpha((int) (alpha * 255));
+                    ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, mLayerPaint);
+                    layerRendered = true;
+                } else {
+                    final int scrollX = hasDisplayList ? 0 : sx;
+                    final int scrollY = hasDisplayList ? 0 : sy;
+                    canvas.saveLayer(scrollX, scrollY,
+                            scrollX + mRight - mLeft, scrollY + mBottom - mTop, mLayerPaint,
+                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+                }
+            }
+
+            if (!layerRendered) {
+                if (!hasDisplayList) {
+                    // Fast path for layouts with no backgrounds
+                    if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+                        if (ViewDebug.TRACE_HIERARCHY) {
+                            ViewDebug.trace(parent, ViewDebug.HierarchyTraceType.DRAW);
+                        }
+                        mPrivateFlags &= ~DIRTY_MASK;
+                        dispatchDraw(canvas);
+                    } else {
+                        draw(canvas);
+                    }
+                } else {
+                    mPrivateFlags &= ~DIRTY_MASK;
+                    ((HardwareCanvas) canvas).drawDisplayList(displayList,
+                            mRight - mLeft, mBottom - mTop, null, flags);
+                }
+            }
+        } else if (cache != null) {
+            mPrivateFlags &= ~DIRTY_MASK;
+            Paint cachePaint;
+
+            if (layerType == LAYER_TYPE_NONE) {
+                cachePaint = parent.mCachePaint;
+                if (cachePaint == null) {
+                    cachePaint = new Paint();
+                    cachePaint.setDither(false);
+                    parent.mCachePaint = cachePaint;
+                }
+                if (alpha < 1.0f) {
+                    cachePaint.setAlpha((int) (alpha * 255));
+                    parent.mGroupFlags |= parent.FLAG_ALPHA_LOWER_THAN_ONE;
+                } else if  ((flags & parent.FLAG_ALPHA_LOWER_THAN_ONE) ==
+                        parent.FLAG_ALPHA_LOWER_THAN_ONE) {
+                    cachePaint.setAlpha(255);
+                    parent.mGroupFlags &= ~parent.FLAG_ALPHA_LOWER_THAN_ONE;
+                }
+            } else {
+                cachePaint = mLayerPaint;
+                cachePaint.setAlpha((int) (alpha * 255));
+            }
+            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
+        }
+
+        canvas.restoreToCount(restoreTo);
+
+        if (a != null && !more) {
+            if (!hardwareAccelerated && !a.getFillAfter()) {
+                onSetAlpha(255);
+            }
+            parent.finishAnimatingView(this, a);
+        }
+
+        if (more && hardwareAccelerated) {
+            // invalidation is the trigger to recreate display lists, so if we're using
+            // display lists to render, force an invalidate to allow the animation to
+            // continue drawing another frame
+            parent.invalidate(true);
+            if (a.hasAlpha() && (mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
+                // alpha animations should cause the child to recreate its display list
+                invalidate(true);
+            }
+        }
+
+        mRecreateDisplayList = false;
+
+        return more;
+    }
+
+    /**
      * Manually render this view (and all of its children) to the given Canvas.
      * The view must have already done a full layout before this function is
      * called.  When implementing a view, implement
@@ -11932,8 +12296,6 @@
      * @param top the top padding in pixels
      * @param end the end padding in pixels
      * @param bottom the bottom padding in pixels
-     *
-     * @hide
      */
     public void setPaddingRelative(int start, int top, int end, int bottom) {
         mUserPaddingRelative = true;
@@ -11988,8 +12350,6 @@
      * scrollbars as well.
      *
      * @return the start padding in pixels
-     *
-     * @hide
      */
     public int getPaddingStart() {
         return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
@@ -12013,8 +12373,6 @@
      * scrollbars as well.
      *
      * @return the end padding in pixels
-     *
-     * @hide
      */
     public int getPaddingEnd() {
         return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
@@ -12028,8 +12386,6 @@
      * @attr ref android.R.styleable#View_paddingEnd
      *
      * @return true if the padding is relative or false if it is not.
-     *
-     * @hide
      */
     public boolean isPaddingRelative() {
         return mUserPaddingRelative;
@@ -12699,11 +13055,6 @@
             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
         }
 
-        if (getAccessibilityNodeProvider() != null) {
-            throw new IllegalStateException("Views with AccessibilityNodeProvider"
-                    + " can't have children.");
-        }
-
         mPrivateFlags |= FORCE_LAYOUT;
         mPrivateFlags |= INVALIDATED;
 
@@ -12734,9 +13085,9 @@
      * </p>
      *
      * <p>
-     * The actual mesurement work of a view is performed in
+     * The actual measurement work of a view is performed in
      * {@link #onMeasure(int, int)}, called by this method. Therefore, only
-     * {@link #onMeasure(int, int)} can and must be overriden by subclasses.
+     * {@link #onMeasure(int, int)} can and must be overridden by subclasses.
      * </p>
      *
      *
@@ -13761,8 +14112,6 @@
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
-     *
-     * @hide
      */
     public int getTextDirection() {
         return mTextDirection;
@@ -13779,8 +14128,6 @@
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
-     *
-     * @hide
      */
     public void setTextDirection(int textDirection) {
         if (textDirection != mTextDirection) {
@@ -13800,8 +14147,6 @@
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
-     *
-     * @hide
      */
     public int getResolvedTextDirection() {
         if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) {
@@ -13811,29 +14156,47 @@
     }
 
     /**
-     * Resolve the text direction.
-     *
-     * @hide
+     * Resolve the text direction. Will call {@link View#onResolveTextDirection()} when resolution
+     * is done.
      */
-    protected void resolveTextDirection() {
+    public void resolveTextDirection() {
+        if (mResolvedTextDirection != TEXT_DIRECTION_INHERIT) {
+            // Resolution has already been done.
+            return;
+        }
         if (mTextDirection != TEXT_DIRECTION_INHERIT) {
             mResolvedTextDirection = mTextDirection;
-            return;
-        }
-        if (mParent != null && mParent instanceof ViewGroup) {
+        } else if (mParent != null && mParent instanceof ViewGroup) {
             mResolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
-            return;
+        } else {
+            mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG;
         }
-        mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG;
+        onResolveTextDirection();
     }
 
     /**
-     * Reset resolved text direction. Will be resolved during a call to getResolvedTextDirection().
-     *
-     * @hide
+     * Called when text direction has been resolved. Subclasses that care about text direction
+     * resolution should override this method. The default implementation does nothing.
      */
-    protected void resetResolvedTextDirection() {
+    public void onResolveTextDirection() {
+    }
+
+    /**
+     * Reset resolved text direction. Text direction can be resolved with a call to
+     * getResolvedTextDirection(). Will call {@link View#onResetResolvedTextDirection()} when
+     * reset is done.
+     */
+    public void resetResolvedTextDirection() {
         mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
+        onResetResolvedTextDirection();
+    }
+
+    /**
+     * Called when text direction is reset. Subclasses that care about text direction reset should
+     * override this method and do a reset of the text direction of their children. The default
+     * implementation does nothing.
+     */
+    public void onResetResolvedTextDirection() {
     }
 
     //
@@ -14137,8 +14500,7 @@
     private final class CheckForTap implements Runnable {
         public void run() {
             mPrivateFlags &= ~PREPRESSED;
-            mPrivateFlags |= PRESSED;
-            refreshDrawableState();
+            setPressed(true);
             checkForLongClick(ViewConfiguration.getTapTimeout());
         }
     }
@@ -14658,24 +15020,17 @@
         Canvas mCanvas;
 
         /**
+         * The view root impl.
+         */
+        final ViewRootImpl mViewRootImpl;
+
+        /**
          * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This
          * handler can be used to pump events in the UI events queue.
          */
         final Handler mHandler;
 
         /**
-         * Identifier for messages requesting the view to be invalidated.
-         * Such messages should be sent to {@link #mHandler}.
-         */
-        static final int INVALIDATE_MSG = 0x1;
-
-        /**
-         * Identifier for messages requesting the view to invalidate a region.
-         * Such messages should be sent to {@link #mHandler}.
-         */
-        static final int INVALIDATE_RECT_MSG = 0x2;
-
-        /**
          * Temporary for use in computing invalidate rectangles while
          * calling up the hierarchy.
          */
@@ -14703,10 +15058,11 @@
          * @param handler the events handler the view must use
          */
         AttachInfo(IWindowSession session, IWindow window,
-                Handler handler, Callbacks effectPlayer) {
+                ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {
             mSession = session;
             mWindow = window;
             mWindowToken = window.asBinder();
+            mViewRootImpl = viewRootImpl;
             mHandler = handler;
             mRootCallbacks = effectPlayer;
         }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index c1db572..2a17845 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -375,7 +375,7 @@
     }
 
     private static BufferedWriter sHierarchyTraces;
-    private static ViewRootImpl sHierarhcyRoot;
+    private static ViewRootImpl sHierarchyRoot;
     private static String sHierarchyTracePrefix;
 
     /**
@@ -855,7 +855,7 @@
             return;
         }
 
-        if (sHierarhcyRoot != null) {
+        if (sHierarchyRoot != null) {
             throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
                 " a new trace!");
         }
@@ -874,7 +874,7 @@
             return;
         }
 
-        sHierarhcyRoot = (ViewRootImpl) view.getRootView().getParent();
+        sHierarchyRoot = view.getViewRootImpl();
     }
 
     /**
@@ -896,7 +896,7 @@
             return;
         }
 
-        if (sHierarhcyRoot == null || sHierarchyTraces == null) {
+        if (sHierarchyRoot == null || sHierarchyTraces == null) {
             throw new IllegalStateException("You must call startHierarchyTracing() before" +
                 " stopHierarchyTracing()!");
         }
@@ -921,7 +921,7 @@
             return;
         }
 
-        View view = sHierarhcyRoot.getView();
+        View view = sHierarchyRoot.getView();
         if (view instanceof ViewGroup) {
             ViewGroup group = (ViewGroup) view;
             dumpViewHierarchy(group, out, 0);
@@ -932,7 +932,7 @@
             }
         }
 
-        sHierarhcyRoot = null;
+        sHierarchyRoot = null;
     }
 
     static void dispatchCommand(View view, String command, String parameters,
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d3af618..2848e88 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -77,6 +77,7 @@
  * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
  */
 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
+    private static final String TAG = "ViewGroup";
 
     private static final boolean DBG = false;
 
@@ -103,18 +104,18 @@
      * A Transformation used when drawing children, to
      * apply on the child being drawn.
      */
-    private final Transformation mChildTransformation = new Transformation();
+    final Transformation mChildTransformation = new Transformation();
 
     /**
      * Used to track the current invalidation region.
      */
-    private RectF mInvalidateRegion;
+    RectF mInvalidateRegion;
 
     /**
      * A Transformation used to calculate a correct
      * invalidation area when the application is autoscaled.
      */
-    private Transformation mInvalidationTransformation;
+    Transformation mInvalidationTransformation;
 
     // View currently under an ongoing drag
     private View mCurrentDragView;
@@ -168,9 +169,14 @@
      */
     protected int mGroupFlags;
 
+    /**
+     * NOTE: If you change the flags below make sure to reflect the changes
+     *       the DisplayList class
+     */
+    
     // When set, ViewGroup invalidates only the child's rectangle
     // Set by default
-    private static final int FLAG_CLIP_CHILDREN = 0x1;
+    static final int FLAG_CLIP_CHILDREN = 0x1;
 
     // When set, ViewGroup excludes the padding area from the invalidate rectangle
     // Set by default
@@ -178,7 +184,7 @@
 
     // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
     // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
-    private static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
+    static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
 
     // When set, dispatchDraw() will run the layout animation and unset the flag
     private static final int FLAG_RUN_ANIMATION = 0x8;
@@ -186,7 +192,7 @@
     // When set, there is either no layout animation on the ViewGroup or the layout
     // animation is over
     // Set by default
-    private static final int FLAG_ANIMATION_DONE = 0x10;
+    static final int FLAG_ANIMATION_DONE = 0x10;
 
     // If set, this ViewGroup has padding; if unset there is no padding and we don't need
     // to clip it, even if FLAG_CLIP_TO_PADDING is set
@@ -200,10 +206,10 @@
     // layout animation; this avoid clobbering the hierarchy
     // Automatically set when the layout animation starts, depending on the animation's
     // characteristics
-    private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
+    static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
 
     // When set, the next call to drawChild() will clear mChildTransformation's matrix
-    private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
+    static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
 
     // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
     // the children's Bitmap caches if necessary
@@ -233,7 +239,7 @@
 
     // When the previous drawChild() invocation used an alpha value that was lower than
     // 1.0 and set it in mCachePaint
-    private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
+    static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
 
     /**
      * When set, this ViewGroup's drawable states also include those
@@ -244,13 +250,13 @@
     /**
      * When set, this ViewGroup tries to always draw its children using their drawing cache.
      */
-    private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
+    static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
 
     /**
      * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
      * draw its children with their drawing cache.
      */
-    private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
+    static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
 
     /**
      * When set, this group will go through its list of children to notify them of
@@ -352,7 +358,7 @@
     private static final int ARRAY_CAPACITY_INCREMENT = 12;
 
     // Used to draw cached views
-    private Paint mCachePaint;
+    Paint mCachePaint;
 
     // Used to animate add/remove changes in layout
     private LayoutTransition mTransition;
@@ -368,7 +374,11 @@
 
     // Indicates whether this container will use its children layers to draw
     @ViewDebug.ExportedProperty(category = "drawing")
-    private boolean mDrawLayers = true;
+    boolean mDrawLayers = true;
+
+    // Indicates how many of this container's child subtrees contain transient state
+    @ViewDebug.ExportedProperty(category = "layout")
+    private int mChildCountWithTransientState = 0;
 
     public ViewGroup(Context context) {
         super(context);
@@ -648,6 +658,38 @@
     }
 
     /**
+     * Called when a child view has changed whether or not it is tracking transient state.
+     *
+     * @hide
+     */
+    public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
+        final boolean oldHasTransientState = hasTransientState();
+        if (childHasTransientState) {
+            mChildCountWithTransientState++;
+        } else {
+            mChildCountWithTransientState--;
+        }
+
+        final boolean newHasTransientState = hasTransientState();
+        if (mParent != null && oldHasTransientState != newHasTransientState) {
+            try {
+                mParent.childHasTransientStateChanged(this, newHasTransientState);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, mParent.getClass().getSimpleName() +
+                        " does not fully implement ViewParent", e);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean hasTransientState() {
+        return mChildCountWithTransientState > 0 || super.hasTransientState();
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -2620,336 +2662,20 @@
      *
      * @param canvas The canvas on which to draw the child
      * @param child Who to draw
-     * @param drawingTime The time at which draw is occuring
+     * @param drawingTime The time at which draw is occurring
      * @return True if an invalidate() was issued
      */
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        boolean more = false;
+        return child.draw(canvas, this, drawingTime);
+    }
 
-        final int cl = child.mLeft;
-        final int ct = child.mTop;
-        final int cr = child.mRight;
-        final int cb = child.mBottom;
-
-        final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
-
-        final int flags = mGroupFlags;
-
-        if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
-            mChildTransformation.clear();
-            mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
+    @Override
+    public void requestLayout() {
+        if (mChildrenCount > 0 && getAccessibilityNodeProvider() != null) {
+            throw new IllegalStateException("Views with AccessibilityNodeProvider"
+                    + " can't have children.");
         }
-
-        Transformation transformToApply = null;
-        Transformation invalidationTransform;
-        final Animation a = child.getAnimation();
-        boolean concatMatrix = false;
-
-        boolean scalingRequired = false;
-        boolean caching;
-        int layerType = mDrawLayers ? child.getLayerType() : LAYER_TYPE_NONE;
-
-        final boolean hardwareAccelerated = canvas.isHardwareAccelerated();
-        if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
-                (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
-            caching = true;
-            if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
-        } else {
-            caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
-        }
-
-        if (a != null) {
-            final boolean initialized = a.isInitialized();
-            if (!initialized) {
-                a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
-                a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
-                child.onAnimationStart();
-            }
-
-            more = a.getTransformation(drawingTime, mChildTransformation, 1f);
-            if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
-                if (mInvalidationTransformation == null) {
-                    mInvalidationTransformation = new Transformation();
-                }
-                invalidationTransform = mInvalidationTransformation;
-                a.getTransformation(drawingTime, invalidationTransform, 1f);
-            } else {
-                invalidationTransform = mChildTransformation;
-            }
-            transformToApply = mChildTransformation;
-
-            concatMatrix = a.willChangeTransformationMatrix();
-
-            if (more) {
-                if (!a.willChangeBounds()) {
-                    if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
-                            FLAG_OPTIMIZE_INVALIDATE) {
-                        mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
-                    } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
-                        // The child need to draw an animation, potentially offscreen, so
-                        // make sure we do not cancel invalidate requests
-                        mPrivateFlags |= DRAW_ANIMATION;
-                        invalidate(cl, ct, cr, cb);
-                    }
-                } else {
-                    if (mInvalidateRegion == null) {
-                        mInvalidateRegion = new RectF();
-                    }
-                    final RectF region = mInvalidateRegion;
-                    a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
-
-                    // The child need to draw an animation, potentially offscreen, so
-                    // make sure we do not cancel invalidate requests
-                    mPrivateFlags |= DRAW_ANIMATION;
-
-                    final int left = cl + (int) region.left;
-                    final int top = ct + (int) region.top;
-                    invalidate(left, top, left + (int) (region.width() + .5f),
-                            top + (int) (region.height() + .5f));
-                }
-            }
-        } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
-                FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
-            final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
-            if (hasTransform) {
-                final int transformType = mChildTransformation.getTransformationType();
-                transformToApply = transformType != Transformation.TYPE_IDENTITY ?
-                        mChildTransformation : null;
-                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
-            }
-        }
-
-        concatMatrix |= !childHasIdentityMatrix;
-
-        // Sets the flag as early as possible to allow draw() implementations
-        // to call invalidate() successfully when doing animations
-        child.mPrivateFlags |= DRAWN;
-
-        if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
-                (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
-            return more;
-        }
-
-        if (hardwareAccelerated) {
-            // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
-            // retain the flag's value temporarily in the mRecreateDisplayList flag
-            child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
-            child.mPrivateFlags &= ~INVALIDATED;
-        }
-
-        child.computeScroll();
-
-        final int sx = child.mScrollX;
-        final int sy = child.mScrollY;
-
-        DisplayList displayList = null;
-        Bitmap cache = null;
-        boolean hasDisplayList = false;
-        if (caching) {
-            if (!hardwareAccelerated) {
-                if (layerType != LAYER_TYPE_NONE) {
-                    layerType = LAYER_TYPE_SOFTWARE;
-                    child.buildDrawingCache(true);
-                }
-                cache = child.getDrawingCache(true);
-            } else {
-                switch (layerType) {
-                    case LAYER_TYPE_SOFTWARE:
-                        child.buildDrawingCache(true);
-                        cache = child.getDrawingCache(true);
-                        break;
-                    case LAYER_TYPE_NONE:
-                        // Delay getting the display list until animation-driven alpha values are
-                        // set up and possibly passed on to the view
-                        hasDisplayList = child.canHaveDisplayList();
-                        break;
-                }
-            }
-        }
-
-        final boolean hasNoCache = cache == null || hasDisplayList;
-        final boolean offsetForScroll = cache == null && !hasDisplayList &&
-                layerType != LAYER_TYPE_HARDWARE;
-
-        final int restoreTo = canvas.save();
-        if (offsetForScroll) {
-            canvas.translate(cl - sx, ct - sy);
-        } else {
-            canvas.translate(cl, ct);
-            if (scalingRequired) {
-                // mAttachInfo cannot be null, otherwise scalingRequired == false
-                final float scale = 1.0f / mAttachInfo.mApplicationScale;
-                canvas.scale(scale, scale);
-            }
-        }
-
-        float alpha = child.getAlpha();
-        if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
-            if (transformToApply != null || !childHasIdentityMatrix) {
-                int transX = 0;
-                int transY = 0;
-
-                if (offsetForScroll) {
-                    transX = -sx;
-                    transY = -sy;
-                }
-
-                if (transformToApply != null) {
-                    if (concatMatrix) {
-                        // Undo the scroll translation, apply the transformation matrix,
-                        // then redo the scroll translate to get the correct result.
-                        canvas.translate(-transX, -transY);
-                        canvas.concat(transformToApply.getMatrix());
-                        canvas.translate(transX, transY);
-                        mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
-                    }
-
-                    float transformAlpha = transformToApply.getAlpha();
-                    if (transformAlpha < 1.0f) {
-                        alpha *= transformToApply.getAlpha();
-                        mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
-                    }
-                }
-
-                if (!childHasIdentityMatrix) {
-                    canvas.translate(-transX, -transY);
-                    canvas.concat(child.getMatrix());
-                    canvas.translate(transX, transY);
-                }
-            }
-
-            if (alpha < 1.0f) {
-                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
-                if (hasNoCache) {
-                    final int multipliedAlpha = (int) (255 * alpha);
-                    if (!child.onSetAlpha(multipliedAlpha)) {
-                        int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
-                        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN ||
-                                layerType != LAYER_TYPE_NONE) {
-                            layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
-                        }
-                        if (layerType == LAYER_TYPE_NONE) {
-                            final int scrollX = hasDisplayList ? 0 : sx;
-                            final int scrollY = hasDisplayList ? 0 : sy;
-                            canvas.saveLayerAlpha(scrollX, scrollY, scrollX + cr - cl,
-                                    scrollY + cb - ct, multipliedAlpha, layerFlags);
-                        }
-                    } else {
-                        // Alpha is handled by the child directly, clobber the layer's alpha
-                        child.mPrivateFlags |= ALPHA_SET;
-                    }
-                }
-            }
-        } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
-            child.onSetAlpha(255);
-            child.mPrivateFlags &= ~ALPHA_SET;
-        }
-
-        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
-            if (offsetForScroll) {
-                canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
-            } else {
-                if (!scalingRequired || cache == null) {
-                    canvas.clipRect(0, 0, cr - cl, cb - ct);
-                } else {
-                    canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
-                }
-            }
-        }
-
-        if (hasDisplayList) {
-            displayList = child.getDisplayList();
-            if (!displayList.isValid()) {
-                // Uncommon, but possible. If a view is removed from the hierarchy during the call
-                // to getDisplayList(), the display list will be marked invalid and we should not
-                // try to use it again.
-                displayList = null;
-                hasDisplayList = false;
-            }
-        }
-
-        if (hasNoCache) {
-            boolean layerRendered = false;
-            if (layerType == LAYER_TYPE_HARDWARE) {
-                final HardwareLayer layer = child.getHardwareLayer();
-                if (layer != null && layer.isValid()) {
-                    child.mLayerPaint.setAlpha((int) (alpha * 255));                    
-                    ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, child.mLayerPaint);
-                    layerRendered = true;
-                } else {
-                    final int scrollX = hasDisplayList ? 0 : sx;
-                    final int scrollY = hasDisplayList ? 0 : sy;                    
-                    canvas.saveLayer(scrollX, scrollY,
-                            scrollX + cr - cl, scrollY + cb - ct, child.mLayerPaint,
-                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
-                }
-            }
-
-            if (!layerRendered) {
-                if (!hasDisplayList) {
-                    // Fast path for layouts with no backgrounds
-                    if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
-                        if (ViewDebug.TRACE_HIERARCHY) {
-                            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
-                        }
-                        child.mPrivateFlags &= ~DIRTY_MASK;
-                        child.dispatchDraw(canvas);
-                    } else {
-                        child.draw(canvas);
-                    }
-                } else {
-                    child.mPrivateFlags &= ~DIRTY_MASK;
-                    ((HardwareCanvas) canvas).drawDisplayList(displayList, cr - cl, cb - ct, null);
-                }
-            }
-        } else if (cache != null) {
-            child.mPrivateFlags &= ~DIRTY_MASK;
-            Paint cachePaint;
-
-            if (layerType == LAYER_TYPE_NONE) {
-                cachePaint = mCachePaint;
-                if (cachePaint == null) {
-                    cachePaint = new Paint();
-                    cachePaint.setDither(false);
-                    mCachePaint = cachePaint;
-                }
-                if (alpha < 1.0f) {
-                    cachePaint.setAlpha((int) (alpha * 255));
-                    mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
-                } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
-                    cachePaint.setAlpha(255);
-                    mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
-                }
-            } else {
-                cachePaint = child.mLayerPaint;
-                cachePaint.setAlpha((int) (alpha * 255));
-            }
-            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
-        }
-
-        canvas.restoreToCount(restoreTo);
-
-        if (a != null && !more) {
-            if (!hardwareAccelerated && !a.getFillAfter()) {
-                child.onSetAlpha(255);
-            }
-            finishAnimatingView(child, a);
-        }
-
-        if (more && hardwareAccelerated) {
-            // invalidation is the trigger to recreate display lists, so if we're using
-            // display lists to render, force an invalidate to allow the animation to
-            // continue drawing another frame
-            invalidate(true);
-            if (a.hasAlpha() && (child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
-                // alpha animations should cause the child to recreate its display list
-                child.invalidate(true);
-            }
-        }
-
-        child.mRecreateDisplayList = false;
-
-        return more;
+        super.requestLayout();
     }
 
     /**
@@ -3419,6 +3145,10 @@
         if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
             mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
         }
+
+        if (child.hasTransientState()) {
+            childHasTransientStateChanged(child, true);
+        }
     }
 
     private void addInArray(View child, int index) {
@@ -3615,6 +3345,10 @@
            view.dispatchDetachedFromWindow();
         }
 
+        if (view.hasTransientState()) {
+            childHasTransientStateChanged(view, false);
+        }
+
         onViewRemoved(view);
 
         needGlobalAttributesUpdate(false);
@@ -3686,6 +3420,10 @@
                view.dispatchDetachedFromWindow();
             }
 
+            if (view.hasTransientState()) {
+                childHasTransientStateChanged(view, false);
+            }
+
             needGlobalAttributesUpdate(false);
 
             onViewRemoved(view);
@@ -3751,6 +3489,10 @@
                view.dispatchDetachedFromWindow();
             }
 
+            if (view.hasTransientState()) {
+                childHasTransientStateChanged(view, false);
+            }
+
             onViewRemoved(view);
 
             view.mParent = null;
@@ -3791,6 +3533,10 @@
             child.dispatchDetachedFromWindow();
         }
 
+        if (child.hasTransientState()) {
+            childHasTransientStateChanged(child, false);
+        }
+
         onViewRemoved(child);
     }
 
@@ -3929,130 +3675,136 @@
             // through
             final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
 
-            if (dirty == null) {
-                if (child.mLayerType != LAYER_TYPE_NONE) {
-                    mPrivateFlags |= INVALIDATED;
-                    mPrivateFlags &= ~DRAWING_CACHE_VALID;
-                    child.mLocalDirtyRect.setEmpty();
-                }
-                do {
-                    View view = null;
-                    if (parent instanceof View) {
-                        view = (View) parent;
-                        if (view.mLayerType != LAYER_TYPE_NONE) {
-                            view.mLocalDirtyRect.setEmpty();
-                            if (view.getParent() instanceof View) {
-                                final View grandParent = (View) view.getParent();
-                                grandParent.mPrivateFlags |= INVALIDATED;
-                                grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
+            //noinspection PointlessBooleanExpression
+            if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
+                if (dirty == null) {
+                    if (child.mLayerType != LAYER_TYPE_NONE) {
+                        mPrivateFlags |= INVALIDATED;
+                        mPrivateFlags &= ~DRAWING_CACHE_VALID;
+                        child.mLocalDirtyRect.setEmpty();
+                    }
+                    do {
+                        View view = null;
+                        if (parent instanceof View) {
+                            view = (View) parent;
+                            if (view.mLayerType != LAYER_TYPE_NONE) {
+                                view.mLocalDirtyRect.setEmpty();
+                                if (view.getParent() instanceof View) {
+                                    final View grandParent = (View) view.getParent();
+                                    grandParent.mPrivateFlags |= INVALIDATED;
+                                    grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
+                                }
+                            }
+                            if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
+                                // already marked dirty - we're done
+                                break;
                             }
                         }
-                        if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
-                            // already marked dirty - we're done
-                            break;
+    
+                        if (drawAnimation) {
+                            if (view != null) {
+                                view.mPrivateFlags |= DRAW_ANIMATION;
+                            } else if (parent instanceof ViewRootImpl) {
+                                ((ViewRootImpl) parent).mIsAnimating = true;
+                            }
                         }
-                    }
-
-                    if (drawAnimation) {
-                        if (view != null) {
-                            view.mPrivateFlags |= DRAW_ANIMATION;
-                        } else if (parent instanceof ViewRootImpl) {
-                            ((ViewRootImpl) parent).mIsAnimating = true;
-                        }
-                    }
-
-                    if (parent instanceof ViewRootImpl) {
-                        ((ViewRootImpl) parent).invalidate();
-                        parent = null;
-                    } else if (view != null) {
-                        if ((view.mPrivateFlags & DRAWN) == DRAWN ||
-                                (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
-                            view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
-                            view.mPrivateFlags |= DIRTY;
-                            parent = view.mParent;
-                        } else {
+    
+                        if (parent instanceof ViewRootImpl) {
+                            ((ViewRootImpl) parent).invalidate();
                             parent = null;
+                        } else if (view != null) {
+                            if ((view.mPrivateFlags & DRAWN) == DRAWN ||
+                                    (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
+                                view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
+                                view.mPrivateFlags |= DIRTY;
+                                parent = view.mParent;
+                            } else {
+                                parent = null;
+                            }
                         }
-                    }
-                } while (parent != null);
-            } else {
-                // Check whether the child that requests the invalidate is fully opaque
-                // Views being animated or transformed are not considered opaque because we may
-                // be invalidating their old position and need the parent to paint behind them.
-                Matrix childMatrix = child.getMatrix();
-                final boolean isOpaque = child.isOpaque() && !drawAnimation &&
-                        child.getAnimation() == null && childMatrix.isIdentity();
-                // Mark the child as dirty, using the appropriate flag
-                // Make sure we do not set both flags at the same time
-                int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
-
-                if (child.mLayerType != LAYER_TYPE_NONE) {
-                    mPrivateFlags |= INVALIDATED;
-                    mPrivateFlags &= ~DRAWING_CACHE_VALID;
-                    child.mLocalDirtyRect.union(dirty);
+                    } while (parent != null);
                 }
 
-                final int[] location = attachInfo.mInvalidateChildLocation;
-                location[CHILD_LEFT_INDEX] = child.mLeft;
-                location[CHILD_TOP_INDEX] = child.mTop;
-                if (!childMatrix.isIdentity()) {
-                    RectF boundingRect = attachInfo.mTmpTransformRect;
-                    boundingRect.set(dirty);
-                    //boundingRect.inset(-0.5f, -0.5f);
-                    childMatrix.mapRect(boundingRect);
-                    dirty.set((int) (boundingRect.left - 0.5f),
-                            (int) (boundingRect.top - 0.5f),
-                            (int) (boundingRect.right + 0.5f),
-                            (int) (boundingRect.bottom + 0.5f));
-                }
-
-                do {
-                    View view = null;
-                    if (parent instanceof View) {
-                        view = (View) parent;
-                        if (view.mLayerType != LAYER_TYPE_NONE &&
-                                view.getParent() instanceof View) {
-                            final View grandParent = (View) view.getParent();
-                            grandParent.mPrivateFlags |= INVALIDATED;
-                            grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
-                        }
-                    }
-
-                    if (drawAnimation) {
-                        if (view != null) {
-                            view.mPrivateFlags |= DRAW_ANIMATION;
-                        } else if (parent instanceof ViewRootImpl) {
-                            ((ViewRootImpl) parent).mIsAnimating = true;
-                        }
-                    }
-
-                    // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
-                    // flag coming from the child that initiated the invalidate
-                    if (view != null) {
-                        if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
-                                view.getSolidColor() == 0) {
-                            opaqueFlag = DIRTY;
-                        }
-                        if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
-                            view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
-                        }
-                    }
-
-                    parent = parent.invalidateChildInParent(location, dirty);
-                    if (view != null) {
-                        // Account for transform on current parent
-                        Matrix m = view.getMatrix();
-                        if (!m.isIdentity()) {
-                            RectF boundingRect = attachInfo.mTmpTransformRect;
-                            boundingRect.set(dirty);
-                            m.mapRect(boundingRect);
-                            dirty.set((int) boundingRect.left, (int) boundingRect.top,
-                                    (int) (boundingRect.right + 0.5f),
-                                    (int) (boundingRect.bottom + 0.5f));
-                        }
-                    }
-                } while (parent != null);
+                return;
             }
+
+            // Check whether the child that requests the invalidate is fully opaque
+            // Views being animated or transformed are not considered opaque because we may
+            // be invalidating their old position and need the parent to paint behind them.
+            Matrix childMatrix = child.getMatrix();
+            final boolean isOpaque = child.isOpaque() && !drawAnimation &&
+                    child.getAnimation() == null && childMatrix.isIdentity();
+            // Mark the child as dirty, using the appropriate flag
+            // Make sure we do not set both flags at the same time
+            int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
+
+            if (child.mLayerType != LAYER_TYPE_NONE) {
+                mPrivateFlags |= INVALIDATED;
+                mPrivateFlags &= ~DRAWING_CACHE_VALID;
+                child.mLocalDirtyRect.union(dirty);
+            }
+
+            final int[] location = attachInfo.mInvalidateChildLocation;
+            location[CHILD_LEFT_INDEX] = child.mLeft;
+            location[CHILD_TOP_INDEX] = child.mTop;
+            if (!childMatrix.isIdentity()) {
+                RectF boundingRect = attachInfo.mTmpTransformRect;
+                boundingRect.set(dirty);
+                //boundingRect.inset(-0.5f, -0.5f);
+                childMatrix.mapRect(boundingRect);
+                dirty.set((int) (boundingRect.left - 0.5f),
+                        (int) (boundingRect.top - 0.5f),
+                        (int) (boundingRect.right + 0.5f),
+                        (int) (boundingRect.bottom + 0.5f));
+            }
+
+            do {
+                View view = null;
+                if (parent instanceof View) {
+                    view = (View) parent;
+                    if (view.mLayerType != LAYER_TYPE_NONE &&
+                            view.getParent() instanceof View) {
+                        final View grandParent = (View) view.getParent();
+                        grandParent.mPrivateFlags |= INVALIDATED;
+                        grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
+                    }
+                }
+
+                if (drawAnimation) {
+                    if (view != null) {
+                        view.mPrivateFlags |= DRAW_ANIMATION;
+                    } else if (parent instanceof ViewRootImpl) {
+                        ((ViewRootImpl) parent).mIsAnimating = true;
+                    }
+                }
+
+                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
+                // flag coming from the child that initiated the invalidate
+                if (view != null) {
+                    if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
+                            view.getSolidColor() == 0) {
+                        opaqueFlag = DIRTY;
+                    }
+                    if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
+                        view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
+                    }
+                }
+
+                parent = parent.invalidateChildInParent(location, dirty);
+                if (view != null) {
+                    // Account for transform on current parent
+                    Matrix m = view.getMatrix();
+                    if (!m.isIdentity()) {
+                        RectF boundingRect = attachInfo.mTmpTransformRect;
+                        boundingRect.set(dirty);
+                        m.mapRect(boundingRect);
+                        dirty.set((int) (boundingRect.left - 0.5f),
+                                (int) (boundingRect.top - 0.5f),
+                                (int) (boundingRect.right + 0.5f),
+                                (int) (boundingRect.bottom + 0.5f));
+                    }
+                }
+            } while (parent != null);
         }
     }
 
@@ -4854,7 +4606,7 @@
      * @param view The view whose animation has finished
      * @param animation The animation, cannot be null
      */
-    private void finishAnimatingView(final View view, Animation animation) {
+    void finishAnimatingView(final View view, Animation animation) {
         final ArrayList<View> disappearingChildren = mDisappearingChildren;
         if (disappearingChildren != null) {
             if (disappearingChildren.contains(view)) {
@@ -5177,9 +4929,7 @@
     }
 
     @Override
-    protected void resetResolvedTextDirection() {
-        super.resetResolvedTextDirection();
-
+    public void onResetResolvedTextDirection() {
         // Take care of resetting the children resolution too
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
@@ -5203,6 +4953,10 @@
         return true;
     }
 
+    /** @hide */
+    protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
+    }
+
     /**
      * LayoutParams are used by views to tell their parents how they want to be
      * laid out. See
@@ -5411,49 +5165,52 @@
      */
     public static class MarginLayoutParams extends ViewGroup.LayoutParams {
         /**
-         * The left margin in pixels of the child. Whenever this value is changed, a call to
-         * {@link android.view.View#requestLayout()} needs to be done.
+         * The left margin in pixels of the child.
+         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
+         * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
         public int leftMargin;
 
         /**
-         * The top margin in pixels of the child. Whenever this value is changed, a call to
-         * {@link android.view.View#requestLayout()} needs to be done.
+         * The top margin in pixels of the child.
+         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
+         * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
         public int topMargin;
 
         /**
-         * The right margin in pixels of the child. Whenever this value is changed, a call to
-         * {@link android.view.View#requestLayout()} needs to be done.
+         * The right margin in pixels of the child.
+         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
+         * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
         public int rightMargin;
 
         /**
-         * The bottom margin in pixels of the child. Whenever this value is changed, a call to
-         * {@link android.view.View#requestLayout()} needs to be done.
+         * The bottom margin in pixels of the child.
+         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
+         * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
         public int bottomMargin;
 
         /**
          * The start margin in pixels of the child.
-         *
-         * @hide
-         *
+         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
+         * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
-        protected int startMargin = DEFAULT_RELATIVE;
+        public int startMargin = DEFAULT_RELATIVE;
 
         /**
          * The end margin in pixels of the child.
-         *
-         * @hide
+         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
+         * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
-        protected int endMargin = DEFAULT_RELATIVE;
+        public int endMargin = DEFAULT_RELATIVE;
 
         /**
          * The default start and end margin.
@@ -5585,8 +5342,6 @@
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
          *
          * @return the start margin in pixels.
-         *
-         * @hide
          */
         public int getMarginStart() {
             return startMargin;
@@ -5598,8 +5353,6 @@
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
          *
          * @return the end margin in pixels.
-         *
-         * @hide
          */
         public int getMarginEnd() {
             return endMargin;
@@ -5612,8 +5365,6 @@
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
          *
          * @return true if either marginStart or marginEnd has been set
-         *
-         * @hide
          */
         public boolean isMarginRelative() {
             return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE);
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 873d4bb..8395f1b 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -261,4 +261,14 @@
      * @return True if the event was sent.
      */
     public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event);
+
+    /**
+     * Called when a child view now has or no longer is tracking transient state.
+     *
+     * @param child Child view whose state has changed
+     * @param hasTransientState true if this child has transient state
+     *
+     * @hide
+     */
+    public void childHasTransientStateChanged(View child, boolean hasTransientState);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cbf4b5a..e0d0763 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -54,13 +54,14 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.LongSparseArray;
 import android.util.Pool;
 import android.util.Poolable;
 import android.util.PoolableManager;
 import android.util.Pools;
 import android.util.Slog;
+import android.util.SparseLongArray;
 import android.util.TypedValue;
+import android.view.View.AttachInfo;
 import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
@@ -86,7 +87,9 @@
 import java.io.OutputStream;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -96,9 +99,8 @@
  * {@hide}
  */
 @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
-public final class ViewRootImpl extends Handler implements ViewParent,
-        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks,
-        Choreographer.OnDrawListener {
+public final class ViewRootImpl implements ViewParent,
+        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
     private static final String TAG = "ViewRootImpl";
     private static final boolean DBG = false;
     private static final boolean LOCAL_LOGV = false;
@@ -213,6 +215,7 @@
     final Rect mVisRect; // used to retrieve visible rect of focused view.
 
     boolean mTraversalScheduled;
+    int mTraversalBarrier;
     long mLastTraversalFinishedTimeNanos;
     long mLastDrawFinishedTimeNanos;
     boolean mWillDrawSoon;
@@ -308,7 +311,7 @@
 
     SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
 
-    AccessibilityPrefetchStrategy mAccessibilityPrefetchStrategy;
+    AccessibilityNodePrefetcher mAccessibilityNodePrefetcher;
 
     private final int mDensity;
 
@@ -380,7 +383,7 @@
             new AccessibilityInteractionConnectionManager();
         mAccessibilityManager.addAccessibilityStateChangeListener(
                 mAccessibilityInteractionConnectionManager);
-        mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
+        mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, mHandler, this);
         mViewConfiguration = ViewConfiguration.get(context);
         mDensity = context.getResources().getDisplayMetrics().densityDpi;
         mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
@@ -463,8 +466,6 @@
     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
         synchronized (this) {
             if (mView == null) {
-                mChoreographer.addOnDrawListener(this);
-
                 mView = view;
                 mFallbackEventHandler.setView(view);
                 mWindowAttributes.copyFrom(attrs);
@@ -772,19 +773,29 @@
         return mLayoutRequested;
     }
 
+    void invalidate() {
+        mDirty.set(0, 0, mWidth, mHeight);
+        scheduleTraversals();
+    }
+
     public void invalidateChild(View child, Rect dirty) {
+        invalidateChildInParent(null, dirty);
+    }
+
+    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
         checkThread();
         if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
+
         if (dirty == null) {
-            // Fast invalidation for GL-enabled applications; GL must redraw everything
             invalidate();
-            return;
+            return null;
         }
+
         if (mCurScrollY != 0 || mTranslator != null) {
             mTempRect.set(dirty);
             dirty = mTempRect;
             if (mCurScrollY != 0) {
-               dirty.offset(0, -mCurScrollY);
+                dirty.offset(0, -mCurScrollY);
             }
             if (mTranslator != null) {
                 mTranslator.translateRectInAppWindowToScreen(dirty);
@@ -793,19 +804,24 @@
                 dirty.inset(-1, -1);
             }
         }
-        if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
+
+        final Rect localDirty = mDirty;
+        if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
             mAttachInfo.mSetIgnoreDirtyState = true;
             mAttachInfo.mIgnoreDirtyState = true;
         }
-        mDirty.union(dirty);
+
+        // Add the new dirty rect to the current one
+        localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
+        // Intersect with the bounds of the window to skip
+        // updates that lie outside of the visible region
+        localDirty.intersect(0, 0, mWidth, mHeight);
+
         if (!mWillDrawSoon) {
             scheduleTraversals();
         }
-    }
-    
-    void invalidate() {
-        mDirty.set(0, 0, mWidth, mHeight);
-        scheduleTraversals();
+
+        return null;
     }
 
     void setStopped(boolean stopped) {
@@ -816,13 +832,8 @@
             }
         }
     }
-    
-    public ViewParent getParent() {
-        return null;
-    }
 
-    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
-        invalidateChild(null, dirty);
+    public ViewParent getParent() {
         return null;
     }
 
@@ -841,18 +852,43 @@
     public void scheduleTraversals() {
         if (!mTraversalScheduled) {
             mTraversalScheduled = true;
-            mChoreographer.scheduleDraw();
+            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
+            scheduleFrame();
         }
     }
 
     public void unscheduleTraversals() {
-        mTraversalScheduled = false;
-    }
-
-    @Override
-    public void onDraw() {
         if (mTraversalScheduled) {
             mTraversalScheduled = false;
+            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
+        }
+    }
+
+    void scheduleFrame() {
+        if (!mFrameScheduled) {
+            mFrameScheduled = true;
+            mChoreographer.postDrawCallback(mFrameRunnable);
+        }
+    }
+
+    void unscheduleFrame() {
+        unscheduleTraversals();
+
+        if (mFrameScheduled) {
+            mFrameScheduled = false;
+            mChoreographer.removeDrawCallback(mFrameRunnable);
+        }
+    }
+
+    void doFrame() {
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.consumeBatchedInputEvents();
+        }
+        doProcessInputEvents();
+
+        if (mTraversalScheduled) {
+            mTraversalScheduled = false;
+            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
             doTraversal();
         }
     }
@@ -891,8 +927,6 @@
     }
 
     private void doTraversal() {
-        doProcessInputEvents();
-
         if (mProfile) {
             Debug.startMethodTracing("ViewAncestor");
         }
@@ -1916,7 +1950,7 @@
                 sFirstDrawComplete = true;
                 final int count = sFirstDrawHandlers.size();
                 for (int i = 0; i< count; i++) {
-                    post(sFirstDrawHandlers.get(i));
+                    mHandler.post(sFirstDrawHandlers.get(i));
                 }
             }
         }
@@ -2373,7 +2407,7 @@
             mInputChannel = null;
         }
 
-        mChoreographer.removeOnDrawListener(this);
+        unscheduleFrame();
     }
 
     void updateConfiguration(Configuration config, boolean force) {
@@ -2428,283 +2462,289 @@
         }
     }
 
-    public final static int DIE = 1001;
-    public final static int RESIZED = 1002;
-    public final static int RESIZED_REPORT = 1003;
-    public final static int WINDOW_FOCUS_CHANGED = 1004;
-    public final static int DISPATCH_KEY = 1005;
-    public final static int DISPATCH_APP_VISIBILITY = 1008;
-    public final static int DISPATCH_GET_NEW_SURFACE = 1009;
-    public final static int IME_FINISHED_EVENT = 1010;
-    public final static int DISPATCH_KEY_FROM_IME = 1011;
-    public final static int FINISH_INPUT_CONNECTION = 1012;
-    public final static int CHECK_FOCUS = 1013;
-    public final static int CLOSE_SYSTEM_DIALOGS = 1014;
-    public final static int DISPATCH_DRAG_EVENT = 1015;
-    public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016;
-    public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017;
-    public final static int DISPATCH_GENERIC_MOTION = 1018;
-    public final static int UPDATE_CONFIGURATION = 1019;
-    public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1020;
-    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021;
-    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022;
-    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 1023;
-    public final static int DO_PROCESS_INPUT_EVENTS = 1024;
+    private final static int MSG_INVALIDATE = 1;
+    private final static int MSG_INVALIDATE_RECT = 2;
+    private final static int MSG_DIE = 3;
+    private final static int MSG_RESIZED = 4;
+    private final static int MSG_RESIZED_REPORT = 5;
+    private final static int MSG_WINDOW_FOCUS_CHANGED = 6;
+    private final static int MSG_DISPATCH_KEY = 7;
+    private final static int MSG_DISPATCH_APP_VISIBILITY = 8;
+    private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9;
+    private final static int MSG_IME_FINISHED_EVENT = 10;
+    private final static int MSG_DISPATCH_KEY_FROM_IME = 11;
+    private final static int MSG_FINISH_INPUT_CONNECTION = 12;
+    private final static int MSG_CHECK_FOCUS = 13;
+    private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14;
+    private final static int MSG_DISPATCH_DRAG_EVENT = 15;
+    private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16;
+    private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17;
+    private final static int MSG_UPDATE_CONFIGURATION = 18;
+    private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 19;
+    private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 20;
+    private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 21;
+    private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 22;
+    private final static int MSG_PROCESS_INPUT_EVENTS = 23;
 
-    @Override
-    public String getMessageName(Message message) {
-        switch (message.what) {
-            case DIE:
-                return "DIE";
-            case RESIZED:
-                return "RESIZED";
-            case RESIZED_REPORT:
-                return "RESIZED_REPORT";
-            case WINDOW_FOCUS_CHANGED:
-                return "WINDOW_FOCUS_CHANGED";
-            case DISPATCH_KEY:
-                return "DISPATCH_KEY";
-            case DISPATCH_APP_VISIBILITY:
-                return "DISPATCH_APP_VISIBILITY";
-            case DISPATCH_GET_NEW_SURFACE:
-                return "DISPATCH_GET_NEW_SURFACE";
-            case IME_FINISHED_EVENT:
-                return "IME_FINISHED_EVENT";
-            case DISPATCH_KEY_FROM_IME:
-                return "DISPATCH_KEY_FROM_IME";
-            case FINISH_INPUT_CONNECTION:
-                return "FINISH_INPUT_CONNECTION";
-            case CHECK_FOCUS:
-                return "CHECK_FOCUS";
-            case CLOSE_SYSTEM_DIALOGS:
-                return "CLOSE_SYSTEM_DIALOGS";
-            case DISPATCH_DRAG_EVENT:
-                return "DISPATCH_DRAG_EVENT";
-            case DISPATCH_DRAG_LOCATION_EVENT:
-                return "DISPATCH_DRAG_LOCATION_EVENT";
-            case DISPATCH_SYSTEM_UI_VISIBILITY:
-                return "DISPATCH_SYSTEM_UI_VISIBILITY";
-            case DISPATCH_GENERIC_MOTION:
-                return "DISPATCH_GENERIC_MOTION";
-            case UPDATE_CONFIGURATION:
-                return "UPDATE_CONFIGURATION";
-            case DO_PERFORM_ACCESSIBILITY_ACTION:
-                return "DO_PERFORM_ACCESSIBILITY_ACTION";
-            case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
-                return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
-            case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
-                return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
-            case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
-                return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
-            case DO_PROCESS_INPUT_EVENTS:
-                return "DO_PROCESS_INPUT_EVENTS";
+    final class ViewRootHandler extends Handler {
+        @Override
+        public String getMessageName(Message message) {
+            switch (message.what) {
+                case MSG_INVALIDATE:
+                    return "MSG_INVALIDATE";
+                case MSG_INVALIDATE_RECT:
+                    return "MSG_INVALIDATE_RECT";
+                case MSG_DIE:
+                    return "MSG_DIE";
+                case MSG_RESIZED:
+                    return "MSG_RESIZED";
+                case MSG_RESIZED_REPORT:
+                    return "MSG_RESIZED_REPORT";
+                case MSG_WINDOW_FOCUS_CHANGED:
+                    return "MSG_WINDOW_FOCUS_CHANGED";
+                case MSG_DISPATCH_KEY:
+                    return "MSG_DISPATCH_KEY";
+                case MSG_DISPATCH_APP_VISIBILITY:
+                    return "MSG_DISPATCH_APP_VISIBILITY";
+                case MSG_DISPATCH_GET_NEW_SURFACE:
+                    return "MSG_DISPATCH_GET_NEW_SURFACE";
+                case MSG_IME_FINISHED_EVENT:
+                    return "MSG_IME_FINISHED_EVENT";
+                case MSG_DISPATCH_KEY_FROM_IME:
+                    return "MSG_DISPATCH_KEY_FROM_IME";
+                case MSG_FINISH_INPUT_CONNECTION:
+                    return "MSG_FINISH_INPUT_CONNECTION";
+                case MSG_CHECK_FOCUS:
+                    return "MSG_CHECK_FOCUS";
+                case MSG_CLOSE_SYSTEM_DIALOGS:
+                    return "MSG_CLOSE_SYSTEM_DIALOGS";
+                case MSG_DISPATCH_DRAG_EVENT:
+                    return "MSG_DISPATCH_DRAG_EVENT";
+                case MSG_DISPATCH_DRAG_LOCATION_EVENT:
+                    return "MSG_DISPATCH_DRAG_LOCATION_EVENT";
+                case MSG_DISPATCH_SYSTEM_UI_VISIBILITY:
+                    return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY";
+                case MSG_UPDATE_CONFIGURATION:
+                    return "MSG_UPDATE_CONFIGURATION";
+                case MSG_PERFORM_ACCESSIBILITY_ACTION:
+                    return "MSG_PERFORM_ACCESSIBILITY_ACTION";
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
+                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
+                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
+                case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
+                    return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
+                case MSG_PROCESS_INPUT_EVENTS:
+                    return "MSG_PROCESS_INPUT_EVENTS";
+            }
+            return super.getMessageName(message);
         }
-        return super.getMessageName(message);
-    }
 
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-        case View.AttachInfo.INVALIDATE_MSG:
-            ((View) msg.obj).invalidate();
-            break;
-        case View.AttachInfo.INVALIDATE_RECT_MSG:
-            final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
-            info.target.invalidate(info.left, info.top, info.right, info.bottom);
-            info.release();
-            break;
-        case IME_FINISHED_EVENT:
-            handleImeFinishedEvent(msg.arg1, msg.arg2 != 0);
-            break;
-        case DO_PROCESS_INPUT_EVENTS:
-            mProcessInputEventsScheduled = false;
-            doProcessInputEvents();
-            break;
-        case DISPATCH_APP_VISIBILITY:
-            handleAppVisibility(msg.arg1 != 0);
-            break;
-        case DISPATCH_GET_NEW_SURFACE:
-            handleGetNewSurface();
-            break;
-        case RESIZED:
-            ResizedInfo ri = (ResizedInfo)msg.obj;
-
-            if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
-                    && mPendingContentInsets.equals(ri.coveredInsets)
-                    && mPendingVisibleInsets.equals(ri.visibleInsets)
-                    && ((ResizedInfo)msg.obj).newConfig == null) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MSG_INVALIDATE:
+                ((View) msg.obj).invalidate();
                 break;
-            }
-            // fall through...
-        case RESIZED_REPORT:
-            if (mAdded) {
-                Configuration config = ((ResizedInfo)msg.obj).newConfig;
-                if (config != null) {
-                    updateConfiguration(config, false);
-                }
-                mWinFrame.left = 0;
-                mWinFrame.right = msg.arg1;
-                mWinFrame.top = 0;
-                mWinFrame.bottom = msg.arg2;
-                mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets);
-                mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets);
-                if (msg.what == RESIZED_REPORT) {
-                    mReportNextDraw = true;
-                }
+            case MSG_INVALIDATE_RECT:
+                final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
+                info.target.invalidate(info.left, info.top, info.right, info.bottom);
+                info.release();
+                break;
+            case MSG_IME_FINISHED_EVENT:
+                handleImeFinishedEvent(msg.arg1, msg.arg2 != 0);
+                break;
+            case MSG_PROCESS_INPUT_EVENTS:
+                mProcessInputEventsScheduled = false;
+                doProcessInputEvents();
+                break;
+            case MSG_DISPATCH_APP_VISIBILITY:
+                handleAppVisibility(msg.arg1 != 0);
+                break;
+            case MSG_DISPATCH_GET_NEW_SURFACE:
+                handleGetNewSurface();
+                break;
+            case MSG_RESIZED:
+                ResizedInfo ri = (ResizedInfo)msg.obj;
 
-                if (mView != null) {
-                    forceLayout(mView);
+                if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
+                        && mPendingContentInsets.equals(ri.coveredInsets)
+                        && mPendingVisibleInsets.equals(ri.visibleInsets)
+                        && ((ResizedInfo)msg.obj).newConfig == null) {
+                    break;
                 }
-                requestLayout();
-            }
-            break;
-        case WINDOW_FOCUS_CHANGED: {
-            if (mAdded) {
-                boolean hasWindowFocus = msg.arg1 != 0;
-                mAttachInfo.mHasWindowFocus = hasWindowFocus;
-                
-                profileRendering(hasWindowFocus);
+                // fall through...
+            case MSG_RESIZED_REPORT:
+                if (mAdded) {
+                    Configuration config = ((ResizedInfo)msg.obj).newConfig;
+                    if (config != null) {
+                        updateConfiguration(config, false);
+                    }
+                    mWinFrame.left = 0;
+                    mWinFrame.right = msg.arg1;
+                    mWinFrame.top = 0;
+                    mWinFrame.bottom = msg.arg2;
+                    mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets);
+                    mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets);
+                    if (msg.what == MSG_RESIZED_REPORT) {
+                        mReportNextDraw = true;
+                    }
 
-                if (hasWindowFocus) {
-                    boolean inTouchMode = msg.arg2 != 0;
-                    ensureTouchModeLocally(inTouchMode);
+                    if (mView != null) {
+                        forceLayout(mView);
+                    }
+                    requestLayout();
+                }
+                break;
+            case MSG_WINDOW_FOCUS_CHANGED: {
+                if (mAdded) {
+                    boolean hasWindowFocus = msg.arg1 != 0;
+                    mAttachInfo.mHasWindowFocus = hasWindowFocus;
 
-                    if (mAttachInfo.mHardwareRenderer != null &&
-                            mSurface != null && mSurface.isValid()) {
-                        mFullRedrawNeeded = true;
-                        try {
-                            mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
-                                    mHolder);
-                        } catch (Surface.OutOfResourcesException e) {
-                            Log.e(TAG, "OutOfResourcesException locking surface", e);
+                    profileRendering(hasWindowFocus);
+
+                    if (hasWindowFocus) {
+                        boolean inTouchMode = msg.arg2 != 0;
+                        ensureTouchModeLocally(inTouchMode);
+
+                        if (mAttachInfo.mHardwareRenderer != null &&
+                                mSurface != null && mSurface.isValid()) {
+                            mFullRedrawNeeded = true;
                             try {
-                                if (!sWindowSession.outOfMemory(mWindow)) {
-                                    Slog.w(TAG, "No processes killed for memory; killing self");
-                                    Process.killProcess(Process.myPid());
+                                mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
+                                        mHolder);
+                            } catch (Surface.OutOfResourcesException e) {
+                                Log.e(TAG, "OutOfResourcesException locking surface", e);
+                                try {
+                                    if (!sWindowSession.outOfMemory(mWindow)) {
+                                        Slog.w(TAG, "No processes killed for memory; killing self");
+                                        Process.killProcess(Process.myPid());
+                                    }
+                                } catch (RemoteException ex) {
                                 }
-                            } catch (RemoteException ex) {
+                                // Retry in a bit.
+                                sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500);
+                                return;
                             }
-                            // Retry in a bit.
-                            sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500);
-                            return;
                         }
                     }
-                }
 
-                mLastWasImTarget = WindowManager.LayoutParams
-                        .mayUseInputMethod(mWindowAttributes.flags);
+                    mLastWasImTarget = WindowManager.LayoutParams
+                            .mayUseInputMethod(mWindowAttributes.flags);
 
-                InputMethodManager imm = InputMethodManager.peekInstance();
-                if (mView != null) {
-                    if (hasWindowFocus && imm != null && mLastWasImTarget) {
-                        imm.startGettingWindowFocus(mView);
+                    InputMethodManager imm = InputMethodManager.peekInstance();
+                    if (mView != null) {
+                        if (hasWindowFocus && imm != null && mLastWasImTarget) {
+                            imm.startGettingWindowFocus(mView);
+                        }
+                        mAttachInfo.mKeyDispatchState.reset();
+                        mView.dispatchWindowFocusChanged(hasWindowFocus);
                     }
-                    mAttachInfo.mKeyDispatchState.reset();
-                    mView.dispatchWindowFocusChanged(hasWindowFocus);
-                }
 
-                // Note: must be done after the focus change callbacks,
-                // so all of the view state is set up correctly.
-                if (hasWindowFocus) {
-                    if (imm != null && mLastWasImTarget) {
-                        imm.onWindowFocus(mView, mView.findFocus(),
-                                mWindowAttributes.softInputMode,
-                                !mHasHadWindowFocus, mWindowAttributes.flags);
-                    }
-                    // Clear the forward bit.  We can just do this directly, since
-                    // the window manager doesn't care about it.
-                    mWindowAttributes.softInputMode &=
-                            ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
-                    ((WindowManager.LayoutParams)mView.getLayoutParams())
-                            .softInputMode &=
+                    // Note: must be done after the focus change callbacks,
+                    // so all of the view state is set up correctly.
+                    if (hasWindowFocus) {
+                        if (imm != null && mLastWasImTarget) {
+                            imm.onWindowFocus(mView, mView.findFocus(),
+                                    mWindowAttributes.softInputMode,
+                                    !mHasHadWindowFocus, mWindowAttributes.flags);
+                        }
+                        // Clear the forward bit.  We can just do this directly, since
+                        // the window manager doesn't care about it.
+                        mWindowAttributes.softInputMode &=
                                 ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
-                    mHasHadWindowFocus = true;
-                }
+                        ((WindowManager.LayoutParams)mView.getLayoutParams())
+                                .softInputMode &=
+                                    ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+                        mHasHadWindowFocus = true;
+                    }
 
-                if (hasWindowFocus && mView != null) {
-                    sendAccessibilityEvents();
+                    if (hasWindowFocus && mView != null && mAccessibilityManager.isEnabled()) {
+                        mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+                    }
                 }
+            } break;
+            case MSG_DIE:
+                doDie();
+                break;
+            case MSG_DISPATCH_KEY: {
+                KeyEvent event = (KeyEvent)msg.obj;
+                enqueueInputEvent(event, null, 0, true);
+            } break;
+            case MSG_DISPATCH_KEY_FROM_IME: {
+                if (LOCAL_LOGV) Log.v(
+                    TAG, "Dispatching key "
+                    + msg.obj + " from IME to " + mView);
+                KeyEvent event = (KeyEvent)msg.obj;
+                if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
+                    // The IME is trying to say this event is from the
+                    // system!  Bad bad bad!
+                    //noinspection UnusedAssignment
+                    event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
+                }
+                enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
+            } break;
+            case MSG_FINISH_INPUT_CONNECTION: {
+                InputMethodManager imm = InputMethodManager.peekInstance();
+                if (imm != null) {
+                    imm.reportFinishInputConnection((InputConnection)msg.obj);
+                }
+            } break;
+            case MSG_CHECK_FOCUS: {
+                InputMethodManager imm = InputMethodManager.peekInstance();
+                if (imm != null) {
+                    imm.checkFocus();
+                }
+            } break;
+            case MSG_CLOSE_SYSTEM_DIALOGS: {
+                if (mView != null) {
+                    mView.onCloseSystemDialogs((String)msg.obj);
+                }
+            } break;
+            case MSG_DISPATCH_DRAG_EVENT:
+            case MSG_DISPATCH_DRAG_LOCATION_EVENT: {
+                DragEvent event = (DragEvent)msg.obj;
+                event.mLocalState = mLocalDragState;    // only present when this app called startDrag()
+                handleDragEvent(event);
+            } break;
+            case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
+                handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo)msg.obj);
+            } break;
+            case MSG_UPDATE_CONFIGURATION: {
+                Configuration config = (Configuration)msg.obj;
+                if (config.isOtherSeqNewer(mLastConfiguration)) {
+                    config = mLastConfiguration;
+                }
+                updateConfiguration(config, false);
+            } break;
+            case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
+                if (mView != null) {
+                    getAccessibilityInteractionController()
+                        .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg);
+                }
+            } break;
+            case MSG_PERFORM_ACCESSIBILITY_ACTION: {
+                if (mView != null) {
+                    getAccessibilityInteractionController()
+                        .perfromAccessibilityActionUiThread(msg);
+                }
+            } break;
+            case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
+                if (mView != null) {
+                    getAccessibilityInteractionController()
+                        .findAccessibilityNodeInfoByViewIdUiThread(msg);
+                }
+            } break;
+            case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
+                if (mView != null) {
+                    getAccessibilityInteractionController()
+                        .findAccessibilityNodeInfosByTextUiThread(msg);
+                }
+            } break;
             }
-        } break;
-        case DIE:
-            doDie();
-            break;
-        case DISPATCH_KEY: {
-            KeyEvent event = (KeyEvent)msg.obj;
-            enqueueInputEvent(event, null, 0);
-        } break;
-        case DISPATCH_KEY_FROM_IME: {
-            if (LOCAL_LOGV) Log.v(
-                TAG, "Dispatching key "
-                + msg.obj + " from IME to " + mView);
-            KeyEvent event = (KeyEvent)msg.obj;
-            if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
-                // The IME is trying to say this event is from the
-                // system!  Bad bad bad!
-                //noinspection UnusedAssignment
-                event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
-            }
-            enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME);
-        } break;
-        case FINISH_INPUT_CONNECTION: {
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                imm.reportFinishInputConnection((InputConnection)msg.obj);
-            }
-        } break;
-        case CHECK_FOCUS: {
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                imm.checkFocus();
-            }
-        } break;
-        case CLOSE_SYSTEM_DIALOGS: {
-            if (mView != null) {
-                mView.onCloseSystemDialogs((String)msg.obj);
-            }
-        } break;
-        case DISPATCH_DRAG_EVENT:
-        case DISPATCH_DRAG_LOCATION_EVENT: {
-            DragEvent event = (DragEvent)msg.obj;
-            event.mLocalState = mLocalDragState;    // only present when this app called startDrag()
-            handleDragEvent(event);
-        } break;
-        case DISPATCH_SYSTEM_UI_VISIBILITY: {
-            handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo)msg.obj);
-        } break;
-        case UPDATE_CONFIGURATION: {
-            Configuration config = (Configuration)msg.obj;
-            if (config.isOtherSeqNewer(mLastConfiguration)) {
-                config = mLastConfiguration;
-            }
-            updateConfiguration(config, false);
-        } break;
-        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
-            if (mView != null) {
-                getAccessibilityInteractionController()
-                    .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg);
-            }
-        } break;
-        case DO_PERFORM_ACCESSIBILITY_ACTION: {
-            if (mView != null) {
-                getAccessibilityInteractionController()
-                    .perfromAccessibilityActionUiThread(msg);
-            }
-        } break;
-        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
-            if (mView != null) {
-                getAccessibilityInteractionController()
-                    .findAccessibilityNodeInfoByViewIdUiThread(msg);
-            }
-        } break;
-        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
-            if (mView != null) {
-                getAccessibilityInteractionController()
-                    .findAccessibilityNodeInfosByTextUiThread(msg);
-            }
-        } break;
         }
     }
+    final ViewRootHandler mHandler = new ViewRootHandler();
 
     /**
      * Something in the current window tells us we need to change the touch mode.  For
@@ -2947,7 +2987,7 @@
             case MotionEvent.ACTION_DOWN:
                 x.reset(2);
                 y.reset(2);
-                dispatchKey(new KeyEvent(curTime, curTime,
+                enqueueInputEvent(new KeyEvent(curTime, curTime,
                         KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
                         InputDevice.SOURCE_KEYBOARD));
@@ -2955,7 +2995,7 @@
             case MotionEvent.ACTION_UP:
                 x.reset(2);
                 y.reset(2);
-                dispatchKey(new KeyEvent(curTime, curTime,
+                enqueueInputEvent(new KeyEvent(curTime, curTime,
                         KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
                         InputDevice.SOURCE_KEYBOARD));
@@ -3009,7 +3049,7 @@
                         + keycode);
                 movement--;
                 int repeatCount = accelMovement - movement;
-                dispatchKey(new KeyEvent(curTime, curTime,
+                enqueueInputEvent(new KeyEvent(curTime, curTime,
                         KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
                         InputDevice.SOURCE_KEYBOARD));
@@ -3019,11 +3059,11 @@
                         + keycode);
                 movement--;
                 curTime = SystemClock.uptimeMillis();
-                dispatchKey(new KeyEvent(curTime, curTime,
+                enqueueInputEvent(new KeyEvent(curTime, curTime,
                         KeyEvent.ACTION_DOWN, keycode, 0, metaState,
                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
                         InputDevice.SOURCE_KEYBOARD));
-                dispatchKey(new KeyEvent(curTime, curTime,
+                enqueueInputEvent(new KeyEvent(curTime, curTime,
                         KeyEvent.ACTION_UP, keycode, 0, metaState,
                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
                         InputDevice.SOURCE_KEYBOARD));
@@ -3090,7 +3130,7 @@
 
         if (xDirection != mLastJoystickXDirection) {
             if (mLastJoystickXKeyCode != 0) {
-                dispatchKey(new KeyEvent(time, time,
+                enqueueInputEvent(new KeyEvent(time, time,
                         KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
                 mLastJoystickXKeyCode = 0;
@@ -3101,7 +3141,7 @@
             if (xDirection != 0 && synthesizeNewKeys) {
                 mLastJoystickXKeyCode = xDirection > 0
                         ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
-                dispatchKey(new KeyEvent(time, time,
+                enqueueInputEvent(new KeyEvent(time, time,
                         KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
             }
@@ -3109,7 +3149,7 @@
 
         if (yDirection != mLastJoystickYDirection) {
             if (mLastJoystickYKeyCode != 0) {
-                dispatchKey(new KeyEvent(time, time,
+                enqueueInputEvent(new KeyEvent(time, time,
                         KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
                 mLastJoystickYKeyCode = 0;
@@ -3120,7 +3160,7 @@
             if (yDirection != 0 && synthesizeNewKeys) {
                 mLastJoystickYKeyCode = yDirection > 0
                         ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
-                dispatchKey(new KeyEvent(time, time,
+                enqueueInputEvent(new KeyEvent(time, time,
                         KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
             }
@@ -3526,15 +3566,15 @@
         return mAccessibilityInteractionController;
     }
 
-    public AccessibilityPrefetchStrategy getAccessibilityPrefetchStrategy() {
+    public AccessibilityNodePrefetcher getAccessibilityNodePrefetcher() {
         if (mView == null) {
-            throw new IllegalStateException("getAccessibilityPrefetchStrategy"
+            throw new IllegalStateException("getAccessibilityNodePrefetcher"
                     + " called when there is no mView");
         }
-        if (mAccessibilityPrefetchStrategy == null) {
-            mAccessibilityPrefetchStrategy = new AccessibilityPrefetchStrategy();
+        if (mAccessibilityNodePrefetcher == null) {
+            mAccessibilityNodePrefetcher = new AccessibilityNodePrefetcher();
         }
-        return mAccessibilityPrefetchStrategy;
+        return mAccessibilityNodePrefetcher;
     }
 
     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
@@ -3671,7 +3711,7 @@
         if (immediate) {
             doDie();
         } else {
-            sendEmptyMessage(DIE);
+            mHandler.sendEmptyMessage(MSG_DIE);
         }
     }
 
@@ -3708,8 +3748,8 @@
     }
 
     public void requestUpdateConfiguration(Configuration config) {
-        Message msg = obtainMessage(UPDATE_CONFIGURATION, config);
-        sendMessage(msg);
+        Message msg = mHandler.obtainMessage(MSG_UPDATE_CONFIGURATION, config);
+        mHandler.sendMessage(msg);
     }
 
     private void destroyHardwareRenderer() {
@@ -3721,10 +3761,15 @@
     }
 
     void dispatchImeFinishedEvent(int seq, boolean handled) {
-        Message msg = obtainMessage(IME_FINISHED_EVENT);
+        Message msg = mHandler.obtainMessage(MSG_IME_FINISHED_EVENT);
         msg.arg1 = seq;
         msg.arg2 = handled ? 1 : 0;
-        sendMessage(msg);
+        mHandler.sendMessage(msg);
+    }
+
+    public void dispatchFinishInputConnection(InputConnection connection) {
+        Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection);
+        mHandler.sendMessage(msg);
     }
 
     public void dispatchResized(int w, int h, Rect coveredInsets,
@@ -3733,7 +3778,7 @@
                 + " h=" + h + " coveredInsets=" + coveredInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
                 + " reportDraw=" + reportDraw);
-        Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
+        Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT :MSG_RESIZED);
         if (mTranslator != null) {
             mTranslator.translateRectInScreenToAppWindow(coveredInsets);
             mTranslator.translateRectInScreenToAppWindow(visibleInsets);
@@ -3747,7 +3792,7 @@
         ri.visibleInsets = new Rect(visibleInsets);
         ri.newConfig = newConfig;
         msg.obj = ri;
-        sendMessage(msg);
+        mHandler.sendMessage(msg);
     }
 
     /**
@@ -3805,8 +3850,12 @@
         }
     }
 
+    void enqueueInputEvent(InputEvent event) {
+        enqueueInputEvent(event, null, 0, false);
+    }
+
     void enqueueInputEvent(InputEvent event,
-            InputEventReceiver receiver, int flags) {
+            InputEventReceiver receiver, int flags, boolean processImmediately) {
         QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
 
         if (ViewDebug.DEBUG_LATENCY) {
@@ -3830,13 +3879,17 @@
             last.mNext = q;
         }
 
-        scheduleProcessInputEvents();
+        if (processImmediately) {
+            doProcessInputEvents();
+        } else {
+            scheduleProcessInputEvents();
+        }
     }
 
     private void scheduleProcessInputEvents() {
         if (!mProcessInputEventsScheduled) {
             mProcessInputEventsScheduled = true;
-            sendEmptyMessage(DO_PROCESS_INPUT_EVENTS);
+            mHandler.sendEmptyMessage(MSG_PROCESS_INPUT_EVENTS);
         }
     }
 
@@ -3853,7 +3906,7 @@
         // so we can clear the pending flag immediately.
         if (mProcessInputEventsScheduled) {
             mProcessInputEventsScheduled = false;
-            removeMessages(DO_PROCESS_INPUT_EVENTS);
+            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
         }
     }
 
@@ -3912,6 +3965,16 @@
         }
     }
 
+    final class FrameRunnable implements Runnable {
+        @Override
+        public void run() {
+            mFrameScheduled = false;
+            doFrame();
+        }
+    }
+    final FrameRunnable mFrameRunnable = new FrameRunnable();
+    boolean mFrameScheduled;
+
     final class WindowInputEventReceiver extends InputEventReceiver {
         public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
@@ -3919,51 +3982,79 @@
 
         @Override
         public void onInputEvent(InputEvent event) {
-            enqueueInputEvent(event, this, 0);
+            enqueueInputEvent(event, this, 0, true);
+        }
+
+        @Override
+        public void onBatchedInputEventPending() {
+            scheduleFrame();
         }
     }
     WindowInputEventReceiver mInputEventReceiver;
 
+    public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
+        Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
+        mHandler.sendMessageDelayed(msg, delayMilliseconds);
+    }
+
+    public void cancelInvalidate(View view) {
+        mHandler.removeMessages(MSG_INVALIDATE, view);
+    }
+
+    public void dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info,
+            long delayMilliseconds) {
+        final Message msg = mHandler.obtainMessage(MSG_INVALIDATE_RECT, info);
+        mHandler.sendMessageDelayed(msg, delayMilliseconds);
+    }
+
     public void dispatchKey(KeyEvent event) {
-        enqueueInputEvent(event, null, 0);
+        Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY, event);
+        msg.setAsynchronous(true);
+        mHandler.sendMessage(msg);
+    }
+
+    public void dispatchKeyFromIme(KeyEvent event) {
+        Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event);
+        msg.setAsynchronous(true);
+        mHandler.sendMessage(msg);
     }
 
     public void dispatchAppVisibility(boolean visible) {
-        Message msg = obtainMessage(DISPATCH_APP_VISIBILITY);
+        Message msg = mHandler.obtainMessage(MSG_DISPATCH_APP_VISIBILITY);
         msg.arg1 = visible ? 1 : 0;
-        sendMessage(msg);
+        mHandler.sendMessage(msg);
     }
 
     public void dispatchGetNewSurface() {
-        Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE);
-        sendMessage(msg);
+        Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE);
+        mHandler.sendMessage(msg);
     }
 
     public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
         Message msg = Message.obtain();
-        msg.what = WINDOW_FOCUS_CHANGED;
+        msg.what = MSG_WINDOW_FOCUS_CHANGED;
         msg.arg1 = hasFocus ? 1 : 0;
         msg.arg2 = inTouchMode ? 1 : 0;
-        sendMessage(msg);
+        mHandler.sendMessage(msg);
     }
 
     public void dispatchCloseSystemDialogs(String reason) {
         Message msg = Message.obtain();
-        msg.what = CLOSE_SYSTEM_DIALOGS;
+        msg.what = MSG_CLOSE_SYSTEM_DIALOGS;
         msg.obj = reason;
-        sendMessage(msg);
+        mHandler.sendMessage(msg);
     }
 
     public void dispatchDragEvent(DragEvent event) {
         final int what;
         if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
-            what = DISPATCH_DRAG_LOCATION_EVENT;
-            removeMessages(what);
+            what = MSG_DISPATCH_DRAG_LOCATION_EVENT;
+            mHandler.removeMessages(what);
         } else {
-            what = DISPATCH_DRAG_EVENT;
+            what = MSG_DISPATCH_DRAG_EVENT;
         }
-        Message msg = obtainMessage(what, event);
-        sendMessage(msg);
+        Message msg = mHandler.obtainMessage(what, event);
+        mHandler.sendMessage(msg);
     }
 
     public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
@@ -3973,21 +4064,13 @@
         args.globalVisibility = globalVisibility;
         args.localValue = localValue;
         args.localChanges = localChanges;
-        sendMessage(obtainMessage(DISPATCH_SYSTEM_UI_VISIBILITY, args));
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
     }
 
-    /**
-     * The window is getting focus so if there is anything focused/selected
-     * send an {@link AccessibilityEvent} to announce that.
-     */
-    private void sendAccessibilityEvents() {
-        if (!mAccessibilityManager.isEnabled()) {
-            return;
-        }
-        mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-        View focusedView = mView.findFocus();
-        if (focusedView != null && focusedView != mView) {
-            focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    public void dispatchCheckFocus() {
+        if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
+            // This will result in a call to checkFocus() below.
+            mHandler.sendEmptyMessage(MSG_CHECK_FOCUS);
         }
     }
 
@@ -4004,7 +4087,7 @@
         }
         if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) {
             mSendWindowContentChangedAccessibilityEvent.mIsPending = true;
-            postDelayed(mSendWindowContentChangedAccessibilityEvent,
+            mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent,
                     ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
         }
     }
@@ -4015,7 +4098,7 @@
      */
     private void removeSendWindowContentChangedCallback() {
         if (mSendWindowContentChangedAccessibilityEvent != null) {
-            removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
+            mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
         }
     }
 
@@ -4037,7 +4120,6 @@
         if (mView == null) {
             return false;
         }
-        getAccessibilityPrefetchStrategy().onAccessibilityEvent(event);
         mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
     }
@@ -4058,6 +4140,10 @@
         return scrollToRectOrFocus(rectangle, immediate);
     }
 
+    public void childHasTransientStateChanged(View child, boolean hasTransientState) {
+        // Do nothing.
+    }
+
     class TakenSurfaceHolder extends BaseSurfaceHolder {
         @Override
         public boolean onAllowLockCanvas() {
@@ -4475,6 +4561,9 @@
     }
 
     /**
+     * The run queue is used to enqueue pending work from Views when no Handler is
+     * attached.  The work is executed during the next call to performTraversals on
+     * the thread.
      * @hide
      */
     static final class RunQueue {
@@ -4553,24 +4642,35 @@
         public void onAccessibilityStateChanged(boolean enabled) {
             if (enabled) {
                 ensureConnection();
+                if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
+                    mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+                    View focusedView = mView.findFocus();
+                    if (focusedView != null && focusedView != mView) {
+                        focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                    }
+                }
             } else {
                 ensureNoConnection();
             }
         }
 
         public void ensureConnection() {
-            final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
-            if (!registered) {
-                mAttachInfo.mAccessibilityWindowId =
-                    mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
-                            new AccessibilityInteractionConnection(ViewRootImpl.this));
+            if (mAttachInfo != null) {
+                final boolean registered =
+                    mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
+                if (!registered) {
+                    mAttachInfo.mAccessibilityWindowId =
+                        mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
+                                new AccessibilityInteractionConnection(ViewRootImpl.this));
+                }
             }
         }
 
         public void ensureNoConnection() {
-            final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
+            final boolean registered =
+                mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
             if (registered) {
-                mAttachInfo.mAccessibilityWindowId = View.NO_ID;
+                mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED;
                 mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
             }
         }
@@ -4591,12 +4691,12 @@
 
         public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
-                int interrogatingPid, long interrogatingTid) {
+                int prefetchFlags, int interrogatingPid, long interrogatingTid) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
-                        interactionId, callback, interrogatingPid, interrogatingTid);
+                        interactionId, callback, prefetchFlags, interrogatingPid, interrogatingTid);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
                 try {
@@ -4731,11 +4831,11 @@
 
         public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
                 long accessibilityNodeId, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
-                long interrogatingTid) {
-            Message message = Message.obtain();
-            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
-            message.arg1 = interrogatingPid;
+                IAccessibilityInteractionConnectionCallback callback, int prefetchFlags,
+                int interrogatingPid, long interrogatingTid) {
+            Message message = mHandler.obtainMessage();
+            message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
+            message.arg1 = prefetchFlags;
             SomeArgs args = mPool.acquire();
             args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
             args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
@@ -4748,17 +4848,16 @@
             // client can handle the message to generate the result.
             if (interrogatingPid == Process.myPid()
                     && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
-                message.setTarget(ViewRootImpl.this);
                 AccessibilityInteractionClient.getInstanceForThread(
                         interrogatingTid).setSameThreadMessage(message);
             } else {
-                sendMessage(message);
+                mHandler.sendMessage(message);
             }
         }
 
         public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
+            final int prefetchFlags = message.arg1;
             SomeArgs args = (SomeArgs) message.obj;
-            final int interrogatingPid = message.arg1;
             final int accessibilityViewId = args.argi1;
             final int virtualDescendantId = args.argi2;
             final int interactionId = args.argi3;
@@ -4768,15 +4867,15 @@
             List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
             infos.clear();
             try {
-                View target = findViewByAccessibilityId(accessibilityViewId);
+                View target = null;
+                if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
+                    target = ViewRootImpl.this.mView;
+                } else {
+                    target = findViewByAccessibilityId(accessibilityViewId);
+                }
                 if (target != null && target.getVisibility() == View.VISIBLE) {
-                    AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
-                    if (provider != null) {
-                        infos.add(provider.createAccessibilityNodeInfo(virtualDescendantId));
-                    } else if (virtualDescendantId == View.NO_ID) {
-                        getAccessibilityPrefetchStrategy().prefetchAccessibilityNodeInfos(
-                                interrogatingPid, target, infos);
-                    }
+                    getAccessibilityNodePrefetcher().prefetchAccessibilityNodeInfos(target,
+                            virtualDescendantId, prefetchFlags, infos);
                 }
             } finally {
                 try {
@@ -4791,8 +4890,8 @@
         public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId,
                 int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
                 int interrogatingPid, long interrogatingTid) {
-            Message message = Message.obtain();
-            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
+            Message message = mHandler.obtainMessage();
+            message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
             message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
             SomeArgs args = mPool.acquire();
             args.argi1 = viewId;
@@ -4805,11 +4904,10 @@
             // client can handle the message to generate the result.
             if (interrogatingPid == Process.myPid()
                     && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
-                message.setTarget(ViewRootImpl.this);
                 AccessibilityInteractionClient.getInstanceForThread(
                         interrogatingTid).setSameThreadMessage(message);
             } else {
-                sendMessage(message);
+                mHandler.sendMessage(message);
             }
         }
 
@@ -4824,7 +4922,7 @@
             AccessibilityNodeInfo info = null;
             try {
                 View root = null;
-                if (accessibilityViewId != View.NO_ID) {
+                if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
                     root = findViewByAccessibilityId(accessibilityViewId);
                 } else {
                     root = ViewRootImpl.this.mView;
@@ -4848,8 +4946,8 @@
                 String text, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
                 long interrogatingTid) {
-            Message message = Message.obtain();
-            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
+            Message message = mHandler.obtainMessage();
+            message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
             SomeArgs args = mPool.acquire();
             args.arg1 = text;
             args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
@@ -4863,11 +4961,10 @@
             // client can handle the message to generate the result.
             if (interrogatingPid == Process.myPid()
                     && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
-                message.setTarget(ViewRootImpl.this);
                 AccessibilityInteractionClient.getInstanceForThread(
                         interrogatingTid).setSameThreadMessage(message);
             } else {
-                sendMessage(message);
+                mHandler.sendMessage(message);
             }
         }
 
@@ -4883,7 +4980,7 @@
             List<AccessibilityNodeInfo> infos = null;
             try {
                 View target;
-                if (accessibilityViewId != View.NO_ID) {
+                if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
                     target = findViewByAccessibilityId(accessibilityViewId);
                 } else {
                     target = ViewRootImpl.this.mView;
@@ -4893,7 +4990,7 @@
                     if (provider != null) {
                         infos = provider.findAccessibilityNodeInfosByText(text,
                                 virtualDescendantId);
-                    } else if (virtualDescendantId == View.NO_ID) {
+                    } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
                         ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
                         foundViews.clear();
                         target.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
@@ -4934,8 +5031,8 @@
         public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback,
                 int interogatingPid, long interrogatingTid) {
-            Message message = Message.obtain();
-            message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
+            Message message = mHandler.obtainMessage();
+            message.what = MSG_PERFORM_ACCESSIBILITY_ACTION;
             message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
             message.arg2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
             SomeArgs args = mPool.acquire();
@@ -4949,11 +5046,10 @@
             // client can handle the message to generate the result.
             if (interogatingPid == Process.myPid()
                     && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
-                message.setTarget(ViewRootImpl.this);
                 AccessibilityInteractionClient.getInstanceForThread(
                         interrogatingTid).setSameThreadMessage(message);
             } else {
-                sendMessage(message);
+                mHandler.sendMessage(message);
             }
         }
 
@@ -4974,7 +5070,7 @@
                     if (provider != null) {
                         succeeded = provider.performAccessibilityAction(action,
                                 virtualDescendantId);
-                    } else if (virtualDescendantId == View.NO_ID) {
+                    } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
                         switch (action) {
                             case AccessibilityNodeInfo.ACTION_FOCUS: {
                                 if (!target.hasFocus()) {
@@ -5040,83 +5136,216 @@
 
     /**
      * This class encapsulates a prefetching strategy for the accessibility APIs for
-     * querying window content.It is responsible to prefetch a batch of
-     * AccessibilityNodeInfos in addition to the one for a requested node. It caches
-     * the ids of the prefeteched nodes such that they are fetched only once.
+     * querying window content. It is responsible to prefetch a batch of
+     * AccessibilityNodeInfos in addition to the one for a requested node.
      */
-    class AccessibilityPrefetchStrategy {
-        private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 100;
+    class AccessibilityNodePrefetcher {
 
-        // We need to keep track of what we have sent for each interrogating
-        // process. Usually there will be only one such process but we
-        // should support the general case. Note that the accessibility event
-        // stream will take care of clearing caches of querying processes that
-        // are not longer alive, so we do not waste memory.
-        private final LongSparseArray<AccessibilityNodeInfoCache> mAccessibilityNodeInfoCaches =
-            new LongSparseArray<AccessibilityNodeInfoCache>();
+        private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
 
-        private AccessibilityNodeInfoCache getCacheForInterrogatingPid(long interrogatingPid) {
-            AccessibilityNodeInfoCache cache = mAccessibilityNodeInfoCaches.get(interrogatingPid);
-            if (cache == null) {
-                cache = AccessibilityNodeInfoCache.newAccessibilityNodeInfoCache();
-                mAccessibilityNodeInfoCaches.put(interrogatingPid, cache);
-            }
-            return cache;
-        }
-
-        public void onAccessibilityEvent(AccessibilityEvent event) {
-            final int cacheCount = mAccessibilityNodeInfoCaches.size();
-            for (int i = 0; i < cacheCount; i++) {
-                AccessibilityNodeInfoCache cache = mAccessibilityNodeInfoCaches.valueAt(i);
-                cache.onAccessibilityEvent(event);
-            }
-        }
-
-        public void prefetchAccessibilityNodeInfos(long interrogatingPid, View root,
+        public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
                 List<AccessibilityNodeInfo> outInfos) {
-            addAndCacheNotCachedNodeInfo(interrogatingPid, root, outInfos);
-            addAndCacheNotCachedPredecessorInfos(interrogatingPid, root, outInfos);
-            addAndCacheNotCachedDescendantInfos(interrogatingPid, root, outInfos);
-        }
-
-        private void addAndCacheNotCachedNodeInfo(long interrogatingPid,
-                View view, List<AccessibilityNodeInfo> outInfos) {
-            final long accessibilityNodeId = AccessibilityNodeInfo.makeNodeId(
-                    view.getAccessibilityViewId(), View.NO_ID);
-            AccessibilityNodeInfoCache cache = getCacheForInterrogatingPid(interrogatingPid);
-            if (!cache.containsKey(accessibilityNodeId)) {
-                // Account for the ids of the fetched infos. The infos will be
-                // cached in the window querying process. We just need to know
-                // which infos are cached to avoid fetching a cached one again.
-                cache.put(accessibilityNodeId, null);
-                outInfos.add(view.createAccessibilityNodeInfo());
+            AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+            if (provider == null) {
+                AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
+                if (root != null) {
+                    outInfos.add(root);
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+                        prefetchPredecessorsOfRealNode(view, outInfos);
+                    }
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+                        prefetchSiblingsOfRealNode(view, outInfos);
+                    }
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+                        prefetchDescendantsOfRealNode(view, outInfos);
+                    }
+                }
+            } else {
+                AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
+                if (root != null) {
+                    outInfos.add(root);
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+                        prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
+                    }
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+                        prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
+                    }
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+                        prefetchDescendantsOfVirtualNode(root, provider, outInfos);
+                    }
+                }
             }
         }
 
-        private void addAndCacheNotCachedPredecessorInfos(long interrogatingPid, View view,
+        private void prefetchPredecessorsOfRealNode(View view,
                 List<AccessibilityNodeInfo> outInfos) {
-            ViewParent predecessor = view.getParent();
-            while (predecessor instanceof View
+            ViewParent parent = view.getParent();
+            while (parent instanceof View
                     && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
-                View predecessorView = (View) predecessor;
-                addAndCacheNotCachedNodeInfo(interrogatingPid, predecessorView, outInfos);
-                predecessor = predecessor.getParent();
+                View parentView = (View) parent;
+                final long parentNodeId = AccessibilityNodeInfo.makeNodeId(
+                        parentView.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
+                AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
+                if (info != null) {
+                    outInfos.add(info);
+                }
+                parent = parent.getParent();
             }
         }
 
-        private void addAndCacheNotCachedDescendantInfos(long interrogatingPid, View view,
+        private void prefetchSiblingsOfRealNode(View current,
                 List<AccessibilityNodeInfo> outInfos) {
-            if (outInfos.size() > MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE
-                    || view.getAccessibilityNodeProvider() != null) {
-                return;
+            ViewParent parent = current.getParent();
+            if (parent instanceof ViewGroup) {
+                ViewGroup parentGroup = (ViewGroup) parent;
+                final int childCount = parentGroup.getChildCount();
+                for (int i = 0; i < childCount; i++) {
+                    View child = parentGroup.getChildAt(i);
+                    if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE
+                            && child.getAccessibilityViewId() != current.getAccessibilityViewId()
+                            && child.getVisibility() == View.VISIBLE) {
+                        final long childNodeId = AccessibilityNodeInfo.makeNodeId(
+                                child.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
+                        AccessibilityNodeInfo info = null;
+                        AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
+                        if (provider == null) {
+                            info = child.createAccessibilityNodeInfo();
+                        } else {
+                            info = provider.createAccessibilityNodeInfo(
+                                    AccessibilityNodeInfo.UNDEFINED);
+                        }
+                        if (info != null) {
+                            outInfos.add(info);
+                        }
+                    }
+                }
             }
-            addAndCacheNotCachedNodeInfo(interrogatingPid, view, outInfos);
-            if (view instanceof ViewGroup) {
-                ViewGroup rootGroup = (ViewGroup) view;
+        }
+
+        private void prefetchDescendantsOfRealNode(View root,
+                List<AccessibilityNodeInfo> outInfos) {
+            if (root instanceof ViewGroup) {
+                ViewGroup rootGroup = (ViewGroup) root;
+                HashMap<View, AccessibilityNodeInfo> addedChildren =
+                    new HashMap<View, AccessibilityNodeInfo>();
                 final int childCount = rootGroup.getChildCount();
                 for (int i = 0; i < childCount; i++) {
-                View child = rootGroup.getChildAt(i);
-                    addAndCacheNotCachedDescendantInfos(interrogatingPid, child, outInfos);
+                    View child = rootGroup.getChildAt(i);
+                    if (child.getVisibility() == View.VISIBLE
+                            && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                        final long childNodeId = AccessibilityNodeInfo.makeNodeId(
+                                child.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
+                        AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
+                        if (provider == null) {
+                            AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
+                            if (info != null) {
+                                outInfos.add(info);
+                                addedChildren.put(child, null);
+                            }
+                        } else {
+                            AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
+                                   AccessibilityNodeInfo.UNDEFINED);
+                            if (info != null) {
+                                outInfos.add(info);
+                                addedChildren.put(child, info);
+                            }
+                        }
+                    }
+                }
+                if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                    for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
+                        View addedChild = entry.getKey();
+                        AccessibilityNodeInfo virtualRoot = entry.getValue();
+                        if (virtualRoot == null) {
+                            prefetchDescendantsOfRealNode(addedChild, outInfos);
+                        } else {
+                            AccessibilityNodeProvider provider =
+                                addedChild.getAccessibilityNodeProvider();
+                            prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
+                        }
+                    }
+                }
+            }
+        }
+
+        private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
+                View providerHost, AccessibilityNodeProvider provider,
+                List<AccessibilityNodeInfo> outInfos) {
+            long parentNodeId = root.getParentNodeId();
+            int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
+            while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+                final int virtualDescendantId =
+                    AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
+                if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+                        || accessibilityViewId == providerHost.getAccessibilityViewId()) {
+                    AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
+                            virtualDescendantId);
+                    if (parent != null) {
+                        outInfos.add(parent);
+                    }
+                    parentNodeId = parent.getParentNodeId();
+                    accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
+                            parentNodeId);
+                } else {
+                    prefetchPredecessorsOfRealNode(providerHost, outInfos);
+                    return;
+                }
+            }
+        }
+
+        private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
+                AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
+            final long parentNodeId = current.getParentNodeId();
+            final int parentAccessibilityViewId =
+                AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
+            final int parentVirtualDescendantId =
+                AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
+            if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+                    || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
+                AccessibilityNodeInfo parent =
+                    provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+                if (parent != null) {
+                    SparseLongArray childNodeIds = parent.getChildNodeIds();
+                    final int childCount = childNodeIds.size();
+                    for (int i = 0; i < childCount; i++) {
+                        final long childNodeId = childNodeIds.get(i);
+                        if (childNodeId != current.getSourceNodeId()
+                                && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                            final int childVirtualDescendantId =
+                                AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
+                            AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
+                                    childVirtualDescendantId);
+                            if (child != null) {
+                                outInfos.add(child);
+                            }
+                        }
+                    }
+                }
+            } else {
+                prefetchSiblingsOfRealNode(providerHost, outInfos);
+            }
+        }
+
+        private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
+                AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
+            SparseLongArray childNodeIds = root.getChildNodeIds();
+            final int initialOutInfosSize = outInfos.size();
+            final int childCount = childNodeIds.size();
+            for (int i = 0; i < childCount; i++) {
+                if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                    final long childNodeId = childNodeIds.get(i);
+                    AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
+                            AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
+                    if (child != null) {
+                        outInfos.add(child);
+                    }
+                }
+            }
+            if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                final int addedChildCount = outInfos.size() - initialOutInfosSize;
+                for (int i = 0; i < addedChildCount; i++) {
+                    AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
+                    prefetchDescendantsOfVirtualNode(child, provider, outInfos);
                 }
             }
         }
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 24a3066..9152cc3 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -91,6 +91,10 @@
     private static final int MSG_VIBRATE = 4;
     private static final int MSG_TIMEOUT = 5;
     private static final int MSG_RINGER_MODE_CHANGED = 6;
+    private static final int MSG_MUTE_CHANGED = 7;
+
+    // Pseudo stream type for master volume
+    private static final int STREAM_MASTER = -100;
 
     protected Context mContext;
     private AudioManager mAudioManager;
@@ -148,7 +152,13 @@
                 R.string.volume_icon_description_notification,
                 R.drawable.ic_audio_notification,
                 R.drawable.ic_audio_notification_mute,
-                true);
+                true),
+        // for now, use media resources for master volume
+        MasterStream(STREAM_MASTER,
+                R.string.volume_icon_description_media,
+                R.drawable.ic_audio_vol,
+                R.drawable.ic_audio_vol_mute,
+                false);
 
         int streamType;
         int descRes;
@@ -173,7 +183,8 @@
         StreamResources.VoiceStream,
         StreamResources.MediaStream,
         StreamResources.NotificationStream,
-        StreamResources.AlarmStream
+        StreamResources.AlarmStream,
+        StreamResources.MasterStream
     };
 
     /** Object that contains data for each slider */
@@ -195,6 +206,16 @@
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         mAudioService = volumeService;
 
+        // For now, only show master volume if master volume is supported
+        boolean useMasterVolume = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
+        if (useMasterVolume) {
+            for (int i = 0; i < STREAMS.length; i++) {
+                StreamResources streamRes = STREAMS[i];
+                streamRes.show = (streamRes.streamType == STREAM_MASTER);
+            }
+        }
+
         LayoutInflater inflater = (LayoutInflater) context
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         View view = mView = inflater.inflate(R.layout.volume_adjust, null);
@@ -245,7 +266,7 @@
         mVibrator = new Vibrator();
 
         mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
-        mShowCombinedVolumes = !mVoiceCapable;
+        mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
         // If we don't want to show multiple volumes, hide the settings button and divider
         if (!mShowCombinedVolumes) {
             mMoreButton.setVisibility(View.GONE);
@@ -274,7 +295,43 @@
     }
 
     private boolean isMuted(int streamType) {
-        return mAudioManager.isStreamMute(streamType);
+        if (streamType == STREAM_MASTER) {
+            return mAudioService.isMasterMute();
+        } else {
+            return mAudioService.isStreamMute(streamType);
+        }
+    }
+
+    private int getStreamMaxVolume(int streamType) {
+        if (streamType == STREAM_MASTER) {
+            return mAudioService.getMasterMaxVolume();
+        } else {
+            return mAudioService.getStreamMaxVolume(streamType);
+        }
+    }
+
+    private int getStreamVolume(int streamType) {
+        if (streamType == STREAM_MASTER) {
+            return mAudioService.getMasterVolume();
+        } else {
+            return mAudioService.getStreamVolume(streamType);
+        }
+    }
+
+    private void setStreamVolume(int streamType, int index, int flags) {
+        if (streamType == STREAM_MASTER) {
+            mAudioService.setMasterVolume(index, flags);
+        } else {
+            mAudioService.setStreamVolume(streamType, index, flags);
+        }
+    }
+
+    private int getLastAudibleStreamVolume(int streamType) {
+        if (streamType == STREAM_MASTER) {
+            return mAudioService.getLastAudibleMasterVolume();
+        } else {
+            return mAudioService.getLastAudibleStreamVolume(streamType);
+        }
     }
 
     private void createSliders() {
@@ -301,7 +358,7 @@
             sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
             int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
                     streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
-            sc.seekbarView.setMax(mAudioManager.getStreamMaxVolume(streamType) + plusOne);
+            sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
             sc.seekbarView.setOnSeekBarChangeListener(this);
             sc.seekbarView.setTag(sc);
             mStreamControls.put(streamType, sc);
@@ -342,7 +399,7 @@
 
     /** Update the mute and progress state of a slider */
     private void updateSlider(StreamControl sc) {
-        sc.seekbarView.setProgress(mAudioManager.getLastAudibleStreamVolume(sc.streamType));
+        sc.seekbarView.setProgress(getLastAudibleStreamVolume(sc.streamType));
         final boolean muted = isMuted(sc.streamType);
         sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
         if (sc.streamType == AudioManager.STREAM_RING && muted
@@ -390,6 +447,23 @@
         obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
     }
 
+    public void postMasterVolumeChanged(int flags) {
+        postVolumeChanged(STREAM_MASTER, flags);
+    }
+
+    public void postMuteChanged(int streamType, int flags) {
+        if (hasMessages(MSG_VOLUME_CHANGED)) return;
+        if (mStreamControls == null) {
+            createSliders();
+        }
+        removeMessages(MSG_FREE_RESOURCES);
+        obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
+    }
+
+    public void postMasterMuteChanged(int flags) {
+        postMuteChanged(STREAM_MASTER, flags);
+    }
+
     /**
      * Override this if you have other work to do when the volume changes (for
      * example, vibrating, playing a sound, etc.). Make sure to call through to
@@ -423,10 +497,22 @@
         resetTimeout();
     }
 
+    protected void onMuteChanged(int streamType, int flags) {
+
+        if (LOGD) Log.d(TAG, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
+
+        StreamControl sc = mStreamControls.get(streamType);
+        if (sc != null) {
+            sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
+        }
+
+        onVolumeChanged(streamType, flags);
+    }
+
     protected void onShowVolumeChanged(int streamType, int flags) {
-        int index = mAudioService.isStreamMute(streamType) ?
-                mAudioService.getLastAudibleStreamVolume(streamType)
-                : mAudioService.getStreamVolume(streamType);
+        int index = isMuted(streamType) ?
+                getLastAudibleStreamVolume(streamType)
+                : getStreamVolume(streamType);
 
         mRingIsSilent = false;
 
@@ -437,7 +523,7 @@
 
         // get max volume for progress bar
 
-        int max = mAudioService.getStreamMaxVolume(streamType);
+        int max = getStreamMaxVolume(streamType);
 
         switch (streamType) {
 
@@ -571,6 +657,7 @@
      * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
      */
     private ToneGenerator getOrCreateToneGenerator(int streamType) {
+        if (streamType == STREAM_MASTER) return null;
         synchronized (this) {
             if (mToneGenerators[streamType] == null) {
                 try {
@@ -620,6 +707,11 @@
                 break;
             }
 
+            case MSG_MUTE_CHANGED: {
+                onMuteChanged(msg.arg1, msg.arg2);
+                break;
+            }
+
             case MSG_FREE_RESOURCES: {
                 onFreeResources();
                 break;
@@ -671,8 +763,8 @@
         final Object tag = seekBar.getTag();
         if (fromUser && tag instanceof StreamControl) {
             StreamControl sc = (StreamControl) tag;
-            if (mAudioManager.getStreamVolume(sc.streamType) != progress) {
-                mAudioManager.setStreamVolume(sc.streamType, progress, 0);
+            if (getStreamVolume(sc.streamType) != progress) {
+                setStreamVolume(sc.streamType, progress, 0);
             }
         }
         resetTimeout();
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 6bdc4e8..6dbdedb 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -480,9 +480,19 @@
         try {
             synchronized (this) {
                 if (mViews != null) {
-                    pw.println("View hierarchy:");
-
                     final int count = mViews.length;
+                    
+                    pw.println("Profile data in ms:");
+
+                    for (int i = 0; i < count; i++) {
+                        ViewRootImpl root = mRoots[i];
+                        HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer;
+                        if (renderer != null) {
+                            renderer.dumpGfxInfo(pw);
+                        }
+                    }
+
+                    pw.println("\nView hierarchy:");
 
                     int viewsCount = 0;
                     int displayListsSize = 0;
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 072fdd8..be74b31 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -24,7 +24,6 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.SparseArray;
-import android.view.AccessibilityNodeInfoCache;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -102,7 +101,7 @@
     // The connection cache is shared between all interrogating threads since
     // at any given time there is only one window allowing querying.
     private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache =
-        AccessibilityNodeInfoCache.newSynchronizedAccessibilityNodeInfoCache();
+        new AccessibilityNodeInfoCache();
 
     /**
      * @return The client for the current thread.
@@ -153,30 +152,34 @@
      *
      * @param connectionId The id of a connection for interacting with the system.
      * @param accessibilityWindowId A unique window id. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
      *     to query the currently active window.
-     * @param accessibilityNodeId A unique node accessibility id
-     *     (accessibility view and virtual descendant id).
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
+     * @param prefetchFlags flags to guide prefetching.
      * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
      */
     public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
-            int accessibilityWindowId, long accessibilityNodeId) {
+            int accessibilityWindowId, long accessibilityNodeId, int prefetchFlags) {
         try {
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
-                AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(accessibilityNodeId); 
+                AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
+                        accessibilityNodeId);
                 if (cachedInfo != null) {
                     return cachedInfo;
                 }
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
                 final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
                         accessibilityWindowId, accessibilityNodeId, interactionId, this,
-                        Thread.currentThread().getId());
+                        Thread.currentThread().getId(), prefetchFlags);
                 // If the scale is zero the call has failed.
                 if (windowScale > 0) {
                     List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
                             interactionId);
-                    finalizeAccessibilityNodeInfos(infos, connectionId, windowScale);
+                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
                     if (infos != null && !infos.isEmpty()) {
                         return infos.get(0);
                     }
@@ -202,10 +205,11 @@
      *
      * @param connectionId The id of a connection for interacting with the system.
      * @param accessibilityWindowId A unique window id. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
      *     to query the currently active window.
-     * @param accessibilityNodeId A unique view id from where to start the search. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
      *     to start from the root.
      * @param viewId The id of the view.
      * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
@@ -224,7 +228,7 @@
                 if (windowScale > 0) {
                     AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
                             interactionId);
-                    finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
+                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
                     return info;
                 }
             } else {
@@ -249,10 +253,12 @@
      *
      * @param connectionId The id of a connection for interacting with the system.
      * @param accessibilityWindowId A unique window id. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
      *     to query the currently active window.
-     * @param accessibilityNodeId A unique view id from where to start the search. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
      * @param text The searched text.
      * @return A list of found {@link AccessibilityNodeInfo}s.
      */
@@ -269,7 +275,7 @@
                 if (windowScale > 0) {
                     List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
                             interactionId);
-                    finalizeAccessibilityNodeInfos(infos, connectionId, windowScale);
+                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
                     return infos;
                 }
             } else {
@@ -291,9 +297,12 @@
      *
      * @param connectionId The id of a connection for interacting with the system.
      * @param accessibilityWindowId A unique window id. Use
-     *     {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
      *     to query the currently active window.
-     * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
+     * @param accessibilityNodeId A unique view id or virtual descendant id from
+     *     where to start the search. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
+     *     to start from the root.
      * @param action The action to perform.
      * @return Whether the action was performed.
      */
@@ -323,16 +332,10 @@
     }
 
     public void clearCache() {
-        if (DEBUG) {
-            Log.w(LOG_TAG, "clearCache()");
-        }
         sAccessibilityNodeInfoCache.clear();
     }
 
     public void removeCachedNode(long accessibilityNodeId) {
-        if (DEBUG) {
-            Log.w(LOG_TAG, "removeCachedNode(" + accessibilityNodeId +")");
-        }
         sAccessibilityNodeInfoCache.remove(accessibilityNodeId);
     }
 
@@ -364,9 +367,6 @@
             if (interactionId > mInteractionId) {
                 mFindAccessibilityNodeInfoResult = info;
                 mInteractionId = interactionId;
-                if (info != null) {
-                    sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info);
-                }
             }
             mInstanceLock.notifyAll();
         }
@@ -404,11 +404,6 @@
                     mFindAccessibilityNodeInfosResult = infos;
                 }
                 mInteractionId = interactionId;
-                final int infoCount = infos.size();
-                for (int i = 0; i < infoCount; i ++) {
-                    AccessibilityNodeInfo info = infos.get(i);
-                    sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info);
-                }
             }
             mInstanceLock.notifyAll();
         }
@@ -513,12 +508,13 @@
      * @param connectionId The id of the connection to the system.
      * @param windowScale The source window compatibility scale.
      */
-    private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId,
+    private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId,
             float windowScale) {
         if (info != null) {
             applyCompatibilityScaleIfNeeded(info, windowScale);
             info.setConnectionId(connectionId);
             info.setSealed(true);
+            sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info);
         }
     }
 
@@ -529,13 +525,13 @@
      * @param connectionId The id of the connection to the system.
      * @param windowScale The source window compatibility scale.
      */
-    private void finalizeAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
+    private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
             int connectionId, float windowScale) {
         if (infos != null) {
             final int infosCount = infos.size();
             for (int i = 0; i < infosCount; i++) {
                 AccessibilityNodeInfo info = infos.get(i);
-                finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
+                finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
             }
         }
     }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index d7d6792..c094fda 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -52,7 +52,23 @@
 
     private static final boolean DEBUG = false;
 
-    private static final int UNDEFINED = -1;
+    /** @hide */
+    public static final int UNDEFINED = -1;
+
+    /** @hide */
+    public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED);
+
+    /** @hide */
+    public static final int ACTIVE_WINDOW_ID = UNDEFINED;
+
+    /** @hide */
+    public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
+
+    /** @hide */
+    public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
+
+    /** @hide */
+    public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000003;
 
     // Actions.
 
@@ -162,8 +178,8 @@
 
     // Data.
     private int mWindowId = UNDEFINED;
-    private long mSourceNodeId = makeNodeId(UNDEFINED, UNDEFINED);
-    private long mParentNodeId = makeNodeId(UNDEFINED, UNDEFINED);
+    private long mSourceNodeId = ROOT_NODE_ID;
+    private long mParentNodeId = ROOT_NODE_ID;
 
     private int mBooleanProperties;
     private final Rect mBoundsInParent = new Rect();
@@ -174,7 +190,7 @@
     private CharSequence mText;
     private CharSequence mContentDescription;
 
-    private SparseLongArray mChildIds = new SparseLongArray();
+    private SparseLongArray mChildNodeIds = new SparseLongArray();
     private int mActions;
 
     private int mConnectionId = UNDEFINED;
@@ -237,12 +253,21 @@
     }
 
     /**
+     * @return The ids of the children.
+     *
+     * @hide
+     */
+    public SparseLongArray getChildNodeIds() {
+        return mChildNodeIds;
+    }
+
+    /**
      * Gets the number of children.
      *
      * @return The child count.
      */
     public int getChildCount() {
-        return mChildIds.size();
+        return mChildNodeIds.size();
     }
 
     /**
@@ -264,9 +289,10 @@
         if (!canPerformRequestOverConnection(mSourceNodeId)) {
             return null;
         }
-        final long childId = mChildIds.get(index);
+        final long childId = mChildNodeIds.get(index);
         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
-        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, childId);
+        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
+                childId, FLAG_PREFETCH_DESCENDANTS);
     }
 
     /**
@@ -301,11 +327,11 @@
      */
     public void addChild(View root, int virtualDescendantId) {
         enforceNotSealed();
-        final int index = mChildIds.size();
+        final int index = mChildNodeIds.size();
         final int rootAccessibilityViewId =
             (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
-        mChildIds.put(index, childNodeId);
+        mChildNodeIds.put(index, childNodeId);
     }
 
     /**
@@ -401,7 +427,16 @@
         }
         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
-                mWindowId, mParentNodeId);
+                mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+    }
+
+    /**
+     * @return The parent node id.
+     *
+     * @hide
+     */
+    public long getParentNodeId() {
+        return mParentNodeId;
     }
 
     /**
@@ -1063,7 +1098,7 @@
         parcel.writeLong(mParentNodeId);
         parcel.writeInt(mConnectionId);
 
-        SparseLongArray childIds = mChildIds;
+        SparseLongArray childIds = mChildNodeIds;
         final int childIdsSize = childIds.size();
         parcel.writeInt(childIdsSize);
         for (int i = 0; i < childIdsSize; i++) {
@@ -1113,7 +1148,7 @@
         mContentDescription = other.mContentDescription;
         mActions= other.mActions;
         mBooleanProperties = other.mBooleanProperties;
-        mChildIds = other.mChildIds.clone();
+        mChildNodeIds = other.mChildNodeIds.clone();
     }
 
     /**
@@ -1128,7 +1163,7 @@
         mParentNodeId = parcel.readLong();
         mConnectionId = parcel.readInt();
 
-        SparseLongArray childIds = mChildIds;
+        SparseLongArray childIds = mChildNodeIds;
         final int childrenSize = parcel.readInt();
         for (int i = 0; i < childrenSize; i++) {
             final long childId = parcel.readLong();
@@ -1160,11 +1195,11 @@
      */
     private void clear() {
         mSealed = false;
-        mSourceNodeId = makeNodeId(UNDEFINED, UNDEFINED);
-        mParentNodeId = makeNodeId(UNDEFINED, UNDEFINED);
+        mSourceNodeId = ROOT_NODE_ID;
+        mParentNodeId = ROOT_NODE_ID;
         mWindowId = UNDEFINED;
         mConnectionId = UNDEFINED;
-        mChildIds.clear();
+        mChildNodeIds.clear();
         mBoundsInParent.set(0, 0, 0, 0);
         mBoundsInScreen.set(0, 0, 0, 0);
         mBooleanProperties = 0;
@@ -1242,7 +1277,7 @@
             builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
             builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
             builder.append("; mParentNodeId: " + mParentNodeId);
-            SparseLongArray childIds = mChildIds;
+            SparseLongArray childIds = mChildNodeIds;
             builder.append("; childAccessibilityIds: [");
             for (int i = 0, count = childIds.size(); i < count; i++) {
                 builder.append(childIds.valueAt(i));
diff --git a/core/java/android/view/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
similarity index 63%
rename from core/java/android/view/AccessibilityNodeInfoCache.java
rename to core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
index 244a491..dfbfc70 100644
--- a/core/java/android/view/AccessibilityNodeInfoCache.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package android.view;
+package android.view.accessibility;
 
+import android.util.Log;
 import android.util.LongSparseArray;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
  * Simple cache for AccessibilityNodeInfos. The cache is mapping an
@@ -30,55 +29,17 @@
  */
 public class AccessibilityNodeInfoCache {
 
-    private final boolean ENABLED = true;
+    private static final String LOG_TAG = AccessibilityNodeInfoCache.class.getSimpleName();
 
-    /**
-     * @return A new <strong>not synchronized</strong> AccessibilityNodeInfoCache.
-     */
-    public static AccessibilityNodeInfoCache newAccessibilityNodeInfoCache() {
-        return new AccessibilityNodeInfoCache();
-    }
+    private static final boolean ENABLED = true;
 
-    /**
-     * @return A new <strong>synchronized</strong> AccessibilityNodeInfoCache.
-     */
-    public static AccessibilityNodeInfoCache newSynchronizedAccessibilityNodeInfoCache() {
-        return new AccessibilityNodeInfoCache() {
-            private final Object mLock = new Object();
+    private static final boolean DEBUG = false;
 
-            @Override
-            public void clear() {
-                synchronized(mLock) {
-                    super.clear();
-                }
-            }
-
-            @Override
-            public AccessibilityNodeInfo get(long accessibilityNodeId) {
-                synchronized(mLock) {
-                    return super.get(accessibilityNodeId);
-                }
-            }
-
-            @Override
-            public void put(long accessibilityNodeId, AccessibilityNodeInfo info) {
-                synchronized(mLock) {
-                   super.put(accessibilityNodeId, info);
-                }
-            }
-
-            @Override
-            public void remove(long accessibilityNodeId) {
-                synchronized(mLock) {
-                   super.remove(accessibilityNodeId);
-                }
-            }
-        };
-    }
+    private final Object mLock = new Object();
 
     private final LongSparseArray<AccessibilityNodeInfo> mCacheImpl;
 
-    private AccessibilityNodeInfoCache() {
+    public AccessibilityNodeInfoCache() {
         if (ENABLED) {
             mCacheImpl = new LongSparseArray<AccessibilityNodeInfo>();
         } else {
@@ -95,6 +56,7 @@
     public void onAccessibilityEvent(AccessibilityEvent event) {
         final int eventType = event.getEventType();
         switch (eventType) {
+            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
             case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
             case AccessibilityEvent.TYPE_VIEW_SCROLLED:
                 clear();
@@ -117,7 +79,18 @@
      */
     public AccessibilityNodeInfo get(long accessibilityNodeId) {
         if (ENABLED) {
-            return mCacheImpl.get(accessibilityNodeId);
+            synchronized(mLock) {
+                AccessibilityNodeInfo info = mCacheImpl.get(accessibilityNodeId);
+                if (info != null) {
+                    // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
+                    // will wipe the data of the cached info.
+                    info = AccessibilityNodeInfo.obtain(info);
+                }
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
+                }
+                return info;
+            }
         } else {
             return null;
         }
@@ -131,7 +104,15 @@
      */
     public void put(long accessibilityNodeId, AccessibilityNodeInfo info) {
         if (ENABLED) {
-            mCacheImpl.put(accessibilityNodeId, info);
+            synchronized(mLock) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "put(" + accessibilityNodeId + ", " + info + ")");
+                }
+                // Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
+                // will wipe the data of the cached info.
+                AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
+                mCacheImpl.put(accessibilityNodeId, clone);
+            }
         }
     }
 
@@ -143,7 +124,9 @@
      */
     public boolean containsKey(long accessibilityNodeId) {
         if (ENABLED) {
-            return (mCacheImpl.indexOfKey(accessibilityNodeId) >= 0);
+            synchronized(mLock) {
+                return (mCacheImpl.indexOfKey(accessibilityNodeId) >= 0);
+            }
         } else {
             return false;
         }
@@ -156,7 +139,12 @@
      */
     public void remove(long accessibilityNodeId) {
         if (ENABLED) {
-            mCacheImpl.remove(accessibilityNodeId);
+            synchronized(mLock) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "remove(" + accessibilityNodeId + ")");
+                }
+                mCacheImpl.remove(accessibilityNodeId);
+            }
         }
     }
 
@@ -165,7 +153,18 @@
      */
     public void clear() {
         if (ENABLED) {
-            mCacheImpl.clear();
+            synchronized(mLock) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "clear()");
+                }
+                // Recycle the nodes before clearing the cache.
+                final int nodeCount = mCacheImpl.size();
+                for (int i = 0; i < nodeCount; i++) {
+                    AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
+                    info.recycle();
+                }
+                mCacheImpl.clear();
+            }
         }
     }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index b60f50e..23b235c 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -56,6 +56,11 @@
     private static final int PROPERTY_FULL_SCREEN = 0x00000080;
     private static final int PROPERTY_SCROLLABLE = 0x00000100;
 
+    private static final int GET_SOURCE_PREFETCH_FLAGS =
+        AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS
+        | AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS
+        | AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
+
     // Housekeeping
     private static final int MAX_POOL_SIZE = 10;
     private static final Object sPoolLock = new Object();
@@ -144,7 +149,7 @@
         }
         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId,
-                mSourceNodeId);
+                mSourceNodeId, GET_SOURCE_PREFETCH_FLAGS);
     }
 
     /**
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index ae6869c..fc3651c 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -28,7 +28,7 @@
 oneway interface IAccessibilityInteractionConnection {
 
     void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback,
+        IAccessibilityInteractionConnectionCallback callback, int prefetchFlags,
         int interrogatingPid, long interrogatingTid);
 
     void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int id, int interactionId,
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index bd02d62..89aba3c 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.SystemClock;
 import android.text.Editable;
 import android.text.NoCopySpan;
@@ -497,15 +496,14 @@
      */
     public boolean sendKeyEvent(KeyEvent event) {
         synchronized (mIMM.mH) {
-            Handler h = mTargetView != null ? mTargetView.getHandler() : null;
-            if (h == null) {
+            ViewRootImpl viewRootImpl = mTargetView != null ? mTargetView.getViewRootImpl() : null;
+            if (viewRootImpl == null) {
                 if (mIMM.mServedView != null) {
-                    h = mIMM.mServedView.getHandler();
+                    viewRootImpl = mIMM.mServedView.getViewRootImpl();
                 }
             }
-            if (h != null) {
-                h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME,
-                        event));
+            if (viewRootImpl != null) {
+                viewRootImpl.dispatchKeyFromIme(event);
             }
         }
         return false;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0985e14..c51d244 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -336,6 +336,7 @@
                 }
                 case MSG_UNBIND: {
                     final int sequence = msg.arg1;
+                    boolean startInput = false;
                     synchronized (mH) {
                         if (mBindSequence == sequence) {
                             if (false) {
@@ -355,7 +356,12 @@
                             if (mServedView != null && mServedView.isFocused()) {
                                 mServedConnecting = true;
                             }
+                            if (mActive) {
+                                startInput = true;
+                            }
                         }
+                    }
+                    if (startInput) {
                         startInputInner();
                     }
                     return;
@@ -669,11 +675,10 @@
             // longer the input target, so it can reset its state.  Schedule
             // this call on its window's Handler so it will be on the correct
             // thread and outside of our lock.
-            Handler vh = mServedView.getHandler();
-            if (vh != null) {
+            ViewRootImpl viewRootImpl = mServedView.getViewRootImpl();
+            if (viewRootImpl != null) {
                 // This will result in a call to reportFinishInputConnection() below.
-                vh.sendMessage(vh.obtainMessage(ViewRootImpl.FINISH_INPUT_CONNECTION,
-                        mServedInputConnection));
+                viewRootImpl.dispatchFinishInputConnection(mServedInputConnection);
             }
         }
     }
@@ -1124,31 +1129,36 @@
     }
 
     static void scheduleCheckFocusLocked(View view) {
-        Handler vh = view.getHandler();
-        if (vh != null && !vh.hasMessages(ViewRootImpl.CHECK_FOCUS)) {
-            // This will result in a call to checkFocus() below.
-            vh.sendMessage(vh.obtainMessage(ViewRootImpl.CHECK_FOCUS));
+        ViewRootImpl viewRootImpl = view.getViewRootImpl();
+        if (viewRootImpl != null) {
+            viewRootImpl.dispatchCheckFocus();
         }
     }
-    
+
     /**
      * @hide
      */
     public void checkFocus() {
+        if (checkFocusNoStartInput()) {
+            startInputInner();
+        }
+    }
+
+    private boolean checkFocusNoStartInput() {
         // This is called a lot, so short-circuit before locking.
         if (mServedView == mNextServedView && !mNextServedNeedsStart) {
-            return;
+            return false;
         }
 
         InputConnection ic = null;
         synchronized (mH) {
             if (mServedView == mNextServedView && !mNextServedNeedsStart) {
-                return;
+                return false;
             }
             if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
                     + " next=" + mNextServedView
                     + " restart=" + mNextServedNeedsStart);
-            
+
             mNextServedNeedsStart = false;
             if (mNextServedView == null) {
                 finishInputLocked();
@@ -1156,22 +1166,22 @@
                 // but no longer do.  We should make sure the input method is
                 // no longer shown, since it serves no purpose.
                 closeCurrentInput();
-                return;
+                return false;
             }
-            
+
             ic = mServedInputConnection;
-            
+
             mServedView = mNextServedView;
             mCurrentTextBoxAttribute = null;
             mCompletions = null;
             mServedConnecting = true;
         }
-        
+
         if (ic != null) {
             ic.finishComposingText();
         }
-        
-        startInputInner();
+
+        return true;
     }
     
     void closeCurrentInput() {
@@ -1200,7 +1210,7 @@
             focusInLocked(focusedView != null ? focusedView : rootView);
         }
         
-        checkFocus();
+        boolean startInput = checkFocusNoStartInput();
         
         synchronized (mH) {
             try {
@@ -1212,6 +1222,10 @@
             } catch (RemoteException e) {
             }
         }
+
+        if (startInput) {
+            startInputInner();
+        }
     }
     
     /** @hide */
@@ -1593,6 +1607,27 @@
     }
 
     /**
+     * Force switch to the next input method and subtype. If there is no IME enabled except
+     * current IME and subtype, do nothing.
+     * @param imeToken Supplies the identifying token given to an input method when it was started,
+     * which allows it to perform this operation on itself.
+     * @param onlyCurrentIme if true, the framework will find the next subtype which
+     * belongs to the current IME
+     * @return true if the current input method and subtype was successfully switched to the next
+     * input method and subtype.
+     */
+    public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
+        synchronized (mH) {
+            try {
+                return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+                return false;
+            }
+        }
+    }
+
+    /**
      * Set additional input method subtypes. Only a process which shares the same uid with the IME
      * can add additional input method subtypes to the IME.
      * Please note that a subtype's status is stored in the system.
diff --git a/libs/rs/rsFifo.cpp b/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl
similarity index 69%
copy from libs/rs/rsFifo.cpp
copy to core/java/android/view/textservice/SentenceSuggestionsInfo.aidl
index 3d5d8c4..d0b6ba6 100644
--- a/libs/rs/rsFifo.cpp
+++ b/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012 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.
@@ -14,18 +14,6 @@
  * limitations under the License.
  */
 
-#include "rsFifoSocket.h"
-#include "utils/Timers.h"
-#include "utils/StopWatch.h"
+package android.view.textservice;
 
-using namespace android;
-using namespace android::renderscript;
-
-Fifo::Fifo() {
-
-}
-
-Fifo::~Fifo() {
-
-}
-
+parcelable SentenceSuggestionsInfo;
diff --git a/core/java/android/view/textservice/SentenceSuggestionsInfo.java b/core/java/android/view/textservice/SentenceSuggestionsInfo.java
new file mode 100644
index 0000000..8d7c6cf
--- /dev/null
+++ b/core/java/android/view/textservice/SentenceSuggestionsInfo.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2012 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 android.view.textservice;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * @hide
+ * This class contains a metadata of sentence level suggestions from the text service
+ */
+public final class SentenceSuggestionsInfo implements Parcelable {
+
+    private final SuggestionsInfo[] mSuggestionsInfos;
+    private final int[] mOffsets;
+    private final int[] mLengths;
+
+    /**
+     * Constructor.
+     * @param suggestionsInfos from the text service
+     * @param offsets the array of offsets of suggestions
+     * @param lengths the array of lengths of suggestions
+     */
+    public SentenceSuggestionsInfo(
+            SuggestionsInfo[] suggestionsInfos, int[] offsets, int[] lengths) {
+        if (suggestionsInfos == null || offsets == null || lengths == null) {
+            throw new NullPointerException();
+        }
+        if (suggestionsInfos.length != offsets.length || offsets.length != lengths.length) {
+            throw new IllegalArgumentException();
+        }
+        final int infoSize = suggestionsInfos.length;
+        mSuggestionsInfos = Arrays.copyOf(suggestionsInfos, infoSize);
+        mOffsets = Arrays.copyOf(offsets, infoSize);
+        mLengths = Arrays.copyOf(lengths, infoSize);
+    }
+
+    public SentenceSuggestionsInfo(Parcel source) {
+        final int infoSize = source.readInt();
+        mSuggestionsInfos = new SuggestionsInfo[infoSize];
+        source.readTypedArray(mSuggestionsInfos, SuggestionsInfo.CREATOR);
+        mOffsets = new int[mSuggestionsInfos.length];
+        source.readIntArray(mOffsets);
+        mLengths = new int[mSuggestionsInfos.length];
+        source.readIntArray(mLengths);
+    }
+
+    /**
+     * Used to package this object into a {@link Parcel}.
+     *
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        final int infoSize = mSuggestionsInfos.length;
+        dest.writeInt(infoSize);
+        dest.writeTypedArray(mSuggestionsInfos, 0);
+        dest.writeIntArray(mOffsets);
+        dest.writeIntArray(mLengths);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @hide
+     */
+    public SuggestionsInfo getSuggestionsInfoAt(int i) {
+        if (i >= 0 && i < mSuggestionsInfos.length) {
+            return mSuggestionsInfos[i];
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public int getOffsetAt(int i) {
+        if (i >= 0 && i < mOffsets.length) {
+            return mOffsets[i];
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    public int getLengthAt(int i) {
+        if (i >= 0 && i < mLengths.length) {
+            return mLengths[i];
+        }
+        return -1;
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final Parcelable.Creator<SentenceSuggestionsInfo> CREATOR
+            = new Parcelable.Creator<SentenceSuggestionsInfo>() {
+        @Override
+        public SentenceSuggestionsInfo createFromParcel(Parcel source) {
+            return new SentenceSuggestionsInfo(source);
+        }
+
+        @Override
+        public SentenceSuggestionsInfo[] newArray(int size) {
+            return new SentenceSuggestionsInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index f6418ce..3491a537 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -115,7 +115,7 @@
                     handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
                     break;
                 case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE:
-                    handleOnGetSuggestionsMultipleForSentence((SuggestionsInfo[]) msg.obj);
+                    handleOnGetSentenceSuggestionsMultiple((SentenceSuggestionsInfo[]) msg.obj);
                     break;
             }
         }
@@ -180,8 +180,8 @@
     /**
      * @hide
      */
-    public void getSuggestionsForSentence(TextInfo textInfo, int suggestionsLimit) {
-        mSpellCheckerSessionListenerImpl.getSuggestionsMultipleForSentence(
+    public void getSentenceSuggestions(TextInfo textInfo, int suggestionsLimit) {
+        mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple(
                 new TextInfo[] {textInfo}, suggestionsLimit);
     }
 
@@ -214,8 +214,8 @@
         mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
     }
 
-    private void handleOnGetSuggestionsMultipleForSentence(SuggestionsInfo[] suggestionInfos) {
-        mSpellCheckerSessionListener.onGetSuggestionsForSentence(suggestionInfos);
+    private void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionInfos) {
+        mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionInfos);
     }
 
     private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
@@ -285,7 +285,7 @@
                             throw new IllegalArgumentException();
                         }
                         try {
-                            session.onGetSuggestionsMultipleForSentence(
+                            session.onGetSentenceSuggestionsMultiple(
                                     scp.mTextInfos, scp.mSuggestionsLimit);
                         } catch (RemoteException e) {
                             Log.e(TAG, "Failed to get suggestions " + e);
@@ -366,9 +366,9 @@
                             suggestionsLimit, sequentialWords));
         }
 
-        public void getSuggestionsMultipleForSentence(TextInfo[] textInfos, int suggestionsLimit) {
+        public void getSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) {
             if (DBG) {
-                Log.w(TAG, "getSuggestionsMultipleForSentence");
+                Log.w(TAG, "getSentenceSuggestionsMultiple");
             }
             processOrEnqueueTask(
                     new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE,
@@ -399,8 +399,8 @@
                         while (!mPendingTasks.isEmpty()) {
                             final SpellCheckerParams tmp = mPendingTasks.poll();
                             if (tmp.mWhat == TASK_CLOSE) {
-                                // Only one close task should be processed, while we need to remove all
-                                // close tasks from the queue
+                                // Only one close task should be processed, while we need to remove
+                                // all close tasks from the queue
                                 closeTask = tmp;
                             }
                         }
@@ -426,7 +426,7 @@
         }
 
         @Override
-        public void onGetSuggestionsForSentence(SuggestionsInfo[] results) {
+        public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
             mHandler.sendMessage(
                     Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
         }
@@ -444,7 +444,7 @@
         /**
          * @hide
          */
-        public void onGetSuggestionsForSentence(SuggestionsInfo[] results);
+        public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results);
     }
 
     private static class InternalListener extends ITextServicesSessionListener.Stub {
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index 9b99770..78bc1a9 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -21,14 +21,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.Arrays;
-
 /**
  * This class contains a metadata of suggestions from the text service
  */
 public final class SuggestionsInfo implements Parcelable {
     private static final String[] EMPTY = ArrayUtils.emptyArray(String.class);
-    private static final int NOT_A_LENGTH = -1;
 
     /**
      * Flag of the attributes of the suggestions that can be obtained by
@@ -50,8 +47,6 @@
     public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 0x0004;
     private final int mSuggestionsAttributes;
     private final String[] mSuggestions;
-    private final int[] mStartPosArray;
-    private final int[] mLengthArray;
     private final boolean mSuggestionsAvailable;
     private int mCookie;
     private int mSequence;
@@ -74,46 +69,12 @@
      */
     public SuggestionsInfo(
             int suggestionsAttributes, String[] suggestions, int cookie, int sequence) {
-        this(suggestionsAttributes, suggestions, cookie, sequence, null, null);
-    }
-
-    /**
-     * @hide
-     * Constructor.
-     * @param suggestionsAttributes from the text service
-     * @param suggestions from the text service
-     * @param cookie the cookie of the input TextInfo
-     * @param sequence the cookie of the input TextInfo
-     * @param startPosArray the array of start positions of suggestions
-     * @param lengthArray the array of length of suggestions
-     */
-    public SuggestionsInfo(
-            int suggestionsAttributes, String[] suggestions, int cookie, int sequence,
-            int[] startPosArray, int[] lengthArray) {
-        final int suggestsLen;
         if (suggestions == null) {
             mSuggestions = EMPTY;
             mSuggestionsAvailable = false;
-            suggestsLen = 0;
-            mStartPosArray = new int[0];
-            mLengthArray = new int[0];
         } else {
             mSuggestions = suggestions;
             mSuggestionsAvailable = true;
-            suggestsLen = suggestions.length;
-            if (startPosArray == null || lengthArray == null) {
-                mStartPosArray = new int[suggestsLen];
-                mLengthArray = new int[suggestsLen];
-                for (int i = 0; i < suggestsLen; ++i) {
-                    mStartPosArray[i] = 0;
-                    mLengthArray[i] = NOT_A_LENGTH;
-                }
-            } else if (suggestsLen != startPosArray.length || suggestsLen != lengthArray.length) {
-                throw new IllegalArgumentException();
-            } else {
-                mStartPosArray = Arrays.copyOf(startPosArray, suggestsLen);
-                mLengthArray = Arrays.copyOf(lengthArray, suggestsLen);
-            }
         }
         mSuggestionsAttributes = suggestionsAttributes;
         mCookie = cookie;
@@ -126,10 +87,6 @@
         mCookie = source.readInt();
         mSequence = source.readInt();
         mSuggestionsAvailable = source.readInt() == 1;
-        mStartPosArray = new int[mSuggestions.length];
-        mLengthArray = new int[mSuggestions.length];
-        source.readIntArray(mStartPosArray);
-        source.readIntArray(mLengthArray);
     }
 
     /**
@@ -145,8 +102,6 @@
         dest.writeInt(mCookie);
         dest.writeInt(mSequence);
         dest.writeInt(mSuggestionsAvailable ? 1 : 0);
-        dest.writeIntArray(mStartPosArray);
-        dest.writeIntArray(mLengthArray);
     }
 
     /**
@@ -227,24 +182,4 @@
     public int describeContents() {
         return 0;
     }
-
-    /**
-     * @hide
-     */
-    public int getSuggestionStartPosAt(int i) {
-        if (i >= 0 && i < mStartPosArray.length) {
-            return mStartPosArray[i];
-        }
-        return -1;
-    }
-
-    /**
-     * @hide
-     */
-    public int getSuggestionLengthAt(int i) {
-        if (i >= 0 && i < mLengthArray.length) {
-            return mLengthArray[i];
-        }
-        return -1;
-    }
 }
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 6fddb1a..8ccc59c7 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -471,18 +471,6 @@
     }
 
     /**
-     * We have received an SSL certificate for the main top-level page.
-     * Used by the Android HTTP stack only.
-     */
-    void certificate(SslCertificate certificate) {
-        if (mIsMainFrame) {
-            // we want to make this call even if the certificate is null
-            // (ie, the site is not secure)
-            mCallbackProxy.onReceivedCertificate(certificate);
-        }
-    }
-
-    /**
      * Destroy all native components of the BrowserFrame.
      */
     public void destroy() {
@@ -515,10 +503,6 @@
                         }
                     }
                 }
-                if (!JniUtil.useChromiumHttpStack()) {
-                    WebViewWorker.getHandler().sendEmptyMessage(
-                            WebViewWorker.MSG_TRIM_CACHE);
-                }
                 break;
             }
 
@@ -767,10 +751,9 @@
         } else if (mSettings.getAllowContentAccess() &&
                    url.startsWith(ANDROID_CONTENT)) {
             try {
-                // Strip off mimetype, for compatibility with ContentLoader.java
-                // If we don't do this, we can fail to load Gmail attachments,
-                // because the URL being loaded doesn't exactly match the URL we
-                // have permission to read.
+                // Strip off MIME type. If we don't do this, we can fail to
+                // load Gmail attachments, because the URL being loaded doesn't
+                // exactly match the URL we have permission to read.
                 int mimeIndex = url.lastIndexOf('?');
                 if (mimeIndex != -1) {
                     url = url.substring(0, mimeIndex);
@@ -787,101 +770,11 @@
     }
 
     /**
-     * Start loading a resource.
-     * @param loaderHandle The native ResourceLoader that is the target of the
-     *                     data.
-     * @param url The url to load.
-     * @param method The http method.
-     * @param headers The http headers.
-     * @param postData If the method is "POST" postData is sent as the request
-     *                 body. Is null when empty.
-     * @param postDataIdentifier If the post data contained form this is the form identifier, otherwise it is 0.
-     * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode
-     * @param mainResource True if the this resource is the main request, not a supporting resource
-     * @param userGesture
-     * @param synchronous True if the load is synchronous.
-     * @return A newly created LoadListener object.
-     */
-    private LoadListener startLoadingResource(int loaderHandle,
-                                              String url,
-                                              String method,
-                                              HashMap headers,
-                                              byte[] postData,
-                                              long postDataIdentifier,
-                                              int cacheMode,
-                                              boolean mainResource,
-                                              boolean userGesture,
-                                              boolean synchronous,
-                                              String username,
-                                              String password) {
-        if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) {
-            cacheMode = mSettings.getCacheMode();
-        }
-
-        if (method.equals("POST")) {
-            // Don't use the cache on POSTs when issuing a normal POST
-            // request.
-            if (cacheMode == WebSettings.LOAD_NORMAL) {
-                cacheMode = WebSettings.LOAD_NO_CACHE;
-            }
-            String[] ret = getUsernamePassword();
-            if (ret != null) {
-                String domUsername = ret[0];
-                String domPassword = ret[1];
-                maybeSavePassword(postData, domUsername, domPassword);
-            }
-        }
-
-        // is this resource the main-frame top-level page?
-        boolean isMainFramePage = mIsMainFrame;
-
-        if (DebugFlags.BROWSER_FRAME) {
-            Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method="
-                    + method + ", postData=" + postData + ", isMainFramePage="
-                    + isMainFramePage + ", mainResource=" + mainResource
-                    + ", userGesture=" + userGesture);
-        }
-
-        // Create a LoadListener
-        LoadListener loadListener = LoadListener.getLoadListener(mContext,
-                this, url, loaderHandle, synchronous, isMainFramePage,
-                mainResource, userGesture, postDataIdentifier, username, password);
-
-        if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) {
-            // send an error message, so that loadListener can be deleted
-            // after this is returned. This is important as LoadListener's 
-            // nativeError will remove the request from its DocLoader's request
-            // list. But the set up is not done until this method is returned.
-            loadListener.error(
-                    android.net.http.EventHandler.ERROR, mContext.getString(
-                            com.android.internal.R.string.httpErrorTooManyRequests));
-            return loadListener;
-        }
-
-        // Note that we are intentionally skipping
-        // inputStreamForAndroidResource.  This is so that FrameLoader will use
-        // the various StreamLoader classes to handle assets.
-        FrameLoader loader = new FrameLoader(loadListener, mSettings, method,
-                mCallbackProxy.shouldInterceptRequest(url));
-        loader.setHeaders(headers);
-        loader.setPostData(postData);
-        // Set the load mode to the mode used for the current page.
-        // If WebKit wants validation, go to network directly.
-        loader.setCacheMode(headers.containsKey("If-Modified-Since")
-                || headers.containsKey("If-None-Match") ? 
-                        WebSettings.LOAD_NO_CACHE : cacheMode);
-        loader.executeLoad();
-        // Set referrer to current URL?
-        return !synchronous ? loadListener : null;
-    }
-
-    /**
      * If this looks like a POST request (form submission) containing a username
      * and password, give the user the option of saving them. Will either do
      * nothing, or block until the UI interaction is complete.
      *
-     * Called by startLoadingResource when using the Apache HTTP stack.
-     * Called directly by WebKit when using the Chrome HTTP stack.
+     * Called directly by WebKit.
      *
      * @param postData The data about to be sent as the body of a POST request.
      * @param username The username entered by the user (sniffed from the DOM).
@@ -1351,15 +1244,6 @@
     private native void nativeAddJavascriptInterface(int nativeFramePointer,
             Object obj, String interfaceName);
 
-    /**
-     * Enable or disable the native cache.
-     */
-    /* FIXME: The native cache is always on for now until we have a better
-     * solution for our 2 caches. */
-    private native void setCacheDisabled(boolean disabled);
-
-    public native boolean cacheDisabled();
-
     public native void clearCache();
 
     /**
diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java
deleted file mode 100644
index 0721045..0000000
--- a/core/java/android/webkit/CacheLoader.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.
- */
-
-package android.webkit;
-
-import android.net.http.Headers;
-import android.text.TextUtils;
-import android.webkit.JniUtil;
-
-/**
- * This class is a concrete implementation of StreamLoader that uses a
- * CacheResult as the source for the stream. The CacheResult stored mimetype
- * and encoding is added to the HTTP response headers.
- */
-class CacheLoader extends StreamLoader {
-
-    CacheManager.CacheResult mCacheResult;  // Content source
-
-    /**
-     * Constructs a CacheLoader for use when loading content from the cache.
-     *
-     * @param loadListener LoadListener to pass the content to
-     * @param result CacheResult used as the source for the content.
-     */
-    CacheLoader(LoadListener loadListener, CacheManager.CacheResult result) {
-        super(loadListener);
-
-        assert !JniUtil.useChromiumHttpStack();
-
-        mCacheResult = result;
-    }
-
-    @Override
-    protected boolean setupStreamAndSendStatus() {
-        mDataStream = mCacheResult.inStream;
-        mContentLength = mCacheResult.contentLength;
-        mLoadListener.status(1, 1, mCacheResult.httpStatusCode, "OK");
-        return true;
-    }
-
-    @Override
-    protected void buildHeaders(Headers headers) {
-        StringBuilder sb = new StringBuilder(mCacheResult.mimeType);
-        if (!TextUtils.isEmpty(mCacheResult.encoding)) {
-            sb.append(';');
-            sb.append(mCacheResult.encoding);
-        }
-        headers.setContentType(sb.toString());
-
-        if (!TextUtils.isEmpty(mCacheResult.location)) {
-            headers.setLocation(mCacheResult.location);
-        }
-
-        if (!TextUtils.isEmpty(mCacheResult.expiresString)) {
-            headers.setExpires(mCacheResult.expiresString);
-        }
-
-        if (!TextUtils.isEmpty(mCacheResult.contentdisposition)) {
-            headers.setContentDisposition(mCacheResult.contentdisposition);
-        }
-
-        if (!TextUtils.isEmpty(mCacheResult.crossDomain)) {
-            headers.setXPermittedCrossDomainPolicies(mCacheResult.crossDomain);
-        }
-    }
-}
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index e21a02e..6a85e00 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -37,18 +37,18 @@
 import com.android.org.bouncycastle.crypto.digests.SHA1Digest;
 
 /**
- * The class CacheManager provides the persistent cache of content that is
- * received over the network. The component handles parsing of HTTP headers and
- * utilizes the relevant cache headers to determine if the content should be
- * stored and if so, how long it is valid for. Network requests are provided to
- * this component and if they can not be resolved by the cache, the HTTP headers
- * are attached, as appropriate, to the request for revalidation of content. The
- * class also manages the cache size.
- *
- * CacheManager may only be used if your activity contains a WebView.
- *
+ * Manages the HTTP cache used by an application's {@link WebView} instances.
  * @deprecated Access to the HTTP cache will be removed in a future release.
  */
+// The class CacheManager provides the persistent cache of content that is
+// received over the network. The component handles parsing of HTTP headers and
+// utilizes the relevant cache headers to determine if the content should be
+// stored and if so, how long it is valid for. Network requests are provided to
+// this component and if they can not be resolved by the cache, the HTTP headers
+// are attached, as appropriate, to the request for revalidation of content. The
+// class also manages the cache size.
+//
+// CacheManager may only be used if your activity contains a WebView.
 @Deprecated
 public final class CacheManager {
 
@@ -57,40 +57,12 @@
     static final String HEADER_KEY_IFMODIFIEDSINCE = "if-modified-since";
     static final String HEADER_KEY_IFNONEMATCH = "if-none-match";
 
-    private static final String NO_STORE = "no-store";
-    private static final String NO_CACHE = "no-cache";
-    private static final String MAX_AGE = "max-age";
-    private static final String MANIFEST_MIME = "text/cache-manifest";
-
-    private static long CACHE_THRESHOLD = 6 * 1024 * 1024;
-    private static long CACHE_TRIM_AMOUNT = 2 * 1024 * 1024;
-
-    // Limit the maximum cache file size to half of the normal capacity
-    static long CACHE_MAX_SIZE = (CACHE_THRESHOLD - CACHE_TRIM_AMOUNT) / 2;
-
-    private static boolean mDisabled;
-
-    // Reference count the enable/disable transaction
-    private static int mRefCount;
-
-    // trimCacheIfNeeded() is called when a page is fully loaded. But JavaScript
-    // can load the content, e.g. in a slideshow, continuously, so we need to
-    // trim the cache on a timer base too. endCacheTransaction() is called on a 
-    // timer base. We share the same timer with less frequent update.
-    private static int mTrimCacheCount = 0;
-    private static final int TRIM_CACHE_INTERVAL = 5;
-
-    private static WebViewDatabase mDataBase;
     private static File mBaseDir;
     
-    // Flag to clear the cache when the CacheManager is initialized
-    private static boolean mClearCacheOnInit = false;
-
     /**
-     * This class represents a resource retrieved from the HTTP cache.
-     * Instances of this class can be obtained by invoking the
-     * CacheManager.getCacheFile() method.
-     *
+     * Represents a resource stored in the HTTP cache. Instances of this class
+     * can be obtained by calling
+     * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>))}.
      * @deprecated Access to the HTTP cache will be removed in a future release.
      */
     @Deprecated
@@ -114,64 +86,136 @@
         OutputStream outStream;
         File outFile;
 
+        /**
+         * Gets the status code of this cache entry.
+         * @return The status code of this cache entry
+         */
         public int getHttpStatusCode() {
             return httpStatusCode;
         }
 
+        /**
+         * Gets the content length of this cache entry.
+         * @return The content length of this cache entry
+         */
         public long getContentLength() {
             return contentLength;
         }
 
+        /**
+         * Gets the path of the file used to store the content of this cache
+         * entry, relative to the base directory of the cache. See
+         * {@link CacheManager#getCacheFileBaseDir CacheManager.getCacheFileBaseDir()}.
+         * @return The path of the file used to store this cache entry
+         */
         public String getLocalPath() {
             return localPath;
         }
 
+        /**
+         * Gets the expiry date of this cache entry, expressed in milliseconds
+         * since midnight, January 1, 1970 UTC.
+         * @return The expiry date of this cache entry
+         */
         public long getExpires() {
             return expires;
         }
 
+        /**
+         * Gets the expiry date of this cache entry, expressed as a string.
+         * @return The expiry date of this cache entry
+         *
+         */
         public String getExpiresString() {
             return expiresString;
         }
 
+        /**
+         * Gets the date at which this cache entry was last modified, expressed
+         * as a string.
+         * @return The date at which this cache entry was last modified
+         */
         public String getLastModified() {
             return lastModified;
         }
 
+        /**
+         * Gets the entity tag of this cache entry.
+         * @return The entity tag of this cache entry
+         */
         public String getETag() {
             return etag;
         }
 
+        /**
+         * Gets the MIME type of this cache entry.
+         * @return The MIME type of this cache entry
+         */
         public String getMimeType() {
             return mimeType;
         }
 
+        /**
+         * Gets the value of the HTTP 'Location' header with which this cache
+         * entry was received.
+         * @return The HTTP 'Location' header for this cache entry
+         */
         public String getLocation() {
             return location;
         }
 
+        /**
+         * Gets the encoding of this cache entry.
+         * @return The encoding of this cache entry
+         */
         public String getEncoding() {
             return encoding;
         }
 
+        /**
+         * Gets the value of the HTTP 'Content-Disposition' header with which
+         * this cache entry was received.
+         * @return The HTTP 'Content-Disposition' header for this cache entry
+         *
+         */
         public String getContentDisposition() {
             return contentdisposition;
         }
 
-        // For out-of-package access to the underlying streams.
+        /**
+         * Gets the input stream to the content of this cache entry, to allow
+         * content to be read. See
+         * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>)}.
+         * @return An input stream to the content of this cache entry
+         */
         public InputStream getInputStream() {
             return inStream;
         }
 
+        /**
+         * Gets an output stream to the content of this cache entry, to allow
+         * content to be written. See
+         * {@link CacheManager#saveCacheFile CacheManager.saveCacheFile(String, CacheResult)}.
+         * @return An output stream to the content of this cache entry
+         */
+        // Note that this is always null for objects returned by getCacheFile()!
         public OutputStream getOutputStream() {
             return outStream;
         }
 
-        // These fields can be set manually.
+
+        /**
+         * Sets an input stream to the content of this cache entry.
+         * @param stream An input stream to the content of this cache entry
+         */
         public void setInputStream(InputStream stream) {
             this.inStream = stream;
         }
 
+        /**
+         * Sets the encoding of this cache entry.
+         * @param encoding The encoding of this cache entry
+         */
         public void setEncoding(String encoding) {
             this.encoding = encoding;
         }
@@ -185,70 +229,25 @@
     }
 
     /**
-     * Initialize the CacheManager.
-     *
-     * Note that this is called automatically when a {@link android.webkit.WebView} is created.
-     *
-     * @param context The application context.
+     * Initializes the HTTP cache. This method must be called before any
+     * CacheManager methods are used. Note that this is called automatically
+     * when a {@link WebView} is created.
+     * @param context The application context
      */
     static void init(Context context) {
-        if (JniUtil.useChromiumHttpStack()) {
-            // This isn't actually where the real cache lives, but where we put files for the
-            // purpose of getCacheFile().
-            mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging");
-            if (!mBaseDir.exists()) {
-                mBaseDir.mkdirs();
-            }
-            return;
-        }
-
-        mDataBase = WebViewDatabase.getInstance(context.getApplicationContext());
-        mBaseDir = new File(context.getCacheDir(), "webviewCache");
-        if (createCacheDirectory() && mClearCacheOnInit) {
-            removeAllCacheFiles();
-            mClearCacheOnInit = false;
-        }
-    }
-
-    /**
-     * Create the cache directory if it does not already exist.
-     *
-     * @return true if the cache directory didn't exist and was created.
-     */
-    static private boolean createCacheDirectory() {
-        assert !JniUtil.useChromiumHttpStack();
-
+        // This isn't actually where the real cache lives, but where we put files for the
+        // purpose of getCacheFile().
+        mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging");
         if (!mBaseDir.exists()) {
-            if(!mBaseDir.mkdirs()) {
-                Log.w(LOGTAG, "Unable to create webviewCache directory");
-                return false;
-            }
-            FileUtils.setPermissions(
-                    mBaseDir.toString(),
-                    FileUtils.S_IRWXU | FileUtils.S_IRWXG,
-                    -1, -1);
-            // If we did create the directory, we need to flush
-            // the cache database. The directory could be recreated
-            // because the system flushed all the data/cache directories
-            // to free up disk space.
-            // delete rows in the cache database
-            WebViewWorker.getHandler().sendEmptyMessage(
-                    WebViewWorker.MSG_CLEAR_CACHE);
-            return true;
+            mBaseDir.mkdirs();
         }
-        return false;
     }
 
     /**
-     * Get the base directory of the cache. Together with the local path of the CacheResult,
-     * obtained from {@link android.webkit.CacheManager.CacheResult#getLocalPath}, this
-     * identifies the cache file.
-     *
-     * Cache files are not guaranteed to be in this directory before
-     * CacheManager#getCacheFile(String, Map<String, String>) is called.
-     *
-     * @return File The base directory of the cache.
-     *
+     * Gets the base directory in which the files used to store the contents of
+     * cache entries are placed. See
+     * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}.
+     * @return The base directory of the cache
      * @deprecated Access to the HTTP cache will be removed in a future release.
      */
     @Deprecated
@@ -257,93 +256,32 @@
     }
 
     /**
-     * Sets whether the cache is disabled.
-     *
-     * @param disabled Whether the cache should be disabled
-     */
-    static void setCacheDisabled(boolean disabled) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (disabled == mDisabled) {
-            return;
-        }
-        mDisabled = disabled;
-        if (mDisabled) {
-            removeAllCacheFiles();
-        }
-    }
-
-    /**
-     * Whether the cache is disabled.
-     *
-     * @return return Whether the cache is disabled
-     *
+     * Gets whether the HTTP cache is disabled.
+     * @return True if the HTTP cache is disabled
      * @deprecated Access to the HTTP cache will be removed in a future release.
      */
     @Deprecated
     public static boolean cacheDisabled() {
-        return mDisabled;
-    }
-
-    // only called from WebViewWorkerThread
-    // make sure to call enableTransaction/disableTransaction in pair
-    static boolean enableTransaction() {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (++mRefCount == 1) {
-            mDataBase.startCacheTransaction();
-            return true;
-        }
         return false;
     }
 
-    // only called from WebViewWorkerThread
-    // make sure to call enableTransaction/disableTransaction in pair
-    static boolean disableTransaction() {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (--mRefCount == 0) {
-            mDataBase.endCacheTransaction();
-            return true;
-        }
-        return false;
-    }
-
-    // only called from WebViewWorkerThread
-    // make sure to call startTransaction/endTransaction in pair
-    static boolean startTransaction() {
-        assert !JniUtil.useChromiumHttpStack();
-
-        return mDataBase.startCacheTransaction();
-    }
-
-    // only called from WebViewWorkerThread
-    // make sure to call startTransaction/endTransaction in pair
-    static boolean endTransaction() {
-        assert !JniUtil.useChromiumHttpStack();
-
-        boolean ret = mDataBase.endCacheTransaction();
-        if (++mTrimCacheCount >= TRIM_CACHE_INTERVAL) {
-            mTrimCacheCount = 0;
-            trimCacheIfNeeded();
-        }
-        return ret;
-    }
-
-    // only called from WebCore Thread
-    // make sure to call startCacheTransaction/endCacheTransaction in pair
     /**
-     * @deprecated Always returns false.
+     * Starts a cache transaction. Returns true if this is the only running
+     * transaction. Otherwise, this transaction is nested inside currently
+     * running transactions and false is returned.
+     * @return True if this is the only running transaction
+     * @deprecated This method no longer has any effect and always returns false
      */
     @Deprecated
     public static boolean startCacheTransaction() {
         return false;
     }
 
-    // only called from WebCore Thread
-    // make sure to call startCacheTransaction/endCacheTransaction in pair
     /**
-     * @deprecated Always returns false.
+     * Ends the innermost cache transaction and returns whether this was the
+     * only running transaction.
+     * @return True if this was the only running transaction
+     * @deprecated This method no longer has any effect and always returns false
      */
     @Deprecated
     public static boolean endCacheTransaction() {
@@ -351,15 +289,15 @@
     }
 
     /**
-     * Given a URL, returns the corresponding CacheResult if it exists, or null otherwise.
-     *
-     * The input stream of the CacheEntry object is initialized and opened and should be closed by
-     * the caller when access to the underlying file is no longer required.
-     * If a non-zero value is provided for the headers map, and the cache entry needs validation,
-     * HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE will be set in headers.
-     *
-     * @return The CacheResult for the given URL
-     *
+     * Gets the cache entry for the specified URL, or null if none is found.
+     * If a non-null value is provided for the HTTP headers map, and the cache
+     * entry needs validation, appropriate headers will be added to the map.
+     * The input stream of the CacheEntry object should be closed by the caller
+     * when access to the underlying file is no longer required.
+     * @param url The URL for which a cache entry is requested
+     * @param headers A map from HTTP header name to value, to be populated
+     *                for the returned cache entry
+     * @return The cache entry for the specified URL
      * @deprecated Access to the HTTP cache will be removed in a future release.
      */
     @Deprecated
@@ -370,54 +308,22 @@
 
     static CacheResult getCacheFile(String url, long postIdentifier,
             Map<String, String> headers) {
-        if (mDisabled) {
-            return null;
-        }
-
-        if (JniUtil.useChromiumHttpStack()) {
-            CacheResult result = nativeGetCacheResult(url);
-            if (result == null) {
-                return null;
-            }
-            // A temporary local file will have been created native side and localPath set
-            // appropriately.
-            File src = new File(mBaseDir, result.localPath);
-            try {
-                // Open the file here so that even if it is deleted, the content
-                // is still readable by the caller until close() is called.
-                result.inStream = new FileInputStream(src);
-            } catch (FileNotFoundException e) {
-                Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e);
-                // TODO: The files in the cache directory can be removed by the
-                // system. If it is gone, what should we do?
-                return null;
-            }
-            return result;
-        }
-
-        String databaseKey = getDatabaseKey(url, postIdentifier);
-        CacheResult result = mDataBase.getCache(databaseKey);
+        CacheResult result = nativeGetCacheResult(url);
         if (result == null) {
             return null;
         }
-        if (result.contentLength == 0) {
-            if (!isCachableRedirect(result.httpStatusCode)) {
-                // This should not happen. If it does, remove it.
-                mDataBase.removeCache(databaseKey);
-                return null;
-            }
-        } else {
-            File src = new File(mBaseDir, result.localPath);
-            try {
-                // Open the file here so that even if it is deleted, the content
-                // is still readable by the caller until close() is called.
-                result.inStream = new FileInputStream(src);
-            } catch (FileNotFoundException e) {
-                // The files in the cache directory can be removed by the
-                // system. If it is gone, clean up the database.
-                mDataBase.removeCache(databaseKey);
-                return null;
-            }
+        // A temporary local file will have been created native side and localPath set
+        // appropriately.
+        File src = new File(mBaseDir, result.localPath);
+        try {
+            // Open the file here so that even if it is deleted, the content
+            // is still readable by the caller until close() is called.
+            result.inStream = new FileInputStream(src);
+        } catch (FileNotFoundException e) {
+            Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e);
+            // TODO: The files in the cache directory can be removed by the
+            // system. If it is gone, what should we do?
+            return null;
         }
 
         // A null value for headers is used by CACHE_MODE_CACHE_ONLY to imply
@@ -454,89 +360,23 @@
      * CacheResult, and is used to supply surrogate responses for URL
      * interception.
      * @return CacheResult for a given url
-     * @hide - hide createCacheFile since it has a parameter of type headers, which is
-     * in a hidden package.
-     *
-     * @deprecated Access to the HTTP cache will be removed in a future release.
      */
-    @Deprecated
-    public static CacheResult createCacheFile(String url, int statusCode,
-            Headers headers, String mimeType, boolean forceCache) {
-        if (JniUtil.useChromiumHttpStack()) {
-            // This method is public but hidden. We break functionality.
-            return null;
-        }
-
-        return createCacheFile(url, statusCode, headers, mimeType, 0,
-                forceCache);
-    }
-
     static CacheResult createCacheFile(String url, int statusCode,
-            Headers headers, String mimeType, long postIdentifier,
-            boolean forceCache) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (!forceCache && mDisabled) {
-            return null;
-        }
-
-        String databaseKey = getDatabaseKey(url, postIdentifier);
-
-        // according to the rfc 2616, the 303 response MUST NOT be cached.
-        if (statusCode == 303) {
-            // remove the saved cache if there is any
-            mDataBase.removeCache(databaseKey);
-            return null;
-        }
-
-        // like the other browsers, do not cache redirects containing a cookie
-        // header.
-        if (isCachableRedirect(statusCode) && !headers.getSetCookie().isEmpty()) {
-            // remove the saved cache if there is any
-            mDataBase.removeCache(databaseKey);
-            return null;
-        }
-
-        CacheResult ret = parseHeaders(statusCode, headers, mimeType);
-        if (ret == null) {
-            // this should only happen if the headers has "no-store" in the
-            // cache-control. remove the saved cache if there is any
-            mDataBase.removeCache(databaseKey);
-        } else {
-            setupFiles(databaseKey, ret);
-            try {
-                ret.outStream = new FileOutputStream(ret.outFile);
-            } catch (FileNotFoundException e) {
-                // This can happen with the system did a purge and our
-                // subdirectory has gone, so lets try to create it again
-                if (createCacheDirectory()) {
-                    try {
-                        ret.outStream = new FileOutputStream(ret.outFile);
-                    } catch  (FileNotFoundException e2) {
-                        // We failed to create the file again, so there
-                        // is something else wrong. Return null.
-                        return null;
-                    }
-                } else {
-                    // Failed to create cache directory
-                    return null;
-                }
-            }
-            ret.mimeType = mimeType;
-        }
-
-        return ret;
+            Headers headers, String mimeType, boolean forceCache) {
+        // This method is public but hidden. We break functionality.
+        return null;
     }
 
     /**
-     * Save the info of a cache file for a given url to the CacheMap so that it
-     * can be reused later
-     *
+     * Adds a cache entry to the HTTP cache for the specicifed URL. Also closes
+     * the cache entry's output stream.
+     * @param url The URL for which the cache entry should be added
+     * @param cacheResult The cache entry to add
      * @deprecated Access to the HTTP cache will be removed in a future release.
      */
     @Deprecated
-    public static void saveCacheFile(String url, CacheResult cacheRet) {
-        saveCacheFile(url, 0, cacheRet);
+    public static void saveCacheFile(String url, CacheResult cacheResult) {
+        saveCacheFile(url, 0, cacheResult);
     }
 
     static void saveCacheFile(String url, long postIdentifier,
@@ -547,54 +387,24 @@
             return;
         }
 
-        if (JniUtil.useChromiumHttpStack()) {
-            // This method is exposed in the public API but the API provides no way to obtain a
-            // new CacheResult object with a non-null output stream ...
-            // - CacheResult objects returned by getCacheFile() have a null output stream.
-            // - new CacheResult objects have a null output stream and no setter is provided.
-            // Since for the Android HTTP stack this method throws a null pointer exception in this
-            // case, this method is effectively useless from the point of view of the public API.
-
-            // We should already have thrown an exception above, to maintain 'backward
-            // compatibility' with the Android HTTP stack.
-            assert false;
-        }
-
-        if (!cacheRet.outFile.exists()) {
-            // the file in the cache directory can be removed by the system
-            return;
-        }
-
-        boolean redirect = isCachableRedirect(cacheRet.httpStatusCode);
-        if (redirect) {
-            // location is in database, no need to keep the file
-            cacheRet.contentLength = 0;
-            cacheRet.localPath = "";
-        }
-        if ((redirect || cacheRet.contentLength == 0)
-                && !cacheRet.outFile.delete()) {
-            Log.e(LOGTAG, cacheRet.outFile.getPath() + " delete failed.");
-        }
-        if (cacheRet.contentLength == 0) {
-            return;
-        }
-
-        mDataBase.addCache(getDatabaseKey(url, postIdentifier), cacheRet);
-
-        if (DebugFlags.CACHE_MANAGER) {
-            Log.v(LOGTAG, "saveCacheFile for url " + url);
-        }
-    }
-
-    static boolean cleanupCacheFile(CacheResult cacheRet) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        try {
-            cacheRet.outStream.close();
-        } catch (IOException e) {
-            return false;
-        }
-        return cacheRet.outFile.delete();
+        // This method is exposed in the public API but the API provides no
+        // way to obtain a new CacheResult object with a non-null output
+        // stream ...
+        // - CacheResult objects returned by getCacheFile() have a null
+        //   output stream.
+        // - new CacheResult objects have a null output stream and no
+        //   setter is provided.
+        // Since this method throws a null pointer exception in this case,
+        // it is effectively useless from the point of view of the public
+        // API.
+        //
+        // With the Chromium HTTP stack we continue to throw the same
+        // exception for 'backwards compatibility' with the Android HTTP
+        // stack.
+        //
+        // This method is not used from within this package, and for public API
+        // use, we should already have thrown an exception above.
+        assert false;
     }
 
     /**
@@ -603,21 +413,6 @@
      * @return Whether the removal succeeded.
      */
     static boolean removeAllCacheFiles() {
-        // Note, this is called before init() when the database is
-        // created or upgraded.
-        if (mBaseDir == null) {
-            // This method should not be called before init() when using the
-            // chrome http stack
-            assert !JniUtil.useChromiumHttpStack();
-            // Init() has not been called yet, so just flag that
-            // we need to clear the cache when init() is called.
-            mClearCacheOnInit = true;
-            return true;
-        }
-        // delete rows in the cache database
-        if (!JniUtil.useChromiumHttpStack())
-            WebViewWorker.getHandler().sendEmptyMessage(WebViewWorker.MSG_CLEAR_CACHE);
-
         // delete cache files in a separate thread to not block UI.
         final Runnable clearCache = new Runnable() {
             public void run() {
@@ -642,323 +437,5 @@
         return true;
     }
 
-    static void trimCacheIfNeeded() {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (mDataBase.getCacheTotalSize() > CACHE_THRESHOLD) {
-            List<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT);
-            int size = pathList.size();
-            for (int i = 0; i < size; i++) {
-                File f = new File(mBaseDir, pathList.get(i));
-                if (!f.delete()) {
-                    Log.e(LOGTAG, f.getPath() + " delete failed.");
-                }
-            }
-            // remove the unreferenced files in the cache directory
-            final List<String> fileList = mDataBase.getAllCacheFileNames();
-            if (fileList == null) return;
-            String[] toDelete = mBaseDir.list(new FilenameFilter() {
-                public boolean accept(File dir, String filename) {
-                    if (fileList.contains(filename)) {
-                        return false;
-                    } else {
-                        return true;
-                    }
-                }
-            });
-            if (toDelete == null) return;
-            size = toDelete.length;
-            for (int i = 0; i < size; i++) {
-                File f = new File(mBaseDir, toDelete[i]);
-                if (!f.delete()) {
-                    Log.e(LOGTAG, f.getPath() + " delete failed.");
-                }
-            }
-        }
-    }
-
-    static void clearCache() {
-        assert !JniUtil.useChromiumHttpStack();
-
-        // delete database
-        mDataBase.clearCache();
-    }
-
-    private static boolean isCachableRedirect(int statusCode) {
-        if (statusCode == 301 || statusCode == 302 || statusCode == 307) {
-            // as 303 can't be cached, we do not return true
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private static String getDatabaseKey(String url, long postIdentifier) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (postIdentifier == 0) return url;
-        return postIdentifier + url;
-    }
-
-    @SuppressWarnings("deprecation")
-    private static void setupFiles(String url, CacheResult cacheRet) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (true) {
-            // Note: SHA1 is much stronger hash. But the cost of setupFiles() is
-            // 3.2% cpu time for a fresh load of nytimes.com. While a simple
-            // String.hashCode() is only 0.6%. If adding the collision resolving
-            // to String.hashCode(), it makes the cpu time to be 1.6% for a 
-            // fresh load, but 5.3% for the worst case where all the files 
-            // already exist in the file system, but database is gone. So it
-            // needs to resolve collision for every file at least once.
-            int hashCode = url.hashCode();
-            StringBuffer ret = new StringBuffer(8);
-            appendAsHex(hashCode, ret);
-            String path = ret.toString();
-            File file = new File(mBaseDir, path);
-            if (true) {
-                boolean checkOldPath = true;
-                // Check hash collision. If the hash file doesn't exist, just
-                // continue. There is a chance that the old cache file is not
-                // same as the hash file. As mDataBase.getCache() is more 
-                // expansive than "leak" a file until clear cache, don't bother.
-                // If the hash file exists, make sure that it is same as the 
-                // cache file. If it is not, resolve the collision.
-                while (file.exists()) {
-                    if (checkOldPath) {
-                        CacheResult oldResult = mDataBase.getCache(url);
-                        if (oldResult != null && oldResult.contentLength > 0) {
-                            if (path.equals(oldResult.localPath)) {
-                                path = oldResult.localPath;
-                            } else {
-                                path = oldResult.localPath;
-                                file = new File(mBaseDir, path);
-                            }
-                            break;
-                        }
-                        checkOldPath = false;
-                    }
-                    ret = new StringBuffer(8);
-                    appendAsHex(++hashCode, ret);
-                    path = ret.toString();
-                    file = new File(mBaseDir, path);
-                }
-            }
-            cacheRet.localPath = path;
-            cacheRet.outFile = file;
-        } else {
-            // get hash in byte[]
-            Digest digest = new SHA1Digest();
-            int digestLen = digest.getDigestSize();
-            byte[] hash = new byte[digestLen];
-            int urlLen = url.length();
-            byte[] data = new byte[urlLen];
-            url.getBytes(0, urlLen, data, 0);
-            digest.update(data, 0, urlLen);
-            digest.doFinal(hash, 0);
-            // convert byte[] to hex String
-            StringBuffer result = new StringBuffer(2 * digestLen);
-            for (int i = 0; i < digestLen; i = i + 4) {
-                int h = (0x00ff & hash[i]) << 24 | (0x00ff & hash[i + 1]) << 16
-                        | (0x00ff & hash[i + 2]) << 8 | (0x00ff & hash[i + 3]);
-                appendAsHex(h, result);
-            }
-            cacheRet.localPath = result.toString();
-            cacheRet.outFile = new File(mBaseDir, cacheRet.localPath);
-        }
-    }
-
-    private static void appendAsHex(int i, StringBuffer ret) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        String hex = Integer.toHexString(i);
-        switch (hex.length()) {
-            case 1:
-                ret.append("0000000");
-                break;
-            case 2:
-                ret.append("000000");
-                break;
-            case 3:
-                ret.append("00000");
-                break;
-            case 4:
-                ret.append("0000");
-                break;
-            case 5:
-                ret.append("000");
-                break;
-            case 6:
-                ret.append("00");
-                break;
-            case 7:
-                ret.append("0");
-                break;
-        }
-        ret.append(hex);
-    }
-
-    private static CacheResult parseHeaders(int statusCode, Headers headers,
-            String mimeType) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        // if the contentLength is already larger than CACHE_MAX_SIZE, skip it
-        if (headers.getContentLength() > CACHE_MAX_SIZE) return null;
-
-        // The HTML 5 spec, section 6.9.4, step 7.3 of the application cache
-        // process states that HTTP caching rules are ignored for the
-        // purposes of the application cache download process.
-        // At this point we can't tell that if a file is part of this process,
-        // except for the manifest, which has its own mimeType.
-        // TODO: work out a way to distinguish all responses that are part of
-        // the application download process and skip them.
-        if (MANIFEST_MIME.equals(mimeType)) return null;
-
-        // TODO: if authenticated or secure, return null
-        CacheResult ret = new CacheResult();
-        ret.httpStatusCode = statusCode;
-
-        ret.location = headers.getLocation();
-
-        ret.expires = -1;
-        ret.expiresString = headers.getExpires();
-        if (ret.expiresString != null) {
-            try {
-                ret.expires = AndroidHttpClient.parseDate(ret.expiresString);
-            } catch (IllegalArgumentException ex) {
-                // Take care of the special "-1" and "0" cases
-                if ("-1".equals(ret.expiresString)
-                        || "0".equals(ret.expiresString)) {
-                    // make it expired, but can be used for history navigation
-                    ret.expires = 0;
-                } else {
-                    Log.e(LOGTAG, "illegal expires: " + ret.expiresString);
-                }
-            }
-        }
-
-        ret.contentdisposition = headers.getContentDisposition();
-
-        ret.crossDomain = headers.getXPermittedCrossDomainPolicies();
-
-        // lastModified and etag may be set back to http header. So they can't
-        // be empty string.
-        String lastModified = headers.getLastModified();
-        if (lastModified != null && lastModified.length() > 0) {
-            ret.lastModified = lastModified;
-        }
-
-        String etag = headers.getEtag();
-        if (etag != null && etag.length() > 0) {
-            ret.etag = etag;
-        }
-
-        String cacheControl = headers.getCacheControl();
-        if (cacheControl != null) {
-            String[] controls = cacheControl.toLowerCase().split("[ ,;]");
-            boolean noCache = false;
-            for (int i = 0; i < controls.length; i++) {
-                if (NO_STORE.equals(controls[i])) {
-                    return null;
-                }
-                // According to the spec, 'no-cache' means that the content
-                // must be re-validated on every load. It does not mean that
-                // the content can not be cached. set to expire 0 means it
-                // can only be used in CACHE_MODE_CACHE_ONLY case
-                if (NO_CACHE.equals(controls[i])) {
-                    ret.expires = 0;
-                    noCache = true;
-                // if cache control = no-cache has been received, ignore max-age
-                // header, according to http spec:
-                // If a request includes the no-cache directive, it SHOULD NOT
-                // include min-fresh, max-stale, or max-age.
-                } else if (controls[i].startsWith(MAX_AGE) && !noCache) {
-                    int separator = controls[i].indexOf('=');
-                    if (separator < 0) {
-                        separator = controls[i].indexOf(':');
-                    }
-                    if (separator > 0) {
-                        String s = controls[i].substring(separator + 1);
-                        try {
-                            long sec = Long.parseLong(s);
-                            if (sec >= 0) {
-                                ret.expires = System.currentTimeMillis() + 1000
-                                        * sec;
-                            }
-                        } catch (NumberFormatException ex) {
-                            if ("1d".equals(s)) {
-                                // Take care of the special "1d" case
-                                ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000
-                            } else {
-                                Log.e(LOGTAG, "exception in parseHeaders for "
-                                        + "max-age:"
-                                        + controls[i].substring(separator + 1));
-                                ret.expires = 0;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // According to RFC 2616 section 14.32:
-        // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the
-        // client had sent "Cache-Control: no-cache"
-        if (NO_CACHE.equals(headers.getPragma())) {
-            ret.expires = 0;
-        }
-
-        // According to RFC 2616 section 13.2.4, if an expiration has not been
-        // explicitly defined a heuristic to set an expiration may be used.
-        if (ret.expires == -1) {
-            if (ret.httpStatusCode == 301) {
-                // If it is a permanent redirect, and it did not have an
-                // explicit cache directive, then it never expires
-                ret.expires = Long.MAX_VALUE;
-            } else if (ret.httpStatusCode == 302 || ret.httpStatusCode == 307) {
-                // If it is temporary redirect, expires
-                ret.expires = 0;
-            } else if (ret.lastModified == null) {
-                // When we have no last-modified, then expire the content with
-                // in 24hrs as, according to the RFC, longer time requires a
-                // warning 113 to be added to the response.
-
-                // Only add the default expiration for non-html markup. Some
-                // sites like news.google.com have no cache directives.
-                if (!mimeType.startsWith("text/html")) {
-                    ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000
-                } else {
-                    // Setting a expires as zero will cache the result for
-                    // forward/back nav.
-                    ret.expires = 0;
-                }
-            } else {
-                // If we have a last-modified value, we could use it to set the
-                // expiration. Suggestion from RFC is 10% of time since
-                // last-modified. As we are on mobile, loads are expensive,
-                // increasing this to 20%.
-
-                // 24 * 60 * 60 * 1000
-                long lastmod = System.currentTimeMillis() + 86400000;
-                try {
-                    lastmod = AndroidHttpClient.parseDate(ret.lastModified);
-                } catch (IllegalArgumentException ex) {
-                    Log.e(LOGTAG, "illegal lastModified: " + ret.lastModified);
-                }
-                long difference = System.currentTimeMillis() - lastmod;
-                if (difference > 0) {
-                    ret.expires = System.currentTimeMillis() + difference / 5;
-                } else {
-                    // last modified is in the future, expire the content
-                    // on the last modified
-                    ret.expires = lastmod;
-                }
-            }
-        }
-
-        return ret;
-    }
-
     private static native CacheResult nativeGetCacheResult(String url);
 }
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 75ee338..3a05bca 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -920,7 +920,6 @@
         if (PERF_PROBE) {
             mWebCoreThreadTime = SystemClock.currentThreadTimeMillis();
             mWebCoreIdleTime = 0;
-            Network.getInstance(mContext).startTiming();
             // un-comment this if PERF_PROBE is true
 //            Looper.myQueue().setWaitCallback(mIdleCallback);
         }
@@ -938,7 +937,6 @@
             Log.d("WebCore", "WebCore thread used " +
                     (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime)
                     + " ms and idled " + mWebCoreIdleTime + " ms");
-            Network.getInstance(mContext).stopTiming();
         }
         Message msg = obtainMessage(PAGE_FINISHED, url);
         sendMessage(msg);
diff --git a/core/java/android/webkit/ContentLoader.java b/core/java/android/webkit/ContentLoader.java
deleted file mode 100644
index d13210aa..0000000
--- a/core/java/android/webkit/ContentLoader.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.net.http.EventHandler;
-import android.net.http.Headers;
-import android.net.Uri;
-
-/**
- * This class is a concrete implementation of StreamLoader that loads
- * "content:" URIs
- */
-class ContentLoader extends StreamLoader {
-
-    private String mUrl;
-    private String mContentType;
-
-    /**
-     * Construct a ContentLoader with the specified content URI
-     *
-     * @param rawUrl "content:" url pointing to content to be loaded. This url
-     *               is the same url passed in to the WebView.
-     * @param loadListener LoadListener to pass the content to
-     */
-    ContentLoader(String rawUrl, LoadListener loadListener) {
-        super(loadListener);
-
-        /* strip off mimetype */
-        int mimeIndex = rawUrl.lastIndexOf('?');
-        if (mimeIndex != -1) {
-            mUrl = rawUrl.substring(0, mimeIndex);
-            mContentType = rawUrl.substring(mimeIndex + 1);
-        } else {
-            mUrl = rawUrl;
-        }
-
-    }
-
-    private String errString(Exception ex) {
-        String exMessage = ex.getMessage();
-        String errString = mContext.getString(
-                com.android.internal.R.string.httpErrorFileNotFound);
-        if (exMessage != null) {
-            errString += " " + exMessage;
-        }
-        return errString;
-    }
-
-    @Override
-    protected boolean setupStreamAndSendStatus() {
-        Uri uri = Uri.parse(mUrl);
-        if (uri == null) {
-            mLoadListener.error(
-                    EventHandler.FILE_NOT_FOUND_ERROR,
-                    mContext.getString(
-                            com.android.internal.R.string.httpErrorBadUrl) +
-                    " " + mUrl);
-            return false;
-        }
-
-        try {
-            mDataStream = mContext.getContentResolver().openInputStream(uri);
-            mLoadListener.status(1, 1, 200, "OK");
-        } catch (java.io.FileNotFoundException ex) {
-            mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
-            return false;
-        } catch (RuntimeException ex) {
-            // readExceptionWithFileNotFoundExceptionFromParcel in DatabaseUtils
-            // can throw a serial of RuntimeException. Catch them all here.
-            mLoadListener.error(EventHandler.FILE_ERROR, errString(ex));
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    protected void buildHeaders(Headers headers) {
-        if (mContentType != null) {
-            headers.setContentType("text/html");
-        }
-        // content can change, we don't want WebKit to cache it
-        headers.setCacheControl("no-store, no-cache");
-    }
-}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 9fa5593..497cab7 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -34,7 +34,8 @@
 import java.util.TreeSet;
 
 /**
- * CookieManager manages cookies according to RFC2109 spec.
+ * Manages the cookies used by an application's {@link WebView} instances.
+ * Cookies are manipulated according to RFC2109.
  */
 public final class CookieManager {
 
@@ -94,12 +95,7 @@
     //  max domain count to limit RAM cookie takes less than 100k,
     private static final int MAX_RAM_DOMAIN_COUNT = 15;
 
-    private Map<String, ArrayList<Cookie>> mCookieMap = new LinkedHashMap
-            <String, ArrayList<Cookie>>(MAX_DOMAIN_COUNT, 0.75f, true);
-
-    private boolean mAcceptCookie = true;
-
-    private int pendingCookieOperations = 0;
+    private int mPendingCookieOperations = 0;
 
     /**
      * This contains a list of 2nd-level domains that aren't allowed to have
@@ -246,12 +242,12 @@
     }
 
     /**
-     * Get a singleton CookieManager. If this is called before any
-     * {@link WebView} is created or outside of {@link WebView} context, the
-     * caller needs to call {@link CookieSyncManager#createInstance(Context)}
+     * Gets the singleton CookieManager instance. If this method is used
+     * before the application instantiates a {@link WebView} instance,
+     * {@link CookieSyncManager#createInstance(Context)} must be called
      * first.
      * 
-     * @return CookieManager
+     * @return The singleton CookieManager instance
      */
     public static synchronized CookieManager getInstance() {
         if (sRef == null) {
@@ -261,69 +257,45 @@
     }
 
     /**
-     * Control whether cookie is enabled or disabled
-     * @param accept TRUE if accept cookie
+     * Sets whether the application's {@link WebView} instances should send and
+     * accept cookies.
+     * @param accept Whether {@link WebView} instances should send and accept
+     *               cookies
      */
     public synchronized void setAcceptCookie(boolean accept) {
-        if (JniUtil.useChromiumHttpStack()) {
-            nativeSetAcceptCookie(accept);
-            return;
-        }
-
-        mAcceptCookie = accept;
+        nativeSetAcceptCookie(accept);
     }
 
     /**
-     * Return whether cookie is enabled
-     * @return TRUE if accept cookie
+     * Gets whether the application's {@link WebView} instances send and accept
+     * cookies.
+     * @return True if {@link WebView} instances send and accept cookies
      */
     public synchronized boolean acceptCookie() {
-        if (JniUtil.useChromiumHttpStack()) {
-            return nativeAcceptCookie();
-        }
-
-        return mAcceptCookie;
+        return nativeAcceptCookie();
     }
 
     /**
-     * Set cookie for a given url. The old cookie with same host/path/name will
-     * be removed. The new cookie will be added if it is not expired or it does
-     * not have expiration which implies it is session cookie.
-     * @param url The url which cookie is set for
-     * @param value The value for set-cookie: in http response header
+     * Sets a cookie for the given URL. Any existing cookie with the same host,
+     * path and name will be replaced with the new cookie. The cookie being set
+     * must not have expired and must not be a session cookie, otherwise it
+     * will be ignored.
+     * @param url The URL for which the cookie is set
+     * @param value The cookie as a string, using the format of the
+     *              'Set-Cookie' HTTP response header
      */
     public void setCookie(String url, String value) {
-        if (JniUtil.useChromiumHttpStack()) {
-            setCookie(url, value, false);
-            return;
-        }
-
-        WebAddress uri;
-        try {
-            uri = new WebAddress(url);
-        } catch (ParseException ex) {
-            Log.e(LOGTAG, "Bad address: " + url);
-            return;
-        }
-
-        setCookie(uri, value);
+        setCookie(url, value, false);
     }
 
     /**
-     * Set cookie for a given url. The old cookie with same host/path/name will
-     * be removed. The new cookie will be added if it is not expired or it does
-     * not have expiration which implies it is session cookie.
-     * @param url The url which cookie is set for
-     * @param value The value for set-cookie: in http response header
-     * @param privateBrowsing cookie jar to use
-     * @hide hiding private browsing
+     * See {@link setCookie(String, String)}
+     * @param url The URL for which the cookie is set
+     * @param value The value of the cookie, as a string, using the format of
+     *              the 'Set-Cookie' HTTP response header
+     * @param privateBrowsing Whether to use the private browsing cookie jar
      */
-    public void setCookie(String url, String value, boolean privateBrowsing) {
-        if (!JniUtil.useChromiumHttpStack()) {
-            setCookie(url, value);
-            return;
-        }
-
+    void setCookie(String url, String value, boolean privateBrowsing) {
         WebAddress uri;
         try {
             uri = new WebAddress(url);
@@ -336,155 +308,24 @@
     }
 
     /**
-     * Set cookie for a given uri. The old cookie with same host/path/name will
-     * be removed. The new cookie will be added if it is not expired or it does
-     * not have expiration which implies it is session cookie.
-     * @param uri The uri which cookie is set for
-     * @param value The value for set-cookie: in http response header
-     * @hide - hide this because it takes in a parameter of type WebAddress,
-     * a system private class.
-     */
-    public synchronized void setCookie(WebAddress uri, String value) {
-        if (JniUtil.useChromiumHttpStack()) {
-            nativeSetCookie(uri.toString(), value, false);
-            return;
-        }
-
-        if (value != null && value.length() > MAX_COOKIE_LENGTH) {
-            return;
-        }
-        if (!mAcceptCookie || uri == null) {
-            return;
-        }
-        if (DebugFlags.COOKIE_MANAGER) {
-            Log.v(LOGTAG, "setCookie: uri: " + uri + " value: " + value);
-        }
-
-        String[] hostAndPath = getHostAndPath(uri);
-        if (hostAndPath == null) {
-            return;
-        }
-        
-        // For default path, when setting a cookie, the spec says:
-        //Path:   Defaults to the path of the request URL that generated the
-        // Set-Cookie response, up to, but not including, the
-        // right-most /.
-        if (hostAndPath[1].length() > 1) {
-            int index = hostAndPath[1].lastIndexOf(PATH_DELIM);
-            hostAndPath[1] = hostAndPath[1].substring(0, 
-                    index > 0 ? index : index + 1);
-        }
-
-        ArrayList<Cookie> cookies = null;
-        try {
-            cookies = parseCookie(hostAndPath[0], hostAndPath[1], value);
-        } catch (RuntimeException ex) {
-            Log.e(LOGTAG, "parse cookie failed for: " + value);
-        }
-
-        if (cookies == null || cookies.size() == 0) {
-            return;
-        }
-
-        String baseDomain = getBaseDomain(hostAndPath[0]);
-        ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain);
-        if (cookieList == null) {
-            cookieList = CookieSyncManager.getInstance()
-                    .getCookiesForDomain(baseDomain);
-            mCookieMap.put(baseDomain, cookieList);
-        }
-
-        long now = System.currentTimeMillis();
-        int size = cookies.size();
-        for (int i = 0; i < size; i++) {
-            Cookie cookie = cookies.get(i);
-
-            boolean done = false;
-            Iterator<Cookie> iter = cookieList.iterator();
-            while (iter.hasNext()) {
-                Cookie cookieEntry = iter.next();
-                if (cookie.exactMatch(cookieEntry)) {
-                    // expires == -1 means no expires defined. Otherwise
-                    // negative means far future
-                    if (cookie.expires < 0 || cookie.expires > now) {
-                        // secure cookies can't be overwritten by non-HTTPS url
-                        if (!cookieEntry.secure || HTTPS.equals(uri.getScheme())) {
-                            cookieEntry.value = cookie.value;
-                            cookieEntry.expires = cookie.expires;
-                            cookieEntry.secure = cookie.secure;
-                            cookieEntry.lastAcessTime = now;
-                            cookieEntry.lastUpdateTime = now;
-                            cookieEntry.mode = Cookie.MODE_REPLACED;
-                        }
-                    } else {
-                        cookieEntry.lastUpdateTime = now;
-                        cookieEntry.mode = Cookie.MODE_DELETED;
-                    }
-                    done = true;
-                    break;
-                }
-            }
-
-            // expires == -1 means no expires defined. Otherwise negative means
-            // far future
-            if (!done && (cookie.expires < 0 || cookie.expires > now)) {
-                cookie.lastAcessTime = now;
-                cookie.lastUpdateTime = now;
-                cookie.mode = Cookie.MODE_NEW;
-                if (cookieList.size() > MAX_COOKIE_COUNT_PER_BASE_DOMAIN) {
-                    Cookie toDelete = new Cookie();
-                    toDelete.lastAcessTime = now;
-                    Iterator<Cookie> iter2 = cookieList.iterator();
-                    while (iter2.hasNext()) {
-                        Cookie cookieEntry2 = iter2.next();
-                        if ((cookieEntry2.lastAcessTime < toDelete.lastAcessTime)
-                                && cookieEntry2.mode != Cookie.MODE_DELETED) {
-                            toDelete = cookieEntry2;
-                        }
-                    }
-                    toDelete.mode = Cookie.MODE_DELETED;
-                }
-                cookieList.add(cookie);
-            }
-        }
-    }
-
-    /**
-     * Get cookie(s) for a given url so that it can be set to "cookie:" in http
-     * request header.
-     * @param url The url needs cookie
-     * @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
+     * Gets the cookies for the given URL.
+     * @param url The URL for which the cookies are requested
+     * @return value The cookies as a string, using the format of the 'Cookie'
+     *               HTTP request header
      */
     public String getCookie(String url) {
-        if (JniUtil.useChromiumHttpStack()) {
-            return getCookie(url, false);
-        }
-
-        WebAddress uri;
-        try {
-            uri = new WebAddress(url);
-        } catch (ParseException ex) {
-            Log.e(LOGTAG, "Bad address: " + url);
-            return null;
-        }
-
-        return getCookie(uri);
+        return getCookie(url, false);
     }
 
     /**
-     * Get cookie(s) for a given url so that it can be set to "cookie:" in http
-     * request header.
-     * @param url The url needs cookie
-     * @param privateBrowsing cookie jar to use
-     * @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
-     * @hide Private mode is not very well exposed for now
+     * See {@link getCookie(String)}
+     * @param url The URL for which the cookies are requested
+     * @param privateBrowsing Whether to use the private browsing cookie jar
+     * @return value The cookies as a string, using the format of the 'Cookie'
+     *               HTTP request header
+     * @hide Used by Browser, no intention to publish.
      */
     public String getCookie(String url, boolean privateBrowsing) {
-        if (!JniUtil.useChromiumHttpStack()) {
-            // Just redirect to regular get cookie for android stack
-            return getCookie(url);
-        }
-
         WebAddress uri;
         try {
             uri = new WebAddress(url);
@@ -499,93 +340,23 @@
     /**
      * Get cookie(s) for a given uri so that it can be set to "cookie:" in http
      * request header.
-     * @param uri The uri needs cookie
-     * @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
-     * @hide - hide this because it has a parameter of type WebAddress, which
-     * is a system private class.
+     * @param uri The WebAddress for which the cookies are requested
+     * @return value The cookies as a string, using the format of the 'Cookie'
+     *               HTTP request header
+     * @hide Used by RequestHandle, no intention to publish.
      */
     public synchronized String getCookie(WebAddress uri) {
-        if (JniUtil.useChromiumHttpStack()) {
-            return nativeGetCookie(uri.toString(), false);
-        }
-
-        if (!mAcceptCookie || uri == null) {
-            return null;
-        }
-   
-        String[] hostAndPath = getHostAndPath(uri);
-        if (hostAndPath == null) {
-            return null;
-        }
-
-        String baseDomain = getBaseDomain(hostAndPath[0]);
-        ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain);
-        if (cookieList == null) {
-            cookieList = CookieSyncManager.getInstance()
-                    .getCookiesForDomain(baseDomain);
-            mCookieMap.put(baseDomain, cookieList);
-        }
-
-        long now = System.currentTimeMillis();
-        boolean secure = HTTPS.equals(uri.getScheme());
-        Iterator<Cookie> iter = cookieList.iterator();
-
-        SortedSet<Cookie> cookieSet = new TreeSet<Cookie>(COMPARATOR);
-        while (iter.hasNext()) {
-            Cookie cookie = iter.next();
-            if (cookie.domainMatch(hostAndPath[0]) &&
-                    cookie.pathMatch(hostAndPath[1])
-                    // expires == -1 means no expires defined. Otherwise
-                    // negative means far future
-                    && (cookie.expires < 0 || cookie.expires > now)
-                    && (!cookie.secure || secure)
-                    && cookie.mode != Cookie.MODE_DELETED) {
-                cookie.lastAcessTime = now;
-                cookieSet.add(cookie);
-            }
-        }
-
-        StringBuilder ret = new StringBuilder(256);
-        Iterator<Cookie> setIter = cookieSet.iterator();
-        while (setIter.hasNext()) {
-            Cookie cookie = setIter.next();
-            if (ret.length() > 0) {
-                ret.append(SEMICOLON);
-                // according to RC2109, SEMICOLON is official separator,
-                // but when log in yahoo.com, it needs WHITE_SPACE too.
-                ret.append(WHITE_SPACE);
-            }
-
-            ret.append(cookie.name);
-            if (cookie.value != null) {
-                ret.append(EQUAL);
-                ret.append(cookie.value);
-            }
-        }
-
-        if (ret.length() > 0) {
-            if (DebugFlags.COOKIE_MANAGER) {
-                Log.v(LOGTAG, "getCookie: uri: " + uri + " value: " + ret);
-            }
-            return ret.toString();
-        } else {
-            if (DebugFlags.COOKIE_MANAGER) {
-                Log.v(LOGTAG, "getCookie: uri: " + uri
-                        + " But can't find cookie.");
-            }
-            return null;
-        }
+        return nativeGetCookie(uri.toString(), false);
     }
 
     /**
      * Waits for pending operations to completed.
-     * {@hide}  Too late to release publically.
      */
-    public void waitForCookieOperationsToComplete() {
+    void waitForCookieOperationsToComplete() {
         // Note that this function is applicable for both the java
         // and native http stacks, and works correctly with either.
         synchronized (this) {
-            while (pendingCookieOperations > 0) {
+            while (mPendingCookieOperations > 0) {
                 try {
                     wait();
                 } catch (InterruptedException e) { }
@@ -594,131 +365,59 @@
     }
 
     private synchronized void signalCookieOperationsComplete() {
-        pendingCookieOperations--;
-        assert pendingCookieOperations > -1;
+        mPendingCookieOperations--;
+        assert mPendingCookieOperations > -1;
         notify();
     }
 
     private synchronized void signalCookieOperationsStart() {
-        pendingCookieOperations++;
+        mPendingCookieOperations++;
     }
 
     /**
-     * Remove all session cookies, which are cookies without expiration date
+     * Removes all session cookies, which are cookies without an expiration
+     * date.
      */
     public void removeSessionCookie() {
         signalCookieOperationsStart();
-        if (JniUtil.useChromiumHttpStack()) {
-            new AsyncTask<Void, Void, Void>() {
-                protected Void doInBackground(Void... none) {
-                    nativeRemoveSessionCookie();
-                    signalCookieOperationsComplete();
-                    return null;
-                }
-            }.execute();
-            return;
-        }
-
-        final Runnable clearCache = new Runnable() {
-            public void run() {
-                synchronized(CookieManager.this) {
-                    Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
-                    Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
-                    while (listIter.hasNext()) {
-                        ArrayList<Cookie> list = listIter.next();
-                        Iterator<Cookie> iter = list.iterator();
-                        while (iter.hasNext()) {
-                            Cookie cookie = iter.next();
-                            if (cookie.expires == -1) {
-                                iter.remove();
-                            }
-                        }
-                    }
-                    CookieSyncManager.getInstance().clearSessionCookies();
-                    signalCookieOperationsComplete();
-                }
+        new AsyncTask<Void, Void, Void>() {
+            protected Void doInBackground(Void... none) {
+                nativeRemoveSessionCookie();
+                signalCookieOperationsComplete();
+                return null;
             }
-        };
-        new Thread(clearCache).start();
+        }.execute();
     }
 
     /**
-     * Remove all cookies
+     * Removes all cookies.
      */
     public void removeAllCookie() {
-        if (JniUtil.useChromiumHttpStack()) {
-            nativeRemoveAllCookie();
-            return;
-        }
-
-        final Runnable clearCache = new Runnable() {
-            public void run() {
-                synchronized(CookieManager.this) {
-                    mCookieMap = new LinkedHashMap<String, ArrayList<Cookie>>(
-                            MAX_DOMAIN_COUNT, 0.75f, true);
-                    CookieSyncManager.getInstance().clearAllCookies();
-                }
-            }
-        };
-        new Thread(clearCache).start();
+        nativeRemoveAllCookie();
     }
 
     /**
-     *  Return true if there are stored cookies.
+     * Gets whether there are stored cookies.
+     * @return True if there are stored cookies.
      */
     public synchronized boolean hasCookies() {
-        if (JniUtil.useChromiumHttpStack()) {
-            return hasCookies(false);
-        }
-
-        return CookieSyncManager.getInstance().hasCookies();
+        return hasCookies(false);
     }
 
     /**
-     *  Return true if there are stored cookies.
-     *  @param privateBrowsing cookie jar to use
-     *  @hide Hiding private mode
+     * See {@link hasCookies()}.
+     * @param privateBrowsing Whether to use the private browsing cookie jar
+     * @hide Used by Browser, no intention to publish.
      */
     public synchronized boolean hasCookies(boolean privateBrowsing) {
-        if (!JniUtil.useChromiumHttpStack()) {
-            return hasCookies();
-        }
-
         return nativeHasCookies(privateBrowsing);
     }
 
     /**
-     * Remove all expired cookies
+     * Removes all expired cookies.
      */
     public void removeExpiredCookie() {
-        if (JniUtil.useChromiumHttpStack()) {
-            nativeRemoveExpiredCookie();
-            return;
-        }
-
-        final Runnable clearCache = new Runnable() {
-            public void run() {
-                synchronized(CookieManager.this) {
-                    long now = System.currentTimeMillis();
-                    Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
-                    Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
-                    while (listIter.hasNext()) {
-                        ArrayList<Cookie> list = listIter.next();
-                        Iterator<Cookie> iter = list.iterator();
-                        while (iter.hasNext()) {
-                            Cookie cookie = iter.next();
-                            // expires == -1 means no expires defined. Otherwise 
-                            // negative means far future
-                            if (cookie.expires > 0 && cookie.expires < now) {
-                                iter.remove();
-                            }
-                        }
-                    }
-                    CookieSyncManager.getInstance().clearExpiredCookies(now);
-                }
-            }
-        };
-        new Thread(clearCache).start();
+        nativeRemoveExpiredCookie();
     }
 
     /**
@@ -727,483 +426,31 @@
      * Flush all cookies managed by the Chrome HTTP stack to flash.
      */
     void flushCookieStore() {
-        if (JniUtil.useChromiumHttpStack()) {
-            nativeFlushCookieStore();
-        }
+        nativeFlushCookieStore();
     }
 
     /**
-     * Whether cookies are accepted for file scheme URLs.
+     * Gets whether the application's {@link WebView} instances send and accept
+     * cookies for file scheme URLs.
+     * @return True if {@link WebView} instances send and accept cookies for
+     *         file scheme URLs
      */
     public static boolean allowFileSchemeCookies() {
-        if (JniUtil.useChromiumHttpStack()) {
-            return nativeAcceptFileSchemeCookies();
-        } else {
-            return true;
-        }
+        return nativeAcceptFileSchemeCookies();
     }
 
     /**
-     * Sets whether cookies are accepted for file scheme URLs.
-     *
-     * Use of cookies with file scheme URLs is potentially insecure. Do not use this feature unless
-     * you can be sure that no unintentional sharing of cookie data can take place.
+     * Sets whether the application's {@link WebView} instances should send and
+     * accept cookies for file scheme URLs.
+     * Use of cookies with file scheme URLs is potentially insecure. Do not use
+     * this feature unless you can be sure that no unintentional sharing of
+     * cookie data can take place.
      * <p>
-     * Note that calls to this method will have no effect if made after a WebView or CookieManager
-     * instance has been created.
+     * Note that calls to this method will have no effect if made after a
+     * {@link WebView} or CookieManager instance has been created.
      */
     public static void setAcceptFileSchemeCookies(boolean accept) {
-        if (JniUtil.useChromiumHttpStack()) {
-            nativeSetAcceptFileSchemeCookies(accept);
-        }
-    }
-
-    /**
-     * Package level api, called from CookieSyncManager
-     *
-     * Get a list of cookies which are updated since a given time.
-     * @param last The given time in millisec
-     * @return A list of cookies
-     */
-    synchronized ArrayList<Cookie> getUpdatedCookiesSince(long last) {
-        ArrayList<Cookie> cookies = new ArrayList<Cookie>();
-        Collection<ArrayList<Cookie>> cookieList = mCookieMap.values();
-        Iterator<ArrayList<Cookie>> listIter = cookieList.iterator();
-        while (listIter.hasNext()) {
-            ArrayList<Cookie> list = listIter.next();
-            Iterator<Cookie> iter = list.iterator();
-            while (iter.hasNext()) {
-                Cookie cookie = iter.next();
-                if (cookie.lastUpdateTime > last) {
-                    cookies.add(cookie);
-                }
-            }
-        }
-        return cookies;
-    }
-
-    /**
-     * Package level api, called from CookieSyncManager
-     *
-     * Delete a Cookie in the RAM
-     * @param cookie Cookie to be deleted
-     */
-    synchronized void deleteACookie(Cookie cookie) {
-        if (cookie.mode == Cookie.MODE_DELETED) {
-            String baseDomain = getBaseDomain(cookie.domain);
-            ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain);
-            if (cookieList != null) {
-                cookieList.remove(cookie);
-                if (cookieList.isEmpty()) {
-                    mCookieMap.remove(baseDomain);
-                }
-            }
-        }
-    }
-
-    /**
-     * Package level api, called from CookieSyncManager
-     *
-     * Called after a cookie is synced to FLASH
-     * @param cookie Cookie to be synced
-     */
-    synchronized void syncedACookie(Cookie cookie) {
-        cookie.mode = Cookie.MODE_NORMAL;
-    }
-
-    /**
-     * Package level api, called from CookieSyncManager
-     *
-     * Delete the least recent used domains if the total cookie count in RAM
-     * exceeds the limit
-     * @return A list of cookies which are removed from RAM
-     */
-    synchronized ArrayList<Cookie> deleteLRUDomain() {
-        int count = 0;
-        int byteCount = 0;
-        int mapSize = mCookieMap.size();
-
-        if (mapSize < MAX_RAM_DOMAIN_COUNT) {
-            Collection<ArrayList<Cookie>> cookieLists = mCookieMap.values();
-            Iterator<ArrayList<Cookie>> listIter = cookieLists.iterator();
-            while (listIter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
-                ArrayList<Cookie> list = listIter.next();
-                if (DebugFlags.COOKIE_MANAGER) {
-                    Iterator<Cookie> iter = list.iterator();
-                    while (iter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
-                        Cookie cookie = iter.next();
-                        // 14 is 3 * sizeof(long) + sizeof(boolean)
-                        // + sizeof(byte)
-                        byteCount += cookie.domain.length()
-                                + cookie.path.length()
-                                + cookie.name.length()
-                                + (cookie.value != null
-                                        ? cookie.value.length()
-                                        : 0)
-                                + 14;
-                        count++;
-                    }
-                } else {
-                    count += list.size();
-                }
-            }
-        }
-
-        ArrayList<Cookie> retlist = new ArrayList<Cookie>();
-        if (mapSize >= MAX_RAM_DOMAIN_COUNT || count >= MAX_RAM_COOKIES_COUNT) {
-            if (DebugFlags.COOKIE_MANAGER) {
-                Log.v(LOGTAG, count + " cookies used " + byteCount
-                        + " bytes with " + mapSize + " domains");
-            }
-            Object[] domains = mCookieMap.keySet().toArray();
-            int toGo = mapSize / 10 + 1;
-            while (toGo-- > 0){
-                String domain = domains[toGo].toString();
-                if (DebugFlags.COOKIE_MANAGER) {
-                    Log.v(LOGTAG, "delete domain: " + domain
-                            + " from RAM cache");
-                }
-                retlist.addAll(mCookieMap.get(domain));
-                mCookieMap.remove(domain);
-            }
-        }
-        return retlist;
-    }
-
-    /**
-     * Extract the host and path out of a uri
-     * @param uri The given WebAddress
-     * @return The host and path in the format of String[], String[0] is host
-     *          which has at least two periods, String[1] is path which always
-     *          ended with "/"
-     */
-    private String[] getHostAndPath(WebAddress uri) {
-        if (uri.getHost() != null && uri.getPath() != null) {
-
-            /*
-             * The domain (i.e. host) portion of the cookie is supposed to be
-             * case-insensitive. We will consistently return the domain in lower
-             * case, which allows us to do the more efficient equals comparison
-             * instead of equalIgnoreCase.
-             *
-             * See: http://www.ieft.org/rfc/rfc2965.txt (Section 3.3.3)
-             */
-            String[] ret = new String[2];
-            ret[0] = uri.getHost().toLowerCase();
-            ret[1] = uri.getPath();
-
-            int index = ret[0].indexOf(PERIOD);
-            if (index == -1) {
-                if (uri.getScheme().equalsIgnoreCase("file")) {
-                    // There is a potential bug where a local file path matches
-                    // another file in the local web server directory. Still
-                    // "localhost" is the best pseudo domain name.
-                    ret[0] = "localhost";
-                }
-            } else if (index == ret[0].lastIndexOf(PERIOD)) {
-                // cookie host must have at least two periods
-                ret[0] = PERIOD + ret[0];
-            }
-
-            if (ret[1].charAt(0) != PATH_DELIM) {
-                return null;
-            }
-
-            /*
-             * find cookie path, e.g. for http://www.google.com, the path is "/"
-             * for http://www.google.com/lab/, the path is "/lab"
-             * for http://www.google.com/lab/foo, the path is "/lab/foo"
-             * for http://www.google.com/lab?hl=en, the path is "/lab"
-             * for http://www.google.com/lab.asp?hl=en, the path is "/lab.asp"
-             * Note: the path from URI has at least one "/"
-             * See:
-             * http://www.unix.com.ua/rfc/rfc2109.html
-             */
-            index = ret[1].indexOf(QUESTION_MARK);
-            if (index != -1) {
-                ret[1] = ret[1].substring(0, index);
-            }
-
-            return ret;
-        } else
-            return null;
-    }
-
-    /**
-     * Get the base domain for a give host. E.g. mail.google.com will return
-     * google.com
-     * @param host The give host
-     * @return the base domain
-     */
-    private String getBaseDomain(String host) {
-        int startIndex = 0;
-        int nextIndex = host.indexOf(PERIOD);
-        int lastIndex = host.lastIndexOf(PERIOD);
-        while (nextIndex < lastIndex) {
-            startIndex = nextIndex + 1;
-            nextIndex = host.indexOf(PERIOD, startIndex);
-        }
-        if (startIndex > 0) {
-            return host.substring(startIndex);
-        } else {
-            return host;
-        }
-    }
-
-    /**
-     * parseCookie() parses the cookieString which is a comma-separated list of
-     * one or more cookies in the format of "NAME=VALUE; expires=DATE;
-     * path=PATH; domain=DOMAIN_NAME; secure httponly" to a list of Cookies.
-     * Here is a sample: IGDND=1, IGPC=ET=UB8TSNwtDmQ:AF=0; expires=Sun,
-     * 17-Jan-2038 19:14:07 GMT; path=/ig; domain=.google.com, =,
-     * PREF=ID=408909b1b304593d:TM=1156459854:LM=1156459854:S=V-vCAU6Sh-gobCfO;
-     * expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com which
-     * contains 3 cookies IGDND, IGPC, PREF and an empty cookie
-     * @param host The default host
-     * @param path The default path
-     * @param cookieString The string coming from "Set-Cookie:"
-     * @return A list of Cookies
-     */
-    private ArrayList<Cookie> parseCookie(String host, String path,
-            String cookieString) {
-        ArrayList<Cookie> ret = new ArrayList<Cookie>();
-
-        int index = 0;
-        int length = cookieString.length();
-        while (true) {
-            Cookie cookie = null;
-
-            // done
-            if (index < 0 || index >= length) {
-                break;
-            }
-
-            // skip white space
-            if (cookieString.charAt(index) == WHITE_SPACE) {
-                index++;
-                continue;
-            }
-
-            /*
-             * get NAME=VALUE; pair. detecting the end of a pair is tricky, it
-             * can be the end of a string, like "foo=bluh", it can be semicolon
-             * like "foo=bluh;path=/"; or it can be enclosed by \", like
-             * "foo=\"bluh bluh\";path=/"
-             *
-             * Note: in the case of "foo=bluh, bar=bluh;path=/", we interpret
-             * it as one cookie instead of two cookies.
-             */
-            int semicolonIndex = cookieString.indexOf(SEMICOLON, index);
-            int equalIndex = cookieString.indexOf(EQUAL, index);
-            cookie = new Cookie(host, path);
-
-            // Cookies like "testcookie; path=/;" are valid and used
-            // (lovefilm.se).
-            // Look for 2 cases:
-            // 1. "foo" or "foo;" where equalIndex is -1
-            // 2. "foo; path=..." where the first semicolon is before an equal
-            //    and a semicolon exists.
-            if ((semicolonIndex != -1 && (semicolonIndex < equalIndex)) ||
-                    equalIndex == -1) {
-                // Fix up the index in case we have a string like "testcookie"
-                if (semicolonIndex == -1) {
-                    semicolonIndex = length;
-                }
-                cookie.name = cookieString.substring(index, semicolonIndex);
-                cookie.value = null;
-            } else {
-                cookie.name = cookieString.substring(index, equalIndex);
-                // Make sure we do not throw an exception if the cookie is like
-                // "foo="
-                if ((equalIndex < length - 1) &&
-                        (cookieString.charAt(equalIndex + 1) == QUOTATION)) {
-                    index = cookieString.indexOf(QUOTATION, equalIndex + 2);
-                    if (index == -1) {
-                        // bad format, force return
-                        break;
-                    }
-                }
-                // Get the semicolon index again in case it was contained within
-                // the quotations.
-                semicolonIndex = cookieString.indexOf(SEMICOLON, index);
-                if (semicolonIndex == -1) {
-                    semicolonIndex = length;
-                }
-                if (semicolonIndex - equalIndex > MAX_COOKIE_LENGTH) {
-                    // cookie is too big, trim it
-                    cookie.value = cookieString.substring(equalIndex + 1,
-                            equalIndex + 1 + MAX_COOKIE_LENGTH);
-                } else if (equalIndex + 1 == semicolonIndex
-                        || semicolonIndex < equalIndex) {
-                    // this is an unusual case like "foo=;" or "foo="
-                    cookie.value = "";
-                } else {
-                    cookie.value = cookieString.substring(equalIndex + 1,
-                            semicolonIndex);
-                }
-            }
-            // get attributes
-            index = semicolonIndex;
-            while (true) {
-                // done
-                if (index < 0 || index >= length) {
-                    break;
-                }
-
-                // skip white space and semicolon
-                if (cookieString.charAt(index) == WHITE_SPACE
-                        || cookieString.charAt(index) == SEMICOLON) {
-                    index++;
-                    continue;
-                }
-
-                // comma means next cookie
-                if (cookieString.charAt(index) == COMMA) {
-                    index++;
-                    break;
-                }
-
-                // "secure" is a known attribute doesn't use "=";
-                // while sites like live.com uses "secure="
-                if (length - index >= SECURE_LENGTH
-                        && cookieString.substring(index, index + SECURE_LENGTH).
-                        equalsIgnoreCase(SECURE)) {
-                    index += SECURE_LENGTH;
-                    cookie.secure = true;
-                    if (index == length) break;
-                    if (cookieString.charAt(index) == EQUAL) index++;
-                    continue;
-                }
-
-                // "httponly" is a known attribute doesn't use "=";
-                // while sites like live.com uses "httponly="
-                if (length - index >= HTTP_ONLY_LENGTH
-                        && cookieString.substring(index,
-                            index + HTTP_ONLY_LENGTH).
-                        equalsIgnoreCase(HTTP_ONLY)) {
-                    index += HTTP_ONLY_LENGTH;
-                    if (index == length) break;
-                    if (cookieString.charAt(index) == EQUAL) index++;
-                    // FIXME: currently only parse the attribute
-                    continue;
-                }
-                equalIndex = cookieString.indexOf(EQUAL, index);
-                if (equalIndex > 0) {
-                    String name = cookieString.substring(index, equalIndex).toLowerCase();
-                    int valueIndex = equalIndex + 1;
-                    while (valueIndex < length && cookieString.charAt(valueIndex) == WHITE_SPACE) {
-                        valueIndex++;
-                    }
-
-                    if (name.equals(EXPIRES)) {
-                        int comaIndex = cookieString.indexOf(COMMA, equalIndex);
-
-                        // skip ',' in (Wdy, DD-Mon-YYYY HH:MM:SS GMT) or
-                        // (Weekday, DD-Mon-YY HH:MM:SS GMT) if it applies.
-                        // "Wednesday" is the longest Weekday which has length 9
-                        if ((comaIndex != -1) &&
-                                (comaIndex - valueIndex <= 10)) {
-                            index = comaIndex + 1;
-                        }
-                    }
-                    semicolonIndex = cookieString.indexOf(SEMICOLON, index);
-                    int commaIndex = cookieString.indexOf(COMMA, index);
-                    if (semicolonIndex == -1 && commaIndex == -1) {
-                        index = length;
-                    } else if (semicolonIndex == -1) {
-                        index = commaIndex;
-                    } else if (commaIndex == -1) {
-                        index = semicolonIndex;
-                    } else {
-                        index = Math.min(semicolonIndex, commaIndex);
-                    }
-                    String value = cookieString.substring(valueIndex, index);
-                    
-                    // Strip quotes if they exist
-                    if (value.length() > 2 && value.charAt(0) == QUOTATION) {
-                        int endQuote = value.indexOf(QUOTATION, 1);
-                        if (endQuote > 0) {
-                            value = value.substring(1, endQuote);
-                        }
-                    }
-                    if (name.equals(EXPIRES)) {
-                        try {
-                            cookie.expires = AndroidHttpClient.parseDate(value);
-                        } catch (IllegalArgumentException ex) {
-                            Log.e(LOGTAG,
-                                    "illegal format for expires: " + value);
-                        }
-                    } else if (name.equals(MAX_AGE)) {
-                        try {
-                            cookie.expires = System.currentTimeMillis() + 1000
-                                    * Long.parseLong(value);
-                        } catch (NumberFormatException ex) {
-                            Log.e(LOGTAG,
-                                    "illegal format for max-age: " + value);
-                        }
-                    } else if (name.equals(PATH)) {
-                        // only allow non-empty path value
-                        if (value.length() > 0) {
-                            cookie.path = value;
-                        }
-                    } else if (name.equals(DOMAIN)) {
-                        int lastPeriod = value.lastIndexOf(PERIOD);
-                        if (lastPeriod == 0) {
-                            // disallow cookies set for TLDs like [.com]
-                            cookie.domain = null;
-                            continue;
-                        }
-                        try {
-                            Integer.parseInt(value.substring(lastPeriod + 1));
-                            // no wildcard for ip address match
-                            if (!value.equals(host)) {
-                                // no cross-site cookie
-                                cookie.domain = null;
-                            }
-                            continue;
-                        } catch (NumberFormatException ex) {
-                            // ignore the exception, value is a host name
-                        }
-                        value = value.toLowerCase();
-                        if (value.charAt(0) != PERIOD) {
-                            // pre-pended dot to make it as a domain cookie
-                            value = PERIOD + value;
-                            lastPeriod++;
-                        }
-                        if (host.endsWith(value.substring(1))) {
-                            int len = value.length();
-                            int hostLen = host.length();
-                            if (hostLen > (len - 1)
-                                    && host.charAt(hostLen - len) != PERIOD) {
-                                // make sure the bar.com doesn't match .ar.com
-                                cookie.domain = null;
-                                continue;
-                            }
-                            // disallow cookies set on ccTLDs like [.co.uk]
-                            if ((len == lastPeriod + 3)
-                                    && (len >= 6 && len <= 8)) {
-                                String s = value.substring(1, lastPeriod);
-                                if (Arrays.binarySearch(BAD_COUNTRY_2LDS, s) >= 0) {
-                                    cookie.domain = null;
-                                    continue;
-                                }
-                            }
-                            cookie.domain = value;
-                        } else {
-                            // no cross-site or more specific sub-domain cookie
-                            cookie.domain = null;
-                        }
-                    }
-                } else {
-                    // bad format, force return
-                    index = length;
-                }
-            }
-            if (cookie != null && cookie.domain != null) {
-                ret.add(cookie);
-            }
-        }
-        return ret;
+        nativeSetAcceptFileSchemeCookies(accept);
     }
 
     // Native functions
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index a699800..19fa096 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -100,77 +100,6 @@
         return sRef;
     }
 
-    /**
-     * Package level api, called from CookieManager. Get all the cookies which
-     * matches a given base domain.
-     * @param domain
-     * @return A list of Cookie
-     */
-    ArrayList<Cookie> getCookiesForDomain(String domain) {
-        // null mDataBase implies that the host application doesn't support
-        // persistent cookie. No sync needed.
-        if (mDataBase == null) {
-            return new ArrayList<Cookie>();
-        }
-
-        return mDataBase.getCookiesForDomain(domain);
-    }
-
-    /**
-     * Package level api, called from CookieManager Clear all cookies in the
-     * database
-     */
-    void clearAllCookies() {
-        // null mDataBase implies that the host application doesn't support
-        // persistent cookie.
-        if (mDataBase == null) {
-            return;
-        }
-
-        mDataBase.clearCookies();
-    }
-
-    /**
-     * Returns true if there are any saved cookies.
-     */
-    boolean hasCookies() {
-        // null mDataBase implies that the host application doesn't support
-        // persistent cookie.
-        if (mDataBase == null) {
-            return false;
-        }
-
-        return mDataBase.hasCookies();
-    }
-
-    /**
-     * Package level api, called from CookieManager Clear all session cookies in
-     * the database
-     */
-    void clearSessionCookies() {
-        // null mDataBase implies that the host application doesn't support
-        // persistent cookie.
-        if (mDataBase == null) {
-            return;
-        }
-
-        mDataBase.clearSessionCookies();
-    }
-
-    /**
-     * Package level api, called from CookieManager Clear all expired cookies in
-     * the database
-     */
-    void clearExpiredCookies(long now) {
-        // null mDataBase implies that the host application doesn't support
-        // persistent cookie.
-        if (mDataBase == null) {
-            return;
-        }
-
-        mDataBase.clearExpiredCookies(now);
-    }
-
     protected void syncFromRamToFlash() {
         if (DebugFlags.COOKIE_SYNC_MANAGER) {
             Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash STARTS");
@@ -182,41 +111,13 @@
             return;
         }
 
-        if (JniUtil.useChromiumHttpStack()) {
-            manager.flushCookieStore();
-        } else {
-            ArrayList<Cookie> cookieList = manager.getUpdatedCookiesSince(mLastUpdate);
-            mLastUpdate = System.currentTimeMillis();
-            syncFromRamToFlash(cookieList);
-
-            ArrayList<Cookie> lruList = manager.deleteLRUDomain();
-            syncFromRamToFlash(lruList);
-        }
+        manager.flushCookieStore();
 
         if (DebugFlags.COOKIE_SYNC_MANAGER) {
             Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash DONE");
         }
     }
 
-    private void syncFromRamToFlash(ArrayList<Cookie> list) {
-        Iterator<Cookie> iter = list.iterator();
-        while (iter.hasNext()) {
-            Cookie cookie = iter.next();
-            if (cookie.mode != Cookie.MODE_NORMAL) {
-                if (cookie.mode != Cookie.MODE_NEW) {
-                    mDataBase.deleteCookies(cookie.domain, cookie.path,
-                            cookie.name);
-                }
-                if (cookie.mode != Cookie.MODE_DELETED) {
-                    mDataBase.addCookie(cookie);
-                    CookieManager.getInstance().syncedACookie(cookie);
-                } else {
-                    CookieManager.getInstance().deleteACookie(cookie);
-                }
-            }
-        }
-    }
-
     private static void checkInstanceIsCreated() {
         if (sRef == null) {
             throw new IllegalStateException(
diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java
deleted file mode 100644
index e8d9069..0000000
--- a/core/java/android/webkit/DataLoader.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.
- */
-
-package android.webkit;
-
-import android.net.http.EventHandler;
-
-import com.android.internal.R;
-
-import java.io.ByteArrayInputStream;
-
-import libcore.io.Base64;
-
-/**
- * This class is a concrete implementation of StreamLoader that uses the
- * content supplied as a URL as the source for the stream. The mimetype
- * optionally provided in the URL is extracted and inserted into the HTTP
- * response headers.
- */
-class DataLoader extends StreamLoader {
-
-    /**
-     * Constructor uses the dataURL as the source for an InputStream
-     * @param dataUrl data: URL string optionally containing a mimetype
-     * @param loadListener LoadListener to pass the content to
-     */
-    DataLoader(String dataUrl, LoadListener loadListener) {
-        super(loadListener);
-
-        String url = dataUrl.substring("data:".length());
-        byte[] data = null;
-        int commaIndex = url.indexOf(',');
-        if (commaIndex != -1) {
-            String contentType = url.substring(0, commaIndex);
-            data = url.substring(commaIndex + 1).getBytes();
-            loadListener.parseContentTypeHeader(contentType);
-            if ("base64".equals(loadListener.transferEncoding())) {
-                data = Base64.decode(data);
-            }
-        } else {
-            data = url.getBytes();
-        }
-        if (data != null) {
-            mDataStream = new ByteArrayInputStream(data);
-            mContentLength = data.length;
-        }
-    }
-
-    @Override
-    protected boolean setupStreamAndSendStatus() {
-        if (mDataStream != null) {
-            mLoadListener.status(1, 1, 200, "OK");
-            return true;
-        } else {
-            mLoadListener.error(EventHandler.ERROR,
-                    mContext.getString(R.string.httpError));
-            return false;
-        }
-    }
-
-    @Override
-    protected void buildHeaders(android.net.http.Headers h) {
-    }
-}
diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java
deleted file mode 100644
index e21e9ef8..0000000
--- a/core/java/android/webkit/FileLoader.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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.
- */
-
-package android.webkit;
-
-import com.android.internal.R;
-
-import android.content.res.AssetManager;
-import android.net.http.EventHandler;
-import android.net.http.Headers;
-import android.util.Log;
-import android.util.TypedValue;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.lang.reflect.Field;
-
-/**
- * This class is a concrete implementation of StreamLoader that uses a
- * file or asset as the source for the stream.
- *
- */
-class FileLoader extends StreamLoader {
-
-    private String mPath;  // Full path to the file to load
-    private int mType;  // Indicates the type of the load
-    private boolean mAllowFileAccess; // Allow/block file system access
-
-    // used for files under asset directory
-    static final int TYPE_ASSET = 1;
-    // used for files under res directory
-    static final int TYPE_RES = 2;
-    // generic file
-    static final int TYPE_FILE = 3;
-
-    private static final String LOGTAG = "webkit";
-
-    /**
-     * Construct a FileLoader with the file URL specified as the content
-     * source.
-     *
-     * @param url Full file url pointing to content to be loaded
-     * @param loadListener LoadListener to pass the content to
-     * @param asset true if url points to an asset.
-     * @param allowFileAccess true if this WebView is allowed to access files
-     *                        on the file system.
-     */
-    FileLoader(String url, LoadListener loadListener, int type,
-            boolean allowFileAccess) {
-        super(loadListener);
-        mType = type;
-        mAllowFileAccess = allowFileAccess;
-
-        // clean the Url
-        int index = url.indexOf('?');
-        if (mType == TYPE_ASSET) {
-            mPath = index > 0 ? URLUtil.stripAnchor(
-                    url.substring(URLUtil.ASSET_BASE.length(), index)) :
-                    URLUtil.stripAnchor(url.substring(
-                            URLUtil.ASSET_BASE.length()));
-        } else if (mType == TYPE_RES) {
-            mPath = index > 0 ? URLUtil.stripAnchor(
-                    url.substring(URLUtil.RESOURCE_BASE.length(), index)) :
-                    URLUtil.stripAnchor(url.substring(
-                            URLUtil.RESOURCE_BASE.length()));
-        } else {
-            mPath = index > 0 ? URLUtil.stripAnchor(
-                    url.substring(URLUtil.FILE_BASE.length(), index)) :
-                    URLUtil.stripAnchor(url.substring(
-                            URLUtil.FILE_BASE.length()));
-        }
-    }
-
-    private String errString(Exception ex) {
-        String exMessage = ex.getMessage();
-        String errString = mContext.getString(R.string.httpErrorFileNotFound);
-        if (exMessage != null) {
-            errString += " " + exMessage;
-        }
-        return errString;
-    }
-
-    @Override
-    protected boolean setupStreamAndSendStatus() {
-        try {
-            if (mType == TYPE_ASSET) {
-                try {
-                    mDataStream = mContext.getAssets().open(mPath);
-                } catch (java.io.FileNotFoundException ex) {
-                    // try the rest files included in the package
-                    mDataStream = mContext.getAssets().openNonAsset(mPath);
-                }
-            } else if (mType == TYPE_RES) {
-                // get the resource id from the path. e.g. for the path like
-                // drawable/foo.png, the id is located at field "foo" of class
-                // "<package>.R$drawable"
-                if (mPath == null || mPath.length() == 0) {
-                    Log.e(LOGTAG, "Need a path to resolve the res file");
-                    mLoadListener.error(EventHandler.FILE_ERROR, mContext
-                            .getString(R.string.httpErrorFileNotFound));
-                    return false;
-
-                }
-                int slash = mPath.indexOf('/');
-                int dot = mPath.indexOf('.', slash);
-                if (slash == -1 || dot == -1) {
-                    Log.e(LOGTAG, "Incorrect res path: " + mPath);
-                    mLoadListener.error(EventHandler.FILE_ERROR, mContext
-                            .getString(R.string.httpErrorFileNotFound));
-                    return false;
-                }
-                String subClassName = mPath.substring(0, slash);
-                String fieldName = mPath.substring(slash + 1, dot);
-                String errorMsg = null;
-                try {
-                    final Class<?> d = mContext.getApplicationContext()
-                            .getClassLoader().loadClass(
-                                    mContext.getPackageName() + ".R$"
-                                            + subClassName);
-                    final Field field = d.getField(fieldName);
-                    final int id = field.getInt(null);
-                    TypedValue value = new TypedValue();
-                    mContext.getResources().getValue(id, value, true);
-                    if (value.type == TypedValue.TYPE_STRING) {
-                        mDataStream = mContext.getAssets().openNonAsset(
-                                value.assetCookie, value.string.toString(),
-                                AssetManager.ACCESS_STREAMING);
-                    } else {
-                        errorMsg = "Only support TYPE_STRING for the res files";
-                    }
-                } catch (ClassNotFoundException e) {
-                    errorMsg = "Can't find class:  "
-                            + mContext.getPackageName() + ".R$" + subClassName;
-                } catch (SecurityException e) {
-                    errorMsg = "Caught SecurityException: " + e;
-                } catch (NoSuchFieldException e) {
-                    errorMsg = "Can't find field:  " + fieldName + " in "
-                            + mContext.getPackageName() + ".R$" + subClassName;
-                } catch (IllegalArgumentException e) {
-                    errorMsg = "Caught IllegalArgumentException: " + e;
-                } catch (IllegalAccessException e) {
-                    errorMsg = "Caught IllegalAccessException: " + e;
-                }
-                if (errorMsg != null) {
-                    mLoadListener.error(EventHandler.FILE_ERROR, mContext
-                            .getString(R.string.httpErrorFileNotFound));
-                    return false;
-                }
-            } else {
-                if (!mAllowFileAccess) {
-                    mLoadListener.error(EventHandler.FILE_ERROR,
-                            mContext.getString(R.string.httpErrorFileNotFound));
-                    return false;
-                }
-
-                mDataStream = new FileInputStream(mPath);
-                mContentLength = (new File(mPath)).length();
-            }
-            mLoadListener.status(1, 1, 200, "OK");
-
-        } catch (java.io.FileNotFoundException ex) {
-            mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
-            return false;
-
-        } catch (java.io.IOException ex) {
-            mLoadListener.error(EventHandler.FILE_ERROR, errString(ex));
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    protected void buildHeaders(Headers headers) {
-        // do nothing.
-    }
-}
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index fffa90b..10b0885 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -43,7 +43,9 @@
     private Resources mResources;
     private boolean mMatchesFound;
     private int mNumberOfMatches;
+    private int mActiveMatchIndex;
     private ActionMode mActionMode;
+    private String mLastFind;
 
     FindActionModeCallback(Context context) {
         mCustomView = LayoutInflater.from(context).inflate(
@@ -132,16 +134,13 @@
             mWebView.clearMatches();
             mMatches.setVisibility(View.GONE);
             mMatchesFound = false;
+            mLastFind = null;
         } else {
             mMatchesFound = true;
-            mMatches.setVisibility(View.VISIBLE);
-            mNumberOfMatches = mWebView.findAll(find.toString());
-            if (0 == mNumberOfMatches) {
-                mMatches.setText(mResources.getString(
-                        com.android.internal.R.string.no_matches));
-            } else {
-                updateMatchesString();
-            }
+            mMatches.setVisibility(View.INVISIBLE);
+            mNumberOfMatches = 0;
+            mLastFind = find.toString();
+            mWebView.findAllAsync(mLastFind);
         }
     }
 
@@ -151,17 +150,31 @@
         mInput.showSoftInput(mEditText, 0);
     }
 
+    public void updateMatchCount(int matchIndex, int matchCount,
+        String findText) {
+        if (mLastFind != null && mLastFind.equals(findText)) {
+            mNumberOfMatches = matchCount;
+            mActiveMatchIndex = matchIndex;
+            updateMatchesString();
+        } else {
+            mMatches.setVisibility(View.INVISIBLE);
+            mNumberOfMatches = 0;
+        }
+    }
+
     /*
      * Update the string which tells the user how many matches were found, and
      * which match is currently highlighted.
-     * Not to be called when mNumberOfMatches is 0.
      */
     private void updateMatchesString() {
-        String template = mResources.getQuantityString(
+        if (mNumberOfMatches == 0) {
+            mMatches.setText(com.android.internal.R.string.no_matches);
+        } else {
+            mMatches.setText(mResources.getQuantityString(
                 com.android.internal.R.plurals.matches_found, mNumberOfMatches,
-                mWebView.findIndex() + 1, mNumberOfMatches);
-
-        mMatches.setText(template);
+                mActiveMatchIndex + 1, mNumberOfMatches));
+        }
+        mMatches.setVisibility(View.VISIBLE);
     }
 
     // OnLongClickListener implementation
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
deleted file mode 100644
index 0d80302..0000000
--- a/core/java/android/webkit/FrameLoader.java
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright (C) 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 android.webkit;
-
-import android.net.http.ErrorStrings;
-import android.net.http.EventHandler;
-import android.net.http.RequestHandle;
-import android.os.Build;
-import android.util.Log;
-import android.webkit.CacheManager.CacheResult;
-import android.webkit.JniUtil;
-
-import java.util.HashMap;
-import java.util.Map;
-
-class FrameLoader {
-
-    private final LoadListener mListener;
-    private final String mMethod;
-    private final WebSettings mSettings;
-    private Map<String, String> mHeaders;
-    private byte[] mPostData;
-    private Network mNetwork;
-    private int mCacheMode;
-    private String mReferrer;
-    private String mContentType;
-    private final String mUaprofHeader;
-    private final WebResourceResponse mInterceptResponse;
-
-    private static final int URI_PROTOCOL = 0x100;
-
-    private static final String CONTENT_TYPE = "content-type";
-
-    // Contents of an about:blank page
-    private static final String mAboutBlank =
-            "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EB\">" +
-            "<html><head><title>about:blank</title></head><body></body></html>";
-
-    static final String HEADER_STR = "text/xml, text/html, " +
-            "application/xhtml+xml, image/png, text/plain, */*;q=0.8";
-
-    private static final String LOGTAG = "webkit";
-    
-    FrameLoader(LoadListener listener, WebSettings settings,
-            String method, WebResourceResponse interceptResponse) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        mListener = listener;
-        mHeaders = null;
-        mMethod = method;
-        mCacheMode = WebSettings.LOAD_NORMAL;
-        mSettings = settings;
-        mInterceptResponse = interceptResponse;
-        mUaprofHeader = mListener.getContext().getResources().getString(
-                com.android.internal.R.string.config_useragentprofile_url, Build.MODEL);
-    }
-
-    public void setReferrer(String ref) {
-        // only set referrer for http or https
-        if (URLUtil.isNetworkUrl(ref)) mReferrer = ref;
-    }
-
-    public void setPostData(byte[] postData) {
-        mPostData = postData;
-    }
-
-    public void setContentTypeForPost(String postContentType) {
-        mContentType = postContentType;
-    }
-
-    public void setCacheMode(int cacheMode) {
-        mCacheMode = cacheMode;
-    }
-
-    public void setHeaders(HashMap headers) {
-        mHeaders = headers;
-    }
-
-    public LoadListener getLoadListener() {
-        return mListener;
-    }
-
-    /**
-     * Issues the load request.
-     *
-     * Return value does not indicate if the load was successful or not. It
-     * simply indicates that the load request is reasonable.
-     *
-     * @return true if the load is reasonable.
-     */
-    public boolean executeLoad() {
-        String url = mListener.url();
-
-        // Process intercepted requests first as they could be any url.
-        if (mInterceptResponse != null) {
-            if (mListener.isSynchronous()) {
-                mInterceptResponse.loader(mListener).load();
-            } else {
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_ADD_STREAMLOADER,
-                        mInterceptResponse.loader(mListener)).sendToTarget();
-            }
-            return true;
-        } else if (URLUtil.isNetworkUrl(url)){
-            if (mSettings.getBlockNetworkLoads()) {
-                mListener.error(EventHandler.ERROR_BAD_URL,
-                        mListener.getContext().getString(
-                                com.android.internal.R.string.httpErrorBadUrl));
-                return false;
-            }
-            // Make sure the host part of the url is correctly
-            // encoded before sending the request
-            if (!URLUtil.verifyURLEncoding(mListener.host())) {
-                mListener.error(EventHandler.ERROR_BAD_URL,
-                        mListener.getContext().getString(
-                        com.android.internal.R.string.httpErrorBadUrl));
-                return false;
-            }
-            mNetwork = Network.getInstance(mListener.getContext());
-            if (mListener.isSynchronous()) {
-                return handleHTTPLoad();
-            }
-            WebViewWorker.getHandler().obtainMessage(
-                    WebViewWorker.MSG_ADD_HTTPLOADER, this).sendToTarget();
-            return true;
-        } else if (handleLocalFile(url, mListener, mSettings)) {
-            return true;
-        }
-        if (DebugFlags.FRAME_LOADER) {
-            Log.v(LOGTAG, "FrameLoader.executeLoad: url protocol not supported:"
-                    + mListener.url());
-        }
-        mListener.error(EventHandler.ERROR_UNSUPPORTED_SCHEME,
-                mListener.getContext().getText(
-                        com.android.internal.R.string.httpErrorUnsupportedScheme).toString());
-        return false;
-
-    }
-
-    private static boolean handleLocalFile(String url, LoadListener loadListener,
-            WebSettings settings) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        // Attempt to decode the percent-encoded url before passing to the
-        // local loaders.
-        try {
-            url = new String(URLUtil.decode(url.getBytes()));
-        } catch (IllegalArgumentException e) {
-            loadListener.error(EventHandler.ERROR_BAD_URL,
-                    loadListener.getContext().getString(
-                            com.android.internal.R.string.httpErrorBadUrl));
-            // Return true here so we do not trigger an unsupported scheme
-            // error.
-            return true;
-        }
-        if (URLUtil.isAssetUrl(url)) {
-            if (loadListener.isSynchronous()) {
-                new FileLoader(url, loadListener, FileLoader.TYPE_ASSET,
-                        true).load();
-            } else {
-                // load asset in a separate thread as it involves IO
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_ADD_STREAMLOADER,
-                        new FileLoader(url, loadListener, FileLoader.TYPE_ASSET,
-                                true)).sendToTarget();
-            }
-            return true;
-        } else if (URLUtil.isResourceUrl(url)) {
-            if (loadListener.isSynchronous()) {
-                new FileLoader(url, loadListener, FileLoader.TYPE_RES,
-                        true).load();
-            } else {
-                // load resource in a separate thread as it involves IO
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_ADD_STREAMLOADER,
-                        new FileLoader(url, loadListener, FileLoader.TYPE_RES,
-                                true)).sendToTarget();
-            }
-            return true;
-        } else if (URLUtil.isFileUrl(url)) {
-            if (loadListener.isSynchronous()) {
-                new FileLoader(url, loadListener, FileLoader.TYPE_FILE,
-                        settings.getAllowFileAccess()).load();
-            } else {
-                // load file in a separate thread as it involves IO
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_ADD_STREAMLOADER,
-                        new FileLoader(url, loadListener, FileLoader.TYPE_FILE,
-                                settings.getAllowFileAccess())).sendToTarget();
-            }
-            return true;
-        } else if (settings.getAllowContentAccess() &&
-                   URLUtil.isContentUrl(url)) {
-            // Send the raw url to the ContentLoader because it will do a
-            // permission check and the url has to match.
-            if (loadListener.isSynchronous()) {
-                new ContentLoader(loadListener.url(), loadListener).load();
-            } else {
-                // load content in a separate thread as it involves IO
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_ADD_STREAMLOADER,
-                        new ContentLoader(loadListener.url(), loadListener))
-                        .sendToTarget();
-            }
-            return true;
-        } else if (URLUtil.isDataUrl(url)) {
-            // load data in the current thread to reduce the latency
-            new DataLoader(url, loadListener).load();
-            return true;
-        } else if (URLUtil.isAboutUrl(url)) {
-            loadListener.data(mAboutBlank.getBytes(), mAboutBlank.length());
-            loadListener.endData();
-            return true;
-        }
-        return false;
-    }
-
-    boolean handleHTTPLoad() {
-        if (mHeaders == null) {
-            mHeaders = new HashMap<String, String>();
-        }
-        populateStaticHeaders();
-        populateHeaders();
-
-        // response was handled by Cache, don't issue HTTP request
-        if (handleCache()) {
-            // push the request data down to the LoadListener
-            // as response from the cache could be a redirect
-            // and we may need to initiate a network request if the cache
-            // can't satisfy redirect URL
-            mListener.setRequestData(mMethod, mHeaders, mPostData);
-            return true;
-        }
-
-        if (DebugFlags.FRAME_LOADER) {
-            Log.v(LOGTAG, "FrameLoader: http " + mMethod + " load for: "
-                    + mListener.url());
-        }
-
-        boolean ret = false;
-        int error = EventHandler.ERROR_UNSUPPORTED_SCHEME;
-        
-        try {
-            ret = mNetwork.requestURL(mMethod, mHeaders,
-                    mPostData, mListener);
-        } catch (android.net.ParseException ex) {
-            error = EventHandler.ERROR_BAD_URL;
-        } catch (java.lang.RuntimeException ex) {
-            /* probably an empty header set by javascript.  We want
-               the same result as bad URL  */
-            error = EventHandler.ERROR_BAD_URL;
-        }
-        if (!ret) {
-            mListener.error(error, ErrorStrings.getString(error, mListener.getContext()));
-            return false;
-        }
-        return true;
-    }
-
-    /*
-     * This function is used by handleCache to
-     * setup a load from the byte stream in a CacheResult.
-     */
-    private void startCacheLoad(CacheResult result) {
-        if (DebugFlags.FRAME_LOADER) {
-            Log.v(LOGTAG, "FrameLoader: loading from cache: "
-                  + mListener.url());
-        }
-        // Tell the Listener respond with the cache file
-        CacheLoader cacheLoader =
-                new CacheLoader(mListener, result);
-        mListener.setCacheLoader(cacheLoader);
-        if (mListener.isSynchronous()) {
-            cacheLoader.load();
-        } else {
-            // Load the cached file in a separate thread
-            WebViewWorker.getHandler().obtainMessage(
-                    WebViewWorker.MSG_ADD_STREAMLOADER, cacheLoader).sendToTarget();
-        }
-    }
-
-    /*
-     * This function is used by the handleHTTPLoad to setup the cache headers
-     * correctly.
-     * Returns true if the response was handled from the cache
-     */
-    private boolean handleCache() {
-        switch (mCacheMode) {
-            // This mode is normally used for a reload, it instructs the http
-            // loader to not use the cached content.
-            case WebSettings.LOAD_NO_CACHE:
-                break;
-                
-                
-            // This mode is used when the content should only be loaded from
-            // the cache. If it is not there, then fail the load. This is used
-            // to load POST content in a history navigation.
-            case WebSettings.LOAD_CACHE_ONLY: {
-                CacheResult result = CacheManager.getCacheFile(mListener.url(),
-                        mListener.postIdentifier(), null);
-                if (result != null) {
-                    startCacheLoad(result);
-                } else {
-                    // This happens if WebCore was first told that the POST
-                    // response was in the cache, then when we try to use it
-                    // it has gone.
-                    // Generate a file not found error
-                    int err = EventHandler.FILE_NOT_FOUND_ERROR;
-                    mListener.error(err,
-                            ErrorStrings.getString(err, mListener.getContext()));
-                }
-                return true;
-            }
-
-            // This mode is for when the user is doing a history navigation
-            // in the browser and should returned cached content regardless
-            // of it's state. If it is not in the cache, then go to the 
-            // network.
-            case WebSettings.LOAD_CACHE_ELSE_NETWORK: {
-                if (DebugFlags.FRAME_LOADER) {
-                    Log.v(LOGTAG, "FrameLoader: checking cache: "
-                            + mListener.url());
-                }
-                // Get the cache file name for the current URL, passing null for
-                // the validation headers causes no validation to occur
-                CacheResult result = CacheManager.getCacheFile(mListener.url(),
-                        mListener.postIdentifier(), null);
-                if (result != null) {
-                    startCacheLoad(result);
-                    return true;
-                }
-                break;
-            }
-
-            // This is the default case, which is to check to see if the
-            // content in the cache can be used. If it can be used, then
-            // use it. If it needs revalidation then the relevant headers
-            // are added to the request.
-            default:
-            case WebSettings.LOAD_NORMAL:
-                return mListener.checkCache(mHeaders);
-        }// end of switch
-
-        return false;
-    }
-    
-    /**
-     * Add the static headers that don't change with each request.
-     */
-    private void populateStaticHeaders() {
-        // Accept header should already be there as they are built by WebCore,
-        // but in the case they are missing, add some.
-        String accept = mHeaders.get("Accept");
-        if (accept == null || accept.length() == 0) {
-            mHeaders.put("Accept", HEADER_STR);
-        }
-        mHeaders.put("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7");
-
-        String acceptLanguage = mSettings.getAcceptLanguage();
-        if (acceptLanguage.length() > 0) {
-            mHeaders.put("Accept-Language", acceptLanguage);
-        }
-        
-        mHeaders.put("User-Agent", mSettings.getUserAgentString());
-
-        // Set the x-wap-profile header
-        if (mUaprofHeader != null && mUaprofHeader.length() > 0) {
-            mHeaders.put("x-wap-profile", mUaprofHeader);
-        }
-    }
-
-    /**
-     * Add the content related headers. These headers contain user private data
-     * and is not used when we are proxying an untrusted request.
-     */
-    private void populateHeaders() {
-        
-        if (mReferrer != null) mHeaders.put("Referer", mReferrer);
-        if (mContentType != null) mHeaders.put(CONTENT_TYPE, mContentType);
-
-        // if we have an active proxy and have proxy credentials, do pre-emptive
-        // authentication to avoid an extra round-trip:
-        if (mNetwork.isValidProxySet()) {
-            String username;
-            String password;
-            /* The proxy credentials can be set in the Network thread */
-            synchronized (mNetwork) {
-                username = mNetwork.getProxyUsername();
-                password = mNetwork.getProxyPassword();
-            }
-            if (username != null && password != null) {
-                // we collect credentials ONLY if the proxy scheme is BASIC!!!
-                String proxyHeader = RequestHandle.authorizationHeader(true);
-                mHeaders.put(proxyHeader,
-                        "Basic " + RequestHandle.computeBasicAuthResponse(
-                                username, password));
-            }
-        }
-
-        // Set cookie header
-        String cookie = CookieManager.getInstance().getCookie(
-                mListener.getWebAddress());
-        if (cookie != null && cookie.length() > 0) {
-            mHeaders.put("Cookie", cookie);
-        }
-    }
-}
diff --git a/core/java/android/webkit/HttpAuthHandlerImpl.java b/core/java/android/webkit/HttpAuthHandlerImpl.java
deleted file mode 100644
index 01e8eb8..0000000
--- a/core/java/android/webkit/HttpAuthHandlerImpl.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * 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 android.webkit;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-import java.util.ListIterator;
-import java.util.LinkedList;
-
-/**
- * HttpAuthHandler implementation is used only by the Android Java HTTP stack.
- * <p>
- * This class is not needed when we're using the Chromium HTTP stack.
- */
-class HttpAuthHandlerImpl extends HttpAuthHandler {
-    /*
-     * It is important that the handler is in Network, because we want to share
-     * it accross multiple loaders and windows (like our subwindow and the main
-     * window).
-     */
-
-    private static final String LOGTAG = "network";
-
-    /**
-     * Network.
-     */
-    private Network mNetwork;
-
-    /**
-     * Loader queue.
-     */
-    private LinkedList<LoadListener> mLoaderQueue;
-
-
-    // Message id for handling the user response
-    private static final int AUTH_PROCEED = 100;
-    private static final int AUTH_CANCEL = 200;
-
-    // Use to synchronize when making synchronous calls to
-    // onReceivedHttpAuthRequest(). We can't use a single Boolean object for
-    // both the lock and the state, because Boolean is immutable.
-    Object mRequestInFlightLock = new Object();
-    boolean mRequestInFlight;
-    String mUsername;
-    String mPassword;
-
-    /**
-     * Creates a new HTTP authentication handler with an empty
-     * loader queue
-     *
-     * @param network The parent network object
-     */
-    /* package */ HttpAuthHandlerImpl(Network network) {
-        mNetwork = network;
-        mLoaderQueue = new LinkedList<LoadListener>();
-    }
-
-
-    @Override
-    public void handleMessage(Message msg) {
-        LoadListener loader = null;
-        synchronized (mLoaderQueue) {
-            loader = mLoaderQueue.poll();
-        }
-        assert(loader.isSynchronous() == false);
-
-        switch (msg.what) {
-            case AUTH_PROCEED:
-                String username = msg.getData().getString("username");
-                String password = msg.getData().getString("password");
-
-                loader.handleAuthResponse(username, password);
-                break;
-
-            case AUTH_CANCEL:
-                loader.handleAuthResponse(null, null);
-                break;
-        }
-
-        processNextLoader();
-    }
-
-    /**
-     * Helper method used to unblock handleAuthRequest(), which in the case of a
-     * synchronous request will wait for proxy.onReceivedHttpAuthRequest() to
-     * call back to either proceed() or cancel().
-     *
-     * @param username The username to use for authentication
-     * @param password The password to use for authentication
-     * @return True if the request is synchronous and handleAuthRequest() has
-     * been unblocked
-     */
-    private boolean handleResponseForSynchronousRequest(String username, String password) {
-        LoadListener loader = null;
-        synchronized (mLoaderQueue) {
-            loader = mLoaderQueue.peek();
-        }
-        if (loader.isSynchronous()) {
-            mUsername = username;
-            mPassword = password;
-            return true;
-        }
-        return false;
-    }
-
-    private void signalRequestComplete() {
-        synchronized (mRequestInFlightLock) {
-            assert(mRequestInFlight);
-            mRequestInFlight = false;
-            mRequestInFlightLock.notify();
-        }
-    }
-
-    /**
-     * Proceed with the authorization with the given credentials
-     *
-     * May be called on the UI thread, rather than the WebCore thread.
-     *
-     * @param username The username to use for authentication
-     * @param password The password to use for authentication
-     */
-    public void proceed(String username, String password) {
-        if (handleResponseForSynchronousRequest(username, password)) {
-            signalRequestComplete();
-            return;
-        }
-        Message msg = obtainMessage(AUTH_PROCEED);
-        msg.getData().putString("username", username);
-        msg.getData().putString("password", password);
-        sendMessage(msg);
-        signalRequestComplete();
-    }
-
-    /**
-     * Cancel the authorization request
-     *
-     * May be called on the UI thread, rather than the WebCore thread.
-     *
-     */
-    public void cancel() {
-        if (handleResponseForSynchronousRequest(null, null)) {
-            signalRequestComplete();
-            return;
-        }
-        sendMessage(obtainMessage(AUTH_CANCEL));
-        signalRequestComplete();
-    }
-
-    /**
-     * @return True if we can use user credentials on record
-     * (ie, if we did not fail trying to use them last time)
-     */
-    public boolean useHttpAuthUsernamePassword() {
-        LoadListener loader = null;
-        synchronized (mLoaderQueue) {
-            loader = mLoaderQueue.peek();
-        }
-        if (loader != null) {
-            return !loader.authCredentialsInvalid();
-        }
-
-        return false;
-    }
-
-    /**
-     * Enqueues the loader, if the loader is the only element
-     * in the queue, starts processing the loader
-     *
-     * @param loader The loader that resulted in this http
-     * authentication request
-     */
-    /* package */ void handleAuthRequest(LoadListener loader) {
-        // The call to proxy.onReceivedHttpAuthRequest() may be asynchronous. If
-        // the request is synchronous, we must block here until we have a
-        // response.
-        if (loader.isSynchronous()) {
-            // If there's a request in flight, wait for it to complete. The
-            // response will queue a message on this thread.
-            waitForRequestToComplete();
-            // Make a request to the proxy for this request, jumping the queue.
-            // We use the queue so that the loader is present in
-            // useHttpAuthUsernamePassword().
-            synchronized (mLoaderQueue) {
-                mLoaderQueue.addFirst(loader);
-            }
-            processNextLoader();
-            // Wait for this request to complete.
-            waitForRequestToComplete();
-            // Pop the loader from the queue.
-            synchronized (mLoaderQueue) {
-                assert(mLoaderQueue.peek() == loader);
-                mLoaderQueue.poll();
-            }
-            // Call back.
-            loader.handleAuthResponse(mUsername, mPassword);
-            // The message queued by the response from the last asynchronous
-            // request, if present, will start the next request.
-            return;
-        }
-
-        boolean processNext = false;
-
-        synchronized (mLoaderQueue) {
-            mLoaderQueue.offer(loader);
-            processNext =
-                (mLoaderQueue.size() == 1);
-        }
-
-        if (processNext) {
-            processNextLoader();
-        }
-    }
-
-    /**
-     * Wait for the request in flight, if any, to complete
-     */
-    private void waitForRequestToComplete() {
-        synchronized (mRequestInFlightLock) {
-            while (mRequestInFlight) {
-                try {
-                    mRequestInFlightLock.wait();
-                } catch(InterruptedException e) {
-                    Log.e(LOGTAG, "Interrupted while waiting for request to complete");
-                }
-            }
-        }
-    }
-
-    /**
-     * Process the next loader in the queue (helper method)
-     */
-    private void processNextLoader() {
-        LoadListener loader = null;
-        synchronized (mLoaderQueue) {
-            loader = mLoaderQueue.peek();
-        }
-        if (loader != null) {
-            synchronized (mRequestInFlightLock) {
-                assert(mRequestInFlight == false);
-                mRequestInFlight = true;
-            }
-
-            CallbackProxy proxy = loader.getFrame().getCallbackProxy();
-
-            String hostname = loader.proxyAuthenticate() ?
-                mNetwork.getProxyHostname() : loader.host();
-
-            String realm = loader.realm();
-
-            proxy.onReceivedHttpAuthRequest(this, hostname, realm);
-        }
-    }
-
-    /**
-     * Informs the WebView of a new set of credentials.
-     */
-    public static void onReceivedCredentials(LoadListener loader,
-            String host, String realm, String username, String password) {
-        CallbackProxy proxy = loader.getFrame().getCallbackProxy();
-        proxy.onReceivedHttpAuthCredentials(host, realm, username, password);
-    }
-}
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index 7b44938..343d34a 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -37,7 +37,6 @@
     // Used by the Chromium HTTP stack.
     private static String sDatabaseDirectory;
     private static String sCacheDirectory;
-    private static Boolean sUseChromiumHttpStack;
     private static Context sContext;
 
     private static void checkInitialized() {
@@ -111,10 +110,9 @@
         // content://
         if (url.startsWith(ANDROID_CONTENT)) {
             try {
-                // Strip off mimetype, for compatibility with ContentLoader.java
-                // If we don't do this, we can fail to load Gmail attachments,
-                // because the URL being loaded doesn't exactly match the URL we
-                // have permission to read.
+                // Strip off MIME type. If we don't do this, we can fail to
+                // load Gmail attachments, because the URL being loaded doesn't
+                // exactly match the URL we have permission to read.
                 int mimeIndex = url.lastIndexOf('?');
                 if (mimeIndex != -1) {
                     url = url.substring(0, mimeIndex);
@@ -152,6 +150,7 @@
         if (url.startsWith(ANDROID_CONTENT)) {
             try {
                 // Strip off mimetype, for compatibility with ContentLoader.java
+                // (used with Android HTTP stack, now removed).
                 // If we don't do this, we can fail to load Gmail attachments,
                 // because the URL being loaded doesn't exactly match the URL we
                 // have permission to read.
@@ -170,19 +169,6 @@
         }
     }
 
-    /**
-     * Returns true if we're using the Chromium HTTP stack.
-     *
-     * TODO: Remove this if/when we permanently switch to the Chromium HTTP stack
-     * http:/b/3118772
-     */
-    static boolean useChromiumHttpStack() {
-        if (sUseChromiumHttpStack == null) {
-            sUseChromiumHttpStack = nativeUseChromiumHttpStack();
-        }
-        return sUseChromiumHttpStack;
-    }
-
     private static synchronized String getAutofillQueryUrl() {
         checkInitialized();
         // If the device has not checked in it won't have pulled down the system setting for the
@@ -200,7 +186,4 @@
         long leftToAllocate = memInfo.availMem - memInfo.threshold;
         return !memInfo.lowMemory && bytesRequested < leftToAllocate;
     }
-
-
-    private static native boolean nativeUseChromiumHttpStack();
 }
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
deleted file mode 100644
index 37e8bc0..0000000
--- a/core/java/android/webkit/LoadListener.java
+++ /dev/null
@@ -1,1685 +0,0 @@
-/*
- * Copyright (C) 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 android.webkit;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.ParseException;
-import android.net.Uri;
-import android.net.WebAddress;
-import android.net.http.EventHandler;
-import android.net.http.Headers;
-import android.net.http.HttpAuthHeader;
-import android.net.http.RequestHandle;
-import android.net.http.SslCertificate;
-import android.net.http.SslError;
-
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import android.webkit.CacheManager.CacheResult;
-import android.webkit.JniUtil;
-
-import com.android.internal.R;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-class LoadListener extends Handler implements EventHandler {
-
-    private static final String LOGTAG = "webkit";
-
-    // Messages used internally to communicate state between the
-    // Network thread and the WebCore thread.
-    private static final int MSG_CONTENT_HEADERS = 100;
-    private static final int MSG_CONTENT_DATA = 110;
-    private static final int MSG_CONTENT_FINISHED = 120;
-    private static final int MSG_CONTENT_ERROR = 130;
-    private static final int MSG_LOCATION_CHANGED = 140;
-    private static final int MSG_LOCATION_CHANGED_REQUEST = 150;
-    private static final int MSG_STATUS = 160;
-    private static final int MSG_SSL_CERTIFICATE = 170;
-    private static final int MSG_SSL_ERROR = 180;
-
-    // Standard HTTP status codes in a more representative format
-    private static final int HTTP_OK = 200;
-    private static final int HTTP_PARTIAL_CONTENT = 206;
-    private static final int HTTP_MOVED_PERMANENTLY = 301;
-    private static final int HTTP_FOUND = 302;
-    private static final int HTTP_SEE_OTHER = 303;
-    private static final int HTTP_NOT_MODIFIED = 304;
-    private static final int HTTP_TEMPORARY_REDIRECT = 307;
-    private static final int HTTP_AUTH = 401;
-    private static final int HTTP_NOT_FOUND = 404;
-    private static final int HTTP_PROXY_AUTH = 407;
-
-    private static int sNativeLoaderCount;
-
-    private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder();
-
-    private String   mUrl;
-    private WebAddress mUri;
-    private boolean  mPermanent;
-    private String   mOriginalUrl;
-    private Context  mContext;
-    private BrowserFrame mBrowserFrame;
-    private int      mNativeLoader;
-    private String   mMimeType;
-    private String   mEncoding;
-    private String   mTransferEncoding;
-    private int      mStatusCode;
-    private String   mStatusText;
-    public long mContentLength; // Content length of the incoming data
-    private boolean  mCancelled;  // The request has been cancelled.
-    private boolean  mAuthFailed;  // indicates that the prev. auth failed
-    private CacheLoader mCacheLoader;
-    private boolean  mFromCache = false;
-    private HttpAuthHeader mAuthHeader;
-    private int      mErrorID = OK;
-    private String   mErrorDescription;
-    private SslError mSslError;
-    private RequestHandle mRequestHandle;
-    private RequestHandle mSslErrorRequestHandle;
-    private long     mPostIdentifier;
-    private boolean  mSetNativeResponse;
-
-    // Request data. It is only valid when we are doing a load from the
-    // cache. It is needed if the cache returns a redirect
-    private String mMethod;
-    private Map<String, String> mRequestHeaders;
-    private byte[] mPostData;
-    // Flag to indicate that this load is synchronous.
-    private boolean mSynchronous;
-    private Vector<Message> mMessageQueue;
-
-    // Does this loader correspond to the main-frame top-level page?
-    private boolean mIsMainPageLoader;
-    // Does this loader correspond to the main content (as opposed to a supporting resource)
-    private final boolean mIsMainResourceLoader;
-    private final boolean mUserGesture;
-
-    private Headers mHeaders;
-
-    private final String mUsername;
-    private final String mPassword;
-
-    // =========================================================================
-    // Public functions
-    // =========================================================================
-
-    public static LoadListener getLoadListener(Context context,
-            BrowserFrame frame, String url, int nativeLoader,
-            boolean synchronous, boolean isMainPageLoader,
-            boolean isMainResource, boolean userGesture, long postIdentifier,
-            String username, String password) {
-
-        sNativeLoaderCount += 1;
-        return new LoadListener(context, frame, url, nativeLoader, synchronous,
-                isMainPageLoader, isMainResource, userGesture, postIdentifier,
-                username, password);
-    }
-
-    public static int getNativeLoaderCount() {
-        return sNativeLoaderCount;
-    }
-
-    LoadListener(Context context, BrowserFrame frame, String url,
-            int nativeLoader, boolean synchronous, boolean isMainPageLoader,
-            boolean isMainResource, boolean userGesture, long postIdentifier,
-            String username, String password) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener constructor url=" + url);
-        }
-        mContext = context;
-        mBrowserFrame = frame;
-        setUrl(url);
-        mNativeLoader = nativeLoader;
-        mSynchronous = synchronous;
-        if (synchronous) {
-            mMessageQueue = new Vector<Message>();
-        }
-        mIsMainPageLoader = isMainPageLoader;
-        mIsMainResourceLoader = isMainResource;
-        mUserGesture = userGesture;
-        mPostIdentifier = postIdentifier;
-        mUsername = username;
-        mPassword = password;
-    }
-
-    /**
-     * We keep a count of refs to the nativeLoader so we do not create
-     * so many LoadListeners that the GREFs blow up
-     */
-    private void clearNativeLoader() {
-        sNativeLoaderCount -= 1;
-        mNativeLoader = 0;
-        mSetNativeResponse = false;
-    }
-
-    /*
-     * This message handler is to facilitate communication between the network
-     * thread and the browser thread.
-     */
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case MSG_CONTENT_HEADERS:
-                /*
-                 * This message is sent when the LoadListener has headers
-                 * available. The headers are sent onto WebCore to see what we
-                 * should do with them.
-                 */
-                handleHeaders((Headers) msg.obj);
-                break;
-
-            case MSG_CONTENT_DATA:
-                /*
-                 * This message is sent when the LoadListener has data available
-                 * in it's data buffer. This data buffer could be filled from a
-                 * file (this thread) or from http (Network thread).
-                 */
-                if (mNativeLoader != 0 && !ignoreCallbacks()) {
-                    commitLoad();
-                }
-                break;
-
-            case MSG_CONTENT_FINISHED:
-                /*
-                 * This message is sent when the LoadListener knows that the
-                 * load is finished. This message is not sent in the case of an
-                 * error.
-                 *
-                 */
-                handleEndData();
-                break;
-
-            case MSG_CONTENT_ERROR:
-                /*
-                 * This message is sent when a load error has occured. The
-                 * LoadListener will clean itself up.
-                 */
-                handleError(msg.arg1, (String) msg.obj);
-                break;
-
-            case MSG_LOCATION_CHANGED:
-                /*
-                 * This message is sent from LoadListener.endData to inform the
-                 * browser activity that the location of the top level page
-                 * changed.
-                 */
-                doRedirect();
-                break;
-
-            case MSG_LOCATION_CHANGED_REQUEST:
-                /*
-                 * This message is sent from endData on receipt of a 307
-                 * Temporary Redirect in response to a POST -- the user must
-                 * confirm whether to continue loading. If the user says Yes,
-                 * we simply call MSG_LOCATION_CHANGED. If the user says No,
-                 * we call MSG_CONTENT_FINISHED.
-                 */
-                Message contMsg = obtainMessage(MSG_LOCATION_CHANGED);
-                Message stopMsg = obtainMessage(MSG_CONTENT_FINISHED);
-                mBrowserFrame.getCallbackProxy().onFormResubmission(
-                        stopMsg, contMsg);
-                break;
-
-            case MSG_STATUS:
-                /*
-                 * This message is sent from the network thread when the http
-                 * stack has received the status response from the server.
-                 */
-                HashMap status = (HashMap) msg.obj;
-                handleStatus(((Integer) status.get("major")).intValue(),
-                        ((Integer) status.get("minor")).intValue(),
-                        ((Integer) status.get("code")).intValue(),
-                        (String) status.get("reason"));
-                break;
-
-            case MSG_SSL_CERTIFICATE:
-                /*
-                 * This message is sent when the network thread receives a ssl
-                 * certificate.
-                 */
-                handleCertificate((SslCertificate) msg.obj);
-                break;
-
-            case MSG_SSL_ERROR:
-                /*
-                 * This message is sent when the network thread encounters a
-                 * ssl error.
-                 */
-                handleSslError((SslError) msg.obj);
-                break;
-        }
-    }
-
-    /**
-     * @return The loader's BrowserFrame.
-     */
-    BrowserFrame getFrame() {
-        return mBrowserFrame;
-    }
-
-    Context getContext() {
-        return mContext;
-    }
-
-    /* package */ boolean isSynchronous() {
-        return mSynchronous;
-    }
-
-    /**
-     * @return True iff the load has been cancelled
-     */
-    public boolean cancelled() {
-        return mCancelled;
-    }
-
-    /**
-     * Parse the headers sent from the server.
-     * @param headers gives up the HeaderGroup
-     * IMPORTANT: as this is called from network thread, can't call native
-     * directly
-     */
-    public void headers(Headers headers) {
-        if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers");
-        // call db (setCookie) in the non-WebCore thread
-        if (mCancelled) return;
-        ArrayList<String> cookies = headers.getSetCookie();
-        for (int i = 0; i < cookies.size(); ++i) {
-            CookieManager.getInstance().setCookie(mUri, cookies.get(i));
-        }
-        sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers));
-    }
-
-    // This is the same regex that DOMImplementation uses to check for xml
-    // content. Use this to check if another Activity wants to handle the
-    // content before giving it to webkit.
-    private static final String XML_MIME_TYPE =
-            "^[\\w_\\-+~!$\\^{}|.%'`#&*]+/" +
-            "[\\w_\\-+~!$\\^{}|.%'`#&*]+\\+xml$";
-
-    // Does the header parsing work on the WebCore thread.
-    private void handleHeaders(Headers headers) {
-        if (mCancelled) return;
-
-        // Note: the headers we care in LoadListeners, like
-        // content-type/content-length, should not be updated for partial
-        // content. Just skip here and go ahead with adding data.
-        if (mStatusCode == HTTP_PARTIAL_CONTENT) {
-            // we don't support cache for partial content yet
-            WebViewWorker.getHandler().obtainMessage(
-                    WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
-            return;
-        }
-
-        mHeaders = headers;
-
-        long contentLength = headers.getContentLength();
-        if (contentLength != Headers.NO_CONTENT_LENGTH) {
-            mContentLength = contentLength;
-        } else {
-            mContentLength = 0;
-        }
-
-        String contentType = headers.getContentType();
-        if (contentType != null) {
-            parseContentTypeHeader(contentType);
-            mMimeType = MimeTypeMap.getSingleton().remapGenericMimeType(
-                    mMimeType, mUrl, headers.getContentDisposition());
-        } else {
-            /* Often when servers respond with 304 Not Modified or a
-               Redirect, then they don't specify a MIMEType. When this
-               occurs, the function below is called.  In the case of
-               304 Not Modified, the cached headers are used rather
-               than the headers that are returned from the server. */
-            guessMimeType();
-        }
-        // At this point, mMimeType has been set to non-null.
-        if (mIsMainPageLoader && mIsMainResourceLoader && mUserGesture &&
-                Pattern.matches(XML_MIME_TYPE, mMimeType) &&
-                !mMimeType.equalsIgnoreCase("application/xhtml+xml")) {
-            Intent i = new Intent(Intent.ACTION_VIEW);
-            i.setDataAndType(Uri.parse(url()), mMimeType);
-            ResolveInfo info = mContext.getPackageManager().resolveActivity(i,
-                    PackageManager.MATCH_DEFAULT_ONLY);
-            if (info != null && !mContext.getPackageName().equals(
-                    info.activityInfo.packageName)) {
-                // someone (other than the current app) knows how to
-                // handle this mime type.
-                try {
-                    mContext.startActivity(i);
-                    mBrowserFrame.stopLoading();
-                    return;
-                } catch (ActivityNotFoundException ex) {
-                    // continue loading internally.
-                }
-            }
-        }
-
-        // is it an authentication request?
-        boolean mustAuthenticate = (mStatusCode == HTTP_AUTH ||
-                mStatusCode == HTTP_PROXY_AUTH);
-        // is it a proxy authentication request?
-        boolean isProxyAuthRequest = (mStatusCode == HTTP_PROXY_AUTH);
-        // is this authentication request due to a failed attempt to
-        // authenticate ealier?
-        mAuthFailed = false;
-
-        // if we tried to authenticate ourselves last time
-        if (mAuthHeader != null) {
-            // we failed, if we must authenticate again now and
-            // we have a proxy-ness match
-            mAuthFailed = (mustAuthenticate &&
-                    isProxyAuthRequest == mAuthHeader.isProxy());
-
-            // if we did NOT fail and last authentication request was a
-            // proxy-authentication request
-            if (!mAuthFailed && mAuthHeader.isProxy()) {
-                Network network = Network.getInstance(mContext);
-                // if we have a valid proxy set
-                if (network.isValidProxySet()) {
-                    /* The proxy credentials can be read in the WebCore thread
-                    */
-                    synchronized (network) {
-                        // save authentication credentials for pre-emptive proxy
-                        // authentication
-                        network.setProxyUsername(mAuthHeader.getUsername());
-                        network.setProxyPassword(mAuthHeader.getPassword());
-                    }
-                }
-            }
-        }
-
-        // it is only here that we can reset the last mAuthHeader object
-        // (if existed) and start a new one!!!
-        mAuthHeader = null;
-        if (mustAuthenticate) {
-            if (mStatusCode == HTTP_AUTH) {
-                mAuthHeader = parseAuthHeader(
-                        headers.getWwwAuthenticate());
-            } else {
-                mAuthHeader = parseAuthHeader(
-                        headers.getProxyAuthenticate());
-                // if successfully parsed the header
-                if (mAuthHeader != null) {
-                    // mark the auth-header object as a proxy
-                    mAuthHeader.setProxy();
-                }
-            }
-        }
-
-        // Only create a cache file if the server has responded positively.
-        if ((mStatusCode == HTTP_OK ||
-                mStatusCode == HTTP_FOUND ||
-                mStatusCode == HTTP_MOVED_PERMANENTLY ||
-                mStatusCode == HTTP_TEMPORARY_REDIRECT) && 
-                mNativeLoader != 0) {
-            // for POST request, only cache the result if there is an identifier
-            // associated with it. postUrl() or form submission should set the
-            // identifier while XHR POST doesn't.
-            if (!mFromCache && mRequestHandle != null
-                    && (!mRequestHandle.getMethod().equals("POST")
-                            || mPostIdentifier != 0)) {
-                WebViewWorker.CacheCreateData data = new WebViewWorker.CacheCreateData();
-                data.mListener = this;
-                data.mUrl = mUrl;
-                data.mMimeType = mMimeType;
-                data.mStatusCode = mStatusCode;
-                data.mPostId = mPostIdentifier;
-                data.mHeaders = headers;
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_CREATE_CACHE, data).sendToTarget();
-            }
-            WebViewWorker.CacheEncoding ce = new WebViewWorker.CacheEncoding();
-            ce.mEncoding = mEncoding;
-            ce.mListener = this;
-            WebViewWorker.getHandler().obtainMessage(
-                    WebViewWorker.MSG_UPDATE_CACHE_ENCODING, ce).sendToTarget();
-        }
-        commitHeadersCheckRedirect();
-    }
-
-    /**
-     * @return True iff this loader is in the proxy-authenticate state.
-     */
-    boolean proxyAuthenticate() {
-        if (mAuthHeader != null) {
-            return mAuthHeader.isProxy();
-        }
-
-        return false;
-    }
-
-    /**
-     * Report the status of the response.
-     * TODO: Comments about each parameter.
-     * IMPORTANT: as this is called from network thread, can't call native
-     * directly
-     */
-    public void status(int majorVersion, int minorVersion,
-            int code, /* Status-Code value */ String reasonPhrase) {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener: from: " + mUrl
-                    + " major: " + majorVersion
-                    + " minor: " + minorVersion
-                    + " code: " + code
-                    + " reason: " + reasonPhrase);
-        }
-        HashMap status = new HashMap();
-        status.put("major", majorVersion);
-        status.put("minor", minorVersion);
-        status.put("code", code);
-        status.put("reason", reasonPhrase);
-        // New status means new data. Clear the old.
-        mDataBuilder.clear();
-        mMimeType = "";
-        mEncoding = "";
-        mTransferEncoding = "";
-        sendMessageInternal(obtainMessage(MSG_STATUS, status));
-    }
-
-    // Handle the status callback on the WebCore thread.
-    private void handleStatus(int major, int minor, int code, String reason) {
-        if (mCancelled) return;
-
-        mStatusCode = code;
-        mStatusText = reason;
-        mPermanent = false;
-    }
-
-    /**
-     * Implementation of certificate handler for EventHandler. Called
-     * before a resource is requested. In this context, can be called
-     * multiple times if we have redirects
-     *
-     * IMPORTANT: as this is called from network thread, can't call
-     * native directly
-     *
-     * @param certificate The SSL certifcate or null if the request
-     * was not secure
-     */
-    public void certificate(SslCertificate certificate) {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.certificate: " + certificate);
-        }
-        sendMessageInternal(obtainMessage(MSG_SSL_CERTIFICATE, certificate));
-    }
-
-    // Handle the certificate on the WebCore thread.
-    private void handleCertificate(SslCertificate certificate) {
-        // if this is main resource of the top frame
-        if (mIsMainPageLoader && mIsMainResourceLoader) {
-            // update the browser frame with certificate
-            mBrowserFrame.certificate(certificate);
-        }
-    }
-
-    /**
-     * Implementation of error handler for EventHandler.
-     * Subclasses should call this method to have error fields set.
-     * @param id The error id described by EventHandler.
-     * @param description A string description of the error.
-     * IMPORTANT: as this is called from network thread, can't call native
-     * directly
-     */
-    public void error(int id, String description) {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.error url:" +
-                    url() + " id:" + id + " description:" + description);
-        }
-        sendMessageInternal(obtainMessage(MSG_CONTENT_ERROR, id, 0, description));
-    }
-
-    // Handle the error on the WebCore thread.
-    private void handleError(int id, String description) {
-        mErrorID = id;
-        mErrorDescription = description;
-        detachRequestHandle();
-        notifyError();
-        tearDown();
-    }
-
-    /**
-     * Add data to the internal collection of data. This function is used by
-     * the data: scheme, about: scheme and http/https schemes.
-     * @param data A byte array containing the content.
-     * @param length The length of data.
-     * IMPORTANT: as this is called from network thread, can't call native
-     * directly
-     * XXX: Unlike the other network thread methods, this method can do the
-     * work of decoding the data and appending it to the data builder.
-     */
-    public void data(byte[] data, int length) {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.data(): url: " + url());
-        }
-
-        // The reason isEmpty() and append() need to synchronized together is
-        // because it is possible for getFirstChunk() to be called multiple
-        // times between isEmpty() and append(). This could cause commitLoad()
-        // to finish before processing the newly appended data and no message
-        // will be sent.
-        boolean sendMessage = false;
-        synchronized (mDataBuilder) {
-            sendMessage = mDataBuilder.isEmpty();
-            mDataBuilder.append(data, 0, length);
-        }
-        if (sendMessage) {
-            // Send a message whenever data comes in after a write to WebCore
-            sendMessageInternal(obtainMessage(MSG_CONTENT_DATA));
-        }
-    }
-
-    /**
-     * Event handler's endData call. Send a message to the handler notifying
-     * them that the data has finished.
-     * IMPORTANT: as this is called from network thread, can't call native
-     * directly
-     */
-    public void endData() {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.endData(): url: " + url());
-        }
-        sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED));
-    }
-
-    // Handle the end of data.
-    private void handleEndData() {
-        if (mCancelled) return;
-
-        switch (mStatusCode) {
-            case HTTP_MOVED_PERMANENTLY:
-                // 301 - permanent redirect
-                mPermanent = true;
-            case HTTP_FOUND:
-            case HTTP_SEE_OTHER:
-            case HTTP_TEMPORARY_REDIRECT:
-                // 301, 302, 303, and 307 - redirect
-                if (mStatusCode == HTTP_TEMPORARY_REDIRECT) {
-                    if (mRequestHandle != null && 
-                                mRequestHandle.getMethod().equals("POST")) {
-                        sendMessageInternal(obtainMessage(
-                                MSG_LOCATION_CHANGED_REQUEST));  
-                    } else if (mMethod != null && mMethod.equals("POST")) {
-                        sendMessageInternal(obtainMessage(
-                                MSG_LOCATION_CHANGED_REQUEST));
-                    } else {
-                        sendMessageInternal(obtainMessage(MSG_LOCATION_CHANGED));
-                    }
-                } else {
-                    sendMessageInternal(obtainMessage(MSG_LOCATION_CHANGED));
-                }
-                return;
-
-            case HTTP_AUTH:
-            case HTTP_PROXY_AUTH:
-                // According to rfc2616, the response for HTTP_AUTH must include
-                // WWW-Authenticate header field and the response for 
-                // HTTP_PROXY_AUTH must include Proxy-Authenticate header field.
-                if (mAuthHeader != null &&
-                        (Network.getInstance(mContext).isValidProxySet() ||
-                         !mAuthHeader.isProxy())) {
-                    // If this is the first attempt to authenticate, try again with the username and
-                    // password supplied in the URL, if present.
-                    if (!mAuthFailed && mUsername != null && mPassword != null) {
-                        String host = mAuthHeader.isProxy() ?
-                                Network.getInstance(mContext).getProxyHostname() :
-                                mUri.getHost();
-                        HttpAuthHandlerImpl.onReceivedCredentials(this, host,
-                                mAuthHeader.getRealm(), mUsername, mPassword);
-                        makeAuthResponse(mUsername, mPassword);
-                    } else {
-                        Network.getInstance(mContext).handleAuthRequest(this);
-                    }
-                    return;
-                }
-                break;  // use default
-
-            case HTTP_NOT_MODIFIED:
-                // Server could send back NOT_MODIFIED even if we didn't
-                // ask for it, so make sure we have a valid CacheLoader
-                // before calling it.
-                if (mCacheLoader != null) {
-                    if (isSynchronous()) {
-                        mCacheLoader.load();
-                    } else {
-                        // Load the cached file in a separate thread
-                        WebViewWorker.getHandler().obtainMessage(
-                                WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
-                                .sendToTarget();
-                    }
-                    mFromCache = true;
-                    if (DebugFlags.LOAD_LISTENER) {
-                        Log.v(LOGTAG, "LoadListener cache load url=" + url());
-                    }
-                    return;
-                }
-                break;  // use default
-
-            case HTTP_NOT_FOUND:
-                // Not an error, the server can send back content.
-            default:
-                break;
-        }
-        detachRequestHandle();
-        tearDown();
-    }
-
-    /* This method is called from CacheLoader when the initial request is
-     * serviced by the Cache. */
-    /* package */ void setCacheLoader(CacheLoader c) {
-        mCacheLoader = c;
-        mFromCache = true;
-    }
-
-    /**
-     * Check the cache for the current URL, and load it if it is valid.
-     *
-     * @param headers for the request
-     * @return true if cached response is used.
-     */
-    boolean checkCache(Map<String, String> headers) {
-        // Get the cache file name for the current URL
-        CacheResult result = CacheManager.getCacheFile(url(), mPostIdentifier,
-                headers);
-
-        // Go ahead and set the cache loader to null in case the result is
-        // null.
-        mCacheLoader = null;
-        // reset the flag
-        mFromCache = false;
-
-        if (result != null) {
-            // The contents of the cache may need to be revalidated so just
-            // remember the cache loader in the case that the server responds
-            // positively to the cached content. This is also used to detect if
-            // a redirect came from the cache.
-            mCacheLoader = new CacheLoader(this, result);
-
-            // If I got a cachedUrl and the revalidation header was not
-            // added, then the cached content valid, we should use it.
-            if (!headers.containsKey(
-                    CacheManager.HEADER_KEY_IFNONEMATCH) &&
-                    !headers.containsKey(
-                            CacheManager.HEADER_KEY_IFMODIFIEDSINCE)) {
-                if (DebugFlags.LOAD_LISTENER) {
-                    Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " +
-                            "and usable: " + url());
-                }
-                if (isSynchronous()) {
-                    mCacheLoader.load();
-                } else {
-                    // Load the cached file in a separate thread
-                    WebViewWorker.getHandler().obtainMessage(
-                            WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
-                            .sendToTarget();
-                }
-                mFromCache = true;
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * SSL certificate error callback. Handles SSL error(s) on the way up
-     * to the user.
-     * IMPORTANT: as this is called from network thread, can't call native
-     * directly
-     */
-    public boolean handleSslErrorRequest(SslError error) {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG,
-                    "LoadListener.handleSslErrorRequest(): url:" + url() +
-                    " primary error: " + error.getPrimaryError() +
-                    " certificate: " + error.getCertificate());
-        }
-        // Check the cached preference table before sending a message. This
-        // will prevent waiting for an already available answer.
-        if (Network.getInstance(mContext).checkSslPrefTable(this, error)) {
-            return true;
-        }
-        // Do not post a message for a synchronous request. This will cause a
-        // deadlock. Just bail on the request.
-        if (isSynchronous()) {
-            mRequestHandle.handleSslErrorResponse(false);
-            return true;
-        }
-        sendMessageInternal(obtainMessage(MSG_SSL_ERROR, error));
-        // if it has been canceled, return false so that the network thread
-        // won't be blocked. If it is not canceled, save the mRequestHandle
-        // so that if it is canceled when MSG_SSL_ERROR is handled, we can
-        // still call handleSslErrorResponse which will call restartConnection
-        // to unblock the network thread.
-        if (!mCancelled) {
-            mSslErrorRequestHandle = mRequestHandle;
-        }
-        return !mCancelled;
-    }
-
-    // Handle the ssl error on the WebCore thread.
-    private void handleSslError(SslError error) {
-        if (!mCancelled) {
-            mSslError = error;
-            Network.getInstance(mContext).handleSslErrorRequest(this);
-        } else if (mSslErrorRequestHandle != null) {
-            mSslErrorRequestHandle.handleSslErrorResponse(true);
-        }
-        mSslErrorRequestHandle = null;
-    }
-
-    /**
-     * @return HTTP authentication realm or null if none.
-     */
-    String realm() {
-        if (mAuthHeader == null) {
-            return null;
-        } else {
-            return mAuthHeader.getRealm();
-        }
-    }
-
-    /**
-     * Returns true iff an HTTP authentication problem has
-     * occured (credentials invalid).
-     */
-    boolean authCredentialsInvalid() {
-        // if it is digest and the nonce is stale, we just
-        // resubmit with a new nonce
-        return (mAuthFailed &&
-                !(mAuthHeader.isDigest() && mAuthHeader.getStale()));
-    }
-
-    /**
-     * @return The last SSL error or null if there is none
-     */
-    SslError sslError() {
-        return mSslError;
-    }
-
-    /**
-     * Handles SSL error(s) on the way down from the user
-     * (the user has already provided their feedback).
-     */
-    void handleSslErrorResponse(boolean proceed) {
-        if (mRequestHandle != null) {
-            mRequestHandle.handleSslErrorResponse(proceed);
-        }
-        if (!proceed) {
-            mBrowserFrame.stopLoading();
-            tearDown();
-        }
-    }
-
-    /**
-     * Uses user-supplied credentials to restart a request. If the credentials
-     * are null, cancel the request.
-     */
-    void handleAuthResponse(String username, String password) {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.handleAuthResponse: url: " + mUrl
-                    + " username: " + username
-                    + " password: " + password);
-        }
-        if (username != null && password != null) {
-            makeAuthResponse(username, password);
-        } else {
-            // Commit whatever data we have and tear down the loader.
-            commitLoad();
-            tearDown();
-        }
-    }
-
-    void makeAuthResponse(String username, String password) {
-        if (mAuthHeader == null || mRequestHandle == null) {
-            return;
-        }
-
-        mAuthHeader.setUsername(username);
-        mAuthHeader.setPassword(password);
-
-        int scheme = mAuthHeader.getScheme();
-        if (scheme == HttpAuthHeader.BASIC) {
-            // create a basic response
-            boolean isProxy = mAuthHeader.isProxy();
-
-            mRequestHandle.setupBasicAuthResponse(isProxy, username, password);
-        } else if (scheme == HttpAuthHeader.DIGEST) {
-            // create a digest response
-            boolean isProxy = mAuthHeader.isProxy();
-
-            String realm     = mAuthHeader.getRealm();
-            String nonce     = mAuthHeader.getNonce();
-            String qop       = mAuthHeader.getQop();
-            String algorithm = mAuthHeader.getAlgorithm();
-            String opaque    = mAuthHeader.getOpaque();
-
-            mRequestHandle.setupDigestAuthResponse(isProxy, username, password,
-                    realm, nonce, qop, algorithm, opaque);
-        }
-    }
-
-    /**
-     * This is called when a request can be satisfied by the cache, however,
-     * the cache result could be a redirect. In this case we need to issue
-     * the network request.
-     * @param method
-     * @param headers
-     * @param postData
-     */
-    void setRequestData(String method, Map<String, String> headers, 
-            byte[] postData) {
-        mMethod = method;
-        mRequestHeaders = headers;
-        mPostData = postData;
-    }
-
-    /**
-     * @return The current URL associated with this load.
-     */
-    String url() {
-        return mUrl;
-    }
-
-    /**
-     * @return The current WebAddress associated with this load.
-     */
-    WebAddress getWebAddress() {
-        return mUri;
-    }
-
-    /**
-     * @return URL hostname (current URL).
-     */
-    String host() {
-        if (mUri != null) {
-            return mUri.getHost();
-        }
-
-        return null;
-    }
-
-    /**
-     * @return The original URL associated with this load.
-     */
-    String originalUrl() {
-        if (mOriginalUrl != null) {
-            return mOriginalUrl;
-        } else {
-            return mUrl;
-        }
-    }
-
-    long postIdentifier() {
-        return mPostIdentifier;
-    }
-
-    void attachRequestHandle(RequestHandle requestHandle) {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.attachRequestHandle(): " +
-                    "requestHandle: " +  requestHandle);
-        }
-        mRequestHandle = requestHandle;
-    }
-
-    void detachRequestHandle() {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.detachRequestHandle(): " +
-                    "requestHandle: " + mRequestHandle);
-        }
-        mRequestHandle = null;
-    }
-
-    /*
-     * This function is called from native WebCore code to
-     * notify this LoadListener that the content it is currently
-     * downloading should be saved to a file and not sent to
-     * WebCore.
-     */
-    void downloadFile() {
-        // remove the cache
-        WebViewWorker.getHandler().obtainMessage(
-                WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
-
-        // Inform the client that they should download a file
-        mBrowserFrame.getCallbackProxy().onDownloadStart(url(), 
-                mBrowserFrame.getUserAgentString(),
-                mHeaders.getContentDisposition(), 
-                mMimeType, mContentLength);
-
-        // Cancel the download. We need to stop the http load.
-        // The native loader object will get cleared by the call to
-        // cancel() but will also be cleared on the WebCore side
-        // when this function returns.
-        cancel();
-    }
-    
-    /*
-     * This function is called from native WebCore code to
-     * find out if the given URL is in the cache, and if it can
-     * be used. This is just for forward/back navigation to a POST
-     * URL.
-     */
-    static boolean willLoadFromCache(String url, long identifier) {
-        assert !JniUtil.useChromiumHttpStack();
-        boolean inCache =
-                CacheManager.getCacheFile(url, identifier, null) != null;
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "willLoadFromCache: " + url + " in cache: " + 
-                    inCache);
-        }
-        return inCache;
-    }
-
-    /*
-     * Reset the cancel flag. This is used when we are resuming a stopped
-     * download. To suspend a download, we cancel it. It can also be cancelled
-     * when it has run out of disk space. In this situation, the download
-     * can be resumed.
-     */
-    void resetCancel() {
-        mCancelled = false;
-    }
-
-    String mimeType() {
-        return mMimeType;
-    }
-
-    String transferEncoding() {
-        return mTransferEncoding;
-    }
-
-    /*
-     * Return the size of the content being downloaded. This represents the
-     * full content size, even under the situation where the download has been
-     * resumed after interruption.
-     *
-     * @ return full content size
-     */
-    long contentLength() {
-        return mContentLength;
-    }
-
-    // Commit the headers if the status code is not a redirect.
-    private void commitHeadersCheckRedirect() {
-        if (mCancelled) return;
-
-        // do not call webcore if it is redirect. According to the code in
-        // InspectorController::willSendRequest(), the response is only updated
-        // when it is not redirect. If we received a not-modified response from
-        // the server and mCacheLoader is not null, do not send the response to
-        // webkit. This is just a validation response for loading from the
-        // cache.
-        if ((mStatusCode >= 301 && mStatusCode <= 303) || mStatusCode == 307 ||
-                (mStatusCode == 304 && mCacheLoader != null)) {
-            return;
-        }
-
-        commitHeaders();
-    }
-
-    // This commits the headers without checking the response status code.
-    private void commitHeaders() {
-        if (mIsMainPageLoader && CertTool.getCertType(mMimeType) != null) {
-            // In the case of downloading certificate, we will save it to the
-            // KeyStore in commitLoad. Do not call webcore.
-            return;
-        }
-
-        // If the response is an authentication and we've resent the
-        // request with some credentials then don't commit the headers
-        // of this response; wait for the response to the request with the
-        // credentials.
-        if (mAuthHeader != null) {
-            return;
-        }
-
-        setNativeResponse();
-    }
-
-    private void setNativeResponse() {
-        int nativeResponse = createNativeResponse();
-        // The native code deletes the native response object.
-        nativeReceivedResponse(nativeResponse);
-        mSetNativeResponse = true;
-    }
-
-    /**
-     * Create a WebCore response object so that it can be used by
-     * nativeReceivedResponse or nativeRedirectedToUrl
-     * @return native response pointer
-     */
-    private int createNativeResponse() {
-        // If WebCore sends if-modified-since, mCacheLoader is null. If 
-        // CacheManager sends it, mCacheLoader is not null. In this case, if the
-        // server responds with a 304, then we treat it like it was a 200 code 
-        // and proceed with loading the file from the cache.
-        int statusCode = (mStatusCode == HTTP_NOT_MODIFIED &&
-                mCacheLoader != null) ? HTTP_OK : mStatusCode;
-        // pass content-type content-length and content-encoding
-        final int nativeResponse = nativeCreateResponse(
-                originalUrl(), statusCode, mStatusText,
-                mMimeType, mContentLength, mEncoding);
-        if (mHeaders != null) {
-            mHeaders.getHeaders(new Headers.HeaderCallback() {
-                    public void header(String name, String value) {
-                        nativeSetResponseHeader(nativeResponse, name, value);
-                    }
-                });
-        }
-        return nativeResponse;
-    }
-
-    /**
-     * Commit the load.  It should be ok to call repeatedly but only before
-     * tearDown is called.
-     */
-    private void commitLoad() {
-        if (mCancelled) return;
-        if (!mSetNativeResponse) {
-            setNativeResponse();
-        }
-
-        if (mIsMainPageLoader) {
-            String type = CertTool.getCertType(mMimeType);
-            if (type != null) {
-                // This must be synchronized so that no more data can be added
-                // after getByteSize returns.
-                synchronized (mDataBuilder) {
-                    // In the case of downloading certificate, we will save it
-                    // to the KeyStore and stop the current loading so that it
-                    // will not generate a new history page
-                    byte[] cert = new byte[mDataBuilder.getByteSize()];
-                    int offset = 0;
-                    while (true) {
-                        ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk();
-                        if (c == null) break;
-
-                        if (c.mLength != 0) {
-                            System.arraycopy(c.mArray, 0, cert, offset, c.mLength);
-                            offset += c.mLength;
-                        }
-                        c.release();
-                    }
-                    CertTool.addCertificate(mContext, type, cert);
-                    mBrowserFrame.stopLoading();
-                    return;
-                }
-            }
-        }
-
-        // Give the data to WebKit now. We don't have to synchronize on
-        // mDataBuilder here because pulling each chunk removes it from the
-        // internal list so it cannot be modified.
-        ByteArrayBuilder.Chunk c;
-        while (true) {
-            c = mDataBuilder.getFirstChunk();
-            if (c == null) break;
-
-            if (c.mLength != 0) {
-                nativeAddData(c.mArray, c.mLength);
-                WebViewWorker.CacheData data = new WebViewWorker.CacheData();
-                data.mListener = this;
-                data.mChunk = c;
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_APPEND_CACHE, data).sendToTarget();
-            } else {
-                c.release();
-            }
-        }
-    }
-
-    /**
-     * Tear down the load. Subclasses should clean up any mess because of
-     * cancellation or errors during the load.
-     */
-    void tearDown() {
-        if (getErrorID() == OK) {
-            WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData();
-            data.mListener = this;
-            data.mUrl = mUrl;
-            data.mPostId = mPostIdentifier;
-            WebViewWorker.getHandler().obtainMessage(
-                    WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget();
-        } else {
-            WebViewWorker.getHandler().obtainMessage(
-                    WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
-        }
-        if (mNativeLoader != 0) {
-            if (!mSetNativeResponse) {
-                setNativeResponse();
-            }
-
-            nativeFinished();
-            clearNativeLoader();
-        }
-    }
-
-    /**
-     * Helper for getting the error ID.
-     * @return errorID.
-     */
-    private int getErrorID() {
-        return mErrorID;
-    }
-
-    /**
-     * Return the error description.
-     * @return errorDescription.
-     */
-    private String getErrorDescription() {
-        return mErrorDescription;
-    }
-
-    /**
-     * Notify the loader we encountered an error.
-     */
-    void notifyError() {
-        if (mNativeLoader != 0) {
-            String description = getErrorDescription();
-            if (description == null) description = "";
-            nativeError(getErrorID(), description, url());
-            clearNativeLoader();
-        }
-    }
-
-    /**
-     * Pause the load. For example, if a plugin is unable to accept more data,
-     * we pause reading from the request. Called directly from the WebCore thread.
-     */
-    void pauseLoad(boolean pause) {
-        if (mRequestHandle != null) {
-            mRequestHandle.pauseRequest(pause);
-        }
-    }
-
-    /**
-     * Cancel a request.
-     * FIXME: This will only work if the request has yet to be handled. This
-     * is in no way guarenteed if requests are served in a separate thread.
-     * It also causes major problems if cancel is called during an
-     * EventHandler's method call.
-     */
-    public void cancel() {
-        if (DebugFlags.LOAD_LISTENER) {
-            if (mRequestHandle == null) {
-                Log.v(LOGTAG, "LoadListener.cancel(): no requestHandle");
-            } else {
-                Log.v(LOGTAG, "LoadListener.cancel()");
-            }
-        }
-        if (mRequestHandle != null) {
-            mRequestHandle.cancel();
-            mRequestHandle = null;
-        }
-
-        WebViewWorker.getHandler().obtainMessage(
-                WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
-        mCancelled = true;
-
-        clearNativeLoader();
-    }
-
-    // This count is transferred from RequestHandle to LoadListener when
-    // loading from the cache so that we can detect redirect loops that switch
-    // between the network and the cache.
-    private int mCacheRedirectCount;
-
-    /*
-     * Perform the actual redirection. This involves setting up the new URL,
-     * informing WebCore and then telling the Network to start loading again.
-     */
-    private void doRedirect() {
-        // as cancel() can cancel the load before doRedirect() is
-        // called through handleMessage, needs to check to see if we
-        // are canceled before proceed
-        if (mCancelled) {
-            return;
-        }
-
-        // Do the same check for a redirect loop that
-        // RequestHandle.setupRedirect does.
-        if (mCacheRedirectCount >= RequestHandle.MAX_REDIRECT_COUNT) {
-            handleError(EventHandler.ERROR_REDIRECT_LOOP, mContext.getString(
-                    R.string.httpErrorRedirectLoop));
-            return;
-        }
-
-        String redirectTo = mHeaders.getLocation();
-        if (redirectTo != null) {
-            int nativeResponse = createNativeResponse();
-            redirectTo =
-                    nativeRedirectedToUrl(mUrl, redirectTo, nativeResponse);
-            // nativeRedirectedToUrl() may call cancel(), e.g. when redirect
-            // from a https site to a http site, check mCancelled again
-            if (mCancelled) {
-                return;
-            }
-            if (redirectTo == null) {
-                Log.d(LOGTAG, "Redirection failed for "
-                        + mHeaders.getLocation());
-                cancel();
-                return;
-            } else if (!URLUtil.isNetworkUrl(redirectTo)) {
-                final String text = mContext
-                        .getString(R.string.open_permission_deny)
-                        + "\n" + redirectTo;
-                if (!mSetNativeResponse) {
-                    setNativeResponse();
-                }
-                nativeAddData(text.getBytes(), text.length());
-                nativeFinished();
-                clearNativeLoader();
-                return;
-            }
-
-
-            // Cache the redirect response
-            if (getErrorID() == OK) {
-                WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData();
-                data.mListener = this;
-                data.mUrl = mUrl;
-                data.mPostId = mPostIdentifier;
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget();
-            } else {
-                WebViewWorker.getHandler().obtainMessage(
-                        WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
-            }
-
-            // Saving a copy of the unstripped url for the response
-            mOriginalUrl = redirectTo;
-            // This will strip the anchor
-            setUrl(redirectTo);
-
-            // Redirect may be in the cache
-            if (mRequestHeaders == null) {
-                mRequestHeaders = new HashMap<String, String>();
-            }
-            boolean fromCache = false;
-            if (mCacheLoader != null) {
-                // This is a redirect from the cache loader. Increment the
-                // redirect count to avoid redirect loops.
-                mCacheRedirectCount++;
-                fromCache = true;
-            }
-            if (!checkCache(mRequestHeaders)) {
-                // mRequestHandle can be null when the request was satisfied
-                // by the cache, and the cache returned a redirect
-                if (mRequestHandle != null) {
-                    try {
-                        mRequestHandle.setupRedirect(mUrl, mStatusCode,
-                                mRequestHeaders);
-                    } catch(RuntimeException e) {
-                        Log.e(LOGTAG, e.getMessage());
-                        // Signal a bad url error if we could not load the
-                        // redirection.
-                        handleError(EventHandler.ERROR_BAD_URL,
-                                mContext.getString(R.string.httpErrorBadUrl));
-                        return;
-                    }
-                } else {
-                    // If the original request came from the cache, there is no
-                    // RequestHandle, we have to create a new one through
-                    // Network.requestURL.
-                    Network network = Network.getInstance(getContext());
-                    if (!network.requestURL(mMethod, mRequestHeaders,
-                            mPostData, this)) {
-                        // Signal a bad url error if we could not load the
-                        // redirection.
-                        handleError(EventHandler.ERROR_BAD_URL,
-                                mContext.getString(R.string.httpErrorBadUrl));
-                        return;
-                    }
-                }
-                if (fromCache) {
-                    // If we are coming from a cache load, we need to transfer
-                    // the redirect count to the new (or old) RequestHandle to
-                    // keep the redirect count in sync.
-                    mRequestHandle.setRedirectCount(mCacheRedirectCount);
-                }
-            } else if (!fromCache) {
-                // Switching from network to cache means we need to grab the
-                // redirect count from the RequestHandle to keep the count in
-                // sync. Add 1 to account for the current redirect.
-                mCacheRedirectCount = mRequestHandle.getRedirectCount() + 1;
-            }
-        } else {
-            commitHeaders();
-            commitLoad();
-            tearDown();
-        }
-
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.onRedirect(): redirect to: " +
-                    redirectTo);
-        }
-    }
-
-    /**
-     * Parses the content-type header.
-     * The first part only allows '-' if it follows x or X.
-     */
-    private static final Pattern CONTENT_TYPE_PATTERN =
-            Pattern.compile("^((?:[xX]-)?[a-zA-Z\\*]+/[\\w\\+\\*-]+[\\.[\\w\\+-]+]*)$");
-
-    /* package */ void parseContentTypeHeader(String contentType) {
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "LoadListener.parseContentTypeHeader: " +
-                    "contentType: " + contentType);
-        }
-
-        if (contentType != null) {
-            int i = contentType.indexOf(';');
-            if (i >= 0) {
-                mMimeType = contentType.substring(0, i);
-
-                int j = contentType.indexOf('=', i);
-                if (j > 0) {
-                    i = contentType.indexOf(';', j);
-                    if (i < j) {
-                        i = contentType.length();
-                    }
-                    mEncoding = contentType.substring(j + 1, i);
-                } else {
-                    mEncoding = contentType.substring(i + 1);
-                }
-                // Trim excess whitespace.
-                mEncoding = mEncoding.trim().toLowerCase();
-
-                if (i < contentType.length() - 1) {
-                    // for data: uri the mimeType and encoding have
-                    // the form image/jpeg;base64 or text/plain;charset=utf-8
-                    // or text/html;charset=utf-8;base64
-                    mTransferEncoding =
-                            contentType.substring(i + 1).trim().toLowerCase();
-                }
-            } else {
-                mMimeType = contentType;
-            }
-
-            // Trim leading and trailing whitespace
-            mMimeType = mMimeType.trim();
-
-            try {
-                Matcher m = CONTENT_TYPE_PATTERN.matcher(mMimeType);
-                if (m.find()) {
-                    mMimeType = m.group(1);
-                } else {
-                    guessMimeType();
-                }
-            } catch (IllegalStateException ex) {
-                guessMimeType();
-            }
-        }
-        // Ensure mMimeType is lower case.
-        mMimeType = mMimeType.toLowerCase();
-    }
-
-    /**
-     * @return The HTTP-authentication object or null if there
-     * is no supported scheme in the header.
-     * If there are several valid schemes present, we pick the
-     * strongest one. If there are several schemes of the same
-     * strength, we pick the one that comes first.
-     */
-    private HttpAuthHeader parseAuthHeader(String header) {
-        if (header != null) {
-            int posMax = 256;
-            int posLen = 0;
-            int[] pos = new int [posMax];
-
-            int headerLen = header.length();
-            if (headerLen > 0) {
-                // first, we find all unquoted instances of 'Basic' and 'Digest'
-                boolean quoted = false;
-                for (int i = 0; i < headerLen && posLen < posMax; ++i) {
-                    if (header.charAt(i) == '\"') {
-                        quoted = !quoted;
-                    } else {
-                        if (!quoted) {
-                            if (header.regionMatches(true, i,
-                                    HttpAuthHeader.BASIC_TOKEN, 0,
-                                    HttpAuthHeader.BASIC_TOKEN.length())) {
-                                pos[posLen++] = i;
-                                continue;
-                            }
-
-                            if (header.regionMatches(true, i,
-                                    HttpAuthHeader.DIGEST_TOKEN, 0,
-                                    HttpAuthHeader.DIGEST_TOKEN.length())) {
-                                pos[posLen++] = i;
-                                continue;
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (posLen > 0) {
-                // consider all digest schemes first (if any)
-                for (int i = 0; i < posLen; i++) {
-                    if (header.regionMatches(true, pos[i],
-                                HttpAuthHeader.DIGEST_TOKEN, 0,
-                                HttpAuthHeader.DIGEST_TOKEN.length())) {
-                        String sub = header.substring(pos[i],
-                                (i + 1 < posLen ? pos[i + 1] : headerLen));
-
-                        HttpAuthHeader rval = new HttpAuthHeader(sub);
-                        if (rval.isSupportedScheme()) {
-                            // take the first match
-                            return rval;
-                        }
-                    }
-                }
-
-                // ...then consider all basic schemes (if any)
-                for (int i = 0; i < posLen; i++) {
-                    if (header.regionMatches(true, pos[i],
-                                HttpAuthHeader.BASIC_TOKEN, 0,
-                                HttpAuthHeader.BASIC_TOKEN.length())) {
-                        String sub = header.substring(pos[i],
-                                (i + 1 < posLen ? pos[i + 1] : headerLen));
-
-                        HttpAuthHeader rval = new HttpAuthHeader(sub);
-                        if (rval.isSupportedScheme()) {
-                            // take the first match
-                            return rval;
-                        }
-                    }
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * If the content is a redirect or not modified we should not send
-     * any data into WebCore as that will cause it create a document with
-     * the data, then when we try to provide the real content, it will assert.
-     *
-     * @return True iff the callback should be ignored.
-     */
-    private boolean ignoreCallbacks() {
-        return (mCancelled || mAuthHeader != null ||
-                // Allow 305 (Use Proxy) to call through.
-                (mStatusCode > 300 && mStatusCode < 400 && mStatusCode != 305));
-    }
-
-    /**
-     * Sets the current URL associated with this load.
-     */
-    void setUrl(String url) {
-        if (url != null) {
-            mUri = null;
-            if (URLUtil.isNetworkUrl(url)) {
-                mUrl = URLUtil.stripAnchor(url);
-                try {
-                    mUri = new WebAddress(mUrl);
-                } catch (ParseException e) {
-                    e.printStackTrace();
-                }
-            } else {
-                mUrl = url;
-            }
-        }
-    }
-
-    /**
-     * Guesses MIME type if one was not specified. Defaults to 'text/html'. In
-     * addition, tries to guess the MIME type based on the extension.
-     *
-     */
-    private void guessMimeType() {
-        // Data urls must have a valid mime type or a blank string for the mime
-        // type (implying text/plain).
-        if (URLUtil.isDataUrl(mUrl) && mMimeType.length() != 0) {
-            cancel();
-            final String text = mContext.getString(R.string.httpErrorBadUrl);
-            handleError(EventHandler.ERROR_BAD_URL, text);
-        } else {
-            // Note: This is ok because this is used only for the main content
-            // of frames. If no content-type was specified, it is fine to
-            // default to text/html.
-            mMimeType = "text/html";
-            String newMimeType = guessMimeTypeFromExtension(mUrl);
-            if (newMimeType != null) {
-                mMimeType = newMimeType;
-            }
-        }
-    }
-
-    /**
-     * guess MIME type based on the file extension.
-     */
-    private String guessMimeTypeFromExtension(String url) {
-        // PENDING: need to normalize url
-        if (DebugFlags.LOAD_LISTENER) {
-            Log.v(LOGTAG, "guessMimeTypeFromExtension: url = " + url);
-        }
-
-        return MimeTypeMap.getSingleton().getMimeTypeFromExtension(
-                MimeTypeMap.getFileExtensionFromUrl(url));
-    }
-
-    /**
-     * Either send a message to ourselves or queue the message if this is a
-     * synchronous load.
-     */
-    private void sendMessageInternal(Message msg) {
-        if (mSynchronous) {
-            mMessageQueue.add(msg);
-        } else {
-            sendMessage(msg);
-        }
-    }
-
-    /**
-     * Cycle through our messages for synchronous loads.
-     */
-    /* package */ void loadSynchronousMessages() {
-        if (DebugFlags.LOAD_LISTENER && !mSynchronous) {
-            throw new AssertionError();
-        }
-        // Note: this can be called twice if it is a synchronous network load,
-        // and there is a cache, but it needs to go to network to validate. If 
-        // validation succeed, the CacheLoader is used so this is first called 
-        // from http thread. Then it is called again from WebViewCore thread 
-        // after the load is completed. So make sure the queue is cleared but
-        // don't set it to null.
-        while (!mMessageQueue.isEmpty()) {
-            handleMessage(mMessageQueue.remove(0));
-        }
-    }
-
-    //=========================================================================
-    // native functions
-    //=========================================================================
-
-    /**
-     * Create a new native response object.
-     * @param url The url of the resource.
-     * @param statusCode The HTTP status code.
-     * @param statusText The HTTP status text.
-     * @param mimeType HTTP content-type.
-     * @param expectedLength An estimate of the content length or the length
-     *                       given by the server.
-     * @param encoding HTTP encoding.
-     * @return The native response pointer.
-     */
-    private native int nativeCreateResponse(String url, int statusCode,
-            String statusText, String mimeType, long expectedLength,
-            String encoding);
-
-    /**
-     * Add a response header to the native object.
-     * @param nativeResponse The native pointer.
-     * @param key String key.
-     * @param val String value.
-     */
-    private native void nativeSetResponseHeader(int nativeResponse, String key,
-            String val);
-
-    /**
-     * Dispatch the response.
-     * @param nativeResponse The native pointer.
-     */
-    private native void nativeReceivedResponse(int nativeResponse);
-
-    /**
-     * Add data to the loader.
-     * @param data Byte array of data.
-     * @param length Number of objects in data.
-     */
-    private native void nativeAddData(byte[] data, int length);
-
-    /**
-     * Tell the loader it has finished.
-     */
-    private native void nativeFinished();
-
-    /**
-     * tell the loader to redirect
-     * @param baseUrl The base url.
-     * @param redirectTo The url to redirect to.
-     * @param nativeResponse The native pointer.
-     * @return The new url that the resource redirected to.
-     */
-    private native String nativeRedirectedToUrl(String baseUrl,
-            String redirectTo, int nativeResponse);
-
-    /**
-     * Tell the loader there is error
-     * @param id
-     * @param desc
-     * @param failingUrl The url that failed.
-     */
-    private native void nativeError(int id, String desc, String failingUrl);
-
-}
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
deleted file mode 100644
index ee9b949..0000000
--- a/core/java/android/webkit/Network.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 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 android.webkit;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.http.*;
-import android.os.*;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.Map;
-
-import junit.framework.Assert;
-
-class Network {
-
-    private static final String LOGTAG = "network";
-
-    /**
-     * Static instance of a Network object.
-     */
-    private static Network sNetwork;
-    
-    /**
-     * Flag to store the state of platform notifications, for the case
-     * when the Network object has not been constructed yet
-     */
-    private static boolean sPlatformNotifications;
-    
-    /**
-     * Reference count for platform notifications as the network class is a 
-     * static and can exist over multiple activities, thus over multiple 
-     * onPause/onResume pairs. 
-     */
-    private static int sPlatformNotificationEnableRefCount;
-
-    /**
-     * Proxy username if known (used for pre-emptive proxy authentication).
-     */
-    private String mProxyUsername;
-
-    /**
-     * Proxy password if known (used for pre-emptive proxy authentication).
-     */
-    private String mProxyPassword;
-
-    /**
-     * Network request queue (requests are added from the browser thread).
-     */
-    private RequestQueue mRequestQueue;
-
-    /**
-     * SSL error handler: takes care of synchronization of multiple async
-     * loaders with SSL-related problems.
-     */
-    private SslErrorHandlerImpl mSslErrorHandler;
-
-    /**
-     * HTTP authentication handler: takes care of synchronization of HTTP
-     * authentication requests.
-     */
-    private HttpAuthHandlerImpl mHttpAuthHandler;
-
-    private Context mContext;
-
-    /**
-     * True if the currently used network connection is a roaming phone
-     * connection.
-     */
-    private boolean mRoaming;
-
-    /**
-     * Tracks if we are roaming.
-     */
-    private RoamingMonitor mRoamingMonitor;
-
-    /**
-     * @return The singleton instance of the network.
-     */
-    public static synchronized Network getInstance(Context context) {
-        if (sNetwork == null) {
-            // Note Context of the Application is used here, rather than
-            // the what is passed in (usually a Context derived from an 
-            // Activity) so the intent receivers belong to the application
-            // rather than an activity - this fixes the issue where 
-            // Activities are created and destroyed during the lifetime of
-            // an Application
-            sNetwork = new Network(context.getApplicationContext());
-            if (sPlatformNotifications) {
-                // Adjust the ref count before calling enable as it is already
-                // taken into account when the static function was called 
-                // directly
-                --sPlatformNotificationEnableRefCount;
-                enablePlatformNotifications();
-            }
-        }
-        return sNetwork;
-    }
-
-
-    /**
-     * Enables data state and proxy tracking
-     */
-    public static void enablePlatformNotifications() {
-        if (++sPlatformNotificationEnableRefCount == 1) {
-            if (sNetwork != null) {
-                sNetwork.mRequestQueue.enablePlatformNotifications();
-                sNetwork.monitorRoaming();
-            } else {
-                sPlatformNotifications = true;
-            }
-        }
-    }
-
-    /**
-     * If platform notifications are enabled, this should be called
-     * from onPause() or onStop()
-     */
-    public static void disablePlatformNotifications() {
-        if (--sPlatformNotificationEnableRefCount == 0) {
-            if (sNetwork != null) {
-                sNetwork.mRequestQueue.disablePlatformNotifications();
-                sNetwork.stopMonitoringRoaming();
-            } else {
-                sPlatformNotifications = false;
-            }
-        }
-    }
-
-    /**
-     * Creates a new Network object.
-     * XXX: Must be created in the same thread as WebCore!!!!!
-     */
-    private Network(Context context) {
-        if (DebugFlags.NETWORK) {
-            Assert.assertTrue(Thread.currentThread().
-                    getName().equals(WebViewCore.THREAD_NAME));
-        }
-        mContext = context;
-        mSslErrorHandler = new SslErrorHandlerImpl();
-        mHttpAuthHandler = new HttpAuthHandlerImpl(this);
-
-        mRequestQueue = new RequestQueue(context);
-    }
-
-    private class RoamingMonitor extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()))
-                return;
-
-            final ConnectivityManager connManager = (ConnectivityManager) context
-                    .getSystemService(Context.CONNECTIVITY_SERVICE);
-            final NetworkInfo info = connManager.getActiveNetworkInfo();
-            if (info != null)
-                mRoaming = info.isRoaming();
-        };
-    };
-
-    private void monitorRoaming() {
-        mRoamingMonitor = new RoamingMonitor();
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        mContext.registerReceiver(sNetwork.mRoamingMonitor, filter);
-    }
-
-    private void stopMonitoringRoaming() {
-        if (mRoamingMonitor != null) {
-            mContext.unregisterReceiver(mRoamingMonitor);
-            mRoamingMonitor = null;
-        }
-    }
-
-    /**
-     * Request a url from either the network or the file system.
-     * @param url The url to load.
-     * @param method The http method.
-     * @param headers The http headers.
-     * @param postData The body of the request.
-     * @param loader A LoadListener for receiving the results of the request.
-     * @return True if the request was successfully queued.
-     */
-    public boolean requestURL(String method,
-                              Map<String, String> headers,
-                              byte [] postData,
-                              LoadListener loader) {
-
-        String url = loader.url();
-
-        // Not a valid url, return false because we won't service the request!
-        if (!URLUtil.isValidUrl(url)) {
-            return false;
-        }
-
-        // asset, res, file system or data stream are handled in the other code
-        // path. This only handles network request.
-        if (URLUtil.isAssetUrl(url) || URLUtil.isResourceUrl(url)
-                || URLUtil.isFileUrl(url) || URLUtil.isDataUrl(url)) {
-            return false;
-        }
-
-        // If this is a prefetch, abort it if we're roaming.
-        if (mRoaming && headers.containsKey("X-Moz") && "prefetch".equals(headers.get("X-Moz"))) {
-            return false;
-        }
-
-        /* FIXME: this is lame.  Pass an InputStream in, rather than
-           making this lame one here */
-        InputStream bodyProvider = null;
-        int bodyLength = 0;
-        if (postData != null) {
-            bodyLength = postData.length;
-            bodyProvider = new ByteArrayInputStream(postData);
-        }
-
-        RequestQueue q = mRequestQueue;
-        RequestHandle handle = null;
-        if (loader.isSynchronous()) {
-            handle = q.queueSynchronousRequest(url, loader.getWebAddress(),
-                    method, headers, loader, bodyProvider, bodyLength);
-            loader.attachRequestHandle(handle);
-            handle.processRequest();
-            loader.loadSynchronousMessages();
-        } else {
-            handle = q.queueRequest(url, loader.getWebAddress(), method,
-                    headers, loader, bodyProvider, bodyLength);
-            // FIXME: Although this is probably a rare condition, normal network
-            // requests are processed in a separate thread. This means that it
-            // is possible to process part of the request before setting the
-            // request handle on the loader. We should probably refactor this to
-            // ensure the handle is attached before processing begins.
-            loader.attachRequestHandle(handle);
-        }
-
-        return true;
-    }
-
-    /**
-     * @return True iff there is a valid proxy set.
-     */
-    public boolean isValidProxySet() {
-        // The proxy host and port can be set within a different thread during
-        // an Intent broadcast.
-        synchronized (mRequestQueue) {
-            return mRequestQueue.getProxyHost() != null;
-        }
-    }
-
-    /**
-     * Get the proxy hostname.
-     * @return The proxy hostname obtained from the network queue and proxy
-     *         settings.
-     */
-    public String getProxyHostname() {
-        return mRequestQueue.getProxyHost().getHostName();
-    }
-
-    /**
-     * @return The proxy username or null if none.
-     */
-    public synchronized String getProxyUsername() {
-        return mProxyUsername;
-    }
-
-    /**
-     * Sets the proxy username.
-     * @param proxyUsername Username to use when
-     * connecting through the proxy.
-     */
-    public synchronized void setProxyUsername(String proxyUsername) {
-        if (DebugFlags.NETWORK) {
-            Assert.assertTrue(isValidProxySet());
-        }
-
-        mProxyUsername = proxyUsername;
-    }
-
-    /**
-     * @return The proxy password or null if none.
-     */
-    public synchronized String getProxyPassword() {
-        return mProxyPassword;
-    }
-
-    /**
-     * Sets the proxy password.
-     * @param proxyPassword Password to use when
-     * connecting through the proxy.
-     */
-    public synchronized void setProxyPassword(String proxyPassword) {
-        if (DebugFlags.NETWORK) {
-            Assert.assertTrue(isValidProxySet());
-        }
-
-        mProxyPassword = proxyPassword;
-    }
-
-    /**
-     * Saves the state of network handlers (user SSL and HTTP-authentication
-     * preferences).
-     * @param outState The out-state to save (write) to.
-     * @return True iff succeeds.
-     */
-    public boolean saveState(Bundle outState) {
-        if (DebugFlags.NETWORK) {
-            Log.v(LOGTAG, "Network.saveState()");
-        }
-
-        return mSslErrorHandler.saveState(outState);
-    }
-
-    /**
-     * Restores the state of network handlers (user SSL and HTTP-authentication
-     * preferences).
-     * @param inState The in-state to load (read) from.
-     * @return True iff succeeds.
-     */
-    public boolean restoreState(Bundle inState) {
-        if (DebugFlags.NETWORK) {
-            Log.v(LOGTAG, "Network.restoreState()");
-        }
-
-        return mSslErrorHandler.restoreState(inState);
-    }
-
-    /**
-     * Clears user SSL-error preference table.
-     */
-    public void clearUserSslPrefTable() {
-        mSslErrorHandler.clear();
-    }
-
-    /**
-     * Handles SSL error(s) on the way up to the user: the user must decide
-     * whether errors should be ignored or not.
-     * @param loader The loader that resulted in SSL errors.
-     */
-    public void handleSslErrorRequest(LoadListener loader) {
-        if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
-        if (loader != null) {
-            mSslErrorHandler.handleSslErrorRequest(loader);
-        }
-    }
-
-    /* package */ boolean checkSslPrefTable(LoadListener loader,
-            SslError error) {
-        if (loader != null && error != null) {
-            return mSslErrorHandler.checkSslPrefTable(loader, error);
-        }
-        return false;
-    }
-
-     /**
-     * Handles authentication requests on their way up to the user (the user
-     * must provide credentials).
-     * @param loader The loader that resulted in an HTTP
-     * authentication request.
-     */
-    public void handleAuthRequest(LoadListener loader) {
-        if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
-        if (loader != null) {
-            mHttpAuthHandler.handleAuthRequest(loader);
-        }
-    }
-
-    // Performance probe
-    public void startTiming() {
-        mRequestQueue.startTiming();
-    }
-
-    public void stopTiming() {
-        mRequestQueue.stopTiming();
-    }
-}
diff --git a/core/java/android/webkit/SslErrorHandlerImpl.java b/core/java/android/webkit/SslErrorHandlerImpl.java
deleted file mode 100644
index b2e4b13..0000000
--- a/core/java/android/webkit/SslErrorHandlerImpl.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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 android.webkit;
-
-import android.net.http.SslError;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-import java.util.LinkedList;
-import java.util.ListIterator;
-
-/**
- * SslErrorHandler's implementation for Android Java HTTP stack.
- * This class is not needed if the Chromium HTTP stack is used.
- */
-class SslErrorHandlerImpl extends SslErrorHandler {
-    /* One problem here is that there may potentially be multiple SSL errors
-     * coming from multiple loaders. Therefore, we keep a queue of loaders
-     * that have SSL-related problems and process errors one by one in the
-     * order they were received.
-     */
-
-    private static final String LOGTAG = "network";
-
-    /**
-     * Queue of loaders that experience SSL-related problems.
-     */
-    private LinkedList<LoadListener> mLoaderQueue;
-
-    /**
-     * SSL error preference table.
-     */
-    private Bundle mSslPrefTable;
-
-    // These are only used in the client facing SslErrorHandler.
-    private final SslErrorHandler mOriginHandler;
-    private final LoadListener mLoadListener;
-
-    // Message id for handling the response from the client.
-    private static final int HANDLE_RESPONSE = 100;
-
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case HANDLE_RESPONSE:
-                LoadListener loader = (LoadListener) msg.obj;
-                synchronized (SslErrorHandlerImpl.this) {
-                    handleSslErrorResponse(loader, loader.sslError(),
-                            msg.arg1 == 1);
-                    mLoaderQueue.remove(loader);
-                    fastProcessQueuedSslErrors();
-                }
-                break;
-        }
-    }
-
-    /**
-     * Creates a new error handler with an empty loader queue.
-     */
-    /* package */ SslErrorHandlerImpl() {
-        mLoaderQueue = new LinkedList<LoadListener>();
-        mSslPrefTable = new Bundle();
-
-        // These are used by client facing SslErrorHandlers.
-        mOriginHandler = null;
-        mLoadListener = null;
-    }
-
-    /**
-     * Create a new error handler that will be passed to the client.
-     */
-    private SslErrorHandlerImpl(SslErrorHandler origin, LoadListener listener) {
-        mOriginHandler = origin;
-        mLoadListener = listener;
-    }
-
-    /**
-     * Saves this handler's state into a map.
-     * @return True iff succeeds.
-     */
-    /* package */ synchronized boolean saveState(Bundle outState) {
-        boolean success = (outState != null);
-        if (success) {
-            // TODO?
-            outState.putBundle("ssl-error-handler", mSslPrefTable);
-        }
-
-        return success;
-    }
-
-    /**
-     * Restores this handler's state from a map.
-     * @return True iff succeeds.
-     */
-    /* package */ synchronized boolean restoreState(Bundle inState) {
-        boolean success = (inState != null);
-        if (success) {
-            success = inState.containsKey("ssl-error-handler");
-            if (success) {
-                mSslPrefTable = inState.getBundle("ssl-error-handler");
-            }
-        }
-
-        return success;
-    }
-
-    /**
-     * Clears SSL error preference table.
-     */
-    /* package */ synchronized void clear() {
-        mSslPrefTable.clear();
-    }
-
-    /**
-     * Handles requests from the network stack about whether to proceed with a
-     * load given an SSL error(s). We may ask the client what to do, or use a
-     * cached response.
-     */
-    /* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
-        if (DebugFlags.SSL_ERROR_HANDLER) {
-            Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
-                  "url=" + loader.url());
-        }
-
-        if (!loader.cancelled()) {
-            mLoaderQueue.offer(loader);
-            if (loader == mLoaderQueue.peek()) {
-                fastProcessQueuedSslErrors();
-            }
-        }
-    }
-
-    /**
-     * Check the preference table to see if we already have a 'proceed' decision
-     * from the client for this host and for an error of equal or greater
-     * severity than the supplied error. If so, instruct the loader to proceed
-     * and return true. Otherwise return false.
-     */
-    /* package */ synchronized boolean checkSslPrefTable(LoadListener loader,
-            SslError error) {
-        final String host = loader.host();
-        final int primary = error.getPrimaryError();
-
-        if (DebugFlags.SSL_ERROR_HANDLER) {
-            assert host != null;
-            assert primary != -1;
-        }
-
-        if (mSslPrefTable.containsKey(host) && primary <= mSslPrefTable.getInt(host)) {
-            if (!loader.cancelled()) {
-                loader.handleSslErrorResponse(true);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Processes queued SSL-error confirmation requests in
-     * a tight loop while there is no need to ask the client.
-     */
-    /* package */void fastProcessQueuedSslErrors() {
-        while (processNextLoader());
-    }
-
-    /**
-     * Processes the next loader in the queue.
-     * @return True iff should proceed to processing the
-     * following loader in the queue
-     */
-    private synchronized boolean processNextLoader() {
-        LoadListener loader = mLoaderQueue.peek();
-        if (loader != null) {
-            // if this loader has been cancelled
-            if (loader.cancelled()) {
-                // go to the following loader in the queue. Make sure this
-                // loader has been removed from the queue.
-                mLoaderQueue.remove(loader);
-                return true;
-            }
-
-            SslError error = loader.sslError();
-
-            if (DebugFlags.SSL_ERROR_HANDLER) {
-                assert error != null;
-            }
-
-            // checkSslPrefTable() will instruct the loader to proceed if we
-            // have a cached 'proceed' decision. It does not remove the loader
-            // from the queue.
-            if (checkSslPrefTable(loader, error)) {
-                mLoaderQueue.remove(loader);
-                return true;
-            }
-
-            // If we can not proceed based on a cached decision, ask the client.
-            CallbackProxy proxy = loader.getFrame().getCallbackProxy();
-            proxy.onReceivedSslError(new SslErrorHandlerImpl(this, loader), error);
-        }
-
-        // the queue must be empty, stop
-        return false;
-    }
-
-    /**
-     * Proceed with this load.
-     */
-    public void proceed() {
-        mOriginHandler.sendMessage(mOriginHandler.obtainMessage(
-                HANDLE_RESPONSE, 1, 0, mLoadListener));
-    }
-
-    /**
-     * Cancel this load and all pending loads for the WebView that had the
-     * error.
-     */
-    public void cancel() {
-        mOriginHandler.sendMessage(mOriginHandler.obtainMessage(
-                HANDLE_RESPONSE, 0, 0, mLoadListener));
-    }
-
-    /**
-     * Handles the response from the client about whether to proceed with this
-     * load. We save the response to be re-used in the future.
-     */
-    /* package */ synchronized void handleSslErrorResponse(LoadListener loader,
-            SslError error, boolean proceed) {
-        if (DebugFlags.SSL_ERROR_HANDLER) {
-            assert loader != null;
-            assert error != null;
-        }
-
-        if (DebugFlags.SSL_ERROR_HANDLER) {
-            Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
-                  + " proceed: " + proceed
-                  + " url:" + loader.url());
-        }
-
-        if (!loader.cancelled()) {
-            if (proceed) {
-                // Update the SSL error preference table
-                int primary = error.getPrimaryError();
-                String host = loader.host();
-
-                if (DebugFlags.SSL_ERROR_HANDLER) {
-                    assert host != null;
-                    assert primary != -1;
-                }
-                boolean hasKey = mSslPrefTable.containsKey(host);
-                if (!hasKey || primary > mSslPrefTable.getInt(host)) {
-                    mSslPrefTable.putInt(host, primary);
-                }
-            }
-            loader.handleSslErrorResponse(proceed);
-        }
-    }
-}
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
deleted file mode 100644
index 7bcd50d..0000000
--- a/core/java/android/webkit/StreamLoader.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.net.http.EventHandler;
-import android.net.http.Headers;
-import android.os.Handler;
-import android.os.Message;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * This abstract class is used for all content loaders that rely on streaming
- * content into the rendering engine loading framework.
- *
- * The class implements a state machine to load the content into the frame in
- * a similar manor to the way content arrives from the network. The class uses
- * messages to move from one state to the next, which enables async. loading of
- * the streamed content.
- *
- * Classes that inherit from this class must implement two methods, the first
- * method is used to setup the InputStream and notify the loading framework if
- * it can load it's content. The other method allows the derived class to add
- * additional HTTP headers to the response.
- *
- * By default, content loaded with a StreamLoader is marked with a HTTP header
- * that indicates the content should not be cached.
- *
- */
-abstract class StreamLoader implements Handler.Callback {
-
-    private static final int MSG_STATUS = 100;  // Send status to loader
-    private static final int MSG_HEADERS = 101; // Send headers to loader
-    private static final int MSG_DATA = 102;  // Send data to loader
-    private static final int MSG_END = 103;  // Send endData to loader
-
-    protected final Context mContext;
-    protected final LoadListener mLoadListener; // loader class
-    protected InputStream mDataStream; // stream to read data from
-    protected long mContentLength; // content length of data
-    private byte [] mData; // buffer to pass data to loader with.
-
-    // Handler which will be initialized in the thread where load() is called.
-    private Handler mHandler;
-
-    /**
-     * Constructor. Although this class calls the LoadListener, it only calls
-     * the EventHandler Interface methods. LoadListener concrete class is used
-     * to avoid the penality of calling an interface.
-     *
-     * @param loadlistener The LoadListener to call with the data.
-     */
-    StreamLoader(LoadListener loadlistener) {
-        mLoadListener = loadlistener;
-        mContext = loadlistener.getContext();
-    }
-
-    /**
-     * This method is called when the derived class should setup mDataStream,
-     * and call mLoadListener.status() to indicate that the load can occur. If it
-     * fails to setup, it should still call status() with the error code.
-     *
-     * @return true if stream was successfully setup
-     */
-    protected abstract boolean setupStreamAndSendStatus();
-
-    /**
-     * This method is called when the headers are about to be sent to the
-     * load framework. The derived class has the opportunity to add addition
-     * headers.
-     *
-     * @param headers Map of HTTP headers that will be sent to the loader.
-     */
-    abstract protected void buildHeaders(Headers headers);
-
-    /**
-     * Calling this method starts the load of the content for this StreamLoader.
-     * This method simply creates a Handler in the current thread and posts a
-     * message to send the status and returns immediately.
-     */
-    final void load() {
-        synchronized (this) {
-            if (mHandler == null) {
-                mHandler = new Handler(this);
-            }
-        }
-
-        if (!mLoadListener.isSynchronous()) {
-            mHandler.sendEmptyMessage(MSG_STATUS);
-        } else {
-            // Load the stream synchronously.
-            if (setupStreamAndSendStatus()) {
-                // We were able to open the stream, create the array
-                // to pass data to the loader
-                mData = new byte[8192];
-                sendHeaders();
-                while (!sendData() && !mLoadListener.cancelled());
-                closeStreamAndSendEndData();
-                mLoadListener.loadSynchronousMessages();
-            }
-        }
-    }
-
-    public boolean handleMessage(Message msg) {
-        if (mLoadListener.isSynchronous()) {
-            throw new AssertionError();
-        }
-        if (mLoadListener.cancelled()) {
-            closeStreamAndSendEndData();
-            return true;
-        }
-        switch(msg.what) {
-            case MSG_STATUS:
-                if (setupStreamAndSendStatus()) {
-                    // We were able to open the stream, create the array
-                    // to pass data to the loader
-                    mData = new byte[8192];
-                    mHandler.sendEmptyMessage(MSG_HEADERS);
-                }
-                break;
-            case MSG_HEADERS:
-                sendHeaders();
-                mHandler.sendEmptyMessage(MSG_DATA);
-                break;
-            case MSG_DATA:
-                if (sendData()) {
-                    mHandler.sendEmptyMessage(MSG_END);
-                } else {
-                    mHandler.sendEmptyMessage(MSG_DATA);
-                }
-                break;
-            case MSG_END:
-                closeStreamAndSendEndData();
-                break;
-            default:
-                return false;
-        }
-        return true;
-    }
-
-    /**
-     * Construct the headers and pass them to the EventHandler.
-     */
-    private void sendHeaders() {
-        Headers headers = new Headers();
-        if (mContentLength > 0) {
-            headers.setContentLength(mContentLength);
-        }
-        buildHeaders(headers);
-        mLoadListener.headers(headers);
-    }
-
-    /**
-     * Read data from the stream and pass it to the EventHandler.
-     * If an error occurs reading the stream, then an error is sent to the
-     * EventHandler, and moves onto the next state - end of data.
-     * @return True if all the data has been read. False if sendData should be
-     *         called again.
-     */
-    private boolean sendData() {
-        if (mDataStream != null) {
-            try {
-                int amount = mDataStream.read(mData);
-                if (amount > 0) {
-                    mLoadListener.data(mData, amount);
-                    return false;
-                }
-            } catch (IOException ex) {
-                mLoadListener.error(EventHandler.FILE_ERROR, ex.getMessage());
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Close the stream and inform the EventHandler that load is complete.
-     */
-    private void closeStreamAndSendEndData() {
-        if (mDataStream != null) {
-            try {
-                mDataStream.close();
-            } catch (IOException ex) {
-                // ignore.
-            }
-        }
-        mLoadListener.endData();
-    }
-}
diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java
index e786838..650310e 100644
--- a/core/java/android/webkit/WebResourceResponse.java
+++ b/core/java/android/webkit/WebResourceResponse.java
@@ -21,40 +21,24 @@
 import java.io.InputStream;
 
 /**
- * A WebResourceResponse is return by
- * {@link WebViewClient#shouldInterceptRequest} and
- * contains the response information for a particular resource.
+ * Encapsulates a resource response. Applications can return an instance of this
+ * class from {@link WebViewClient#shouldInterceptRequest} to provide a custom
+ * response when the WebView requests a particular resource.
  */
 public class WebResourceResponse {
-
-    private class Loader extends StreamLoader {
-        Loader(LoadListener loadListener) {
-            super(loadListener);
-            mDataStream = mInputStream;
-        }
-        @Override
-        protected boolean setupStreamAndSendStatus() {
-            mLoadListener.status(1, 1, mDataStream != null ? 200 : 404, "");
-            return true;
-        }
-        @Override
-        protected void buildHeaders(Headers headers) {
-            headers.setContentType(mMimeType);
-            headers.setContentEncoding(mEncoding);
-        }
-    }
-
     // Accessed by jni, do not rename without modifying the jni code.
     private String mMimeType;
     private String mEncoding;
     private InputStream mInputStream;
 
     /**
-     * Construct a response with the given mime type, encoding, and data.
-     * @param mimeType The mime type of the data (i.e. text/html).
-     * @param encoding The encoding of the bytes read from data.
-     * @param data An InputStream for reading custom data.  The implementation
-     *             must implement {@link InputStream#read(byte[])}.
+     * Constructs a resource response with the given MIME type, encoding, and
+     * input stream. Callers must implement
+     * {@link InputStream#read(byte[]) InputStream.read(byte[])} for the input
+     * stream.
+     * @param mimeType The resource response's MIME type, for example text/html
+     * @param encoding The resource response's encoding
+     * @param data The input stream that provides the resource response's data
      */
     public WebResourceResponse(String mimeType, String encoding,
             InputStream data) {
@@ -64,53 +48,52 @@
     }
 
     /**
-     * Set the mime type of the response data (i.e. text/html).
-     * @param mimeType
+     * Sets the resource response's MIME type, for example text/html.
+     * @param mimeType The resource response's MIME type
      */
     public void setMimeType(String mimeType) {
         mMimeType = mimeType;
     }
 
     /**
-     * @see #setMimeType
+     * Gets the resource response's MIME type.
+     * @return The resource response's MIME type
      */
     public String getMimeType() {
         return mMimeType;
     }
 
     /**
-     * Set the encoding of the response data (i.e. utf-8).  This will be used to
-     * decode the raw bytes from the input stream.
-     * @param encoding
+     * Sets the resource response's encoding, for example UTF-8. This is used
+     * to decode the data from the input stream.
+     * @param encoding The resource response's encoding
      */
     public void setEncoding(String encoding) {
         mEncoding = encoding;
     }
 
     /**
-     * @see #setEncoding
+     * Gets the resource response's encoding.
+     * @return The resource response's encoding
      */
     public String getEncoding() {
         return mEncoding;
     }
 
     /**
-     * Set the input stream containing the data for this resource.
-     * @param data An InputStream for reading custom data.  The implementation
-     *             must implement {@link InputStream#read(byte[])}.
+     * Sets the input stream that provides the resource respone's data. Callers
+     * must implement {@link InputStream#read(byte[]) InputStream.read(byte[])}.
+     * @param data The input stream that provides the resource response's data
      */
     public void setData(InputStream data) {
         mInputStream = data;
     }
 
     /**
-     * @see #setData
+     * Gets the input stream that provides the resource respone's data.
+     * @return The input stream that provides the resource response's data
      */
     public InputStream getData() {
         return mInputStream;
     }
-
-    StreamLoader loader(LoadListener listener) {
-        return new Loader(listener);
-    }
 }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index b4c38db..c463b40 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1156,8 +1156,13 @@
     }
 
     /**
-     * Tell the WebView to load image resources automatically.
-     * @param flag True if the WebView should load images automatically.
+     * Sets whether the WebView should load image resources. Note that this method
+     * controls loading of all images, including those embedded using the data
+     * URI scheme. Use {@link #setBlockNetworkImage} to control loading only
+     * of images specified using network URI schemes. Note that if the value of this
+     * setting is changed from false to true, all images resources referenced
+     * by content currently displayed by the WebView are loaded automatically.
+     * @param flag Whether the WebView should load image resources.
      */
     public synchronized void setLoadsImagesAutomatically(boolean flag) {
         if (mLoadsImagesAutomatically != flag) {
@@ -1167,20 +1172,26 @@
     }
 
     /**
-     * Return true if the WebView will load image resources automatically.
-     * The default is true.
-     * @return True if the WebView loads images automatically.
+     * Returns true if the WebView loads image resources. This includes
+     * images embedded using the data URI scheme. The default is true.
+     * @return True if the WebView loads image resources.
      */
     public synchronized boolean getLoadsImagesAutomatically() {
         return mLoadsImagesAutomatically;
     }
 
     /**
-     * Tell the WebView to block network images. This is only checked when
-     * {@link #getLoadsImagesAutomatically} is true. If you set the value to
-     * false, images will automatically be loaded. Use this api to reduce
-     * bandwidth only. Use {@link #setBlockNetworkLoads} if possible.
-     * @param flag True if the WebView should block network images.
+     * Sets whether the WebView should not load image resources from the
+     * network (resources accessed via http and https URI schemes).  Note
+     * that this method has no effect unless
+     * {@link #getLoadsImagesAutomatically} returns true. Also note that
+     * disabling all network loads using {@link #setBlockNetworkLoads}
+     * will also prevent network images from loading, even if this flag is set
+     * to false. When the value of this setting is changed from true to false,
+     * network images resources referenced by content currently displayed by
+     * the WebView are fetched automatically.
+     * @param flag Whether the WebView should not load image resources from
+     * the network.
      * @see #setBlockNetworkLoads
      */
     public synchronized void setBlockNetworkImage(boolean flag) {
@@ -1191,20 +1202,27 @@
     }
 
     /**
-     * Return true if the WebView will block network images. The default is
-     * false.
-     * @return True if the WebView blocks network images.
+     * Returns true if the WebView does not load image resources from the network.
+     * The default is false.
+     * @return True if the WebView does not load image resources from the network.
      */
     public synchronized boolean getBlockNetworkImage() {
         return mBlockNetworkImage;
     }
 
     /**
-     * Tell the WebView to block all network load requests. If you set the
-     * value to false, you must call {@link android.webkit.WebView#reload} to
-     * fetch remote resources. This flag supercedes the value passed to
-     * {@link #setBlockNetworkImage}.
-     * @param flag True if the WebView should block all network loads.
+     * Sets whether the WebView should not load resources from the network.
+     * Use {@link #setBlockNetworkImage} to only avoid loading
+     * image resources. Note that if the value of this setting is
+     * changed from true to false, network resources referenced by content
+     * currently displayed by the WebView are not fetched until
+     * {@link android.webkit.WebView#reload} is called.
+     * If the application does not have the
+     * {@link android.Manifest.permission#INTERNET} permission, attempts to set
+     * a value of false will cause a {@link java.lang.SecurityException}
+     * to be thrown.
+     * @param flag Whether the WebView should not load any resources
+     * from the network.
      * @see android.webkit.WebView#reload
      */
     public synchronized void setBlockNetworkLoads(boolean flag) {
@@ -1216,9 +1234,11 @@
     }
 
     /**
-     * Return true if the WebView will block all network loads. The default is
-     * false.
-     * @return True if the WebView blocks all network loads.
+     * Returns true if the WebView does not load any resources from the network.
+     * The default value is false if the application has the
+     * {@link android.Manifest.permission#INTERNET} permission, otherwise it is
+     * true.
+     * @return True if the WebView does not load any resources from the network.
      */
     public synchronized boolean getBlockNetworkLoads() {
         return mBlockNetworkLoads;
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 2b59b80..510c168 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -160,14 +160,14 @@
     private MyResultReceiver mReceiver;
 
     // Types used with setType.  Keep in sync with CachedInput.h
-    private static final int NORMAL_TEXT_FIELD = 0;
-    private static final int TEXT_AREA = 1;
-    private static final int PASSWORD = 2;
-    private static final int SEARCH = 3;
-    private static final int EMAIL = 4;
-    private static final int NUMBER = 5;
-    private static final int TELEPHONE = 6;
-    private static final int URL = 7;
+    static final int NORMAL_TEXT_FIELD = 0;
+    static final int TEXT_AREA = 1;
+    static final int PASSWORD = 2;
+    static final int SEARCH = 3;
+    static final int EMAIL = 4;
+    static final int NUMBER = 5;
+    static final int TELEPHONE = 6;
+    static final int URL = 7;
 
     private static final int AUTOFILL_FORM = 100;
     private Handler mHandler;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index fbafc64..b9dfb2e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.animation.ObjectAnimator;
 import android.annotation.Widget;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
@@ -36,6 +37,7 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
 import android.graphics.DrawFilter;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
@@ -59,6 +61,7 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.provider.Settings;
+import android.security.KeyChain;
 import android.speech.tts.TextToSpeech;
 import android.text.Editable;
 import android.text.InputType;
@@ -95,6 +98,7 @@
 import android.webkit.WebTextView.AutoCompleteAdapter;
 import android.webkit.WebViewCore.DrawData;
 import android.webkit.WebViewCore.EventHub;
+import android.webkit.WebViewCore.TextFieldInitData;
 import android.webkit.WebViewCore.TouchEventData;
 import android.webkit.WebViewCore.TouchHighlightData;
 import android.webkit.WebViewCore.WebKitHitTest;
@@ -118,6 +122,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -368,6 +373,9 @@
         // Used for mapping characters to keys typed.
         private KeyCharacterMap mKeyCharacterMap;
         private boolean mIsKeySentByMe;
+        private int mInputType;
+        private int mImeOptions;
+        private String mHint;
 
         public WebViewInputConnection() {
             super(WebView.this, true);
@@ -375,21 +383,24 @@
 
         @Override
         public boolean sendKeyEvent(KeyEvent event) {
-            // Latin IME occasionally sends delete codes directly using
-            // sendKeyEvents. WebViewInputConnection should treat this
-            // as a deleteSurroundingText.
-            if (!mIsKeySentByMe
-                    && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
-                Editable editable = getEditable();
-                int selectionStart = Selection.getSelectionStart(editable);
-                int selectionEnd = Selection.getSelectionEnd(editable);
-                if (selectionEnd > 0 && (selectionStart == selectionEnd)) {
-                    int action = event.getAction();
-                    if (action == KeyEvent.ACTION_UP) {
+            // Some IMEs send key events directly using sendKeyEvents.
+            // WebViewInputConnection should treat these as text changes.
+            if (!mIsKeySentByMe) {
+                if (event.getAction() == KeyEvent.ACTION_UP) {
+                    if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
                         return deleteSurroundingText(1, 0);
-                    } else if (action == KeyEvent.ACTION_DOWN) {
-                        return true; // the delete will happen in ACTION_UP
+                    } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
+                        return deleteSurroundingText(0, 1);
+                    } else if (event.getUnicodeChar() != 0){
+                        String newComposingText =
+                                Character.toString((char)event.getUnicodeChar());
+                        return commitText(newComposingText, 1);
                     }
+                } else if (event.getAction() == KeyEvent.ACTION_DOWN &&
+                        (event.getKeyCode() == KeyEvent.KEYCODE_DEL
+                        || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL
+                        || event.getUnicodeChar() != 0)) {
+                    return true; // only act on action_down
                 }
             }
             return super.sendKeyEvent(event);
@@ -449,6 +460,77 @@
             return super.deleteSurroundingText(leftLength, rightLength);
         }
 
+        public void initEditorInfo(WebViewCore.TextFieldInitData initData) {
+            int type = initData.mType;
+            int inputType = InputType.TYPE_CLASS_TEXT
+                    | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
+            int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
+                    | EditorInfo.IME_FLAG_NO_FULLSCREEN;
+            if (!initData.mIsSpellCheckEnabled) {
+                inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
+            }
+            if (WebTextView.TEXT_AREA != type
+                    && initData.mIsTextFieldNext) {
+                imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
+            }
+            switch (type) {
+                case WebTextView.NORMAL_TEXT_FIELD:
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    break;
+                case WebTextView.TEXT_AREA:
+                    inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
+                            | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
+                            | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
+                    imeOptions |= EditorInfo.IME_ACTION_NONE;
+                    break;
+                case WebTextView.PASSWORD:
+                    inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    break;
+                case WebTextView.SEARCH:
+                    imeOptions |= EditorInfo.IME_ACTION_SEARCH;
+                    break;
+                case WebTextView.EMAIL:
+                    // inputType needs to be overwritten because of the different text variation.
+                    inputType = InputType.TYPE_CLASS_TEXT
+                            | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    break;
+                case WebTextView.NUMBER:
+                    // inputType needs to be overwritten because of the different class.
+                    inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL
+                            | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL;
+                    // Number and telephone do not have both a Tab key and an
+                    // action, so set the action to NEXT
+                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
+                    break;
+                case WebTextView.TELEPHONE:
+                    // inputType needs to be overwritten because of the different class.
+                    inputType = InputType.TYPE_CLASS_PHONE;
+                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
+                    break;
+                case WebTextView.URL:
+                    // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
+                    // exclude it for now.
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    inputType |= InputType.TYPE_TEXT_VARIATION_URI;
+                    break;
+                default:
+                    imeOptions |= EditorInfo.IME_ACTION_GO;
+                    break;
+            }
+            mHint = initData.mLabel;
+            mInputType = inputType;
+            mImeOptions = imeOptions;
+        }
+
+        public void setupEditorInfo(EditorInfo outAttrs) {
+            outAttrs.inputType = mInputType;
+            outAttrs.imeOptions = mImeOptions;
+            outAttrs.hintText = mHint;
+            outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
+        }
+
         /**
          * Sends a text change to webkit indirectly. If it is a single-
          * character add or delete, it sends it as a key stroke. If it cannot
@@ -478,7 +560,7 @@
                 sendCharacter(text.charAt(textLength - 1));
             } else if (isCharacterDelete) {
                 sendDeleteKey();
-            } else if (textLength != originalLength ||
+            } else if ((textLength != originalLength) ||
                     !TextUtils.regionMatches(text, 0, original, 0,
                             textLength)) {
                 // Send a message so that key strokes and text replacement
@@ -505,6 +587,9 @@
                 for (KeyEvent event : events) {
                     sendKeyEvent(event);
                 }
+            } else {
+                Message msg = mPrivateHandler.obtainMessage(KEY_PRESS, (int) c, 0);
+                mPrivateHandler.sendMessage(msg);
             }
         }
 
@@ -830,14 +915,22 @@
     // know to handle Shift and arrows natively first
     private boolean mAccessibilityScriptInjected;
 
+
+    /**
+     * How long the caret handle will last without being touched.
+     */
+    private static final long CARET_HANDLE_STAMINA_MS = 3000;
+
     private Drawable mSelectHandleLeft;
     private Drawable mSelectHandleRight;
+    private Drawable mSelectHandleCenter;
     private Rect mSelectCursorBase = new Rect();
     private int mSelectCursorBaseLayerId;
     private Rect mSelectCursorExtent = new Rect();
     private int mSelectCursorExtentLayerId;
     private Rect mSelectDraggingCursor;
     private Point mSelectDraggingOffset = new Point();
+    private boolean mIsCaretSelection;
     static final int HANDLE_ID_START = 0;
     static final int HANDLE_ID_END = 1;
     static final int HANDLE_ID_BASE = 2;
@@ -913,7 +1006,7 @@
     static final int REPLACE_BASE_CONTENT               = 123;
     static final int FORM_DID_BLUR                      = 124;
     static final int RETURN_LABEL                       = 125;
-    static final int FIND_AGAIN                         = 126;
+    static final int UPDATE_MATCH_COUNT                 = 126;
     static final int CENTER_FIT_RECT                    = 127;
     static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
     static final int SET_SCROLLBAR_MODES                = 129;
@@ -934,6 +1027,8 @@
     static final int COPY_TO_CLIPBOARD                  = 141;
     static final int INIT_EDIT_FIELD                    = 142;
     static final int REPLACE_TEXT                       = 143;
+    static final int CLEAR_CARET_HANDLE                 = 144;
+    static final int KEY_PRESS                          = 145;
 
     private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
     private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
@@ -978,7 +1073,7 @@
         "REPLACE_BASE_CONTENT", //           = 123;
         "FORM_DID_BLUR", //                  = 124;
         "RETURN_LABEL", //                   = 125;
-        "FIND_AGAIN", //                     = 126;
+        "UPDATE_MATCH_COUNT", //             = 126;
         "CENTER_FIT_RECT", //                = 127;
         "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
         "SET_SCROLLBAR_MODES", //            = 129;
@@ -1020,9 +1115,8 @@
 
     // keep these in sync with their counterparts in WebView.cpp
     private static final int DRAW_EXTRAS_NONE = 0;
-    private static final int DRAW_EXTRAS_FIND = 1;
-    private static final int DRAW_EXTRAS_SELECTION = 2;
-    private static final int DRAW_EXTRAS_CURSOR_RING = 3;
+    private static final int DRAW_EXTRAS_SELECTION = 1;
+    private static final int DRAW_EXTRAS_CURSOR_RING = 2;
 
     // keep this in sync with WebCore:ScrollbarMode in WebKit
     private static final int SCROLLBAR_AUTO = 0;
@@ -1303,6 +1397,7 @@
         init();
         setupPackageListener(context);
         setupProxyListener(context);
+        setupTrustStorageListener(context);
         updateMultiTouchSupport(context);
 
         if (privateBrowsing) {
@@ -1312,6 +1407,41 @@
         mAutoFillData = new WebViewCore.AutoFillData();
     }
 
+    private static class TrustStorageListener extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
+                handleCertTrustChanged();
+            }
+        }
+    }
+    private static TrustStorageListener sTrustStorageListener;
+
+    /**
+     * Handles update to the trust storage.
+     */
+    private static void handleCertTrustChanged() {
+        // send a message for indicating trust storage change
+        WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null);
+    }
+
+    /*
+     * @param context This method expects this to be a valid context.
+     */
+    private static void setupTrustStorageListener(Context context) {
+        if (sTrustStorageListener != null ) {
+            return;
+        }
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
+        sTrustStorageListener = new TrustStorageListener();
+        Intent current =
+            context.getApplicationContext().registerReceiver(sTrustStorageListener, filter);
+        if (current != null) {
+            handleCertTrustChanged();
+        }
+    }
+
     private static class ProxyReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -1703,7 +1833,7 @@
     }
 
     /**
-     * returns the height of the titlebarview (if any). Does not care about
+     * Returns the height (in pixels) of the embedded title bar (if any). Does not care about
      * scrolling
      * @hide
      */
@@ -1712,12 +1842,15 @@
     }
 
     /**
-     * Return the amount of the titlebarview (if any) that is visible
+     * Return the visible height (in pixels) of the embedded title bar (if any).
      *
+     * @return This method is obsolete and always returns 0.
      * @deprecated This method is now obsolete.
      */
     @Deprecated
     public int getVisibleTitleHeight() {
+        // Actually, this method returns the height of the embedded title bar if one is set via the
+        // hidden setEmbeddedTitleBar method.
         checkThread();
         return getVisibleTitleHeightImpl();
     }
@@ -1914,7 +2047,6 @@
     public static void enablePlatformNotifications() {
         checkThread();
         synchronized (WebView.class) {
-            Network.enablePlatformNotifications();
             sNotificationsEnabled = true;
             Context context = JniUtil.getContext();
             if (context != null)
@@ -1932,7 +2064,6 @@
     public static void disablePlatformNotifications() {
         checkThread();
         synchronized (WebView.class) {
-            Network.disablePlatformNotifications();
             sNotificationsEnabled = false;
             Context context = JniUtil.getContext();
             if (context != null)
@@ -3671,7 +3802,7 @@
     public void findNext(boolean forward) {
         checkThread();
         if (0 == mNativeClass) return; // client isn't initialized
-        nativeFindNext(forward);
+        mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0);
     }
 
     /*
@@ -3681,13 +3812,40 @@
      *              that were found.
      */
     public int findAll(String find) {
+        return findAllBody(find, false);
+    }
+
+    /**
+     * @hide
+     */
+    public void findAllAsync(String find) {
+        findAllBody(find, true);
+    }
+
+    private int findAllBody(String find, boolean isAsync) {
         checkThread();
         if (0 == mNativeClass) return 0; // client isn't initialized
-        int result = find != null ? nativeFindAll(find.toLowerCase(),
-                find.toUpperCase(), find.equalsIgnoreCase(mLastFind)) : 0;
-        invalidate();
         mLastFind = find;
-        return result;
+        mWebViewCore.removeMessages(EventHub.FIND_ALL);
+        WebViewCore.FindAllRequest request = new
+            WebViewCore.FindAllRequest(find);
+        if (isAsync) {
+            mWebViewCore.sendMessage(EventHub.FIND_ALL, request);
+            return 0; // no need to wait for response
+        }
+        synchronized(request) {
+            try {
+                mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL,
+                    request);
+                while (request.mMatchCount == -1) {
+                    request.wait();
+                }
+            }
+            catch (InterruptedException e) {
+                return 0;
+            }
+        }
+        return request.mMatchCount;
     }
 
     /**
@@ -3723,6 +3881,7 @@
         }
         if (text != null) {
             mFindCallback.setText(text);
+            mFindCallback.findAll();
         }
         return true;
     }
@@ -3742,14 +3901,6 @@
         nativeSetFindIsUp(isUp);
     }
 
-    /**
-     * Return the index of the currently highlighted match.
-     */
-    int findIndex() {
-        if (0 == mNativeClass) return -1;
-        return nativeFindIndex();
-    }
-
     // Used to know whether the find dialog is open.  Affects whether
     // or not we draw the highlights for matches.
     private boolean mFindIsUp;
@@ -3816,10 +3967,11 @@
         checkThread();
         if (mNativeClass == 0)
             return;
-        nativeSetFindIsEmpty();
-        invalidate();
+        mWebViewCore.removeMessages(EventHub.FIND_ALL);
+        mWebViewCore.sendMessage(EventHub.FIND_ALL, null);
     }
 
+
     /**
      * Called when the find ActionMode ends.
      */
@@ -4561,15 +4713,7 @@
         if (mTitleBar != null) {
             canvas.translate(0, getTitleHeight());
         }
-        boolean drawJavaRings = !mTouchHighlightRegion.isEmpty()
-                && (mTouchMode == TOUCH_INIT_MODE
-                || mTouchMode == TOUCH_SHORTPRESS_START_MODE
-                || mTouchMode == TOUCH_SHORTPRESS_MODE
-                || mTouchMode == TOUCH_DONE_MODE);
-        boolean drawNativeRings = !drawJavaRings;
-        if (sDisableNavcache) {
-            drawNativeRings = !drawJavaRings && !isInTouchMode();
-        }
+        boolean drawNativeRings = !sDisableNavcache;
         drawContent(canvas, drawNativeRings);
         canvas.restoreToCount(saveCount);
 
@@ -4582,18 +4726,13 @@
             invalidate();
         }
 
-        // paint the highlight in the end
-        if (drawJavaRings) {
-            long delay = System.currentTimeMillis() - mTouchHighlightRequested;
-            if (delay < ViewConfiguration.getTapTimeout()) {
-                Rect r = mTouchHighlightRegion.getBounds();
-                postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom);
-            } else {
-                RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
-                Rect r = new Rect();
-                while (iter.next(r)) {
-                    canvas.drawRect(r, mTouchHightlightPaint);
-                }
+        if (mFocusTransition != null) {
+            mFocusTransition.draw(canvas);
+        } else if (shouldDrawHighlightRect()) {
+            RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
+            Rect r = new Rect();
+            while (iter.next(r)) {
+                canvas.drawRect(r, mTouchHightlightPaint);
             }
         }
         if (DEBUG_TOUCH_HIGHLIGHT) {
@@ -4803,7 +4942,9 @@
         if (layer == 0 || isPictureAfterFirstLayout) {
             mWebViewCore.resumeWebKitDraw();
         } else if (queueFull) {
-            mWebViewCore.pauseWebKitDraw();
+            // temporarily disable webkit draw throttling
+            // TODO: re-enable
+            // mWebViewCore.pauseWebKitDraw();
         }
 
         if (mHTML5VideoViewProxy != null) {
@@ -4912,12 +5053,12 @@
 
         // decide which adornments to draw
         int extras = DRAW_EXTRAS_NONE;
-        if (mFindIsUp) {
-            extras = DRAW_EXTRAS_FIND;
-        } else if (mSelectingText) {
-            extras = DRAW_EXTRAS_SELECTION;
-        } else if (drawCursorRing) {
-            extras = DRAW_EXTRAS_CURSOR_RING;
+        if (!mFindIsUp) {
+            if (mSelectingText) {
+                extras = DRAW_EXTRAS_SELECTION;
+            } else if (drawCursorRing) {
+                extras = DRAW_EXTRAS_CURSOR_RING;
+            }
         }
         if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "mFindIsUp=" + mFindIsUp
@@ -4982,31 +5123,45 @@
     }
 
     private void drawTextSelectionHandles(Canvas canvas) {
-        if (mSelectHandleLeft == null) {
-            mSelectHandleLeft = mContext.getResources().getDrawable(
-                    com.android.internal.R.drawable.text_select_handle_left);
-        }
         int[] handles = new int[4];
         getSelectionHandles(handles);
         int start_x = contentToViewDimension(handles[0]);
         int start_y = contentToViewDimension(handles[1]);
         int end_x = contentToViewDimension(handles[2]);
         int end_y = contentToViewDimension(handles[3]);
-        // Magic formula copied from TextView
-        start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4;
-        mSelectHandleLeft.setBounds(start_x, start_y,
-                start_x + mSelectHandleLeft.getIntrinsicWidth(),
-                start_y + mSelectHandleLeft.getIntrinsicHeight());
-        if (mSelectHandleRight == null) {
-            mSelectHandleRight = mContext.getResources().getDrawable(
-                    com.android.internal.R.drawable.text_select_handle_right);
+
+        if (mIsCaretSelection) {
+            if (mSelectHandleCenter == null) {
+                mSelectHandleCenter = mContext.getResources().getDrawable(
+                        com.android.internal.R.drawable.text_select_handle_middle);
+            }
+            // Caret handle is centered
+            start_x -= (mSelectHandleCenter.getIntrinsicWidth() / 2);
+            mSelectHandleCenter.setBounds(start_x, start_y,
+                    start_x + mSelectHandleCenter.getIntrinsicWidth(),
+                    start_y + mSelectHandleCenter.getIntrinsicHeight());
+            mSelectHandleCenter.draw(canvas);
+        } else {
+            if (mSelectHandleLeft == null) {
+                mSelectHandleLeft = mContext.getResources().getDrawable(
+                        com.android.internal.R.drawable.text_select_handle_left);
+            }
+            // Magic formula copied from TextView
+            start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4;
+            mSelectHandleLeft.setBounds(start_x, start_y,
+                    start_x + mSelectHandleLeft.getIntrinsicWidth(),
+                    start_y + mSelectHandleLeft.getIntrinsicHeight());
+            if (mSelectHandleRight == null) {
+                mSelectHandleRight = mContext.getResources().getDrawable(
+                        com.android.internal.R.drawable.text_select_handle_right);
+            }
+            end_x -= mSelectHandleRight.getIntrinsicWidth() / 4;
+            mSelectHandleRight.setBounds(end_x, end_y,
+                    end_x + mSelectHandleRight.getIntrinsicWidth(),
+                    end_y + mSelectHandleRight.getIntrinsicHeight());
+            mSelectHandleLeft.draw(canvas);
+            mSelectHandleRight.draw(canvas);
         }
-        end_x -= mSelectHandleRight.getIntrinsicWidth() / 4;
-        mSelectHandleRight.setBounds(end_x, end_y,
-                end_x + mSelectHandleRight.getIntrinsicWidth(),
-                end_y + mSelectHandleRight.getIntrinsicHeight());
-        mSelectHandleLeft.draw(canvas);
-        mSelectHandleRight.draw(canvas);
     }
 
     /**
@@ -5108,18 +5263,10 @@
 
     @Override
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        outAttrs.inputType = EditorInfo.IME_FLAG_NO_FULLSCREEN
-                | EditorInfo.TYPE_CLASS_TEXT
-                | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT
-                | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
-                | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT
-                | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES;
-        outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
-
         if (mInputConnection == null) {
             mInputConnection = new WebViewInputConnection();
         }
-        outAttrs.initialCapsMode = mInputConnection.getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
+        mInputConnection.setupEditorInfo(outAttrs);
         return mInputConnection;
     }
 
@@ -5436,6 +5583,9 @@
                     + "keyCode=" + keyCode
                     + ", " + event + ", unicode=" + event.getUnicodeChar());
         }
+        if (mIsCaretSelection) {
+            selectionDone();
+        }
         if (mBlockWebkitViewMessages) {
             return false;
         }
@@ -5553,7 +5703,7 @@
             return false;
         }
 
-        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+        if (isEnterActionKey(keyCode)) {
             switchOutDrawHistory();
             boolean wantsKeyEvents = nativeCursorNodePointer() == 0
                 || nativeCursorWantsKeyEvents();
@@ -5702,33 +5852,35 @@
                 return true; // discard press if copy in progress
             }
 
-            // perform the single click
-            Rect visibleRect = sendOurVisibleRect();
-            // Note that sendOurVisibleRect calls viewToContent, so the
-            // coordinates should be in content coordinates.
-            if (!nativeCursorIntersects(visibleRect)) {
-                return false;
-            }
-            WebViewCore.CursorData data = cursorData();
-            mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
-            playSoundEffect(SoundEffectConstants.CLICK);
-            if (nativeCursorIsTextInput()) {
-                rebuildWebTextView();
-                centerKeyPressOnTextField();
-                if (inEditingMode()) {
-                    mWebTextView.setDefaultSelection();
+            if (!sDisableNavcache) {
+                // perform the single click
+                Rect visibleRect = sendOurVisibleRect();
+                // Note that sendOurVisibleRect calls viewToContent, so the
+                // coordinates should be in content coordinates.
+                if (!nativeCursorIntersects(visibleRect)) {
+                    return false;
                 }
-                return true;
-            }
-            clearTextEntry();
-            nativeShowCursorTimed();
-            if (mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
-                return true;
-            }
-            if (nativeCursorNodePointer() != 0 && !nativeCursorWantsKeyEvents()) {
-                mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
-                        nativeCursorNodePointer());
-                return true;
+                WebViewCore.CursorData data = cursorData();
+                mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
+                playSoundEffect(SoundEffectConstants.CLICK);
+                if (nativeCursorIsTextInput()) {
+                    rebuildWebTextView();
+                    centerKeyPressOnTextField();
+                    if (inEditingMode()) {
+                        mWebTextView.setDefaultSelection();
+                    }
+                    return true;
+                }
+                clearTextEntry();
+                nativeShowCursorTimed();
+                if (mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
+                    return true;
+                }
+                if (nativeCursorNodePointer() != 0 && !nativeCursorWantsKeyEvents()) {
+                    mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
+                            nativeCursorNodePointer());
+                    return true;
+                }
             }
         }
 
@@ -5746,6 +5898,7 @@
 
     private boolean startSelectActionMode() {
         mSelectCallback = new SelectActionModeCallback();
+        mSelectCallback.setTextSelected(!mIsCaretSelection);
         mSelectCallback.setWebView(this);
         if (startActionMode(mSelectCallback) == null) {
             // There is no ActionMode, so do not allow the user to modify a
@@ -5766,9 +5919,13 @@
 
     private boolean setupWebkitSelect() {
         syncSelectionCursors();
-        if (!startSelectActionMode()) {
-            selectionDone();
-            return false;
+        ClipboardManager cm = (ClipboardManager)(mContext
+                .getSystemService(Context.CLIPBOARD_SERVICE));
+        if (!mIsCaretSelection || cm.hasPrimaryClip()) {
+            if (!startSelectActionMode()) {
+                selectionDone();
+                return false;
+            }
         }
         mSelectingText = true;
         mTouchMode = TOUCH_DRAG_MODE;
@@ -5777,6 +5934,9 @@
 
     private void updateWebkitSelection() {
         int[] handles = null;
+        if (mIsCaretSelection) {
+            mSelectCursorExtent.set(mSelectCursorBase);
+        }
         if (mSelectingText) {
             handles = new int[4];
             handles[0] = mSelectCursorBase.centerX();
@@ -5790,6 +5950,14 @@
         mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles);
     }
 
+    private void resetCaretTimer() {
+        mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
+        if (!mSelectionStarted) {
+            mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE,
+                    CARET_HANDLE_STAMINA_MS);
+        }
+    }
+
     /**
      * Use this method to put the WebView into text selection mode.
      * Do not rely on this functionality; it will be deprecated in the future.
@@ -5817,9 +5985,14 @@
             mSelectingText = false;
             // finish is idempotent, so this is fine even if selectionDone was
             // called by mSelectCallback.onDestroyActionMode
-            mSelectCallback.finish();
-            mSelectCallback = null;
-            updateWebkitSelection();
+            if (mSelectCallback != null) {
+                mSelectCallback.finish();
+                mSelectCallback = null;
+            }
+            if (!mIsCaretSelection) {
+                updateWebkitSelection();
+            }
+            mIsCaretSelection = false;
             invalidate(); // redraw without selection
             mAutoScrollX = 0;
             mAutoScrollY = 0;
@@ -6111,7 +6284,7 @@
         calcOurContentVisibleRectF(mVisibleContentRect);
         nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
                 mGLViewportEmpty ? null : mViewRectViewport,
-                mVisibleContentRect);
+                mVisibleContentRect, getScale());
     }
 
     /**
@@ -6433,18 +6606,26 @@
                                 (eventTime - mLastTouchUpTime), eventTime);
                     }
                     mSelectionStarted = false;
-                    if (mSelectingText && mSelectHandleLeft != null
-                            && mSelectHandleRight != null) {
+                    if (mSelectingText) {
                         int shiftedY = y - getTitleHeight() + mScrollY;
                         int shiftedX = x + mScrollX;
-                        if (mSelectHandleLeft.getBounds()
+                        if (mSelectHandleCenter != null && mSelectHandleCenter.getBounds()
                                 .contains(shiftedX, shiftedY)) {
                             mSelectionStarted = true;
                             mSelectDraggingCursor = mSelectCursorBase;
-                        } else if (mSelectHandleRight.getBounds()
+                            mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
+                        } else if (mSelectHandleLeft != null
+                                && mSelectHandleLeft.getBounds()
+                                    .contains(shiftedX, shiftedY)) {
+                                mSelectionStarted = true;
+                                mSelectDraggingCursor = mSelectCursorBase;
+                        } else if (mSelectHandleRight != null
+                                && mSelectHandleRight.getBounds()
                                 .contains(shiftedX, shiftedY)) {
                             mSelectionStarted = true;
                             mSelectDraggingCursor = mSelectCursorExtent;
+                        } else if (mIsCaretSelection) {
+                            selectionDone();
                         }
                         if (mSelectDraggingCursor != null) {
                             mSelectDraggingOffset.set(
@@ -7095,6 +7276,9 @@
 
         if (mSelectingText) {
             mSelectionStarted = false;
+            if (mIsCaretSelection) {
+                resetCaretTimer();
+            }
             syncSelectionCursors();
             invalidate();
         }
@@ -7737,7 +7921,10 @@
                 }
             }, ViewConfiguration.getPressedStateDuration());
         }
-        if (sDisableNavcache) {
+        if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
+            playSoundEffect(SoundEffectConstants.CLICK);
+            overrideLoading(mFocusedNode.mIntentUrl);
+        } else if (sDisableNavcache) {
             WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
             // use "0" as generation id to inform WebKit to use the same x/y as
             // it used when processing GET_TOUCH_HIGHLIGHT_RECTS
@@ -8840,13 +9027,6 @@
                     }
                     break;
 
-                case FIND_AGAIN:
-                    // Ignore if find has been dismissed.
-                    if (mFindIsUp && mFindCallback != null) {
-                        mFindCallback.findAll();
-                    }
-                    break;
-
                 case DRAG_HELD_MOTIONLESS:
                     mHeldMotionless = MOTIONLESS_TRUE;
                     invalidate();
@@ -8982,24 +9162,7 @@
                     WebKitHitTest hit = (WebKitHitTest) msg.obj;
                     mFocusedNode = hit;
                     setTouchHighlightRects(hit);
-                    if (hit == null) {
-                        mInitialHitTestResult = null;
-                    } else {
-                        mInitialHitTestResult = new HitTestResult();
-                        if (hit.mLinkUrl != null) {
-                            mInitialHitTestResult.mType = HitTestResult.SRC_ANCHOR_TYPE;
-                            mInitialHitTestResult.mExtra = hit.mLinkUrl;
-                            if (hit.mImageUrl != null) {
-                                mInitialHitTestResult.mType = HitTestResult.SRC_IMAGE_ANCHOR_TYPE;
-                                mInitialHitTestResult.mExtra = hit.mImageUrl;
-                            }
-                        } else if (hit.mImageUrl != null) {
-                            mInitialHitTestResult.mType = HitTestResult.IMAGE_TYPE;
-                            mInitialHitTestResult.mExtra = hit.mImageUrl;
-                        } else if (hit.mEditable) {
-                            mInitialHitTestResult.mType = HitTestResult.EDIT_TEXT_TYPE;
-                        }
-                    }
+                    setHitTestResult(hit);
                     break;
 
                 case SAVE_WEBARCHIVE_FINISHED:
@@ -9035,9 +9198,11 @@
 
                 case INIT_EDIT_FIELD:
                     if (mInputConnection != null) {
+                        TextFieldInitData initData = (TextFieldInitData) msg.obj;
                         mTextGeneration = 0;
-                        mFieldPointer = msg.arg1;
-                        mInputConnection.setTextAndKeepSelection((String) msg.obj);
+                        mFieldPointer = initData.mFieldPointer;
+                        mInputConnection.initEditorInfo(initData);
+                        mInputConnection.setTextAndKeepSelection(initData.mText);
                     }
                     break;
 
@@ -9051,6 +9216,21 @@
                     break;
                 }
 
+                case UPDATE_MATCH_COUNT: {
+                    if (mFindCallback != null) {
+                        mFindCallback.updateMatchCount(msg.arg1, msg.arg2,
+                            (String) msg.obj);
+                    }
+                    break;
+                }
+                case CLEAR_CARET_HANDLE:
+                    selectionDone();
+                    break;
+
+                case KEY_PRESS:
+                    mWebViewCore.sendMessage(EventHub.KEY_PRESS, msg.arg1);
+                    break;
+
                 default:
                     super.handleMessage(msg);
                     break;
@@ -9058,10 +9238,169 @@
         }
     }
 
+    private void setHitTestTypeFromUrl(String url) {
+        String substr = null;
+        if (url.startsWith(SCHEME_GEO)) {
+            mInitialHitTestResult.mType = HitTestResult.GEO_TYPE;
+            substr = url.substring(SCHEME_GEO.length());
+        } else if (url.startsWith(SCHEME_TEL)) {
+            mInitialHitTestResult.mType = HitTestResult.PHONE_TYPE;
+            substr = url.substring(SCHEME_TEL.length());
+        } else if (url.startsWith(SCHEME_MAILTO)) {
+            mInitialHitTestResult.mType = HitTestResult.EMAIL_TYPE;
+            substr = url.substring(SCHEME_MAILTO.length());
+        } else {
+            mInitialHitTestResult.mType = HitTestResult.SRC_ANCHOR_TYPE;
+            mInitialHitTestResult.mExtra = url;
+            return;
+        }
+        try {
+            mInitialHitTestResult.mExtra = URLDecoder.decode(substr, "UTF-8");
+        } catch (Throwable e) {
+            Log.w(LOGTAG, "Failed to decode URL! " + substr, e);
+            mInitialHitTestResult.mType = HitTestResult.UNKNOWN_TYPE;
+        }
+    }
+
+    private void setHitTestResult(WebKitHitTest hit) {
+        if (hit == null) {
+            mInitialHitTestResult = null;
+            return;
+        }
+        mInitialHitTestResult = new HitTestResult();
+        if (hit.mLinkUrl != null) {
+            setHitTestTypeFromUrl(hit.mLinkUrl);
+            if (hit.mImageUrl != null
+                    && mInitialHitTestResult.mType == HitTestResult.SRC_ANCHOR_TYPE) {
+                mInitialHitTestResult.mType = HitTestResult.SRC_IMAGE_ANCHOR_TYPE;
+                mInitialHitTestResult.mExtra = hit.mImageUrl;
+            }
+        } else if (hit.mImageUrl != null) {
+            mInitialHitTestResult.mType = HitTestResult.IMAGE_TYPE;
+            mInitialHitTestResult.mExtra = hit.mImageUrl;
+        } else if (hit.mEditable) {
+            mInitialHitTestResult.mType = HitTestResult.EDIT_TEXT_TYPE;
+        } else if (hit.mIntentUrl != null) {
+            setHitTestTypeFromUrl(hit.mIntentUrl);
+        }
+    }
+
+    private boolean shouldDrawHighlightRect() {
+        if (mFocusedNode == null || mInitialHitTestResult == null) {
+            return false;
+        }
+        if (mTouchHighlightRegion.isEmpty()) {
+            return false;
+        }
+        if (mFocusedNode.mHasFocus && !isInTouchMode()) {
+            return !mFocusedNode.mEditable;
+        }
+        if (mInitialHitTestResult.mType == HitTestResult.UNKNOWN_TYPE) {
+            return false;
+        }
+        long delay = System.currentTimeMillis() - mTouchHighlightRequested;
+        if (delay < ViewConfiguration.getTapTimeout()) {
+            Rect r = mTouchHighlightRegion.getBounds();
+            postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom);
+            return false;
+        }
+        return true;
+    }
+
+
+    private FocusTransitionDrawable mFocusTransition = null;
+    static class FocusTransitionDrawable extends Drawable {
+        Region mPreviousRegion;
+        Region mNewRegion;
+        float mProgress = 0;
+        WebView mWebView;
+        Paint mPaint;
+        int mMaxAlpha;
+        Point mTranslate;
+
+        public FocusTransitionDrawable(WebView view) {
+            mWebView = view;
+            mPaint = new Paint(mWebView.mTouchHightlightPaint);
+            mMaxAlpha = mPaint.getAlpha();
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+        }
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+
+        public void setProgress(float p) {
+            mProgress = p;
+            if (mWebView.mFocusTransition == this) {
+                if (mProgress == 1f)
+                    mWebView.mFocusTransition = null;
+                mWebView.invalidate();
+            }
+        }
+
+        public float getProgress() {
+            return mProgress;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            if (mTranslate == null) {
+                Rect bounds = mPreviousRegion.getBounds();
+                Point from = new Point(bounds.centerX(), bounds.centerY());
+                mNewRegion.getBounds(bounds);
+                Point to = new Point(bounds.centerX(), bounds.centerY());
+                mTranslate = new Point(from.x - to.x, from.y - to.y);
+            }
+            int alpha = (int) (mProgress * mMaxAlpha);
+            RegionIterator iter = new RegionIterator(mPreviousRegion);
+            Rect r = new Rect();
+            mPaint.setAlpha(mMaxAlpha - alpha);
+            float tx = mTranslate.x * mProgress;
+            float ty = mTranslate.y * mProgress;
+            int save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+            canvas.translate(-tx, -ty);
+            while (iter.next(r)) {
+                canvas.drawRect(r, mPaint);
+            }
+            canvas.restoreToCount(save);
+            iter = new RegionIterator(mNewRegion);
+            r = new Rect();
+            mPaint.setAlpha(alpha);
+            save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+            tx = mTranslate.x - tx;
+            ty = mTranslate.y - ty;
+            canvas.translate(tx, ty);
+            while (iter.next(r)) {
+                canvas.drawRect(r, mPaint);
+            }
+            canvas.restoreToCount(save);
+        }
+    };
+
+    private boolean shouldAnimateTo(WebKitHitTest hit) {
+        // TODO: Don't be annoying or throw out the animation entirely
+        return false;
+    }
+
     private void setTouchHighlightRects(WebKitHitTest hit) {
+        FocusTransitionDrawable transition = null;
+        if (shouldAnimateTo(hit)) {
+            transition = new FocusTransitionDrawable(this);
+        }
         Rect[] rects = hit != null ? hit.mTouchRects : null;
         if (!mTouchHighlightRegion.isEmpty()) {
             invalidate(mTouchHighlightRegion.getBounds());
+            if (transition != null) {
+                transition.mPreviousRegion = new Region(mTouchHighlightRegion);
+            }
             mTouchHighlightRegion.setEmpty();
         }
         if (rects != null) {
@@ -9081,6 +9420,13 @@
                 }
             }
             invalidate(mTouchHighlightRegion.getBounds());
+            if (transition != null && transition.mPreviousRegion != null) {
+                transition.mNewRegion = new Region(mTouchHighlightRegion);
+                mFocusTransition = transition;
+                ObjectAnimator animator = ObjectAnimator.ofFloat(
+                        mFocusTransition, "progress", 1f);
+                animator.start();
+            }
         }
     }
 
@@ -9181,14 +9527,20 @@
                 mInputConnection.setSelection(data.mStart, data.mEnd);
             }
         }
-
         nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
-        if (data.mSelectTextPtr != 0) {
+
+        if (data.mSelectTextPtr != 0 &&
+                (data.mStart != data.mEnd ||
+                (mFieldPointer == nodePointer && mFieldPointer != 0))) {
+            mIsCaretSelection = (data.mStart == data.mEnd);
             if (!mSelectingText) {
                 setupWebkitSelect();
             } else if (!mSelectionStarted) {
                 syncSelectionCursors();
             }
+            if (mIsCaretSelection) {
+                resetCaretTimer();
+            }
         } else {
             selectionDone();
         }
@@ -9885,11 +10237,8 @@
     private native int      nativeGetDrawGLFunction(int nativeInstance, Rect rect,
             Rect viewRect, RectF visibleRect, float scale, int extras);
     private native void     nativeUpdateDrawGLFunction(Rect rect, Rect viewRect,
-            RectF visibleRect);
+            RectF visibleRect, float scale);
     private native void     nativeExtendSelection(int x, int y);
-    private native int      nativeFindAll(String findLower, String findUpper,
-            boolean sameAsLastSearch);
-    private native void     nativeFindNext(boolean forward);
     /* package */ native int      nativeFocusCandidateFramePointer();
     /* package */ native boolean  nativeFocusCandidateHasNextTextfield();
     /* package */ native boolean  nativeFocusCandidateIsPassword();
@@ -9947,9 +10296,7 @@
     private native boolean  nativePointInNavCache(int x, int y, int slop);
     private native void     nativeSelectBestAt(Rect rect);
     private native void     nativeSelectAt(int x, int y);
-    private native int      nativeFindIndex();
     private native void     nativeSetExtendSelection();
-    private native void     nativeSetFindIsEmpty();
     private native void     nativeSetFindIsUp(boolean isUp);
     private native void     nativeSetHeightCanMeasure(boolean measure);
     private native boolean  nativeSetBaseLayer(int nativeInstance,
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 8a9c12d..e7da1a8 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -26,6 +26,7 @@
 import android.media.MediaFile;
 import android.net.ProxyProperties;
 import android.net.Uri;
+import android.net.http.CertificateChainValidator;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -333,6 +334,15 @@
     }
 
     /**
+     * Called by JNI when the focus node changed.
+     */
+    private void focusNodeChanged(WebKitHitTest hitTest) {
+        if (mWebView == null) return;
+        mWebView.mPrivateHandler.obtainMessage(WebView.HIT_TEST_RESULT, hitTest)
+                .sendToTarget();
+    }
+
+    /**
      * Called by JNI.  Open a file chooser to upload a file.
      * @param acceptType The value of the 'accept' attribute of the
      *         input tag associated with this file picker.
@@ -614,7 +624,6 @@
             int x, int y);
     private native String nativeRetrieveImageSource(int nativeClass,
             int x, int y);
-    private native void nativeStopPaintingCaret(int nativeClass);
     private native void nativeTouchUp(int nativeClass,
             int touchGeneration, int framePtr, int nodePtr, int x, int y);
 
@@ -767,6 +776,11 @@
                                 Message m = (Message)msg.obj;
                                 m.sendToTarget();
                                 break;
+                            case EventHub.TRUST_STORAGE_UPDATED:
+                                // post a task to network thread for updating trust manager
+                                nativeCertTrustChanged();
+                                CertificateChainValidator.handleTrustStorageUpdate();
+                                break;
                         }
                     }
                 };
@@ -865,6 +879,7 @@
 
     static class WebKitHitTest {
         String mLinkUrl;
+        String mIntentUrl;
         String mAnchorText;
         String mImageUrl;
         String mAltDisplayString;
@@ -873,6 +888,7 @@
         boolean mEditable;
         int mTapHighlightColor = WebView.HIGHLIGHT_COLOR;
         Rect[] mEnclosingParentRects;
+        boolean mHasFocus;
 
         // These are the input values that produced this hit test
         int mHitTestX;
@@ -904,6 +920,25 @@
         private String mPreview;
     }
 
+    static class TextFieldInitData {
+        public TextFieldInitData(int fieldPointer,
+                String text, int type, boolean isSpellCheckEnabled,
+                boolean isTextFieldNext, String label) {
+            mFieldPointer = fieldPointer;
+            mText = text;
+            mType = type;
+            mIsSpellCheckEnabled = isSpellCheckEnabled;
+            mIsTextFieldNext = isTextFieldNext;
+            mLabel = label;
+        }
+        int mFieldPointer;
+        String mText;
+        int mType;
+        boolean mIsSpellCheckEnabled;
+        boolean mIsTextFieldNext;
+        String mLabel;
+    }
+
     // mAction of TouchEventData can be MotionEvent.getAction() which uses the
     // last two bytes or one of the following values
     static final int ACTION_LONGPRESS = 0x100;
@@ -987,6 +1022,15 @@
             "REMOVE_JS_INTERFACE", // = 149;
         };
 
+    static class FindAllRequest {
+        public FindAllRequest(String text) {
+            mSearchText = text;
+            mMatchCount = -1;
+        }
+        public String mSearchText;
+        public int mMatchCount;
+    }
+
     /**
      * @hide
      */
@@ -1125,6 +1169,16 @@
         static final int SELECT_WORD_AT = 214;
         static final int SELECT_ALL = 215;
 
+        // for updating state on trust storage change
+        static final int TRUST_STORAGE_UPDATED = 220;
+
+        // find-on-page controls
+        static final int FIND_ALL = 221;
+        static final int FIND_NEXT = 222;
+
+        // key was pressed (down and up)
+        static final int KEY_PRESS = 223;
+
         // Private handler for WebCore messages.
         private Handler mHandler;
         // Message queue for containing messages before the WebCore thread is
@@ -1304,6 +1358,10 @@
                             key((KeyEvent) msg.obj, false);
                             break;
 
+                        case KEY_PRESS:
+                            keyPress(msg.arg1);
+                            break;
+
                         case FAKE_CLICK:
                             nativeClick(mNativeClass, msg.arg1, msg.arg2, true);
                             break;
@@ -1352,21 +1410,12 @@
                             Process.setThreadPriority(mTid,
                                     Process.THREAD_PRIORITY_BACKGROUND);
                             pauseTimers();
-                            if (!JniUtil.useChromiumHttpStack()) {
-                                WebViewWorker.getHandler().sendEmptyMessage(
-                                        WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION);
-                            } else {
-                                nativeCloseIdleConnections(mNativeClass);
-                            }
+                            nativeCloseIdleConnections(mNativeClass);
                             break;
 
                         case RESUME_TIMERS:
                             Process.setThreadPriority(mTid, mSavedPriority);
                             resumeTimers();
-                            if (!JniUtil.useChromiumHttpStack()) {
-                                WebViewWorker.getHandler().sendEmptyMessage(
-                                        WebViewWorker.MSG_RESUME_CACHE_TRANSACTION);
-                            }
                             break;
 
                         case ON_PAUSE:
@@ -1440,14 +1489,10 @@
                         }
 
                         case CLEAR_SSL_PREF_TABLE:
-                            if (JniUtil.useChromiumHttpStack()) {
-                                // FIXME: This will not work for connections currently in use, as
-                                // they cache the certificate responses. See http://b/5324235.
-                                SslCertLookupTable.getInstance().clear();
-                                nativeCloseIdleConnections(mNativeClass);
-                            } else {
-                                Network.getInstance(mContext).clearUserSslPrefTable();
-                            }
+                            // FIXME: This will not work for connections currently in use, as
+                            // they cache the certificate responses. See http://b/5324235.
+                            SslCertLookupTable.getInstance().clear();
+                            nativeCloseIdleConnections(mNativeClass);
                             break;
 
                         case TOUCH_UP:
@@ -1530,9 +1575,6 @@
                             nativeMoveMouseIfLatest(mNativeClass,
                                     cData.mMoveGeneration,
                                     cData.mFrame, cData.mX, cData.mY);
-                            if (msg.arg1 == 1) {
-                                nativeStopPaintingCaret(mNativeClass);
-                            }
                             break;
 
                         case REQUEST_CURSOR_HREF: {
@@ -1771,6 +1813,22 @@
                         case SELECT_ALL:
                             nativeSelectAll(mNativeClass);
                             break;
+                        case FIND_ALL: {
+                            FindAllRequest request = (FindAllRequest) msg.obj;
+                            if (request == null) {
+                                nativeFindAll(mNativeClass, null);
+                            } else {
+                                request.mMatchCount = nativeFindAll(
+                                    mNativeClass, request.mSearchText);
+                                synchronized(request) {
+                                    request.notify();
+                                }
+                            }
+                            break;
+                        }
+                        case FIND_NEXT:
+                            nativeFindNext(mNativeClass, msg.arg1 != 0);
+                            break;
                     }
                 }
             };
@@ -2010,6 +2068,11 @@
         }
     }
 
+    private void keyPress(int unicodeChar) {
+        nativeKey(mNativeClass, 0, unicodeChar, 0, false, false, false, true);
+        nativeKey(mNativeClass, 0, unicodeChar, 0, false, false, false, false);
+    }
+
     // These values are used to avoid requesting a layout based on old values
     private int mCurrentViewWidth = 0;
     private int mCurrentViewHeight = 0;
@@ -2382,14 +2445,6 @@
     // called by JNI
     private void sendNotifyProgressFinished() {
         sendUpdateTextEntry();
-        if (!JniUtil.useChromiumHttpStack()) {
-            // as CacheManager can behave based on database transaction, we need to
-            // call tick() to trigger endTransaction
-            WebViewWorker.getHandler().removeMessages(
-                    WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
-            WebViewWorker.getHandler().sendEmptyMessage(
-                    WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
-        }
         contentDraw();
     }
 
@@ -2769,23 +2824,31 @@
     }
 
     // called by JNI
-    private void sendFindAgain() {
-        if (mWebView == null) return;
+    private void initEditField(int pointer, String text, int inputType,
+            boolean isSpellCheckEnabled, boolean nextFieldIsText,
+            String label, int start, int end, int selectionPtr) {
+        if (mWebView == null) {
+            return;
+        }
+        TextFieldInitData initData = new TextFieldInitData(pointer,
+                text, inputType, isSpellCheckEnabled, nextFieldIsText, label);
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.FIND_AGAIN).sendToTarget();
+                WebView.INIT_EDIT_FIELD, initData).sendToTarget();
+        Message.obtain(mWebView.mPrivateHandler,
+                WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
+                0, new TextSelectionData(start, end, selectionPtr))
+                .sendToTarget();
     }
 
     // called by JNI
-    private void initEditField(int pointer, String text, int start, int end) {
+    private void updateMatchCount(int matchIndex, int matchCount,
+        String findText) {
         if (mWebView == null) {
             return;
         }
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.INIT_EDIT_FIELD, pointer, 0, text).sendToTarget();
-        Message.obtain(mWebView.mPrivateHandler,
-                WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
-                0, new TextSelectionData(start, end, 0))
-                .sendToTarget();
+                WebView.UPDATE_MATCH_COUNT, matchIndex, matchCount,
+                findText).sendToTarget();
     }
 
     private native void nativeUpdateFrameCacheIfLoading(int nativeClass);
@@ -3041,6 +3104,8 @@
 
     private native void nativeAutoFillForm(int nativeClass, int queryId);
     private native void nativeScrollLayer(int nativeClass, int layer, Rect rect);
+    private native int nativeFindAll(int nativeClass, String text);
+    private native void nativeFindNext(int nativeClass, boolean forward);
 
     /**
      * Deletes editable text between two points. Note that the selection may
@@ -3077,4 +3142,6 @@
     private native void nativeClearTextSelection(int nativeClass);
     private native void nativeSelectWordAt(int nativeClass, int x, int y);
     private native void nativeSelectAll(int nativeClass);
+
+    private static native void nativeCertTrustChanged();
 }
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 695c154..757a619 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -31,9 +31,6 @@
 import android.database.sqlite.SQLiteException;
 import android.database.sqlite.SQLiteStatement;
 import android.util.Log;
-import android.webkit.CookieManager.Cookie;
-import android.webkit.CacheManager.CacheResult;
-import android.webkit.JniUtil;
 
 public class WebViewDatabase {
     private static final String DATABASE_FILE = "webview.db";
@@ -55,39 +52,25 @@
     // 10 -> 11 Drop cookies and cache now managed by the chromium stack,
     //          and update the form data table to use the new format
     //          implemented for b/5265606.
-    private static final int CACHE_DATABASE_VERSION = 4;
-    // 1 -> 2 Add expires String
-    // 2 -> 3 Add content-disposition
-    // 3 -> 4 Add crossdomain (For x-permitted-cross-domain-policies header)
 
     private static WebViewDatabase mInstance = null;
 
     private static SQLiteDatabase mDatabase = null;
-    private static SQLiteDatabase mCacheDatabase = null;
 
     // synchronize locks
-    private final Object mCookieLock = new Object();
     private final Object mPasswordLock = new Object();
     private final Object mFormLock = new Object();
     private final Object mHttpAuthLock = new Object();
 
-    // TODO: The Chromium HTTP stack handles cookies independently.
-    // We should consider removing the cookies table if and when we switch to
-    // the Chromium HTTP stack for good.
     private static final String mTableNames[] = {
-        "cookies", "password", "formurl", "formdata", "httpauth"
+        "password", "formurl", "formdata", "httpauth"
     };
 
     // Table ids (they are index to mTableNames)
-    private static final int TABLE_COOKIES_ID = 0;
-
-    private static final int TABLE_PASSWORD_ID = 1;
-
-    private static final int TABLE_FORMURL_ID = 2;
-
-    private static final int TABLE_FORMDATA_ID = 3;
-
-    private static final int TABLE_HTTPAUTH_ID = 4;
+    private static final int TABLE_PASSWORD_ID = 0;
+    private static final int TABLE_FORMURL_ID = 1;
+    private static final int TABLE_FORMDATA_ID = 2;
+    private static final int TABLE_HTTPAUTH_ID = 3;
 
     // column id strings for "_id" which can be used by any table
     private static final String ID_COL = "_id";
@@ -96,51 +79,9 @@
         "_id"
     };
 
-    // column id strings for "cookies" table
-    private static final String COOKIES_NAME_COL = "name";
-
-    private static final String COOKIES_VALUE_COL = "value";
-
-    private static final String COOKIES_DOMAIN_COL = "domain";
-
-    private static final String COOKIES_PATH_COL = "path";
-
-    private static final String COOKIES_EXPIRES_COL = "expires";
-
-    private static final String COOKIES_SECURE_COL = "secure";
-
-    // column id strings for "cache" table
-    private static final String CACHE_URL_COL = "url";
-
-    private static final String CACHE_FILE_PATH_COL = "filepath";
-
-    private static final String CACHE_LAST_MODIFY_COL = "lastmodify";
-
-    private static final String CACHE_ETAG_COL = "etag";
-
-    private static final String CACHE_EXPIRES_COL = "expires";
-
-    private static final String CACHE_EXPIRES_STRING_COL = "expiresstring";
-
-    private static final String CACHE_MIMETYPE_COL = "mimetype";
-
-    private static final String CACHE_ENCODING_COL = "encoding";
-
-    private static final String CACHE_HTTP_STATUS_COL = "httpstatus";
-
-    private static final String CACHE_LOCATION_COL = "location";
-
-    private static final String CACHE_CONTENTLENGTH_COL = "contentlength";
-
-    private static final String CACHE_CONTENTDISPOSITION_COL = "contentdisposition";
-
-    private static final String CACHE_CROSSDOMAIN_COL = "crossdomain";
-
     // column id strings for "password" table
     private static final String PASSWORD_HOST_COL = "host";
-
     private static final String PASSWORD_USERNAME_COL = "username";
-
     private static final String PASSWORD_PASSWORD_COL = "password";
 
     // column id strings for "formurl" table
@@ -148,38 +89,15 @@
 
     // column id strings for "formdata" table
     private static final String FORMDATA_URLID_COL = "urlid";
-
     private static final String FORMDATA_NAME_COL = "name";
-
     private static final String FORMDATA_VALUE_COL = "value";
 
     // column id strings for "httpauth" table
     private static final String HTTPAUTH_HOST_COL = "host";
-
     private static final String HTTPAUTH_REALM_COL = "realm";
-
     private static final String HTTPAUTH_USERNAME_COL = "username";
-
     private static final String HTTPAUTH_PASSWORD_COL = "password";
 
-    // use InsertHelper to improve insert performance by 40%
-    private static DatabaseUtils.InsertHelper mCacheInserter;
-    private static int mCacheUrlColIndex;
-    private static int mCacheFilePathColIndex;
-    private static int mCacheLastModifyColIndex;
-    private static int mCacheETagColIndex;
-    private static int mCacheExpiresColIndex;
-    private static int mCacheExpiresStringColIndex;
-    private static int mCacheMimeTypeColIndex;
-    private static int mCacheEncodingColIndex;
-    private static int mCacheHttpStatusColIndex;
-    private static int mCacheLocationColIndex;
-    private static int mCacheContentLengthColIndex;
-    private static int mCacheContentDispositionColIndex;
-    private static int mCacheCrossDomainColIndex;
-
-    private static int mCacheTransactionRefcount;
-
     // Initially true until the background thread completes.
     private boolean mInitialized = false;
 
@@ -207,11 +125,9 @@
         }
 
         initDatabase(context);
-        if (JniUtil.useChromiumHttpStack()) {
-            context.deleteDatabase(CACHE_DATABASE_FILE);
-        } else {
-            initCacheDatabase(context);
-        }
+	// Before using the Chromium HTTP stack, we stored the WebKit cache in
+	// our own DB. Clean up the DB file if it's still around.
+        context.deleteDatabase(CACHE_DATABASE_FILE);
 
         // Thread done, notify.
         mInitialized = true;
@@ -254,83 +170,6 @@
         mDatabase.setLockingEnabled(false);
     }
 
-    private void initCacheDatabase(Context context) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        try {
-            mCacheDatabase = context.openOrCreateDatabase(
-                    CACHE_DATABASE_FILE, 0, null);
-        } catch (SQLiteException e) {
-            // try again by deleting the old db and create a new one
-            if (context.deleteDatabase(CACHE_DATABASE_FILE)) {
-                mCacheDatabase = context.openOrCreateDatabase(
-                        CACHE_DATABASE_FILE, 0, null);
-            }
-        }
-        mCacheDatabase.enableWriteAheadLogging();
-
-        // mCacheDatabase should not be null,
-        // the only case is RequestAPI test has problem to create db
-        if (mCacheDatabase == null) {
-            mInitialized = true;
-            notify();
-            return;
-        }
-
-        if (mCacheDatabase.getVersion() != CACHE_DATABASE_VERSION) {
-            mCacheDatabase.beginTransactionNonExclusive();
-            try {
-                upgradeCacheDatabase();
-                bootstrapCacheDatabase();
-                mCacheDatabase.setTransactionSuccessful();
-            } finally {
-                mCacheDatabase.endTransaction();
-            }
-            // Erase the files from the file system in the
-            // case that the database was updated and the
-            // there were existing cache content
-            CacheManager.removeAllCacheFiles();
-        }
-
-        // use read_uncommitted to speed up READ
-        mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;");
-        // as only READ can be called in the
-        // non-WebViewWorkerThread, and read_uncommitted is used,
-        // we can turn off database lock to use transaction.
-        mCacheDatabase.setLockingEnabled(false);
-
-        // use InsertHelper for faster insertion
-        mCacheInserter =
-                new DatabaseUtils.InsertHelper(mCacheDatabase,
-                        "cache");
-        mCacheUrlColIndex = mCacheInserter
-                            .getColumnIndex(CACHE_URL_COL);
-        mCacheFilePathColIndex = mCacheInserter
-                .getColumnIndex(CACHE_FILE_PATH_COL);
-        mCacheLastModifyColIndex = mCacheInserter
-                .getColumnIndex(CACHE_LAST_MODIFY_COL);
-        mCacheETagColIndex = mCacheInserter
-                .getColumnIndex(CACHE_ETAG_COL);
-        mCacheExpiresColIndex = mCacheInserter
-                .getColumnIndex(CACHE_EXPIRES_COL);
-        mCacheExpiresStringColIndex = mCacheInserter
-                .getColumnIndex(CACHE_EXPIRES_STRING_COL);
-        mCacheMimeTypeColIndex = mCacheInserter
-                .getColumnIndex(CACHE_MIMETYPE_COL);
-        mCacheEncodingColIndex = mCacheInserter
-                .getColumnIndex(CACHE_ENCODING_COL);
-        mCacheHttpStatusColIndex = mCacheInserter
-                .getColumnIndex(CACHE_HTTP_STATUS_COL);
-        mCacheLocationColIndex = mCacheInserter
-                .getColumnIndex(CACHE_LOCATION_COL);
-        mCacheContentLengthColIndex = mCacheInserter
-                .getColumnIndex(CACHE_CONTENTLENGTH_COL);
-        mCacheContentDispositionColIndex = mCacheInserter
-                .getColumnIndex(CACHE_CONTENTDISPOSITION_COL);
-        mCacheCrossDomainColIndex = mCacheInserter
-                .getColumnIndex(CACHE_CROSSDOMAIN_COL);
-    }
-
     private static void upgradeDatabase() {
         upgradeDatabaseToV10();
         upgradeDatabaseFromV10ToV11();
@@ -347,14 +186,12 @@
             return;
         }
 
-        if (JniUtil.useChromiumHttpStack()) {
-            // Clear out old java stack cookies - this data is now stored in
-            // a separate database managed by the Chrome stack.
-            mDatabase.execSQL("DROP TABLE IF EXISTS " + mTableNames[TABLE_COOKIES_ID]);
+        // Clear out old java stack cookies - this data is now stored in
+        // a separate database managed by the Chrome stack.
+        mDatabase.execSQL("DROP TABLE IF EXISTS cookies");
 
-            // Likewise for the old cache table.
-            mDatabase.execSQL("DROP TABLE IF EXISTS cache");
-        }
+        // Likewise for the old cache table.
+        mDatabase.execSQL("DROP TABLE IF EXISTS cache");
 
         // Update form autocomplete  URLs to match new ICS formatting.
         Cursor c = mDatabase.query(mTableNames[TABLE_FORMURL_ID], null, null,
@@ -397,8 +234,7 @@
             return;
         }
 
-        mDatabase.execSQL("DROP TABLE IF EXISTS "
-                + mTableNames[TABLE_COOKIES_ID]);
+        mDatabase.execSQL("DROP TABLE IF EXISTS cookies");
         mDatabase.execSQL("DROP TABLE IF EXISTS cache");
         mDatabase.execSQL("DROP TABLE IF EXISTS "
                 + mTableNames[TABLE_FORMURL_ID]);
@@ -409,16 +245,6 @@
         mDatabase.execSQL("DROP TABLE IF EXISTS "
                 + mTableNames[TABLE_PASSWORD_ID]);
 
-        // cookies
-        mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_COOKIES_ID]
-                + " (" + ID_COL + " INTEGER PRIMARY KEY, "
-                + COOKIES_NAME_COL + " TEXT, " + COOKIES_VALUE_COL
-                + " TEXT, " + COOKIES_DOMAIN_COL + " TEXT, "
-                + COOKIES_PATH_COL + " TEXT, " + COOKIES_EXPIRES_COL
-                + " INTEGER, " + COOKIES_SECURE_COL + " INTEGER" + ");");
-        mDatabase.execSQL("CREATE INDEX cookiesIndex ON "
-                + mTableNames[TABLE_COOKIES_ID] + " (path)");
-
         // formurl
         mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID]
                 + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL
@@ -449,36 +275,6 @@
                 + ") ON CONFLICT REPLACE);");
     }
 
-    private static void upgradeCacheDatabase() {
-        int oldVersion = mCacheDatabase.getVersion();
-        if (oldVersion != 0) {
-            Log.i(LOGTAG, "Upgrading cache database from version "
-                    + oldVersion + " to "
-                    + CACHE_DATABASE_VERSION + ", which will destroy all old data");
-        }
-        mCacheDatabase.execSQL("DROP TABLE IF EXISTS cache");
-        mCacheDatabase.setVersion(CACHE_DATABASE_VERSION);
-    }
-
-    private static void bootstrapCacheDatabase() {
-        if (mCacheDatabase != null) {
-            mCacheDatabase.execSQL("CREATE TABLE cache"
-                    + " (" + ID_COL + " INTEGER PRIMARY KEY, " + CACHE_URL_COL
-                    + " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, "
-                    + CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL
-                    + " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, "
-                    + CACHE_EXPIRES_STRING_COL + " TEXT, "
-                    + CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL
-                    + " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, "
-                    + CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL
-                    + " INTEGER, " + CACHE_CONTENTDISPOSITION_COL + " TEXT, "
-                    + CACHE_CROSSDOMAIN_COL + " TEXT,"
-                    + " UNIQUE (" + CACHE_URL_COL + ") ON CONFLICT REPLACE);");
-            mCacheDatabase.execSQL("CREATE INDEX cacheUrlIndex ON cache ("
-                    + CACHE_URL_COL + ")");
-        }
-    }
-
     // Wait for the background initialization thread to complete and check the
     // database creation status.
     private boolean checkInitialized() {
@@ -516,422 +312,6 @@
     }
 
     //
-    // cookies functions
-    //
-
-    /**
-     * Get cookies in the format of CookieManager.Cookie inside an ArrayList for
-     * a given domain
-     *
-     * @return ArrayList<Cookie> If nothing is found, return an empty list.
-     */
-    ArrayList<Cookie> getCookiesForDomain(String domain) {
-        ArrayList<Cookie> list = new ArrayList<Cookie>();
-        if (domain == null || !checkInitialized()) {
-            return list;
-        }
-
-        synchronized (mCookieLock) {
-            final String[] columns = new String[] {
-                    ID_COL, COOKIES_DOMAIN_COL, COOKIES_PATH_COL,
-                    COOKIES_NAME_COL, COOKIES_VALUE_COL, COOKIES_EXPIRES_COL,
-                    COOKIES_SECURE_COL
-            };
-            final String selection = "(" + COOKIES_DOMAIN_COL
-                    + " GLOB '*' || ?)";
-            Cursor cursor = null;
-            try {
-                cursor = mDatabase.query(mTableNames[TABLE_COOKIES_ID],
-                        columns, selection, new String[] { domain }, null, null,
-                        null);
-                if (cursor.moveToFirst()) {
-                    int domainCol = cursor.getColumnIndex(COOKIES_DOMAIN_COL);
-                    int pathCol = cursor.getColumnIndex(COOKIES_PATH_COL);
-                    int nameCol = cursor.getColumnIndex(COOKIES_NAME_COL);
-                    int valueCol = cursor.getColumnIndex(COOKIES_VALUE_COL);
-                    int expiresCol = cursor.getColumnIndex(COOKIES_EXPIRES_COL);
-                    int secureCol = cursor.getColumnIndex(COOKIES_SECURE_COL);
-                    do {
-                        Cookie cookie = new Cookie();
-                        cookie.domain = cursor.getString(domainCol);
-                        cookie.path = cursor.getString(pathCol);
-                        cookie.name = cursor.getString(nameCol);
-                        cookie.value = cursor.getString(valueCol);
-                        if (cursor.isNull(expiresCol)) {
-                            cookie.expires = -1;
-                        } else {
-                            cookie.expires = cursor.getLong(expiresCol);
-                        }
-                        cookie.secure = cursor.getShort(secureCol) != 0;
-                        cookie.mode = Cookie.MODE_NORMAL;
-                        list.add(cookie);
-                    } while (cursor.moveToNext());
-                }
-            } catch (IllegalStateException e) {
-                Log.e(LOGTAG, "getCookiesForDomain", e);
-            } finally {
-                if (cursor != null) cursor.close();
-            }
-            return list;
-        }
-    }
-
-    /**
-     * Delete cookies which matches (domain, path, name).
-     *
-     * @param domain If it is null, nothing happens.
-     * @param path If it is null, all the cookies match (domain) will be
-     *            deleted.
-     * @param name If it is null, all the cookies match (domain, path) will be
-     *            deleted.
-     */
-    void deleteCookies(String domain, String path, String name) {
-        if (domain == null || !checkInitialized()) {
-            return;
-        }
-
-        synchronized (mCookieLock) {
-            final String where = "(" + COOKIES_DOMAIN_COL + " == ?) AND ("
-                    + COOKIES_PATH_COL + " == ?) AND (" + COOKIES_NAME_COL
-                    + " == ?)";
-            mDatabase.delete(mTableNames[TABLE_COOKIES_ID], where,
-                    new String[] { domain, path, name });
-        }
-    }
-
-    /**
-     * Add a cookie to the database
-     *
-     * @param cookie
-     */
-    void addCookie(Cookie cookie) {
-        if (cookie.domain == null || cookie.path == null || cookie.name == null
-                || !checkInitialized()) {
-            return;
-        }
-
-        synchronized (mCookieLock) {
-            ContentValues cookieVal = new ContentValues();
-            cookieVal.put(COOKIES_DOMAIN_COL, cookie.domain);
-            cookieVal.put(COOKIES_PATH_COL, cookie.path);
-            cookieVal.put(COOKIES_NAME_COL, cookie.name);
-            cookieVal.put(COOKIES_VALUE_COL, cookie.value);
-            if (cookie.expires != -1) {
-                cookieVal.put(COOKIES_EXPIRES_COL, cookie.expires);
-            }
-            cookieVal.put(COOKIES_SECURE_COL, cookie.secure);
-            mDatabase.insert(mTableNames[TABLE_COOKIES_ID], null, cookieVal);
-        }
-    }
-
-    /**
-     * Whether there is any cookies in the database
-     *
-     * @return TRUE if there is cookie.
-     */
-    boolean hasCookies() {
-        synchronized (mCookieLock) {
-            return hasEntries(TABLE_COOKIES_ID);
-        }
-    }
-
-    /**
-     * Clear cookie database
-     */
-    void clearCookies() {
-        if (!checkInitialized()) {
-            return;
-        }
-
-        synchronized (mCookieLock) {
-            mDatabase.delete(mTableNames[TABLE_COOKIES_ID], null, null);
-        }
-    }
-
-    /**
-     * Clear session cookies, which means cookie doesn't have EXPIRES.
-     */
-    void clearSessionCookies() {
-        if (!checkInitialized()) {
-            return;
-        }
-
-        final String sessionExpired = COOKIES_EXPIRES_COL + " ISNULL";
-        synchronized (mCookieLock) {
-            mDatabase.delete(mTableNames[TABLE_COOKIES_ID], sessionExpired,
-                    null);
-        }
-    }
-
-    /**
-     * Clear expired cookies
-     *
-     * @param now Time for now
-     */
-    void clearExpiredCookies(long now) {
-        if (!checkInitialized()) {
-            return;
-        }
-
-        final String expires = COOKIES_EXPIRES_COL + " <= ?";
-        synchronized (mCookieLock) {
-            mDatabase.delete(mTableNames[TABLE_COOKIES_ID], expires,
-                    new String[] { Long.toString(now) });
-        }
-    }
-
-    //
-    // cache functions
-    //
-
-    // only called from WebViewWorkerThread
-    boolean startCacheTransaction() {
-        if (++mCacheTransactionRefcount == 1) {
-            if (!Thread.currentThread().equals(
-                    WebViewWorker.getHandler().getLooper().getThread())) {
-                Log.w(LOGTAG, "startCacheTransaction should be called from "
-                        + "WebViewWorkerThread instead of from "
-                        + Thread.currentThread().getName());
-            }
-            mCacheDatabase.beginTransactionNonExclusive();
-            return true;
-        }
-        return false;
-    }
-
-    // only called from WebViewWorkerThread
-    boolean endCacheTransaction() {
-        if (--mCacheTransactionRefcount == 0) {
-            if (!Thread.currentThread().equals(
-                    WebViewWorker.getHandler().getLooper().getThread())) {
-                Log.w(LOGTAG, "endCacheTransaction should be called from "
-                        + "WebViewWorkerThread instead of from "
-                        + Thread.currentThread().getName());
-            }
-            try {
-                mCacheDatabase.setTransactionSuccessful();
-            } finally {
-                mCacheDatabase.endTransaction();
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Get a cache item.
-     * 
-     * @param url The url
-     * @return CacheResult The CacheManager.CacheResult
-     */
-    CacheResult getCache(String url) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (url == null || !checkInitialized()) {
-            return null;
-        }
-
-        Cursor cursor = null;
-        final String query = "SELECT filepath, lastmodify, etag, expires, "
-                + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
-                + "contentdisposition, crossdomain FROM cache WHERE url = ?";
-        try {
-            cursor = mCacheDatabase.rawQuery(query, new String[] { url });
-            if (cursor.moveToFirst()) {
-                CacheResult ret = new CacheResult();
-                ret.localPath = cursor.getString(0);
-                ret.lastModified = cursor.getString(1);
-                ret.etag = cursor.getString(2);
-                ret.expires = cursor.getLong(3);
-                ret.expiresString = cursor.getString(4);
-                ret.mimeType = cursor.getString(5);
-                ret.encoding = cursor.getString(6);
-                ret.httpStatusCode = cursor.getInt(7);
-                ret.location = cursor.getString(8);
-                ret.contentLength = cursor.getLong(9);
-                ret.contentdisposition = cursor.getString(10);
-                ret.crossDomain = cursor.getString(11);
-                return ret;
-            }
-        } catch (IllegalStateException e) {
-            Log.e(LOGTAG, "getCache", e);
-        } finally {
-            if (cursor != null) cursor.close();
-        }
-        return null;
-    }
-
-    /**
-     * Remove a cache item.
-     * 
-     * @param url The url
-     */
-    void removeCache(String url) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (url == null || !checkInitialized()) {
-            return;
-        }
-
-        mCacheDatabase.execSQL("DELETE FROM cache WHERE url = ?", new String[] { url });
-    }
-
-    /**
-     * Add or update a cache. CACHE_URL_COL is unique in the table.
-     *
-     * @param url The url
-     * @param c The CacheManager.CacheResult
-     */
-    void addCache(String url, CacheResult c) {
-        assert !JniUtil.useChromiumHttpStack();
-
-        if (url == null || !checkInitialized()) {
-            return;
-        }
-
-        mCacheInserter.prepareForInsert();
-        mCacheInserter.bind(mCacheUrlColIndex, url);
-        mCacheInserter.bind(mCacheFilePathColIndex, c.localPath);
-        mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified);
-        mCacheInserter.bind(mCacheETagColIndex, c.etag);
-        mCacheInserter.bind(mCacheExpiresColIndex, c.expires);
-        mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString);
-        mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType);
-        mCacheInserter.bind(mCacheEncodingColIndex, c.encoding);
-        mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode);
-        mCacheInserter.bind(mCacheLocationColIndex, c.location);
-        mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength);
-        mCacheInserter.bind(mCacheContentDispositionColIndex,
-                c.contentdisposition);
-        mCacheInserter.bind(mCacheCrossDomainColIndex, c.crossDomain);
-        mCacheInserter.execute();
-    }
-
-    /**
-     * Clear cache database
-     */
-    void clearCache() {
-        if (!checkInitialized()) {
-            return;
-        }
-
-        mCacheDatabase.delete("cache", null, null);
-    }
-
-    boolean hasCache() {
-        if (!checkInitialized()) {
-            return false;
-        }
-
-        Cursor cursor = null;
-        boolean ret = false;
-        try {
-            cursor = mCacheDatabase.query("cache", ID_PROJECTION,
-                    null, null, null, null, null);
-            ret = cursor.moveToFirst() == true;
-        } catch (IllegalStateException e) {
-            Log.e(LOGTAG, "hasCache", e);
-        } finally {
-            if (cursor != null) cursor.close();
-        }
-        return ret;
-    }
-
-    long getCacheTotalSize() {
-        if (mCacheDatabase == null) {
-            return 0;
-        }
-        long size = 0;
-        Cursor cursor = null;
-        final String query = "SELECT SUM(contentlength) as sum FROM cache";
-        try {
-            cursor = mCacheDatabase.rawQuery(query, null);
-            if (cursor.moveToFirst()) {
-                size = cursor.getLong(0);
-            }
-        } catch (IllegalStateException e) {
-            Log.e(LOGTAG, "getCacheTotalSize", e);
-        } finally {
-            if (cursor != null) cursor.close();
-        }
-        return size;
-    }
-
-    List<String> trimCache(long amount) {
-        ArrayList<String> pathList = new ArrayList<String>(100);
-        Cursor cursor = null;
-        final String query = "SELECT contentlength, filepath FROM cache ORDER BY expires ASC";
-        try {
-            cursor = mCacheDatabase.rawQuery(query, null);
-            if (cursor.moveToFirst()) {
-                int batchSize = 100;
-                StringBuilder pathStr = new StringBuilder(20 + 16 * batchSize);
-                pathStr.append("DELETE FROM cache WHERE filepath IN (?");
-                for (int i = 1; i < batchSize; i++) {
-                    pathStr.append(", ?");
-                }
-                pathStr.append(")");
-                SQLiteStatement statement = null;
-                try {
-                    statement = mCacheDatabase.compileStatement(
-                            pathStr.toString());
-                    // as bindString() uses 1-based index, initialize index to 1
-                    int index = 1;
-                    do {
-                        long length = cursor.getLong(0);
-                        if (length == 0) {
-                            continue;
-                        }
-                        amount -= length;
-                        String filePath = cursor.getString(1);
-                        statement.bindString(index, filePath);
-                        pathList.add(filePath);
-                        if (index++ == batchSize) {
-                            statement.execute();
-                            statement.clearBindings();
-                            index = 1;
-                        }
-                    } while (cursor.moveToNext() && amount > 0);
-                    if (index > 1) {
-                        // there may be old bindings from the previous statement
-                        // if index is less than batchSize, which is Ok.
-                        statement.execute();
-                    }
-                } catch (IllegalStateException e) {
-                    Log.e(LOGTAG, "trimCache SQLiteStatement", e);
-                } finally {
-                    if (statement != null) statement.close();
-                }
-            }
-        } catch (IllegalStateException e) {
-            Log.e(LOGTAG, "trimCache Cursor", e);
-        } finally {
-            if (cursor != null) cursor.close();
-        }
-        return pathList;
-    }
-
-    List<String> getAllCacheFileNames() {
-        ArrayList<String> pathList = null;
-        Cursor cursor = null;
-        try {
-            cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache",
-                    null);
-            if (cursor != null && cursor.moveToFirst()) {
-                pathList = new ArrayList<String>(cursor.getCount());
-                do {
-                    pathList.add(cursor.getString(0));
-                } while (cursor.moveToNext());
-            }
-        } catch (IllegalStateException e) {
-            Log.e(LOGTAG, "getAllCacheFileNames", e);
-        } finally {
-            if (cursor != null) cursor.close();
-        }
-        return pathList;
-    }
-
-    //
     // password functions
     //
 
diff --git a/core/java/android/webkit/WebViewWorker.java b/core/java/android/webkit/WebViewWorker.java
deleted file mode 100644
index 6a4ca29..0000000
--- a/core/java/android/webkit/WebViewWorker.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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 android.webkit;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import android.net.http.Headers;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-
-/**
- * WebViewWorker executes in a separate thread other than UI and WebViewCore. To
- * avoid blocking UI or WebKit's execution, the caller can send a message to
- * WebViewWorker.getHandler() and it will be handled in the WebViewWorkerThread.
- */
-final class WebViewWorker extends Handler {
-
-    private static final String THREAD_NAME = "WebViewWorkerThread";
-
-    private static WebViewWorker sWorkerHandler;
-
-    private static Map<LoadListener, CacheManager.CacheResult> mCacheResultMap
-            = new HashMap<LoadListener, CacheManager.CacheResult>();
-
-    /**
-     * Package level class to be used while creating a cache entry.
-     */
-    static class CacheCreateData {
-        LoadListener mListener;
-        String mUrl;
-        String mMimeType;
-        int mStatusCode;
-        long mPostId;
-        Headers mHeaders;
-    }
-
-    /**
-     * Package level class to be used while saving a cache entry.
-     */
-    static class CacheSaveData {
-        LoadListener mListener;
-        String mUrl;
-        long mPostId;
-    }
-
-    /**
-     * Package level class to be used while updating a cache entry's encoding.
-     */
-    static class CacheEncoding {
-        LoadListener mListener;
-        String mEncoding;
-    }
-
-    /**
-     * Package level class to be used while appending data to a cache entry.
-     */
-    static class CacheData {
-        LoadListener mListener;
-        ByteArrayBuilder.Chunk mChunk;
-    }
-
-    static synchronized WebViewWorker getHandler() {
-        if (sWorkerHandler == null) {
-            HandlerThread thread = new HandlerThread(THREAD_NAME,
-                    android.os.Process.THREAD_PRIORITY_DEFAULT
-                            + android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
-            thread.start();
-            sWorkerHandler = new WebViewWorker(thread.getLooper());
-        }
-        return sWorkerHandler;
-    }
-
-    private WebViewWorker(Looper looper) {
-        super(looper);
-    }
-
-    // trigger transaction once a minute
-    private static final int CACHE_TRANSACTION_TICKER_INTERVAL = 60 * 1000;
-
-    private static boolean mCacheTickersBlocked = true;
-
-    // message ids
-    static final int MSG_ADD_STREAMLOADER = 101;
-    static final int MSG_ADD_HTTPLOADER = 102;
-    static final int MSG_CREATE_CACHE = 103;
-    static final int MSG_UPDATE_CACHE_ENCODING = 104;
-    static final int MSG_APPEND_CACHE = 105;
-    static final int MSG_SAVE_CACHE = 106;
-    static final int MSG_REMOVE_CACHE = 107;
-    static final int MSG_TRIM_CACHE = 108;
-    static final int MSG_CLEAR_CACHE = 109;
-    static final int MSG_CACHE_TRANSACTION_TICKER = 110;
-    static final int MSG_PAUSE_CACHE_TRANSACTION = 111;
-    static final int MSG_RESUME_CACHE_TRANSACTION = 112;
-
-    @Override
-    public void handleMessage(Message msg) {
-        switch(msg.what) {
-            case MSG_ADD_STREAMLOADER: {
-                StreamLoader loader = (StreamLoader) msg.obj;
-                loader.load();
-                break;
-            }
-            case MSG_ADD_HTTPLOADER: {
-                FrameLoader loader = (FrameLoader) msg.obj;
-                loader.handleHTTPLoad();
-                break;
-            }
-            case MSG_CREATE_CACHE: {
-                assert !JniUtil.useChromiumHttpStack();
-                CacheCreateData data = (CacheCreateData) msg.obj;
-                CacheManager.CacheResult cache = CacheManager.createCacheFile(
-                        data.mUrl, data.mStatusCode, data.mHeaders,
-                        data.mMimeType, data.mPostId, false);
-                if (cache != null) {
-                    mCacheResultMap.put(data.mListener, cache);
-                } else {
-                    mCacheResultMap.remove(data.mListener);
-                }
-                break;
-            }
-            case MSG_UPDATE_CACHE_ENCODING: {
-                assert !JniUtil.useChromiumHttpStack();
-                CacheEncoding data = (CacheEncoding) msg.obj;
-                CacheManager.CacheResult cache = mCacheResultMap
-                        .get(data.mListener);
-                if (cache != null) {
-                    cache.encoding = data.mEncoding;
-                }
-                break;
-            }
-            case MSG_APPEND_CACHE: {
-                assert !JniUtil.useChromiumHttpStack();
-                CacheData data = (CacheData) msg.obj;
-                CacheManager.CacheResult cache = mCacheResultMap
-                        .get(data.mListener);
-                if (cache != null) {
-                    cache.contentLength += data.mChunk.mLength;
-                    if (cache.contentLength > CacheManager.CACHE_MAX_SIZE) {
-                        CacheManager.cleanupCacheFile(cache);
-                        mCacheResultMap.remove(data.mListener);
-                    } else {
-                        try {
-                            cache.outStream.write(data.mChunk.mArray, 0,
-                                    data.mChunk.mLength);
-                        } catch (IOException e) {
-                            CacheManager.cleanupCacheFile(cache);
-                            mCacheResultMap.remove(data.mListener);
-                        }
-                    }
-                }
-                data.mChunk.release();
-                break;
-            }
-            case MSG_SAVE_CACHE: {
-                assert !JniUtil.useChromiumHttpStack();
-                CacheSaveData data = (CacheSaveData) msg.obj;
-                CacheManager.CacheResult cache = mCacheResultMap
-                        .get(data.mListener);
-                if (cache != null) {
-                    CacheManager.saveCacheFile(data.mUrl, data.mPostId, cache);
-                    mCacheResultMap.remove(data.mListener);
-                }
-                break;
-            }
-            case MSG_REMOVE_CACHE: {
-                assert !JniUtil.useChromiumHttpStack();
-                LoadListener listener = (LoadListener) msg.obj;
-                CacheManager.CacheResult cache = mCacheResultMap.get(listener);
-                if (cache != null) {
-                    CacheManager.cleanupCacheFile(cache);
-                    mCacheResultMap.remove(listener);
-                }
-                break;
-            }
-            case MSG_TRIM_CACHE: {
-                assert !JniUtil.useChromiumHttpStack();
-                CacheManager.trimCacheIfNeeded();
-                break;
-            }
-            case MSG_CLEAR_CACHE: {
-                assert !JniUtil.useChromiumHttpStack();
-                CacheManager.clearCache();
-                break;
-            }
-            case MSG_CACHE_TRANSACTION_TICKER: {
-                assert !JniUtil.useChromiumHttpStack();
-                if (!mCacheTickersBlocked) {
-                    CacheManager.endTransaction();
-                    CacheManager.startTransaction();
-                    sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER,
-                            CACHE_TRANSACTION_TICKER_INTERVAL);
-                }
-                break;
-            }
-            case MSG_PAUSE_CACHE_TRANSACTION: {
-                assert !JniUtil.useChromiumHttpStack();
-                if (CacheManager.disableTransaction()) {
-                    mCacheTickersBlocked = true;
-                    removeMessages(MSG_CACHE_TRANSACTION_TICKER);
-                }
-                break;
-            }
-            case MSG_RESUME_CACHE_TRANSACTION: {
-                assert !JniUtil.useChromiumHttpStack();
-                if (CacheManager.enableTransaction()) {
-                    mCacheTickersBlocked = false;
-                    sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER,
-                            CACHE_TRANSACTION_TICKER_INTERVAL);
-                }
-                break;
-            }
-        }
-    }
-}
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index ae2d617..369e883 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -258,6 +258,7 @@
             // Remember the current zoom density before it gets changed.
             final float originalDefault = mDefaultScale;
             // set the new default density
+            mDisplayDensity = density;
             setDefaultZoomScale(density);
             float scaleChange = (originalDefault > 0.0) ? density / originalDefault: 1.0f;
             // adjust the scale if it falls outside the new zoom bounds
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index e94b1cb..5774440 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -36,6 +36,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.StateSet;
 import android.view.ActionMode;
@@ -87,6 +88,8 @@
         ViewTreeObserver.OnTouchModeChangeListener,
         RemoteViewsAdapter.RemoteAdapterConnectionCallback {
 
+    private static final String TAG = "AbsListView";
+
     /**
      * Disables the transcript mode.
      *
@@ -263,6 +266,11 @@
     private RemoteViewsAdapter mRemoteAdapter;
 
     /**
+     * If mAdapter != null, whenever this is true the adapter has stable IDs.
+     */
+    boolean mAdapterHasStableIds;
+
+    /**
      * This flag indicates the a full notify is required when the RemoteViewsAdapter connects
      */
     private boolean mDeferNotifyDataSetChanged = false;
@@ -812,7 +820,8 @@
     @Override
     public void setAdapter(ListAdapter adapter) {
         if (adapter != null) {
-            if (mChoiceMode != CHOICE_MODE_NONE && mAdapter.hasStableIds() &&
+            mAdapterHasStableIds = mAdapter.hasStableIds();
+            if (mChoiceMode != CHOICE_MODE_NONE && mAdapterHasStableIds &&
                     mCheckedIdStates == null) {
                 mCheckedIdStates = new LongSparseArray<Integer>();
             }
@@ -1011,6 +1020,7 @@
 
         if (mChoiceMode != CHOICE_MODE_NONE) {
             handled = true;
+            boolean checkedStateChanged = false;
 
             if (mChoiceMode == CHOICE_MODE_MULTIPLE ||
                     (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {
@@ -1033,6 +1043,7 @@
                             position, id, newValue);
                     dispatchItemClick = false;
                 }
+                checkedStateChanged = true;
             } else if (mChoiceMode == CHOICE_MODE_SINGLE) {
                 boolean newValue = !mCheckStates.get(position, false);
                 if (newValue) {
@@ -1046,11 +1057,12 @@
                 } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
                     mCheckedItemCount = 0;
                 }
+                checkedStateChanged = true;
             }
 
-            mDataChanged = true;
-            rememberSyncState();
-            requestLayout();
+            if (checkedStateChanged) {
+                updateOnScreenCheckedViews();
+            }
         }
 
         if (dispatchItemClick) {
@@ -1061,6 +1073,28 @@
     }
 
     /**
+     * Perform a quick, in-place update of the checked or activated state
+     * on all visible item views. This should only be called when a valid
+     * choice mode is active.
+     */
+    private void updateOnScreenCheckedViews() {
+        final int firstPos = mFirstPosition;
+        final int count = getChildCount();
+        final boolean useActivated = getContext().getApplicationInfo().targetSdkVersion
+                >= android.os.Build.VERSION_CODES.HONEYCOMB;
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            final int position = firstPos + i;
+
+            if (child instanceof Checkable) {
+                ((Checkable) child).setChecked(mCheckStates.get(position));
+            } else if (useActivated) {
+                child.setActivated(mCheckStates.get(position));
+            }
+        }
+    }
+
+    /**
      * @see #setChoiceMode(int)
      *
      * @return The current choice mode
@@ -2011,6 +2045,11 @@
         isScrap[0] = false;
         View scrapView;
 
+        scrapView = mRecycler.getTransientStateView(position);
+        if (scrapView != null) {
+            return scrapView;
+        }
+
         scrapView = mRecycler.getScrapView(position);
 
         View child;
@@ -2051,6 +2090,20 @@
             }
         }
 
+        if (mAdapterHasStableIds) {
+            final ViewGroup.LayoutParams vlp = child.getLayoutParams();
+            LayoutParams lp;
+            if (vlp == null) {
+                lp = (LayoutParams) generateDefaultLayoutParams();
+            } else if (!checkLayoutParams(vlp)) {
+                lp = (LayoutParams) generateLayoutParams(vlp);
+            } else {
+                lp = (LayoutParams) vlp;
+            }
+            lp.itemId = mAdapter.getItemId(position);
+            child.setLayoutParams(lp);
+        }
+
         return child;
     }
 
@@ -4543,7 +4596,9 @@
 
         if (count > 0) {
             detachViewsFromParent(start, count);
+            mRecycler.removeSkippedScrap();
         }
+
         offsetChildrenTopAndBottom(incrementalDeltaY);
 
         if (down) {
@@ -4853,6 +4908,9 @@
             confirmCheckedPositionsById();
         }
 
+        // TODO: In the future we can recycle these views based on stable ID instead.
+        mRecycler.clearTransientStateViews();
+
         if (count > 0) {
             int newPos;
             int selectablePos;
@@ -5357,6 +5415,12 @@
     }
 
     @Override
+    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+        return new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+    }
+
+    @Override
     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
         return new LayoutParams(p);
     }
@@ -5735,6 +5799,11 @@
          */
         int scrappedFromPosition;
 
+        /**
+         * The ID the view represents
+         */
+        long itemId = -1;
+
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
         }
@@ -5807,6 +5876,10 @@
 
         private ArrayList<View> mCurrentScrap;
 
+        private ArrayList<View> mSkippedScrap;
+
+        private SparseArray<View> mTransientStateViews;
+
         public void setViewTypeCount(int viewTypeCount) {
             if (viewTypeCount < 1) {
                 throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
@@ -5838,6 +5911,12 @@
                     }
                 }
             }
+            if (mTransientStateViews != null) {
+                final int count = mTransientStateViews.size();
+                for (int i = 0; i < count; i++) {
+                    mTransientStateViews.valueAt(i).forceLayout();
+                }
+            }
         }
 
         public boolean shouldRecycleViewType(int viewType) {
@@ -5864,6 +5943,9 @@
                     }
                 }
             }
+            if (mTransientStateViews != null) {
+                mTransientStateViews.clear();
+            }
         }
 
         /**
@@ -5910,6 +5992,28 @@
             return null;
         }
 
+        View getTransientStateView(int position) {
+            if (mTransientStateViews == null) {
+                return null;
+            }
+            final int index = mTransientStateViews.indexOfKey(position);
+            if (index < 0) {
+                return null;
+            }
+            final View result = mTransientStateViews.valueAt(index);
+            mTransientStateViews.removeAt(index);
+            return result;
+        }
+
+        /**
+         * Dump any currently saved views with transient state.
+         */
+        void clearTransientStateViews() {
+            if (mTransientStateViews != null) {
+                mTransientStateViews.clear();
+            }
+        }
+
         /**
          * @return A view from the ScrapViews collection. These are unordered.
          */
@@ -5926,7 +6030,7 @@
         }
 
         /**
-         * Put a view into the ScapViews list. These views are unordered.
+         * Put a view into the ScrapViews list. These views are unordered.
          *
          * @param scrap The view to add
          */
@@ -5936,23 +6040,32 @@
                 return;
             }
 
+            lp.scrappedFromPosition = position;
+
             // Don't put header or footer views or views that should be ignored
             // into the scrap heap
             int viewType = lp.viewType;
-            if (!shouldRecycleViewType(viewType)) {
-                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
-                    removeDetachedView(scrap, false);
+            final boolean scrapHasTransientState = scrap.hasTransientState();
+            if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
+                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) {
+                    if (mSkippedScrap == null) {
+                        mSkippedScrap = new ArrayList<View>();
+                    }
+                    mSkippedScrap.add(scrap);
+                }
+                if (scrapHasTransientState) {
+                    if (mTransientStateViews == null) {
+                        mTransientStateViews = new SparseArray<View>();
+                    }
+                    mTransientStateViews.put(position, scrap);
                 }
                 return;
             }
 
-            lp.scrappedFromPosition = position;
-
+            scrap.dispatchStartTemporaryDetach();
             if (mViewTypeCount == 1) {
-                scrap.dispatchStartTemporaryDetach();
                 mCurrentScrap.add(scrap);
             } else {
-                scrap.dispatchStartTemporaryDetach();
                 mScrapViews[viewType].add(scrap);
             }
 
@@ -5962,6 +6075,20 @@
         }
 
         /**
+         * Finish the removal of any views that skipped the scrap heap.
+         */
+        void removeSkippedScrap() {
+            if (mSkippedScrap == null) {
+                return;
+            }
+            final int count = mSkippedScrap.size();
+            for (int i = 0; i < count; i++) {
+                removeDetachedView(mSkippedScrap.get(i), false);
+            }
+            mSkippedScrap.clear();
+        }
+
+        /**
          * Move all views remaining in mActiveViews to mScrapViews.
          */
         void scrapActiveViews() {
@@ -5980,11 +6107,19 @@
 
                     activeViews[i] = null;
 
-                    if (!shouldRecycleViewType(whichScrap)) {
+                    final boolean scrapHasTransientState = victim.hasTransientState();
+                    if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) {
                         // Do not move views that should be ignored
-                        if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
+                        if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER ||
+                                scrapHasTransientState) {
                             removeDetachedView(victim, false);
                         }
+                        if (scrapHasTransientState) {
+                            if (mTransientStateViews == null) {
+                                mTransientStateViews = new SparseArray<View>();
+                            }
+                            mTransientStateViews.put(mFirstActivePosition + i, victim);
+                        }
                         continue;
                     }
 
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index f7a6b272..de11fe9 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1031,7 +1031,9 @@
     public void ensureImeVisible(boolean visible) {
         mPopup.setInputMethodMode(visible
                 ? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
-        showDropDown();
+        if (mPopup.isDropDownAlwaysVisible() || (mFilter != null && enoughToFilter())) {
+            showDropDown();
+        }
     }
 
     /**
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 5c7e5a3..dd53325 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -142,12 +142,8 @@
         resolvePadding();
     }
 
-    /**
-     * @hide
-     */
     @Override
-    protected void resolvePadding() {
-        super.resolvePadding();
+    public void onResolvePadding(int layoutDirection) {
         int newPadding = (mCheckMarkDrawable != null) ?
                 mCheckMarkWidth + mBasePadding : mBasePadding;
         mNeedRequestlayout |= (mPaddingRight != newPadding);
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 326587e..83aa8ba 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -56,7 +56,7 @@
     // Time it will take in ms for a pulled glow to decay to partial strength before release
     private static final int PULL_DECAY_TIME = 1000;
 
-    private static final float MAX_ALPHA = 0.8f;
+    private static final float MAX_ALPHA = 1.f;
     private static final float HELD_EDGE_ALPHA = 0.7f;
     private static final float HELD_EDGE_SCALE_Y = 0.5f;
     private static final float HELD_GLOW_ALPHA = 0.5f;
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 984ec79..fc08cc5 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -127,8 +127,7 @@
  *
  * GridLayout does not provide support for the principle of <em>weight</em>, as defined in
  * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible
- * to configure a GridLayout to distribute excess space in non-trivial proportions between
- * multiple rows or columns.
+ * to configure a GridLayout to distribute excess space between multiple components.
  * <p>
  * Some common use-cases may nevertheless be accommodated as follows.
  * To place equal amounts of space around a component in a cell group;
@@ -209,9 +208,9 @@
 
     static final String TAG = GridLayout.class.getName();
     static final boolean DEBUG = false;
-    static final int PRF = 1;
     static final int MAX_SIZE = 100000;
     static final int DEFAULT_CONTAINER_MARGIN = 0;
+    static final int UNINITIALIZED_HASH = 0;
 
     // Defaults
 
@@ -240,6 +239,7 @@
     boolean useDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
     int alignmentMode = DEFAULT_ALIGNMENT_MODE;
     int defaultGap;
+    int lastLayoutParamsHashCode = UNINITIALIZED_HASH;
 
     // Constructors
 
@@ -566,6 +566,10 @@
                 return FILL;
             case AXIS_SPECIFIED:
                 return CENTER;
+            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | RELATIVE_LAYOUT_DIRECTION):
+                return START;
+            case (AXIS_SPECIFIED | AXIS_PULL_AFTER | RELATIVE_LAYOUT_DIRECTION):
+                return END;
             default:
                 return UNDEFINED_ALIGNMENT;
         }
@@ -590,7 +594,8 @@
         Spec spec = horizontal ? p.columnSpec : p.rowSpec;
         Axis axis = horizontal ? horizontalAxis : verticalAxis;
         Interval span = spec.span;
-        boolean isAtEdge = leading ? (span.min == 0) : (span.max == axis.getCount());
+        boolean leading1 = (horizontal && isLayoutRtl()) ? !leading : leading;
+        boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());
 
         return getDefaultMargin(c, isAtEdge, horizontal, leading);
     }
@@ -663,7 +668,7 @@
         int[] maxSizes = new int[count];
 
         for (int i = 0, N = getChildCount(); i < N; i++) {
-            LayoutParams lp = getLayoutParams1(getChildAt(i));
+            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
 
             final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
             final Interval majorRange = majorSpec.span;
@@ -708,6 +713,7 @@
 
             minor = minor + minorSpan;
         }
+        lastLayoutParamsHashCode = computeLayoutParamsHashCode();
         invalidateStructure();
     }
 
@@ -728,8 +734,11 @@
         }
     }
 
-    private LayoutParams getLayoutParams1(View c) {
-        return (LayoutParams) c.getLayoutParams();
+    /** @hide */
+    @Override
+    protected void onSetLayoutParams(View child, ViewGroup.LayoutParams layoutParams) {
+        super.onSetLayoutParams(child, layoutParams);
+        invalidateStructure();
     }
 
     final LayoutParams getLayoutParams(View c) {
@@ -737,7 +746,7 @@
             validateLayoutParams();
             layoutParamsValid = true;
         }
-        return getLayoutParams1(c);
+        return (LayoutParams) c.getLayoutParams();
     }
 
     @Override
@@ -760,7 +769,12 @@
     private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
         int dx = getPaddingLeft();
         int dy = getPaddingTop();
-        graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint);
+        if (isLayoutRtl()) {
+            int width = getWidth();
+            graphics.drawLine(width - dx - x1, dy + y1, width - dx - x2, dy + y2, paint);
+        } else {
+            graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint);
+        }
     }
 
     private static void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint) {
@@ -849,12 +863,29 @@
         }
     }
 
-    // Measurement
-
-    final boolean isGone(View c) {
-        return c.getVisibility() == View.GONE;
+    private int computeLayoutParamsHashCode() {
+        int result = 1;
+        for (int i = 0, N = getChildCount(); i < N; i++) {
+            View c = getChildAt(i);
+            if (c.getVisibility() == View.GONE) continue;
+            LayoutParams lp = (LayoutParams) c.getLayoutParams();
+            result = 31 * result + lp.hashCode();
+        }
+        return result;
     }
 
+    private void checkForLayoutParamsModification() {
+        int layoutParamsHashCode = computeLayoutParamsHashCode();
+        if (lastLayoutParamsHashCode != UNINITIALIZED_HASH &&
+                lastLayoutParamsHashCode != layoutParamsHashCode) {
+            invalidateStructure();
+            Log.w(TAG, "The fields of some layout parameters were modified in between layout " +
+                    "operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
+        }
+    }
+
+    // Measurement
+
     private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
                                           int childWidth, int childHeight) {
         int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
@@ -867,7 +898,7 @@
     private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
         for (int i = 0, N = getChildCount(); i < N; i++) {
             View c = getChildAt(i);
-            if (isGone(c)) continue;
+            if (c.getVisibility() == View.GONE) continue;
             LayoutParams lp = getLayoutParams(c);
             if (firstPass) {
                 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
@@ -892,6 +923,8 @@
 
     @Override
     protected void onMeasure(int widthSpec, int heightSpec) {
+        checkForLayoutParamsModification();
+
         /** If we have been called by {@link View#measure(int, int)}, one of width or height
          *  is  likely to have changed. We must invalidate if so. */
         invalidateValues();
@@ -922,16 +955,12 @@
                 resolveSizeAndState(measuredHeight, heightSpec, 0));
     }
 
-    private int protect(int alignment) {
-        return (alignment == UNDEFINED) ? 0 : alignment;
-    }
-
     private int getMeasurement(View c, boolean horizontal) {
         return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
     }
 
     final int getMeasurementIncludingMargin(View c, boolean horizontal) {
-        if (isGone(c)) {
+        if (c.getVisibility() == View.GONE) {
             return 0;
         }
         return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
@@ -945,7 +974,7 @@
 
     final Alignment getAlignment(Alignment alignment, boolean horizontal) {
         return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
-                (horizontal ? LEFT : BASELINE);
+                (horizontal ? START : BASELINE);
     }
 
     // Layout container
@@ -964,6 +993,8 @@
      */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        checkForLayoutParamsModification();
+
         int targetWidth = right - left;
         int targetHeight = bottom - top;
 
@@ -980,7 +1011,7 @@
 
         for (int i = 0, N = getChildCount(); i < N; i++) {
             View c = getChildAt(i);
-            if (isGone(c)) continue;
+            if (c.getVisibility() == View.GONE) continue;
             LayoutParams lp = getLayoutParams(c);
             Spec columnSpec = lp.columnSpec;
             Spec rowSpec = lp.rowSpec;
@@ -1003,42 +1034,31 @@
             Alignment hAlign = getAlignment(columnSpec.alignment, true);
             Alignment vAlign = getAlignment(rowSpec.alignment, false);
 
-            int dx, dy;
-
-            Bounds colBounds = horizontalAxis.getGroupBounds().getValue(i);
-            Bounds rowBounds = verticalAxis.getGroupBounds().getValue(i);
+            Bounds boundsX = horizontalAxis.getGroupBounds().getValue(i);
+            Bounds boundsY = verticalAxis.getGroupBounds().getValue(i);
 
             // Gravity offsets: the location of the alignment group relative to its cell group.
-            //noinspection NullableProblems
-            int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(true)));
-            //noinspection NullableProblems
-            int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(true)));
+            int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
+            int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));
 
             int leftMargin = getMargin(c, true, true);
             int topMargin = getMargin(c, false, true);
             int rightMargin = getMargin(c, true, false);
             int bottomMargin = getMargin(c, false, false);
 
-            // Same calculation as getMeasurementIncludingMargin()
-            int mWidth = leftMargin + pWidth + rightMargin;
-            int mHeight = topMargin + pHeight + bottomMargin;
-
             // Alignment offsets: the location of the view relative to its alignment group.
-            int a2vx = colBounds.getOffset(c, hAlign, mWidth);
-            int a2vy = rowBounds.getOffset(c, vAlign, mHeight);
+            int alignmentOffsetX = boundsX.getOffset(c, hAlign, leftMargin + pWidth + rightMargin);
+            int alignmentOffsetY = boundsY.getOffset(c, vAlign, topMargin + pHeight + bottomMargin);
 
-            dx = c2ax + a2vx + leftMargin;
-            dy = c2ay + a2vy + topMargin;
+            int width = hAlign.getSizeInCell(c, pWidth, cellWidth - leftMargin - rightMargin);
+            int height = vAlign.getSizeInCell(c, pHeight, cellHeight - topMargin - bottomMargin);
 
-            cellWidth -= leftMargin + rightMargin;
-            cellHeight -= topMargin + bottomMargin;
+            int dx = x1 + gravityOffsetX + alignmentOffsetX;
 
-            int type = PRF;
-            int width = hAlign.getSizeInCell(c, pWidth, cellWidth, type);
-            int height = vAlign.getSizeInCell(c, pHeight, cellHeight, type);
+            int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx :
+                    targetWidth - width - paddingRight - rightMargin - dx;
+            int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
 
-            int cx = paddingLeft + x1 + dx;
-            int cy = paddingTop + y1 + dy;
             if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
                 c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
             }
@@ -1517,7 +1537,7 @@
             int[] margins = leading ? leadingMargins : trailingMargins;
             for (int i = 0, N = getChildCount(); i < N; i++) {
                 View c = getChildAt(i);
-                if (isGone(c)) continue;
+                if (c.getVisibility() == View.GONE) continue;
                 LayoutParams lp = getLayoutParams(c);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
                 Interval span = spec.span;
@@ -1657,7 +1677,7 @@
      * each cell group. The fundamental parameters associated with each cell group are
      * gathered into their vertical and horizontal components and stored
      * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
-     * {@link android.widget.GridLayout.Spec Specs} are immutable structures
+     * {@link GridLayout.Spec Specs} are immutable structures
      * and may be shared between the layout parameters of different children.
      * <p>
      * The row and column specs contain the leading and trailing indices along each axis
@@ -1710,7 +1730,7 @@
      *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
      *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
      *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
-     *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #LEFT} </li>
+     *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
      * </ul>
      *
      * See {@link GridLayout} for a more complete description of the conventions
@@ -1757,12 +1777,28 @@
         /**
          * The spec that defines the vertical characteristics of the cell group
          * described by these layout parameters.
+         * If an assignment is made to this field after a measurement or layout operation
+         * has already taken place, a call to
+         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
+         * must be made to notify GridLayout of the change. GridLayout is normally able
+         * to detect when code fails to observe this rule, issue a warning and take steps to
+         * compensate for the omission. This facility is implemented on a best effort basis
+         * and should not be relied upon in production code - so it is best to include the above
+         * calls to remove the warnings as soon as it is practical.
          */
         public Spec rowSpec = Spec.UNDEFINED;
 
         /**
          * The spec that defines the horizontal characteristics of the cell group
          * described by these layout parameters.
+         * If an assignment is made to this field after a measurement or layout operation
+         * has already taken place, a call to
+         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
+         * must be made to notify GridLayout of the change. GridLayout is normally able
+         * to detect when code fails to observe this rule, issue a warning and take steps to
+         * compensate for the omission. This facility is implemented on a best effort basis
+         * and should not be relied upon in production code - so it is best to include the above
+         * calls to remove the warnings as soon as it is practical.
          */
         public Spec columnSpec = Spec.UNDEFINED;
 
@@ -1883,7 +1919,7 @@
 
         /**
          * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
-         * See {@link android.view.Gravity}.
+         * See {@link Gravity}.
          *
          * @param gravity the new gravity value
          *
@@ -1907,6 +1943,26 @@
         final void setColumnSpecSpan(Interval span) {
             columnSpec = columnSpec.copyWriteSpan(span);
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            LayoutParams that = (LayoutParams) o;
+
+            if (!columnSpec.equals(that.columnSpec)) return false;
+            if (!rowSpec.equals(that.rowSpec)) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = rowSpec.hashCode();
+            result = 31 * result + columnSpec.hashCode();
+            return result;
+        }
     }
 
     /*
@@ -2049,7 +2105,7 @@
     /*
     For each group (with a given alignment) we need to store the amount of space required
     before the alignment point and the amount of space required after it. One side of this
-    calculation is always 0 for LEADING and TRAILING alignments but we don't make use of this.
+    calculation is always 0 for START and END alignments but we don't make use of this.
     For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
     simple optimisations are possible.
 
@@ -2353,8 +2409,8 @@
      * group is specified by the two alignments which act along each axis independently.
      * <p>
      *  The GridLayout class defines the most common alignments used in general layout:
-     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #CENTER}, {@link
-     * #BASELINE} and {@link #FILL}.
+     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
+     * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
      */
     /*
      * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
@@ -2368,6 +2424,8 @@
         Alignment() {
         }
 
+        abstract int getGravityOffset(View view, int cellDelta);
+
         /**
          * Returns an alignment value. In the case of vertical alignments the value
          * returned should indicate the distance from the top of the view to the
@@ -2390,12 +2448,9 @@
          * @param view              the view to which this alignment should be applied
          * @param viewSize          the measured size of the view
          * @param cellSize          the size of the cell into which this view will be placed
-         * @param measurementType   This parameter is currently unused as GridLayout only supports
-         *                          one type of measurement: {@link View#measure(int, int)}.
-         *
          * @return the aligned size
          */
-        int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
+        int getSizeInCell(View view, int viewSize, int cellSize) {
             return viewSize;
         }
 
@@ -2405,18 +2460,44 @@
     }
 
     static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
+        @Override
+        int getGravityOffset(View view, int cellDelta) {
+            return UNDEFINED;
+        }
+
+        @Override
         public int getAlignmentValue(View view, int viewSize) {
             return UNDEFINED;
         }
     };
 
+    /**
+     * Indicates that a view should be aligned with the <em>start</em>
+     * edges of the other views in its cell group.
+     */
     private static final Alignment LEADING = new Alignment() {
+        @Override
+        int getGravityOffset(View view, int cellDelta) {
+            return 0;
+        }
+
+        @Override
         public int getAlignmentValue(View view, int viewSize) {
             return 0;
         }
     };
 
+    /**
+     * Indicates that a view should be aligned with the <em>end</em>
+     * edges of the other views in its cell group.
+     */
     private static final Alignment TRAILING = new Alignment() {
+        @Override
+        int getGravityOffset(View view, int cellDelta) {
+            return cellDelta;
+        }
+
+        @Override
         public int getAlignmentValue(View view, int viewSize) {
             return viewSize;
         }
@@ -2435,16 +2516,42 @@
     public static final Alignment BOTTOM = TRAILING;
 
     /**
-     * Indicates that a view should be aligned with the <em>right</em>
+     * Indicates that a view should be aligned with the <em>start</em>
      * edges of the other views in its cell group.
      */
-    public static final Alignment RIGHT = TRAILING;
+    public static final Alignment START = LEADING;
+
+    /**
+     * Indicates that a view should be aligned with the <em>end</em>
+     * edges of the other views in its cell group.
+     */
+    public static final Alignment END = TRAILING;
+
+    private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
+        return new Alignment() {
+            @Override
+            int getGravityOffset(View view, int cellDelta) {
+                return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta);
+            }
+
+            @Override
+            public int getAlignmentValue(View view, int viewSize) {
+                return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize);
+            }
+        };
+    }
 
     /**
      * Indicates that a view should be aligned with the <em>left</em>
      * edges of the other views in its cell group.
      */
-    public static final Alignment LEFT = LEADING;
+    public static final Alignment LEFT = createSwitchingAlignment(START, END);
+
+    /**
+     * Indicates that a view should be aligned with the <em>right</em>
+     * edges of the other views in its cell group.
+     */
+    public static final Alignment RIGHT = createSwitchingAlignment(END, START);
 
     /**
      * Indicates that a view should be <em>centered</em> with the other views in its cell group.
@@ -2452,6 +2559,12 @@
      * LayoutParams#columnSpec columnSpecs}.
      */
     public static final Alignment CENTER = new Alignment() {
+        @Override
+        int getGravityOffset(View view, int cellDelta) {
+            return cellDelta >> 1;
+        }
+
+        @Override
         public int getAlignmentValue(View view, int viewSize) {
             return viewSize >> 1;
         }
@@ -2465,10 +2578,13 @@
      * @see View#getBaseline()
      */
     public static final Alignment BASELINE = new Alignment() {
+        @Override
+        int getGravityOffset(View view, int cellDelta) {
+            return 0; // baseline gravity is top
+        }
+
+        @Override
         public int getAlignmentValue(View view, int viewSize) {
-            if (view == null) {
-                return UNDEFINED;
-            }
             int baseline = view.getBaseline();
             return (baseline == -1) ? UNDEFINED : baseline;
         }
@@ -2515,12 +2631,18 @@
      * {@link LayoutParams#columnSpec columnSpecs}.
      */
     public static final Alignment FILL = new Alignment() {
+        @Override
+        int getGravityOffset(View view, int cellDelta) {
+            return 0;
+        }
+
+        @Override
         public int getAlignmentValue(View view, int viewSize) {
             return UNDEFINED;
         }
 
         @Override
-        public int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
+        public int getSizeInCell(View view, int viewSize, int cellSize) {
             return cellSize;
         }
     };
@@ -2530,6 +2652,5 @@
     }
 
     private static final int INFLEXIBLE = 0;
-
     private static final int CAN_STRETCH = 2;
 }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index be2df8e..0dedf8b 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1029,10 +1029,9 @@
         if (count > 0) {
             final View child = obtainView(0, mIsScrap);
 
-            AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams();
+            AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
             if (p == null) {
-                p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                        ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+                p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
                 child.setLayoutParams(p);
             }
             p.viewType = mAdapter.getItemViewType(0);
@@ -1205,6 +1204,7 @@
             // Clear out old views
             //removeAllViewsInLayout();
             detachAllViewsFromParent();
+            recycleBin.removeSkippedScrap();
 
             switch (mLayoutMode) {
             case LAYOUT_SET_SELECTION:
@@ -1361,10 +1361,9 @@
 
         // Respect layout params that are already in the view. Otherwise make
         // some up...
-        AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams();
+        AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
         if (p == null) {
-            p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+            p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
         }
         p.viewType = mAdapter.getItemViewType(position);
 
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 67fd059..71700b3 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1163,8 +1163,7 @@
     private void measureScrapChild(View child, int position, int widthMeasureSpec) {
         LayoutParams p = (LayoutParams) child.getLayoutParams();
         if (p == null) {
-            p = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+            p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
             child.setLayoutParams(p);
         }
         p.viewType = mAdapter.getItemViewType(position);
@@ -1591,6 +1590,7 @@
 
             // Clear out old views
             detachAllViewsFromParent();
+            recycleBin.removeSkippedScrap();
 
             switch (mLayoutMode) {
             case LAYOUT_SET_SELECTION:
@@ -1807,8 +1807,7 @@
         // noinspection unchecked
         AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
         if (p == null) {
-            p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+            p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
         }
         p.viewType = mAdapter.getItemViewType(position);
 
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 570f0f9..a1cf205 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -24,6 +24,7 @@
 import android.text.method.WordIterator;
 import android.text.style.SpellCheckSpan;
 import android.text.style.SuggestionSpan;
+import android.view.textservice.SentenceSuggestionsInfo;
 import android.view.textservice.SpellCheckerSession;
 import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
 import android.view.textservice.SuggestionsInfo;
@@ -277,9 +278,9 @@
     }
 
     @Override
-    public void onGetSuggestionsForSentence(SuggestionsInfo[] results) {
+    public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
         // TODO: Handle the position and length for each suggestion
-        onGetSuggestions(results);
+        // do nothing for now
     }
 
     @Override
@@ -426,7 +427,8 @@
                     }
 
                     // A new word has been created across the interval boundaries with this edit.
-                    // Previous spans (ended on start / started on end) removed, not valid anymore
+                    // The previous spans (that ended on start / started on end) are not valid
+                    // anymore and must be removed.
                     if (wordStart < start && wordEnd > start) {
                         removeSpansAt(editable, start, spellCheckSpans);
                         removeSpansAt(editable, start, suggestionSpans);
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index f5d3746..b870cee 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -735,8 +735,7 @@
          * @param heightAttr the height attribute to fetch
          */
         @Override
-        protected void setBaseAttributes(TypedArray a,
-                int widthAttr, int heightAttr) {
+        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
             this.width = MATCH_PARENT;
             if (a.hasValue(heightAttr)) {
                 this.height = a.getLayoutDimension(heightAttr, "layout_height");
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 164bc64..a4087d5 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -232,34 +232,6 @@
     static final String LOG_TAG = "TextView";
     static final boolean DEBUG_EXTRACT = false;
 
-    private static final int PRIORITY = 100;
-    private int mCurrentAlpha = 255;
-
-    final int[] mTempCoords = new int[2];
-    Rect mTempRect;
-
-    private ColorStateList mTextColor;
-    private int mCurTextColor;
-    private ColorStateList mHintTextColor;
-    private ColorStateList mLinkTextColor;
-    private int mCurHintTextColor;
-    private boolean mFreezesText;
-    private boolean mFrozenWithFocus;
-    private boolean mTemporaryDetach;
-    private boolean mDispatchTemporaryDetach;
-
-    private boolean mDiscardNextActionUp = false;
-    private boolean mIgnoreActionUpEvent = false;
-
-    private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
-    private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
-
-    private float mShadowRadius, mShadowDx, mShadowDy;
-
-    private boolean mPreDrawRegistered;
-
-    private TextUtils.TruncateAt mEllipsize = null;
-
     // Enum for the "typeface" XML parameter.
     // TODO: How can we get this from the XML instead of hardcoding it here?
     private static final int SANS = 1;
@@ -271,122 +243,10 @@
     private static final int SIGNED = 2;
     private static final int DECIMAL = 4;
 
-    static class Drawables {
-        final Rect mCompoundRect = new Rect();
-        Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight,
-                mDrawableStart, mDrawableEnd;
-        int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight,
-                mDrawableSizeStart, mDrawableSizeEnd;
-        int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight,
-                mDrawableHeightStart, mDrawableHeightEnd;
-        int mDrawablePadding;
-    }
-    private Drawables mDrawables;
-
-    private DisplayList mTextDisplayList;
-    private boolean mTextDisplayListIsValid;
-
-    private CharSequence mError;
-    private boolean mErrorWasChanged;
-    private ErrorPopup mPopup;
-    /**
-     * This flag is set if the TextView tries to display an error before it
-     * is attached to the window (so its position is still unknown).
-     * It causes the error to be shown later, when onAttachedToWindow()
-     * is called.
-     */
-    private boolean mShowErrorAfterAttach;
-
-    private CharWrapper mCharWrapper = null;
-
-    private boolean mSelectionMoved = false;
-    private boolean mTouchFocusSelected = false;
-
-    private Marquee mMarquee;
-    private boolean mRestartMarquee;
-
-    private int mMarqueeRepeatLimit = 3;
-
-    static class InputContentType {
-        int imeOptions = EditorInfo.IME_NULL;
-        String privateImeOptions;
-        CharSequence imeActionLabel;
-        int imeActionId;
-        Bundle extras;
-        OnEditorActionListener onEditorActionListener;
-        boolean enterDown;
-    }
-    InputContentType mInputContentType;
-
-    static class InputMethodState {
-        Rect mCursorRectInWindow = new Rect();
-        RectF mTmpRectF = new RectF();
-        float[] mTmpOffset = new float[2];
-        ExtractedTextRequest mExtracting;
-        final ExtractedText mTmpExtracted = new ExtractedText();
-        int mBatchEditNesting;
-        boolean mCursorChanged;
-        boolean mSelectionModeChanged;
-        boolean mContentChanged;
-        int mChangedStart, mChangedEnd, mChangedDelta;
-    }
-    InputMethodState mInputMethodState;
-
-    private int mTextSelectHandleLeftRes;
-    private int mTextSelectHandleRightRes;
-    private int mTextSelectHandleRes;
-
-    private int mTextEditSuggestionItemLayout;
-    private SuggestionsPopupWindow mSuggestionsPopupWindow;
-    private SuggestionRangeSpan mSuggestionRangeSpan;
-    private Runnable mShowSuggestionRunnable;
-
-    private int mCursorDrawableRes;
-    private final Drawable[] mCursorDrawable = new Drawable[2];
-    private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split)
-
-    private Drawable mSelectHandleLeft;
-    private Drawable mSelectHandleRight;
-    private Drawable mSelectHandleCenter;
-
-    // Global listener that detects changes in the global position of the TextView
-    private PositionListener mPositionListener;
-
-    private float mLastDownPositionX, mLastDownPositionY;
-    private Callback mCustomSelectionActionModeCallback;
-
-    // Set when this TextView gained focus with some text selected. Will start selection mode.
-    private boolean mCreatedWithASelection = false;
-
-    private WordIterator mWordIterator;
-
-    private SpellChecker mSpellChecker;
-
-    // The alignment to pass to Layout, or null if not resolved.
-    private Layout.Alignment mLayoutAlignment;
-
-    // The default value for mTextAlign.
-    private TextAlign mTextAlign = TextAlign.INHERIT;
-
-    private static enum TextAlign {
+    private static enum TEXT_ALIGN {
         INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END;
     }
 
-    private boolean mResolvedDrawables = false;
-
-    /**
-     * On some devices the fading edges add a performance penalty if used
-     * extensively in the same layout. This mode indicates how the marquee
-     * is currently being shown, if applicable. (mEllipsize will == MARQUEE)
-     */
-    private int mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
-
-    /**
-     * When mMarqueeFadeMode is not MARQUEE_FADE_NORMAL, this stores
-     * the layout that should be used when the mode switches.
-     */
-    private Layout mSavedMarqueeModeLayout;
-
     /**
      * Draw marquee text with fading edges as usual
      */
@@ -403,6 +263,169 @@
      */
     private static final int MARQUEE_FADE_SWITCH_SHOW_FADE = 2;
 
+    private static final int LINES = 1;
+    private static final int EMS = LINES;
+    private static final int PIXELS = 2;
+
+    private static final RectF TEMP_RECTF = new RectF();
+    private static final float[] TEMP_POSITION = new float[2];
+
+    // XXX should be much larger
+    private static final int VERY_WIDE = 1024*1024;
+    private static final int BLINK = 500;
+    private static final int ANIMATED_SCROLL_GAP = 250;
+
+    private static final InputFilter[] NO_FILTERS = new InputFilter[0];
+    private static final Spanned EMPTY_SPANNED = new SpannedString("");
+
+    private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
+    private static final int CHANGE_WATCHER_PRIORITY = 100;
+
+    // New state used to change background based on whether this TextView is multiline.
+    private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline };
+
+    // System wide time for last cut or copy action.
+    private static long LAST_CUT_OR_COPY_TIME;
+
+    private int mCurrentAlpha = 255;
+
+    private ColorStateList mTextColor;
+    private ColorStateList mHintTextColor;
+    private ColorStateList mLinkTextColor;
+    private int mCurTextColor;
+    private int mCurHintTextColor;
+    private boolean mFreezesText;
+    private boolean mTemporaryDetach;
+    private boolean mDispatchTemporaryDetach;
+
+    private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
+    private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
+
+    private float mShadowRadius, mShadowDx, mShadowDy;
+
+    private boolean mPreDrawRegistered;
+
+    private TextUtils.TruncateAt mEllipsize;
+
+    static class Drawables {
+        final Rect mCompoundRect = new Rect();
+        Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight,
+                mDrawableStart, mDrawableEnd;
+        int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight,
+                mDrawableSizeStart, mDrawableSizeEnd;
+        int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight,
+                mDrawableHeightStart, mDrawableHeightEnd;
+        int mDrawablePadding;
+    }
+    private Drawables mDrawables;
+
+    private CharWrapper mCharWrapper;
+
+    private Marquee mMarquee;
+    private boolean mRestartMarquee;
+
+    private int mMarqueeRepeatLimit = 3;
+
+    // The alignment to pass to Layout, or null if not resolved.
+    private Layout.Alignment mLayoutAlignment;
+
+    // The default value for mTextAlign.
+    private TEXT_ALIGN mTextAlign = TEXT_ALIGN.INHERIT;
+
+    private boolean mResolvedDrawables;
+
+    /**
+     * On some devices the fading edges add a performance penalty if used
+     * extensively in the same layout. This mode indicates how the marquee
+     * is currently being shown, if applicable. (mEllipsize will == MARQUEE)
+     */
+    private int mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
+
+    /**
+     * When mMarqueeFadeMode is not MARQUEE_FADE_NORMAL, this stores
+     * the layout that should be used when the mode switches.
+     */
+    private Layout mSavedMarqueeModeLayout;
+
+    @ViewDebug.ExportedProperty(category = "text")
+    private CharSequence mText;
+    private CharSequence mTransformed;
+    private BufferType mBufferType = BufferType.NORMAL;
+
+    private CharSequence mHint;
+    private Layout mHintLayout;
+
+    private MovementMethod mMovement;
+
+    private TransformationMethod mTransformation;
+    private boolean mAllowTransformationLengthChange;
+    private ChangeWatcher mChangeWatcher;
+
+    private ArrayList<TextWatcher> mListeners;
+
+    // display attributes
+    private final TextPaint mTextPaint;
+    private boolean mUserSetTextScaleX;
+    private Layout mLayout;
+
+    private int mGravity = Gravity.TOP | Gravity.START;
+    private boolean mHorizontallyScrolling;
+
+    private int mAutoLinkMask;
+    private boolean mLinksClickable = true;
+
+    private float mSpacingMult = 1.0f;
+    private float mSpacingAdd = 0.0f;
+
+    private int mMaximum = Integer.MAX_VALUE;
+    private int mMaxMode = LINES;
+    private int mMinimum = 0;
+    private int mMinMode = LINES;
+
+    private int mOldMaximum = mMaximum;
+    private int mOldMaxMode = mMaxMode;
+
+    private int mMaxWidth = Integer.MAX_VALUE;
+    private int mMaxWidthMode = PIXELS;
+    private int mMinWidth = 0;
+    private int mMinWidthMode = PIXELS;
+
+    private boolean mSingleLine;
+    private int mDesiredHeightAtMeasure = -1;
+    private boolean mIncludePad = true;
+
+    // tmp primitives, so we don't alloc them on each draw
+    private Rect mTempRect;
+    private long mLastScroll;
+    private Scroller mScroller;
+
+    private BoringLayout.Metrics mBoring, mHintBoring;
+    private BoringLayout mSavedLayout, mSavedHintLayout;
+
+    private TextDirectionHeuristic mTextDir;
+
+    private InputFilter[] mFilters = NO_FILTERS;
+
+    // Although these fields are specific to editable text, they are not added to Editor because
+    // they are defined by the TextView's style and are theme-dependent.
+    private int mHighlightColor = 0x6633B5E5;
+    private int mCursorDrawableRes;
+    // These four fields, could be moved to Editor, since we know their default values and we
+    // could condition the creation of the Editor to a non standard value. This is however
+    // brittle since the hardcoded values here (such as
+    // com.android.internal.R.drawable.text_select_handle_left) would have to be updated if the
+    // default style is modified.
+    private int mTextSelectHandleLeftRes;
+    private int mTextSelectHandleRightRes;
+    private int mTextSelectHandleRes;
+    private int mTextEditSuggestionItemLayout;
+
+    /**
+     * EditText specific data, created on demand when one of the Editor fields is used.
+     * See {@link #createEditorIfNeeded(String)}.
+     */
+    private Editor mEditor;
+
     /*
      * Kick-start the font cache for the zygote process (to pay the cost of
      * initializing freetype for our default font only once).
@@ -454,14 +477,8 @@
         mTextPaint.density = res.getDisplayMetrics().density;
         mTextPaint.setCompatibilityScaling(compat.applicationScale);
 
-        // If we get the paint from the skin, we should set it to left, since
-        // the layout always wants it to be left.
-        // mTextPaint.setTextAlign(Paint.Align.LEFT);
-
-        mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
-
         mMovement = getDefaultMovementMethod();
+
         mTransformation = null;
 
         int textColorHighlight = 0;
@@ -608,12 +625,6 @@
                 mLinksClickable = a.getBoolean(attr, true);
                 break;
 
-//            TODO uncomment when this attribute is made public in the next release
-//                 also add TextView_showSoftInputOnFocus to the list of attributes above
-//            case com.android.internal.R.styleable.TextView_showSoftInputOnFocus:
-//                setShowSoftInputOnFocus(a.getBoolean(attr, true));
-//                break;
-
             case com.android.internal.R.styleable.TextView_drawableLeft:
                 drawableLeft = a.getDrawable(attr);
                 break;
@@ -805,30 +816,33 @@
                 break;
 
             case com.android.internal.R.styleable.TextView_inputType:
-                inputType = a.getInt(attr, mInputType);
+                inputType = a.getInt(attr, EditorInfo.TYPE_NULL);
                 break;
 
             case com.android.internal.R.styleable.TextView_imeOptions:
-                if (mInputContentType == null) {
-                    mInputContentType = new InputContentType();
+                createEditorIfNeeded("IME options specified in constructor");
+                if (getEditor().mInputContentType == null) {
+                    getEditor().mInputContentType = new InputContentType();
                 }
-                mInputContentType.imeOptions = a.getInt(attr,
-                        mInputContentType.imeOptions);
+                getEditor().mInputContentType.imeOptions = a.getInt(attr,
+                        getEditor().mInputContentType.imeOptions);
                 break;
 
             case com.android.internal.R.styleable.TextView_imeActionLabel:
-                if (mInputContentType == null) {
-                    mInputContentType = new InputContentType();
+                createEditorIfNeeded("IME action label specified in constructor");
+                if (getEditor().mInputContentType == null) {
+                    getEditor().mInputContentType = new InputContentType();
                 }
-                mInputContentType.imeActionLabel = a.getText(attr);
+                getEditor().mInputContentType.imeActionLabel = a.getText(attr);
                 break;
 
             case com.android.internal.R.styleable.TextView_imeActionId:
-                if (mInputContentType == null) {
-                    mInputContentType = new InputContentType();
+                createEditorIfNeeded("IME action id specified in constructor");
+                if (getEditor().mInputContentType == null) {
+                    getEditor().mInputContentType = new InputContentType();
                 }
-                mInputContentType.imeActionId = a.getInt(attr,
-                        mInputContentType.imeActionId);
+                getEditor().mInputContentType.imeActionId = a.getInt(attr,
+                        getEditor().mInputContentType.imeActionId);
                 break;
 
             case com.android.internal.R.styleable.TextView_privateImeOptions:
@@ -866,7 +880,7 @@
                 break;
 
             case com.android.internal.R.styleable.TextView_textIsSelectable:
-                mTextIsSelectable = a.getBoolean(attr, false);
+                setTextIsSelectable(a.getBoolean(attr, false));
                 break;
 
             case com.android.internal.R.styleable.TextView_textAllCaps:
@@ -897,35 +911,39 @@
             }
 
             try {
-                mInput = (KeyListener) c.newInstance();
+                createEditorIfNeeded("inputMethod in ctor");
+                getEditor().mKeyListener = (KeyListener) c.newInstance();
             } catch (InstantiationException ex) {
                 throw new RuntimeException(ex);
             } catch (IllegalAccessException ex) {
                 throw new RuntimeException(ex);
             }
             try {
-                mInputType = inputType != EditorInfo.TYPE_NULL
+                getEditor().mInputType = inputType != EditorInfo.TYPE_NULL
                         ? inputType
-                        : mInput.getInputType();
+                        : getEditor().mKeyListener.getInputType();
             } catch (IncompatibleClassChangeError e) {
-                mInputType = EditorInfo.TYPE_CLASS_TEXT;
+                getEditor().mInputType = EditorInfo.TYPE_CLASS_TEXT;
             }
         } else if (digits != null) {
-            mInput = DigitsKeyListener.getInstance(digits.toString());
+            createEditorIfNeeded("digits in ctor");
+            getEditor().mKeyListener = DigitsKeyListener.getInstance(digits.toString());
             // If no input type was specified, we will default to generic
             // text, since we can't tell the IME about the set of digits
             // that was selected.
-            mInputType = inputType != EditorInfo.TYPE_NULL
+            getEditor().mInputType = inputType != EditorInfo.TYPE_NULL
                     ? inputType : EditorInfo.TYPE_CLASS_TEXT;
         } else if (inputType != EditorInfo.TYPE_NULL) {
             setInputType(inputType, true);
             // If set, the input type overrides what was set using the deprecated singleLine flag.
             singleLine = !isMultilineInputType(inputType);
         } else if (phone) {
-            mInput = DialerKeyListener.getInstance();
-            mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE;
+            createEditorIfNeeded("dialer in ctor");
+            getEditor().mKeyListener = DialerKeyListener.getInstance();
+            getEditor().mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE;
         } else if (numeric != 0) {
-            mInput = DigitsKeyListener.getInstance((numeric & SIGNED) != 0,
+            createEditorIfNeeded("numeric in ctor");
+            getEditor().mKeyListener = DigitsKeyListener.getInstance((numeric & SIGNED) != 0,
                                                    (numeric & DECIMAL) != 0);
             inputType = EditorInfo.TYPE_CLASS_NUMBER;
             if ((numeric & SIGNED) != 0) {
@@ -934,7 +952,7 @@
             if ((numeric & DECIMAL) != 0) {
                 inputType |= EditorInfo.TYPE_NUMBER_FLAG_DECIMAL;
             }
-            mInputType = inputType;
+            getEditor().mInputType = inputType;
         } else if (autotext || autocap != -1) {
             TextKeyListener.Capitalize cap;
 
@@ -961,22 +979,24 @@
                 break;
             }
 
-            mInput = TextKeyListener.getInstance(autotext, cap);
-            mInputType = inputType;
-        } else if (mTextIsSelectable) {
+            createEditorIfNeeded("text input in ctor");
+            getEditor().mKeyListener = TextKeyListener.getInstance(autotext, cap);
+            getEditor().mInputType = inputType;
+        } else if (isTextSelectable()) {
             // Prevent text changes from keyboard.
-            mInputType = EditorInfo.TYPE_NULL;
-            mInput = null;
+            if (mEditor != null) {
+                getEditor().mKeyListener = null;
+                getEditor().mInputType = EditorInfo.TYPE_NULL;
+            }
             bufferType = BufferType.SPANNABLE;
-            // Required to request focus while in touch mode.
-            setFocusableInTouchMode(true);
             // So that selection can be changed using arrow keys and touch is handled.
             setMovementMethod(ArrowKeyMovementMethod.getInstance());
         } else if (editable) {
-            mInput = TextKeyListener.getInstance();
-            mInputType = EditorInfo.TYPE_CLASS_TEXT;
+            createEditorIfNeeded("editable input in ctor");
+            getEditor().mKeyListener = TextKeyListener.getInstance();
+            getEditor().mInputType = EditorInfo.TYPE_CLASS_TEXT;
         } else {
-            mInput = null;
+            if (mEditor != null) getEditor().mKeyListener = null;
 
             switch (buffertype) {
                 case 0:
@@ -991,27 +1011,12 @@
             }
         }
 
-        // mInputType has been set from inputType, possibly modified by mInputMethod.
-        // Specialize mInputType to [web]password if we have a text class and the original input
-        // type was a password.
-        if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
-            if (password || passwordInputType) {
-                mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
-                        | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
-            }
-            if (webPasswordInputType) {
-                mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
-                        | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
-            }
-        } else if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_NUMBER) {
-            if (numberPasswordInputType) {
-                mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
-                        | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD;
-            }
-        }
+        if (mEditor != null) getEditor().adjustInputType(password, passwordInputType, webPasswordInputType,
+                numberPasswordInputType);
 
         if (selectallonfocus) {
-            mSelectAllOnFocus = true;
+            createEditorIfNeeded("selectallonfocus in constructor");
+            getEditor().mSelectAllOnFocus = true;
 
             if (bufferType == BufferType.NORMAL)
                 bufferType = BufferType.SPANNABLE;
@@ -1027,7 +1032,7 @@
         setInputTypeSingleLine(singleLine);
         applySingleLine(singleLine, singleLine, singleLine);
 
-        if (singleLine && mInput == null && ellipsize < 0) {
+        if (singleLine && getKeyListener() == null && ellipsize < 0) {
                 ellipsize = 3; // END
         }
 
@@ -1068,7 +1073,7 @@
         if (password || passwordInputType || webPasswordInputType || numberPasswordInputType) {
             setTransformationMethod(PasswordTransformationMethod.getInstance());
             typefaceIndex = MONOSPACE;
-        } else if ((mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
+        } else if (mEditor != null && (getEditor().mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
                 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) {
             typefaceIndex = MONOSPACE;
         }
@@ -1097,7 +1102,7 @@
                                            com.android.internal.R.styleable.View,
                                            defStyle, 0);
 
-        boolean focusable = mMovement != null || mInput != null;
+        boolean focusable = mMovement != null || getKeyListener() != null;
         boolean clickable = focusable;
         boolean longClickable = focusable;
 
@@ -1205,6 +1210,7 @@
             if (imm != null) imm.restartInput(this);
         }
 
+        if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
         prepareCursorControllers();
 
         // start or stop the cursor blinking as appropriate
@@ -1309,7 +1315,7 @@
      * This will frequently be null for non-EditText TextViews.
      */
     public final KeyListener getKeyListener() {
-        return mInput;
+        return mEditor == null ? null : getEditor().mKeyListener;
     }
 
     /**
@@ -1339,16 +1345,17 @@
         fixFocusableAndClickableSettings();
 
         if (input != null) {
+            createEditorIfNeeded("input is not null");
             try {
-                mInputType = mInput.getInputType();
+                getEditor().mInputType = getEditor().mKeyListener.getInputType();
             } catch (IncompatibleClassChangeError e) {
-                mInputType = EditorInfo.TYPE_CLASS_TEXT;
+                getEditor().mInputType = EditorInfo.TYPE_CLASS_TEXT;
             }
             // Change inputType, without affecting transformation.
             // No need to applySingleLine since mSingleLine is unchanged.
             setInputTypeSingleLine(mSingleLine);
         } else {
-            mInputType = EditorInfo.TYPE_NULL;
+            if (mEditor != null) getEditor().mInputType = EditorInfo.TYPE_NULL;
         }
 
         InputMethodManager imm = InputMethodManager.peekInstance();
@@ -1356,11 +1363,17 @@
     }
 
     private void setKeyListenerOnly(KeyListener input) {
-        mInput = input;
-        if (mInput != null && !(mText instanceof Editable))
-            setText(mText);
+        if (mEditor == null && input == null) return; // null is the default value
 
-        setFilters((Editable) mText, mFilters);
+        createEditorIfNeeded("setKeyListenerOnly");
+        if (getEditor().mKeyListener != input) {
+            getEditor().mKeyListener = input;
+            if (input != null && !(mText instanceof Editable)) {
+                setText(mText);
+            }
+
+            setFilters((Editable) mText, mFilters);
+        }
     }
 
     /**
@@ -1383,19 +1396,22 @@
      * back the way you want it.
      */
     public final void setMovementMethod(MovementMethod movement) {
-        mMovement = movement;
+        if (mMovement != movement) {
+            mMovement = movement;
 
-        if (mMovement != null && !(mText instanceof Spannable))
-            setText(mText);
+            if (movement != null && !(mText instanceof Spannable)) {
+                setText(mText);
+            }
 
-        fixFocusableAndClickableSettings();
+            fixFocusableAndClickableSettings();
 
-        // SelectionModifierCursorController depends on textCanBeSelected, which depends on mMovement
-        prepareCursorControllers();
+            // SelectionModifierCursorController depends on textCanBeSelected, which depends on mMovement
+            prepareCursorControllers();
+        }
     }
 
     private void fixFocusableAndClickableSettings() {
-        if ((mMovement != null) || mInput != null) {
+        if (mMovement != null || (mEditor != null && getEditor().mKeyListener != null)) {
             setFocusable(true);
             setClickable(true);
             setLongClickable(true);
@@ -1438,7 +1454,7 @@
 
         if (method instanceof TransformationMethod2) {
             TransformationMethod2 method2 = (TransformationMethod2) method;
-            mAllowTransformationLengthChange = !mTextIsSelectable && !(mText instanceof Editable);
+            mAllowTransformationLengthChange = !isTextSelectable() && !(mText instanceof Editable);
             method2.setLengthChangesAllowed(mAllowTransformationLengthChange);
         } else {
             mAllowTransformationLengthChange = false;
@@ -2310,6 +2326,7 @@
     public void setHighlightColor(int color) {
         if (mHighlightColor != color) {
             mHighlightColor = color;
+            if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
             invalidate();
         }
     }
@@ -2330,6 +2347,7 @@
         mShadowDx = dx;
         mShadowDy = dy;
 
+        if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
         invalidate();
     }
 
@@ -2821,6 +2839,7 @@
             }
         }
         if (inval) {
+            if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
             invalidate();
         }
     }
@@ -2858,73 +2877,6 @@
         }
     }
 
-    /**
-     * User interface state that is stored by TextView for implementing
-     * {@link View#onSaveInstanceState}.
-     */
-    public static class SavedState extends BaseSavedState {
-        int selStart;
-        int selEnd;
-        CharSequence text;
-        boolean frozenWithFocus;
-        CharSequence error;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeInt(selStart);
-            out.writeInt(selEnd);
-            out.writeInt(frozenWithFocus ? 1 : 0);
-            TextUtils.writeToParcel(text, out, flags);
-
-            if (error == null) {
-                out.writeInt(0);
-            } else {
-                out.writeInt(1);
-                TextUtils.writeToParcel(error, out, flags);
-            }
-        }
-
-        @Override
-        public String toString() {
-            String str = "TextView.SavedState{"
-                    + Integer.toHexString(System.identityHashCode(this))
-                    + " start=" + selStart + " end=" + selEnd;
-            if (text != null) {
-                str += " text=" + text;
-            }
-            return str + "}";
-        }
-
-        @SuppressWarnings("hiding")
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = new Parcelable.Creator<SavedState>() {
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-
-        private SavedState(Parcel in) {
-            super(in);
-            selStart = in.readInt();
-            selEnd = in.readInt();
-            frozenWithFocus = (in.readInt() != 0);
-            text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-
-            if (in.readInt() != 0) {
-                error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-            }
-        }
-    }
-
     @Override
     public Parcelable onSaveInstanceState() {
         Parcelable superState = super.onSaveInstanceState();
@@ -2964,8 +2916,10 @@
                     sp.removeSpan(cw);
                 }
 
-                removeMisspelledSpans(sp);
-                sp.removeSpan(mSuggestionRangeSpan);
+                if (mEditor != null) {
+                    removeMisspelledSpans(sp);
+                    sp.removeSpan(getEditor().mSuggestionRangeSpan);
+                }
 
                 ss.text = sp;
             } else {
@@ -2976,7 +2930,7 @@
                 ss.frozenWithFocus = true;
             }
 
-            ss.error = mError;
+            ss.error = getError();
 
             return ss;
         }
@@ -3030,7 +2984,8 @@
                                            ss.selEnd);
 
                     if (ss.frozenWithFocus) {
-                        mFrozenWithFocus = true;
+                        createEditorIfNeeded("restore instance with focus");
+                        getEditor().mFrozenWithFocus = true;
                     }
                 }
             }
@@ -3188,7 +3143,8 @@
             needEditableForNotification = true;
         }
 
-        if (type == BufferType.EDITABLE || mInput != null || needEditableForNotification) {
+        if (type == BufferType.EDITABLE || getKeyListener() != null || needEditableForNotification) {
+            createEditorIfNeeded("setText with BufferType.EDITABLE or non null mInput");
             Editable t = mEditableFactory.newEditable(text);
             text = t;
             setFilters(t, mFilters);
@@ -3253,10 +3209,10 @@
                 mChangeWatcher = new ChangeWatcher();
 
             sp.setSpan(mChangeWatcher, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE |
-                       (PRIORITY << Spanned.SPAN_PRIORITY_SHIFT));
+                       (CHANGE_WATCHER_PRIORITY << Spanned.SPAN_PRIORITY_SHIFT));
 
-            if (mInput != null) {
-                sp.setSpan(mInput, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            if (mEditor != null && getEditor().mKeyListener != null) {
+                sp.setSpan(getEditor().mKeyListener, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
             }
 
             if (mTransformation != null) {
@@ -3271,7 +3227,7 @@
                  * selection, so reset mSelectionMoved to keep that from
                  * interfering with the normal on-focus selection-setting.
                  */
-                mSelectionMoved = false;
+                if (mEditor != null) getEditor().mSelectionMoved = false;
             }
         }
 
@@ -3325,100 +3281,6 @@
         setText(mCharWrapper, mBufferType, false, oldlen);
     }
 
-    private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations {
-        private char[] mChars;
-        private int mStart, mLength;
-
-        public CharWrapper(char[] chars, int start, int len) {
-            mChars = chars;
-            mStart = start;
-            mLength = len;
-        }
-
-        /* package */ void set(char[] chars, int start, int len) {
-            mChars = chars;
-            mStart = start;
-            mLength = len;
-        }
-
-        public int length() {
-            return mLength;
-        }
-
-        public char charAt(int off) {
-            return mChars[off + mStart];
-        }
-
-        @Override
-        public String toString() {
-            return new String(mChars, mStart, mLength);
-        }
-
-        public CharSequence subSequence(int start, int end) {
-            if (start < 0 || end < 0 || start > mLength || end > mLength) {
-                throw new IndexOutOfBoundsException(start + ", " + end);
-            }
-
-            return new String(mChars, start + mStart, end - start);
-        }
-
-        public void getChars(int start, int end, char[] buf, int off) {
-            if (start < 0 || end < 0 || start > mLength || end > mLength) {
-                throw new IndexOutOfBoundsException(start + ", " + end);
-            }
-
-            System.arraycopy(mChars, start + mStart, buf, off, end - start);
-        }
-
-        public void drawText(Canvas c, int start, int end,
-                             float x, float y, Paint p) {
-            c.drawText(mChars, start + mStart, end - start, x, y, p);
-        }
-
-        public void drawTextRun(Canvas c, int start, int end,
-                int contextStart, int contextEnd, float x, float y, int flags, Paint p) {
-            int count = end - start;
-            int contextCount = contextEnd - contextStart;
-            c.drawTextRun(mChars, start + mStart, count, contextStart + mStart,
-                    contextCount, x, y, flags, p);
-        }
-
-        public float measureText(int start, int end, Paint p) {
-            return p.measureText(mChars, start + mStart, end - start);
-        }
-
-        public int getTextWidths(int start, int end, float[] widths, Paint p) {
-            return p.getTextWidths(mChars, start + mStart, end - start, widths);
-        }
-
-        public float getTextRunAdvances(int start, int end, int contextStart,
-                int contextEnd, int flags, float[] advances, int advancesIndex,
-                Paint p) {
-            int count = end - start;
-            int contextCount = contextEnd - contextStart;
-            return p.getTextRunAdvances(mChars, start + mStart, count,
-                    contextStart + mStart, contextCount, flags, advances,
-                    advancesIndex);
-        }
-
-        public float getTextRunAdvances(int start, int end, int contextStart,
-                int contextEnd, int flags, float[] advances, int advancesIndex,
-                Paint p, int reserved) {
-            int count = end - start;
-            int contextCount = contextEnd - contextStart;
-            return p.getTextRunAdvances(mChars, start + mStart, count,
-                    contextStart + mStart, contextCount, flags, advances,
-                    advancesIndex, reserved);
-        }
-
-        public int getTextRunCursor(int contextStart, int contextEnd, int flags,
-                int offset, int cursorOpt, Paint p) {
-            int contextCount = contextEnd - contextStart;
-            return p.getTextRunCursor(mChars, contextStart + mStart,
-                    contextCount, flags, offset + mStart, cursorOpt);
-        }
-    }
-
     /**
      * Like {@link #setText(CharSequence, android.widget.TextView.BufferType)},
      * except that the cursor position (if any) is retained in the new text.
@@ -3468,6 +3330,11 @@
         if (mText.length() == 0) {
             invalidate();
         }
+
+        // Invalidate display list if hint will be used
+        if (mEditor != null && mText.length() == 0 && mHint != null) {
+            getEditor().mTextDisplayListIsValid = false;
+        }
     }
 
     /**
@@ -3513,8 +3380,8 @@
      * @attr ref android.R.styleable#TextView_inputType
      */
     public void setInputType(int type) {
-        final boolean wasPassword = isPasswordInputType(mInputType);
-        final boolean wasVisiblePassword = isVisiblePasswordInputType(mInputType);
+        final boolean wasPassword = isPasswordInputType(getInputType());
+        final boolean wasVisiblePassword = isVisiblePasswordInputType(getInputType());
         setInputType(type, false);
         final boolean isPassword = isPasswordInputType(type);
         final boolean isVisiblePassword = isVisiblePasswordInputType(type);
@@ -3598,7 +3465,9 @@
      * @attr ref android.R.styleable#TextView_inputType
      */
     public void setRawInputType(int type) {
-        mInputType = type;
+        if (type == InputType.TYPE_NULL && mEditor == null) return; //TYPE_NULL is the default value
+        createEditorIfNeeded("non null input type");
+        getEditor().mInputType = type;
     }
 
     private void setInputType(int type, boolean direct) {
@@ -3639,20 +3508,22 @@
             input = TextKeyListener.getInstance();
         }
         setRawInputType(type);
-        if (direct) mInput = input;
-        else {
+        if (direct) {
+            createEditorIfNeeded("setInputType");
+            getEditor().mKeyListener = input;
+        } else {
             setKeyListenerOnly(input);
         }
     }
 
     /**
-     * Get the type of the content.
+     * Get the type of the editable content.
      *
      * @see #setInputType(int)
      * @see android.text.InputType
      */
     public int getInputType() {
-        return mInputType;
+        return mEditor == null ? EditorInfo.TYPE_NULL : getEditor().mInputType;
     }
 
     /**
@@ -3664,10 +3535,11 @@
      * @attr ref android.R.styleable#TextView_imeOptions
      */
     public void setImeOptions(int imeOptions) {
-        if (mInputContentType == null) {
-            mInputContentType = new InputContentType();
+        createEditorIfNeeded("IME options specified");
+        if (getEditor().mInputContentType == null) {
+            getEditor().mInputContentType = new InputContentType();
         }
-        mInputContentType.imeOptions = imeOptions;
+        getEditor().mInputContentType.imeOptions = imeOptions;
     }
 
     /**
@@ -3677,8 +3549,8 @@
      * @see android.view.inputmethod.EditorInfo
      */
     public int getImeOptions() {
-        return mInputContentType != null
-                ? mInputContentType.imeOptions : EditorInfo.IME_NULL;
+        return mEditor != null && getEditor().mInputContentType != null
+                ? getEditor().mInputContentType.imeOptions : EditorInfo.IME_NULL;
     }
 
     /**
@@ -3692,11 +3564,12 @@
      * @attr ref android.R.styleable#TextView_imeActionId
      */
     public void setImeActionLabel(CharSequence label, int actionId) {
-        if (mInputContentType == null) {
-            mInputContentType = new InputContentType();
+        createEditorIfNeeded("IME action label specified");
+        if (getEditor().mInputContentType == null) {
+            getEditor().mInputContentType = new InputContentType();
         }
-        mInputContentType.imeActionLabel = label;
-        mInputContentType.imeActionId = actionId;
+        getEditor().mInputContentType.imeActionLabel = label;
+        getEditor().mInputContentType.imeActionId = actionId;
     }
 
     /**
@@ -3706,8 +3579,8 @@
      * @see android.view.inputmethod.EditorInfo
      */
     public CharSequence getImeActionLabel() {
-        return mInputContentType != null
-                ? mInputContentType.imeActionLabel : null;
+        return mEditor != null && getEditor().mInputContentType != null
+                ? getEditor().mInputContentType.imeActionLabel : null;
     }
 
     /**
@@ -3717,8 +3590,8 @@
      * @see android.view.inputmethod.EditorInfo
      */
     public int getImeActionId() {
-        return mInputContentType != null
-                ? mInputContentType.imeActionId : 0;
+        return mEditor != null && getEditor().mInputContentType != null
+                ? getEditor().mInputContentType.imeActionId : 0;
     }
 
     /**
@@ -3730,12 +3603,13 @@
      * modifier will, however, allow the user to insert a newline character.
      */
     public void setOnEditorActionListener(OnEditorActionListener l) {
-        if (mInputContentType == null) {
-            mInputContentType = new InputContentType();
+        createEditorIfNeeded("Editor action listener set");
+        if (getEditor().mInputContentType == null) {
+            getEditor().mInputContentType = new InputContentType();
         }
-        mInputContentType.onEditorActionListener = l;
+        getEditor().mInputContentType.onEditorActionListener = l;
     }
-    
+
     /**
      * Called when an attached input method calls
      * {@link InputConnection#performEditorAction(int)
@@ -3757,7 +3631,7 @@
      * @see #setOnEditorActionListener
      */
     public void onEditorAction(int actionCode) {
-        final InputContentType ict = mInputContentType;
+        final InputContentType ict = mEditor == null ? null : getEditor().mInputContentType;
         if (ict != null) {
             if (ict.onEditorActionListener != null) {
                 if (ict.onEditorActionListener.onEditorAction(this,
@@ -3800,21 +3674,21 @@
             }
         }
 
-        Handler h = getHandler();
-        if (h != null) {
+        ViewRootImpl viewRootImpl = getViewRootImpl();
+        if (viewRootImpl != null) {
             long eventTime = SystemClock.uptimeMillis();
-            h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME,
+            viewRootImpl.dispatchKeyFromIme(
                     new KeyEvent(eventTime, eventTime,
                     KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                     KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
-                    | KeyEvent.FLAG_EDITOR_ACTION)));
-            h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME,
+                    | KeyEvent.FLAG_EDITOR_ACTION));
+            viewRootImpl.dispatchKeyFromIme(
                     new KeyEvent(SystemClock.uptimeMillis(), eventTime,
                     KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                     KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
-                    | KeyEvent.FLAG_EDITOR_ACTION)));
+                    | KeyEvent.FLAG_EDITOR_ACTION));
         }
     }
 
@@ -3828,8 +3702,10 @@
      * @attr ref android.R.styleable#TextView_privateImeOptions
      */
     public void setPrivateImeOptions(String type) {
-        if (mInputContentType == null) mInputContentType = new InputContentType();
-        mInputContentType.privateImeOptions = type;
+        createEditorIfNeeded("Private IME option set");
+        if (getEditor().mInputContentType == null)
+            getEditor().mInputContentType = new InputContentType();
+        getEditor().mInputContentType.privateImeOptions = type;
     }
 
     /**
@@ -3839,8 +3715,8 @@
      * @see EditorInfo#privateImeOptions
      */
     public String getPrivateImeOptions() {
-        return mInputContentType != null
-                ? mInputContentType.privateImeOptions : null;
+        return mEditor != null && getEditor().mInputContentType != null
+                ? getEditor().mInputContentType.privateImeOptions : null;
     }
 
     /**
@@ -3854,12 +3730,13 @@
      * @see EditorInfo#extras
      * @attr ref android.R.styleable#TextView_editorExtras
      */
-    public void setInputExtras(int xmlResId)
-            throws XmlPullParserException, IOException {
+    public void setInputExtras(int xmlResId) throws XmlPullParserException, IOException {
+        createEditorIfNeeded("Input extra set");
         XmlResourceParser parser = getResources().getXml(xmlResId);
-        if (mInputContentType == null) mInputContentType = new InputContentType();
-        mInputContentType.extras = new Bundle();
-        getResources().parseBundleExtras(parser, mInputContentType.extras);
+        if (getEditor().mInputContentType == null)
+            getEditor().mInputContentType = new InputContentType();
+        getEditor().mInputContentType.extras = new Bundle();
+        getResources().parseBundleExtras(parser, getEditor().mInputContentType.extras);
     }
 
     /**
@@ -3873,15 +3750,17 @@
      * @attr ref android.R.styleable#TextView_editorExtras
      */
     public Bundle getInputExtras(boolean create) {
-        if (mInputContentType == null) {
+        if (mEditor == null && !create) return null;
+        createEditorIfNeeded("get Input extra");
+        if (getEditor().mInputContentType == null) {
             if (!create) return null;
-            mInputContentType = new InputContentType();
+            getEditor().mInputContentType = new InputContentType();
         }
-        if (mInputContentType.extras == null) {
+        if (getEditor().mInputContentType.extras == null) {
             if (!create) return null;
-            mInputContentType.extras = new Bundle();
+            getEditor().mInputContentType.extras = new Bundle();
         }
-        return mInputContentType.extras;
+        return getEditor().mInputContentType.extras;
     }
 
     /**
@@ -3890,7 +3769,7 @@
      * or if it the error was cleared by the widget after user input.
      */
     public CharSequence getError() {
-        return mError;
+        return mEditor == null ? null : getEditor().mError;
     }
 
     /**
@@ -3924,10 +3803,11 @@
      * be cleared (and you should provide a <code>null</code> icon as well).
      */
     public void setError(CharSequence error, Drawable icon) {
+        createEditorIfNeeded("setError");
         error = TextUtils.stringOrSpannedString(error);
 
-        mError = error;
-        mErrorWasChanged = true;
+        getEditor().mError = error;
+        getEditor().mErrorWasChanged = true;
         final Drawables dr = mDrawables;
         if (dr != null) {
             switch (getResolvedLayoutDirection()) {
@@ -3946,12 +3826,12 @@
         }
 
         if (error == null) {
-            if (mPopup != null) {
-                if (mPopup.isShowing()) {
-                    mPopup.dismiss();
+            if (getEditor().mErrorPopup != null) {
+                if (getEditor().mErrorPopup.isShowing()) {
+                    getEditor().mErrorPopup.dismiss();
                 }
 
-                mPopup = null;
+                getEditor().mErrorPopup = null;
             }
         } else {
             if (isFocused()) {
@@ -3962,83 +3842,29 @@
 
     private void showError() {
         if (getWindowToken() == null) {
-            mShowErrorAfterAttach = true;
+            getEditor().mShowErrorAfterAttach = true;
             return;
         }
 
-        if (mPopup == null) {
+        if (getEditor().mErrorPopup == null) {
             LayoutInflater inflater = LayoutInflater.from(getContext());
             final TextView err = (TextView) inflater.inflate(
                     com.android.internal.R.layout.textview_hint, null);
 
             final float scale = getResources().getDisplayMetrics().density;
-            mPopup = new ErrorPopup(err, (int) (200 * scale + 0.5f), (int) (50 * scale + 0.5f));
-            mPopup.setFocusable(false);
+            getEditor().mErrorPopup = new ErrorPopup(err, (int) (200 * scale + 0.5f), (int) (50 * scale + 0.5f));
+            getEditor().mErrorPopup.setFocusable(false);
             // The user is entering text, so the input method is needed.  We
             // don't want the popup to be displayed on top of it.
-            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+            getEditor().mErrorPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
         }
 
-        TextView tv = (TextView) mPopup.getContentView();
-        chooseSize(mPopup, mError, tv);
-        tv.setText(mError);
+        TextView tv = (TextView) getEditor().mErrorPopup.getContentView();
+        chooseSize(getEditor().mErrorPopup, getEditor().mError, tv);
+        tv.setText(getEditor().mError);
 
-        mPopup.showAsDropDown(this, getErrorX(), getErrorY());
-        mPopup.fixDirection(mPopup.isAboveAnchor());
-    }
-
-    private static class ErrorPopup extends PopupWindow {
-        private boolean mAbove = false;
-        private final TextView mView;
-        private int mPopupInlineErrorBackgroundId = 0;
-        private int mPopupInlineErrorAboveBackgroundId = 0;
-
-        ErrorPopup(TextView v, int width, int height) {
-            super(v, width, height);
-            mView = v;
-            // Make sure the TextView has a background set as it will be used the first time it is
-            // shown and positionned. Initialized with below background, which should have
-            // dimensions identical to the above version for this to work (and is more likely).
-            mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
-                    com.android.internal.R.styleable.Theme_errorMessageBackground);
-            mView.setBackgroundResource(mPopupInlineErrorBackgroundId);
-        }
-
-        void fixDirection(boolean above) {
-            mAbove = above;
-
-            if (above) {
-                mPopupInlineErrorAboveBackgroundId =
-                    getResourceId(mPopupInlineErrorAboveBackgroundId,
-                            com.android.internal.R.styleable.Theme_errorMessageAboveBackground);
-            } else {
-                mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
-                        com.android.internal.R.styleable.Theme_errorMessageBackground);
-            }
-
-            mView.setBackgroundResource(above ? mPopupInlineErrorAboveBackgroundId :
-                mPopupInlineErrorBackgroundId);
-        }
-
-        private int getResourceId(int currentId, int index) {
-            if (currentId == 0) {
-                TypedArray styledAttributes = mView.getContext().obtainStyledAttributes(
-                        R.styleable.Theme);
-                currentId = styledAttributes.getResourceId(index, 0);
-                styledAttributes.recycle();
-            }
-            return currentId;
-        }
-
-        @Override
-        public void update(int x, int y, int w, int h, boolean force) {
-            super.update(x, y, w, h, force);
-
-            boolean above = isAboveAnchor();
-            if (above != mAbove) {
-                fixDirection(above);
-            }
-        }
+        getEditor().mErrorPopup.showAsDropDown(this, getErrorX(), getErrorY());
+        getEditor().mErrorPopup.fixDirection(getEditor().mErrorPopup.isAboveAnchor());
     }
 
     /**
@@ -4053,7 +3879,7 @@
         final float scale = getResources().getDisplayMetrics().density;
 
         final Drawables dr = mDrawables;
-        return getWidth() - mPopup.getWidth() - getPaddingRight() -
+        return getWidth() - getEditor().mErrorPopup.getWidth() - getPaddingRight() -
                 (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f);
     }
 
@@ -4083,13 +3909,13 @@
     }
 
     private void hideError() {
-        if (mPopup != null) {
-            if (mPopup.isShowing()) {
-                mPopup.dismiss();
+        if (getEditor().mErrorPopup != null) {
+            if (getEditor().mErrorPopup.isShowing()) {
+                getEditor().mErrorPopup.dismiss();
             }
         }
 
-        mShowErrorAfterAttach = false;
+        getEditor().mShowErrorAfterAttach = false;
     }
 
     private void chooseSize(PopupWindow pop, CharSequence text, TextView tv) {
@@ -4118,12 +3944,7 @@
     protected boolean setFrame(int l, int t, int r, int b) {
         boolean result = super.setFrame(l, t, r, b);
 
-        if (mPopup != null) {
-            TextView tv = (TextView) mPopup.getContentView();
-            chooseSize(mPopup, mError, tv);
-            mPopup.update(this, getErrorX(), getErrorY(),
-                          mPopup.getWidth(), mPopup.getHeight());
-        }
+        if (mEditor != null) getEditor().setFrame();
 
         restartMarqueeIfNeeded();
 
@@ -4139,7 +3960,7 @@
 
     /**
      * Sets the list of input filters that will be used if the buffer is
-     * Editable.  Has no effect otherwise.
+     * Editable. Has no effect otherwise.
      *
      * @attr ref android.R.styleable#TextView_maxLength
      */
@@ -4160,11 +3981,11 @@
      * and includes mInput in the list if it is an InputFilter.
      */
     private void setFilters(Editable e, InputFilter[] filters) {
-        if (mInput instanceof InputFilter) {
+        if (mEditor != null && getEditor().mKeyListener instanceof InputFilter) {
             InputFilter[] nf = new InputFilter[filters.length + 1];
 
             System.arraycopy(filters, 0, nf, 0, filters.length);
-            nf[filters.length] = (InputFilter) mInput;
+            nf[filters.length] = (InputFilter) getEditor().mKeyListener;
 
             e.setFilters(nf);
         } else {
@@ -4244,14 +4065,14 @@
     }
 
     private void invalidateCursorPath() {
-        if (mHighlightPathBogus) {
+        if (getEditor().mHighlightPathBogus) {
             invalidateCursor();
         } else {
             final int horizontalPadding = getCompoundPaddingLeft();
             final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
 
-            if (mCursorCount == 0) {
-                synchronized (sTempRect) {
+            if (getEditor().mCursorCount == 0) {
+                synchronized (TEMP_RECTF) {
                     /*
                      * The reason for this concern about the thickness of the
                      * cursor and doing the floor/ceil on the coordinates is that
@@ -4268,16 +4089,16 @@
 
                     thick /= 2.0f;
 
-                    mHighlightPath.computeBounds(sTempRect, false);
+                    getEditor().mHighlightPath.computeBounds(TEMP_RECTF, false);
 
-                    invalidate((int) FloatMath.floor(horizontalPadding + sTempRect.left - thick),
-                            (int) FloatMath.floor(verticalPadding + sTempRect.top - thick),
-                            (int) FloatMath.ceil(horizontalPadding + sTempRect.right + thick),
-                            (int) FloatMath.ceil(verticalPadding + sTempRect.bottom + thick));
+                    invalidate((int) FloatMath.floor(horizontalPadding + TEMP_RECTF.left - thick),
+                            (int) FloatMath.floor(verticalPadding + TEMP_RECTF.top - thick),
+                            (int) FloatMath.ceil(horizontalPadding + TEMP_RECTF.right + thick),
+                            (int) FloatMath.ceil(verticalPadding + TEMP_RECTF.bottom + thick));
                 }
             } else {
-                for (int i = 0; i < mCursorCount; i++) {
-                    Rect bounds = mCursorDrawable[i].getBounds();
+                for (int i = 0; i < getEditor().mCursorCount; i++) {
+                    Rect bounds = getEditor().mCursorDrawable[i].getBounds();
                     invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding,
                             bounds.right + horizontalPadding, bounds.bottom + verticalPadding);
                 }
@@ -4331,8 +4152,8 @@
                 int bottom = mLayout.getLineBottom(lineEnd);
 
                 if (invalidateCursor) {
-                    for (int i = 0; i < mCursorCount; i++) {
-                        Rect bounds = mCursorDrawable[i].getBounds();
+                    for (int i = 0; i < getEditor().mCursorCount; i++) {
+                        Rect bounds = getEditor().mCursorDrawable[i].getBounds();
                         top = Math.min(top, bounds.top);
                         bottom = Math.max(bottom, bounds.bottom);
                     }
@@ -4382,8 +4203,8 @@
              */
             int curs = getSelectionEnd();
             // Do not create the controller if it is not already created.
-            if (mSelectionModifierCursorController != null &&
-                    mSelectionModifierCursorController.isSelectionStartDragged()) {
+            if (mEditor != null && getEditor().mSelectionModifierCursorController != null &&
+                    getEditor().mSelectionModifierCursorController.isSelectionStartDragged()) {
                 curs = getSelectionStart();
             }
 
@@ -4392,8 +4213,7 @@
              * it already was before the text changed.  I'm not sure
              * of a good way to tell from here if it was.
              */
-            if (curs < 0 &&
-                  (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
+            if (curs < 0 && (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
                 curs = mText.length();
             }
 
@@ -4407,9 +4227,9 @@
         // This has to be checked here since:
         // - onFocusChanged cannot start it when focus is given to a view with selected text (after
         //   a screen rotation) since layout is not yet initialized at that point.
-        if (mCreatedWithASelection) {
+        if (mEditor != null && getEditor().mCreatedWithASelection) {
             startSelectionActionMode();
-            mCreatedWithASelection = false;
+            getEditor().mCreatedWithASelection = false;
         }
 
         // Phone specific code (there is no ExtractEditText on tablets).
@@ -4431,25 +4251,15 @@
 
         mTemporaryDetach = false;
         
-        if (mShowErrorAfterAttach) {
+        if (mEditor != null && getEditor().mShowErrorAfterAttach) {
             showError();
-            mShowErrorAfterAttach = false;
-        }
-
-        final ViewTreeObserver observer = getViewTreeObserver();
-        // No need to create the controller.
-        // The get method will add the listener on controller creation.
-        if (mInsertionPointCursorController != null) {
-            observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
-        }
-        if (mSelectionModifierCursorController != null) {
-            observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+            getEditor().mShowErrorAfterAttach = false;
         }
 
         // Resolve drawables as the layout direction has been resolved
         resolveDrawables();
 
-        updateSpellCheckSpans(0, mText.length(), true /* create the spell checker if needed */);
+        if (mEditor != null) getEditor().onAttachedToWindow();
     }
 
     @Override
@@ -4461,40 +4271,9 @@
             mPreDrawRegistered = false;
         }
 
-        if (mError != null) {
-            hideError();
-        }
-
-        if (mBlink != null) {
-            mBlink.removeCallbacks(mBlink);
-        }
-
-        if (mInsertionPointCursorController != null) {
-            mInsertionPointCursorController.onDetached();
-        }
-
-        if (mSelectionModifierCursorController != null) {
-            mSelectionModifierCursorController.onDetached();
-        }
-
-        if (mShowSuggestionRunnable != null) {
-            removeCallbacks(mShowSuggestionRunnable);
-        }
-
-        hideControllers();
-
         resetResolvedDrawables();
 
-        if (mTextDisplayList != null) {
-            mTextDisplayList.invalidate();
-        }
-
-        if (mSpellChecker != null) {
-            mSpellChecker.closeSession();
-            // Forces the creation of a new SpellChecker next time this window is created.
-            // Will handle the cases where the settings has been changed in the meantime.
-            mSpellChecker = null;
-        }
+        if (mEditor != null) getEditor().onDetachedFromWindow();
     }
 
     @Override
@@ -4630,19 +4409,25 @@
         // Alpha is supported if and only if the drawing can be done in one pass.
         // TODO text with spans with a background color currently do not respect this alpha.
         if (getBackground() == null) {
-            mCurrentAlpha = alpha;
-            final Drawables dr = mDrawables;
-            if (dr != null) {
-                if (dr.mDrawableLeft != null) dr.mDrawableLeft.mutate().setAlpha(alpha);
-                if (dr.mDrawableTop != null) dr.mDrawableTop.mutate().setAlpha(alpha);
-                if (dr.mDrawableRight != null) dr.mDrawableRight.mutate().setAlpha(alpha);
-                if (dr.mDrawableBottom != null) dr.mDrawableBottom.mutate().setAlpha(alpha);
-                if (dr.mDrawableStart != null) dr.mDrawableStart.mutate().setAlpha(alpha);
-                if (dr.mDrawableEnd != null) dr.mDrawableEnd.mutate().setAlpha(alpha);
+            if (mCurrentAlpha != alpha) {
+                mCurrentAlpha = alpha;
+                final Drawables dr = mDrawables;
+                if (dr != null) {
+                    if (dr.mDrawableLeft != null) dr.mDrawableLeft.mutate().setAlpha(alpha);
+                    if (dr.mDrawableTop != null) dr.mDrawableTop.mutate().setAlpha(alpha);
+                    if (dr.mDrawableRight != null) dr.mDrawableRight.mutate().setAlpha(alpha);
+                    if (dr.mDrawableBottom != null) dr.mDrawableBottom.mutate().setAlpha(alpha);
+                    if (dr.mDrawableStart != null) dr.mDrawableStart.mutate().setAlpha(alpha);
+                    if (dr.mDrawableEnd != null) dr.mDrawableEnd.mutate().setAlpha(alpha);
+                }
+                if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
             }
             return true;
         }
 
+        if (mCurrentAlpha != 255) {
+            if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
+        }
         mCurrentAlpha = 255;
         return false;
     }
@@ -4665,12 +4450,12 @@
      * @attr ref android.R.styleable#TextView_textIsSelectable
      */
     public boolean isTextSelectable() {
-        return mTextIsSelectable;
+        return mEditor == null ? false : getEditor().mTextIsSelectable;
     }
 
     /**
      * Sets whether or not (default) the content of this view is selectable by the user.
-     * 
+     *
      * Note that this methods affect the {@link #setFocusable(boolean)},
      * {@link #setFocusableInTouchMode(boolean)} {@link #setClickable(boolean)} and
      * {@link #setLongClickable(boolean)} states and you may want to restore these if they were
@@ -4681,16 +4466,18 @@
      * @param selectable Whether or not the content of this TextView should be selectable.
      */
     public void setTextIsSelectable(boolean selectable) {
-        if (mTextIsSelectable == selectable) return;
+        if (!selectable && mEditor == null) return; // false is default value with no edit data
 
-        mTextIsSelectable = selectable;
+        createEditorIfNeeded("setTextIsSelectable");
+        if (getEditor().mTextIsSelectable == selectable) return;
 
+        getEditor().mTextIsSelectable = selectable;
         setFocusableInTouchMode(selectable);
         setFocusable(selectable);
         setClickable(selectable);
         setLongClickable(selectable);
 
-        // mInputType is already EditorInfo.TYPE_NULL and mInput is null;
+        // mInputType should already be EditorInfo.TYPE_NULL and mInput should be null
 
         setMovementMethod(selectable ? ArrowKeyMovementMethod.getInstance() : null);
         setText(getText(), selectable ? BufferType.SPANNABLE : BufferType.NORMAL);
@@ -4710,7 +4497,7 @@
             mergeDrawableStates(drawableState, MULTILINE_STATE_SET);
         }
 
-        if (mTextIsSelectable) {
+        if (isTextSelectable()) {
             // Disable pressed state, which was introduced when TextView was made clickable.
             // Prevents text color change.
             // setClickable(false) would have a similar effect, but it also disables focus changes
@@ -4809,7 +4596,6 @@
         }
 
         Layout layout = mLayout;
-        int cursorcolor = color;
 
         if (mHint != null && mText.length() == 0) {
             if (mHintTextColor != null) {
@@ -4834,10 +4620,14 @@
         int extendedPaddingTop = getExtendedPaddingTop();
         int extendedPaddingBottom = getExtendedPaddingBottom();
 
+        final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
+        final int maxScrollY = mLayout.getHeight() - vspace;
+
         float clipLeft = compoundPaddingLeft + scrollX;
-        float clipTop = extendedPaddingTop + scrollY;
+        float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
         float clipRight = right - left - compoundPaddingRight + scrollX;
-        float clipBottom = bottom - top - extendedPaddingBottom + scrollY;
+        float clipBottom = bottom - top + scrollY -
+                ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
 
         if (mShadowRadius != 0) {
             clipLeft += Math.min(0, mShadowDx - mShadowRadius);
@@ -4853,14 +4643,12 @@
         int voffsetCursor = 0;
 
         // translate in by our padding
-        {
-            /* shortcircuit calling getVerticaOffset() */
-            if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
-                voffsetText = getVerticalOffset(false);
-                voffsetCursor = getVerticalOffset(true);
-            }
-            canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
+        /* shortcircuit calling getVerticaOffset() */
+        if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
+            voffsetText = getVerticalOffset(false);
+            voffsetCursor = getVerticalOffset(true);
         }
+        canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
 
         final int layoutDirection = getResolvedLayoutDirection();
         final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
@@ -4877,150 +4665,17 @@
             }
         }
 
-        Path highlight = null;
-        int selStart = -1, selEnd = -1;
-        boolean drawCursor = false;
-
-        //  If there is no movement method, then there can be no selection.
-        //  Check that first and attempt to skip everything having to do with
-        //  the cursor.
-        //  XXX This is not strictly true -- a program could set the
-        //  selection manually if it really wanted to.
-        if (mMovement != null && (isFocused() || isPressed())) {
-            selStart = getSelectionStart();
-            selEnd = getSelectionEnd();
-
-            if (selStart >= 0) {
-                if (mHighlightPath == null) mHighlightPath = new Path();
-
-                if (selStart == selEnd) {
-                    if (isCursorVisible() &&
-                            (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) {
-                        if (mHighlightPathBogus) {
-                            mHighlightPath.reset();
-                            mLayout.getCursorPath(selStart, mHighlightPath, mText);
-                            updateCursorsPositions();
-                            mHighlightPathBogus = false;
-                        }
-
-                        // XXX should pass to skin instead of drawing directly
-                        mHighlightPaint.setColor(cursorcolor);
-                        if (mCurrentAlpha != 255) {
-                            mHighlightPaint.setAlpha(
-                                    (mCurrentAlpha * Color.alpha(cursorcolor)) / 255);
-                        }
-                        mHighlightPaint.setStyle(Paint.Style.STROKE);
-                        highlight = mHighlightPath;
-                        drawCursor = mCursorCount > 0;
-                    }
-                } else if (textCanBeSelected()) {
-                    if (mHighlightPathBogus) {
-                        mHighlightPath.reset();
-                        mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
-                        mHighlightPathBogus = false;
-                    }
-
-                    // XXX should pass to skin instead of drawing directly
-                    mHighlightPaint.setColor(mHighlightColor);
-                    if (mCurrentAlpha != 255) {
-                        mHighlightPaint.setAlpha(
-                                (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255);
-                    }
-                    mHighlightPaint.setStyle(Paint.Style.FILL);
-
-                    highlight = mHighlightPath;
-                }
-            }
-        }
-
-        final InputMethodState ims = mInputMethodState;
         final int cursorOffsetVertical = voffsetCursor - voffsetText;
-        if (ims != null && ims.mBatchEditNesting == 0) {
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                if (imm.isActive(this)) {
-                    boolean reported = false;
-                    if (ims.mContentChanged || ims.mSelectionModeChanged) {
-                        // We are in extract mode and the content has changed
-                        // in some way... just report complete new text to the
-                        // input method.
-                        reported = reportExtractedText();
-                    }
-                    if (!reported && highlight != null) {
-                        int candStart = -1;
-                        int candEnd = -1;
-                        if (mText instanceof Spannable) {
-                            Spannable sp = (Spannable)mText;
-                            candStart = EditableInputConnection.getComposingSpanStart(sp);
-                            candEnd = EditableInputConnection.getComposingSpanEnd(sp);
-                        }
-                        imm.updateSelection(this, selStart, selEnd, candStart, candEnd);
-                    }
-                }
-                
-                if (imm.isWatchingCursor(this) && highlight != null) {
-                    highlight.computeBounds(ims.mTmpRectF, true);
-                    ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0;
-    
-                    canvas.getMatrix().mapPoints(ims.mTmpOffset);
-                    ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]);
-    
-                    ims.mTmpRectF.offset(0, cursorOffsetVertical);
-    
-                    ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5),
-                            (int)(ims.mTmpRectF.top + 0.5),
-                            (int)(ims.mTmpRectF.right + 0.5),
-                            (int)(ims.mTmpRectF.bottom + 0.5));
-    
-                    imm.updateCursor(this,
-                            ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top,
-                            ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom);
-                }
-            }
-        }
 
-        if (mCorrectionHighlighter != null) {
-            mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
-        }
-
-        if (drawCursor) {
-            drawCursor(canvas, cursorOffsetVertical);
-            // Rely on the drawable entirely, do not draw the cursor line.
-            // Has to be done after the IMM related code above which relies on the highlight.
-            highlight = null;
-        }
-
-        if (canHaveDisplayList() && canvas.isHardwareAccelerated()) {
-            final int width = mRight - mLeft;
-            final int height = mBottom - mTop;
-
-            if (mTextDisplayList == null || !mTextDisplayList.isValid() ||
-                    !mTextDisplayListIsValid) {
-                if (mTextDisplayList == null) {
-                    mTextDisplayList = getHardwareRenderer().createDisplayList("Text");
-                }
-
-                final HardwareCanvas hardwareCanvas = mTextDisplayList.start();
-                try {
-                    hardwareCanvas.setViewport(width, height);
-                    // The dirty rect should always be null for a display list
-                    hardwareCanvas.onPreDraw(null);
-                    layout.draw(hardwareCanvas, highlight, mHighlightPaint, cursorOffsetVertical);
-                } finally {
-                    hardwareCanvas.onPostDraw();
-                    mTextDisplayList.end();
-                    mTextDisplayListIsValid = true;
-                }
-            }
-            ((HardwareCanvas) canvas).drawDisplayList(mTextDisplayList,
-                    mScrollX + width, mScrollY + height, null);
+        if (mEditor != null) {
+            getEditor().onDraw(canvas, layout, cursorOffsetVertical);
         } else {
-            layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
-        }
+            layout.draw(canvas, null, null, cursorOffsetVertical);
 
-        if (mMarquee != null && mMarquee.shouldDrawGhost()) {
-            canvas.translate((int) mMarquee.getGhostOffset(), 0.0f);
-            layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
+            if (mMarquee != null && mMarquee.shouldDrawGhost()) {
+                canvas.translate((int) mMarquee.getGhostOffset(), 0.0f);
+                layout.draw(canvas, null, null, cursorOffsetVertical);
+            }
         }
 
         canvas.restore();
@@ -5028,7 +4683,7 @@
 
     private void updateCursorsPositions() {
         if (mCursorDrawableRes == 0) {
-            mCursorCount = 0;
+            getEditor().mCursorCount = 0;
             return; 
         }
 
@@ -5037,40 +4692,39 @@
         final int top = mLayout.getLineTop(line);
         final int bottom = mLayout.getLineTop(line + 1);
 
-        mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1;
+        getEditor().mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1;
 
         int middle = bottom;
-        if (mCursorCount == 2) {
+        if (getEditor().mCursorCount == 2) {
             // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)}
             middle = (top + bottom) >> 1;
         }
 
         updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset));
 
-        if (mCursorCount == 2) {
+        if (getEditor().mCursorCount == 2) {
             updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset));
         }
     }
 
     private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
-        if (mCursorDrawable[cursorIndex] == null)
-            mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes);
+        if (getEditor().mCursorDrawable[cursorIndex] == null)
+            getEditor().mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes);
 
         if (mTempRect == null) mTempRect = new Rect();
-
-        mCursorDrawable[cursorIndex].getPadding(mTempRect);
-        final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth();
+        getEditor().mCursorDrawable[cursorIndex].getPadding(mTempRect);
+        final int width = getEditor().mCursorDrawable[cursorIndex].getIntrinsicWidth();
         horizontal = Math.max(0.5f, horizontal - 0.5f);
         final int left = (int) (horizontal) - mTempRect.left;
-        mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
+        getEditor().mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
                 bottom + mTempRect.bottom);
     }
 
     private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
         final boolean translate = cursorOffsetVertical != 0;
         if (translate) canvas.translate(0, cursorOffsetVertical);
-        for (int i = 0; i < mCursorCount; i++) {
-            mCursorDrawable[i].draw(canvas);
+        for (int i = 0; i < getEditor().mCursorCount; i++) {
+            getEditor().mCursorDrawable[i].draw(canvas);
         }
         if (translate) canvas.translate(0, -cursorOffsetVertical);
     }
@@ -5104,18 +4758,23 @@
                 r.left = (int) mLayout.getPrimaryHorizontal(selStart);
                 r.right = (int) mLayout.getPrimaryHorizontal(selEnd);
             } else {
-                // Selection extends across multiple lines -- the focused
-                // rect covers the entire width.
-                if (mHighlightPath == null) mHighlightPath = new Path();
-                if (mHighlightPathBogus) {
-                    mHighlightPath.reset();
-                    mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
-                    mHighlightPathBogus = false;
-                }
-                synchronized (sTempRect) {
-                    mHighlightPath.computeBounds(sTempRect, true);
-                    r.left = (int)sTempRect.left-1;
-                    r.right = (int)sTempRect.right+1;
+                // Selection extends across multiple lines -- make the focused
+                // rect cover the entire width.
+                if (mEditor != null) {
+                    if (getEditor().mHighlightPath == null) getEditor().mHighlightPath = new Path();
+                    if (getEditor().mHighlightPathBogus) {
+                        getEditor().mHighlightPath.reset();
+                        mLayout.getSelectionPath(selStart, selEnd, getEditor().mHighlightPath);
+                        getEditor().mHighlightPathBogus = false;
+                    }
+                    synchronized (TEMP_RECTF) {
+                        getEditor().mHighlightPath.computeBounds(TEMP_RECTF, true);
+                        r.left = (int)TEMP_RECTF.left-1;
+                        r.right = (int)TEMP_RECTF.right+1;
+                    }
+                } else {
+                    r.left = 0;
+                    r.right = getMeasuredWidth();
                 }
             }
         }
@@ -5127,6 +4786,8 @@
             paddingTop += getVerticalOffset(false);
         }
         r.offset(paddingLeft, paddingTop);
+        int paddingBottom = getExtendedPaddingBottom();
+        r.bottom += paddingBottom;
     }
 
     /**
@@ -5211,7 +4872,7 @@
     @Override
     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK) {
-            boolean isInSelectionMode = mSelectionActionMode != null;
+            boolean isInSelectionMode = mEditor != null && getEditor().mSelectionActionMode != null;
 
             if (isInSelectionMode) {
                 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
@@ -5269,14 +4930,16 @@
         // but adding that is a more complicated change.
         KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
         if (which == 1) {
-            mInput.onKeyUp(this, (Editable)mText, keyCode, up);
+            // mEditor and getEditor().mInput are not null from doKeyDown
+            getEditor().mKeyListener.onKeyUp(this, (Editable)mText, keyCode, up);
             while (--repeatCount > 0) {
-                mInput.onKeyDown(this, (Editable)mText, keyCode, down);
-                mInput.onKeyUp(this, (Editable)mText, keyCode, up);
+                getEditor().mKeyListener.onKeyDown(this, (Editable)mText, keyCode, down);
+                getEditor().mKeyListener.onKeyUp(this, (Editable)mText, keyCode, up);
             }
             hideErrorIfUnchanged();
 
         } else if (which == 2) {
+            // mMovement is not null from doKeyDown
             mMovement.onKeyUp(this, (Spannable)mText, keyCode, up);
             while (--repeatCount > 0) {
                 mMovement.onKeyDown(this, (Spannable)mText, keyCode, down);
@@ -5294,7 +4957,7 @@
      * lines but where it doesn't make sense to insert newlines.
      */
     private boolean shouldAdvanceFocusOnEnter() {
-        if (mInput == null) {
+        if (getKeyListener() == null) {
             return false;
         }
 
@@ -5302,8 +4965,8 @@
             return true;
         }
 
-        if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
-            int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
+        if (mEditor != null && (getEditor().mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
+            int variation = getEditor().mInputType & EditorInfo.TYPE_MASK_VARIATION;
             if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
                     || variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT) {
                 return true;
@@ -5318,9 +4981,9 @@
      * of inserting the character.  Insert tabs only in multi-line editors.
      */
     private boolean shouldAdvanceFocusOnTab() {
-        if (mInput != null && !mSingleLine) {
-            if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
-                int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
+        if (getKeyListener() != null && !mSingleLine) {
+            if (mEditor != null && (getEditor().mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
+                int variation = getEditor().mInputType & EditorInfo.TYPE_MASK_VARIATION;
                 if (variation == EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE
                         || variation == EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) {
                     return false;
@@ -5342,13 +5005,13 @@
                     // running in a "modern" cupcake environment, so don't need
                     // to worry about the application trying to capture
                     // enter key events.
-                    if (mInputContentType != null) {
+                    if (mEditor != null && getEditor().mInputContentType != null) {
                         // If there is an action listener, given them a
                         // chance to consume the event.
-                        if (mInputContentType.onEditorActionListener != null &&
-                                mInputContentType.onEditorActionListener.onEditorAction(
+                        if (getEditor().mInputContentType.onEditorActionListener != null &&
+                                getEditor().mInputContentType.onEditorActionListener.onEditorAction(
                                 this, EditorInfo.IME_NULL, event)) {
-                            mInputContentType.enterDown = true;
+                            getEditor().mInputContentType.enterDown = true;
                             // We are consuming the enter key for them.
                             return -1;
                         }
@@ -5385,21 +5048,21 @@
 
                 // Has to be done on key down (and not on key up) to correctly be intercepted.
             case KeyEvent.KEYCODE_BACK:
-                if (mSelectionActionMode != null) {
+                if (mEditor != null && getEditor().mSelectionActionMode != null) {
                     stopSelectionActionMode();
                     return -1;
                 }
                 break;
         }
 
-        if (mInput != null) {
+        if (mEditor != null && getEditor().mKeyListener != null) {
             resetErrorChangedFlag();
 
             boolean doDown = true;
             if (otherEvent != null) {
                 try {
                     beginBatchEdit();
-                    final boolean handled = mInput.onKeyOther(this, (Editable) mText, otherEvent);
+                    final boolean handled = getEditor().mKeyListener.onKeyOther(this, (Editable) mText, otherEvent);
                     hideErrorIfUnchanged();
                     doDown = false;
                     if (handled) {
@@ -5415,7 +5078,7 @@
             
             if (doDown) {
                 beginBatchEdit();
-                final boolean handled = mInput.onKeyDown(this, (Editable) mText, keyCode, event);
+                final boolean handled = getEditor().mKeyListener.onKeyDown(this, (Editable) mText, keyCode, event);
                 endBatchEdit();
                 hideErrorIfUnchanged();
                 if (handled) return 1;
@@ -5461,14 +5124,14 @@
          * that error showing.  Otherwise, we take down whatever
          * error was showing when the user types something.
          */
-        mErrorWasChanged = false;
+        if (mEditor != null) getEditor().mErrorWasChanged = false;
     }
 
     /**
      * @hide
      */
     public void hideErrorIfUnchanged() {
-        if (mError != null && !mErrorWasChanged) {
+        if (mEditor != null && getEditor().mError != null && !getEditor().mErrorWasChanged) {
             setError(null, null);
         }
     }
@@ -5506,11 +5169,11 @@
 
             case KeyEvent.KEYCODE_ENTER:
                 if (event.hasNoModifiers()) {
-                    if (mInputContentType != null
-                            && mInputContentType.onEditorActionListener != null
-                            && mInputContentType.enterDown) {
-                        mInputContentType.enterDown = false;
-                        if (mInputContentType.onEditorActionListener.onEditorAction(
+                    if (mEditor != null && getEditor().mInputContentType != null
+                            && getEditor().mInputContentType.onEditorActionListener != null
+                            && getEditor().mInputContentType.enterDown) {
+                        getEditor().mInputContentType.enterDown = false;
+                        if (getEditor().mInputContentType.onEditorActionListener.onEditorAction(
                                 this, EditorInfo.IME_NULL, event)) {
                             return true;
                         }
@@ -5561,8 +5224,8 @@
                 break;
         }
 
-        if (mInput != null)
-            if (mInput.onKeyUp(this, (Editable) mText, keyCode, event))
+        if (mEditor != null && getEditor().mKeyListener != null)
+            if (getEditor().mKeyListener.onKeyUp(this, (Editable) mText, keyCode, event))
                 return true;
 
         if (mMovement != null && mLayout != null)
@@ -5574,22 +5237,23 @@
 
     @Override
     public boolean onCheckIsTextEditor() {
-        return mInputType != EditorInfo.TYPE_NULL;
+        return mEditor != null && getEditor().mInputType != EditorInfo.TYPE_NULL;
     }
 
     @Override
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        createEditorIfNeeded("onCreateInputConnection");
         if (onCheckIsTextEditor() && isEnabled()) {
-            if (mInputMethodState == null) {
-                mInputMethodState = new InputMethodState();
+            if (getEditor().mInputMethodState == null) {
+                getEditor().mInputMethodState = new InputMethodState();
             }
-            outAttrs.inputType = mInputType;
-            if (mInputContentType != null) {
-                outAttrs.imeOptions = mInputContentType.imeOptions;
-                outAttrs.privateImeOptions = mInputContentType.privateImeOptions;
-                outAttrs.actionLabel = mInputContentType.imeActionLabel;
-                outAttrs.actionId = mInputContentType.imeActionId;
-                outAttrs.extras = mInputContentType.extras;
+            outAttrs.inputType = getInputType();
+            if (getEditor().mInputContentType != null) {
+                outAttrs.imeOptions = getEditor().mInputContentType.imeOptions;
+                outAttrs.privateImeOptions = getEditor().mInputContentType.privateImeOptions;
+                outAttrs.actionLabel = getEditor().mInputContentType.imeActionLabel;
+                outAttrs.actionId = getEditor().mInputContentType.imeActionId;
+                outAttrs.extras = getEditor().mInputContentType.extras;
             } else {
                 outAttrs.imeOptions = EditorInfo.IME_NULL;
             }
@@ -5623,7 +5287,7 @@
                 InputConnection ic = new EditableInputConnection(this);
                 outAttrs.initialSelStart = getSelectionStart();
                 outAttrs.initialSelEnd = getSelectionEnd();
-                outAttrs.initialCapsMode = ic.getCursorCapsMode(mInputType);
+                outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
                 return ic;
             }
         }
@@ -5715,13 +5379,13 @@
     }
     
     boolean reportExtractedText() {
-        final InputMethodState ims = mInputMethodState;
+        final InputMethodState ims = getEditor().mInputMethodState;
         if (ims != null) {
             final boolean contentChanged = ims.mContentChanged;
             if (contentChanged || ims.mSelectionModeChanged) {
                 ims.mContentChanged = false;
                 ims.mSelectionModeChanged = false;
-                final ExtractedTextRequest req = mInputMethodState.mExtracting;
+                final ExtractedTextRequest req = ims.mExtracting;
                 if (req != null) {
                     InputMethodManager imm = InputMethodManager.peekInstance();
                     if (imm != null) {
@@ -5737,8 +5401,7 @@
                                     + ims.mTmpExtracted.partialStartOffset
                                     + " end=" + ims.mTmpExtracted.partialEndOffset
                                     + ": " + ims.mTmpExtracted.text);
-                            imm.updateExtractedText(this, req.token,
-                                    mInputMethodState.mTmpExtracted);
+                            imm.updateExtractedText(this, req.token, ims.mTmpExtracted);
                             ims.mChangedStart = EXTRACT_UNKNOWN;
                             ims.mChangedEnd = EXTRACT_UNKNOWN;
                             ims.mChangedDelta = 0;
@@ -5815,8 +5478,8 @@
      * @hide
      */
     public void setExtracting(ExtractedTextRequest req) {
-        if (mInputMethodState != null) {
-            mInputMethodState.mExtracting = req;
+        if (getEditor().mInputMethodState != null) {
+            getEditor().mInputMethodState.mExtracting = req;
         }
         // This would stop a possible selection mode, but no such mode is started in case
         // extracted mode will start. Some text is selected though, and will trigger an action mode
@@ -5847,109 +5510,20 @@
      * @param info The auto correct info about the text that was corrected.
      */
     public void onCommitCorrection(CorrectionInfo info) {
-        if (mCorrectionHighlighter == null) {
-            mCorrectionHighlighter = new CorrectionHighlighter();
+        if (mEditor == null) return;
+        if (getEditor().mCorrectionHighlighter == null) {
+            getEditor().mCorrectionHighlighter = new CorrectionHighlighter();
         } else {
-            mCorrectionHighlighter.invalidate(false);
+            getEditor().mCorrectionHighlighter.invalidate(false);
         }
 
-        mCorrectionHighlighter.highlight(info);
-    }
-
-    private class CorrectionHighlighter {
-        private final Path mPath = new Path();
-        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        private int mStart, mEnd;
-        private long mFadingStartTime;
-        private final static int FADE_OUT_DURATION = 400;
-
-        public CorrectionHighlighter() {
-            mPaint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale);
-            mPaint.setStyle(Paint.Style.FILL);
-        }
-
-        public void highlight(CorrectionInfo info) {
-            mStart = info.getOffset();
-            mEnd = mStart + info.getNewText().length();
-            mFadingStartTime = SystemClock.uptimeMillis();
-
-            if (mStart < 0 || mEnd < 0) {
-                stopAnimation();
-            }
-        }
-
-        public void draw(Canvas canvas, int cursorOffsetVertical) {
-            if (updatePath() && updatePaint()) {
-                if (cursorOffsetVertical != 0) {
-                    canvas.translate(0, cursorOffsetVertical);
-                }
-
-                canvas.drawPath(mPath, mPaint);
-
-                if (cursorOffsetVertical != 0) {
-                    canvas.translate(0, -cursorOffsetVertical);
-                }
-                invalidate(true); // TODO invalidate cursor region only
-            } else {
-                stopAnimation();
-                invalidate(false); // TODO invalidate cursor region only
-            }
-        }
-
-        private boolean updatePaint() {
-            final long duration = SystemClock.uptimeMillis() - mFadingStartTime;
-            if (duration > FADE_OUT_DURATION) return false;
-
-            final float coef = 1.0f - (float) duration / FADE_OUT_DURATION;
-            final int highlightColorAlpha = Color.alpha(mHighlightColor);
-            final int color = (mHighlightColor & 0x00FFFFFF) +
-                    ((int) (highlightColorAlpha * coef) << 24);
-            mPaint.setColor(color);
-            return true;
-        }
-
-        private boolean updatePath() {
-            final Layout layout = TextView.this.mLayout;
-            if (layout == null) return false;
-
-            // Update in case text is edited while the animation is run
-            final int length = mText.length();
-            int start = Math.min(length, mStart);
-            int end = Math.min(length, mEnd);
-
-            mPath.reset();
-            TextView.this.mLayout.getSelectionPath(start, end, mPath);
-            return true;
-        }
-
-        private void invalidate(boolean delayed) {
-            if (TextView.this.mLayout == null) return;
-
-            synchronized (sTempRect) {
-                mPath.computeBounds(sTempRect, false);
-
-                int left = getCompoundPaddingLeft();
-                int top = getExtendedPaddingTop() + getVerticalOffset(true);
-
-                if (delayed) {
-                    TextView.this.postInvalidateDelayed(16, // 60 Hz update
-                            left + (int) sTempRect.left, top + (int) sTempRect.top,
-                            left + (int) sTempRect.right, top + (int) sTempRect.bottom);
-                } else {
-                    TextView.this.postInvalidate((int) sTempRect.left, (int) sTempRect.top,
-                            (int) sTempRect.right, (int) sTempRect.bottom);
-                }
-            }
-        }
-
-        private void stopAnimation() {
-            TextView.this.mCorrectionHighlighter = null;
-        }
+        getEditor().mCorrectionHighlighter.highlight(info);
     }
 
     public void beginBatchEdit() {
-        mInBatchEditControllers = true;
-        final InputMethodState ims = mInputMethodState;
+        if (mEditor == null) return;
+        getEditor().mInBatchEditControllers = true;
+        final InputMethodState ims = getEditor().mInputMethodState;
         if (ims != null) {
             int nesting = ++ims.mBatchEditNesting;
             if (nesting == 1) {
@@ -5971,8 +5545,9 @@
     }
     
     public void endBatchEdit() {
-        mInBatchEditControllers = false;
-        final InputMethodState ims = mInputMethodState;
+        if (mEditor == null) return;
+        getEditor().mInBatchEditControllers = false;
+        final InputMethodState ims = getEditor().mInputMethodState;
         if (ims != null) {
             int nesting = --ims.mBatchEditNesting;
             if (nesting == 0) {
@@ -5982,7 +5557,7 @@
     }
     
     void ensureEndedBatchEdit() {
-        final InputMethodState ims = mInputMethodState;
+        final InputMethodState ims = getEditor().mInputMethodState;
         if (ims != null && ims.mBatchEditNesting != 0) {
             ims.mBatchEditNesting = 0;
             finishBatchEdit(ims);
@@ -6010,7 +5585,7 @@
         }
 
         if (curs >= 0) {
-            mHighlightPathBogus = true;
+            getEditor().mHighlightPathBogus = true;
             makeBlink();
             bringPointIntoView(curs);
         }
@@ -6025,7 +5600,7 @@
     public void onBeginBatchEdit() {
         // intentionally empty
     }
-    
+
     /**
      * Called by the framework in response to a request to end a batch
      * of edit operations through a call to link {@link #endBatchEdit}.
@@ -6090,8 +5665,8 @@
         super.resetResolvedLayoutDirection();
 
         if (mLayoutAlignment != null &&
-                (mTextAlign == TextAlign.VIEW_START ||
-                mTextAlign == TextAlign.VIEW_END)) {
+                (mTextAlign == TEXT_ALIGN.VIEW_START ||
+                mTextAlign == TEXT_ALIGN.VIEW_END)) {
             mLayoutAlignment = null;
         }
     }
@@ -6099,7 +5674,7 @@
     private Layout.Alignment getLayoutAlignment() {
         if (mLayoutAlignment == null) {
             Layout.Alignment alignment;
-            TextAlign textAlign = mTextAlign;
+            TEXT_ALIGN textAlign = mTextAlign;
             switch (textAlign) {
                 case INHERIT:
                     // fall through to gravity temporarily
@@ -6167,7 +5742,7 @@
         mOldMaximum = mMaximum;
         mOldMaxMode = mMaxMode;
 
-        mHighlightPathBogus = true;
+        if (mEditor != null) getEditor().mHighlightPathBogus = true;
 
         if (wantWidth < 0) {
             wantWidth = 0;
@@ -6177,7 +5752,7 @@
         }
 
         Layout.Alignment alignment = getLayoutAlignment();
-        boolean shouldEllipsize = mEllipsize != null && mInput == null;
+        boolean shouldEllipsize = mEllipsize != null && getKeyListener() == null;
         final boolean switchEllipsize = mEllipsize == TruncateAt.MARQUEE &&
                 mMarqueeFadeMode != MARQUEE_FADE_NORMAL;
         TruncateAt effectiveEllipsize = mEllipsize;
@@ -6294,7 +5869,7 @@
         if (mText instanceof Spannable) {
             result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth,
                     alignment, mTextDir, mSpacingMult,
-                    mSpacingAdd, mIncludePad, mInput == null ? effectiveEllipsize : null,
+                    mSpacingAdd, mIncludePad, getKeyListener() == null ? effectiveEllipsize : null,
                             ellipsisWidth);
         } else {
             if (boring == UNKNOWN_BORING) {
@@ -6755,7 +6330,7 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        if (changed) mTextDisplayListIsValid = false;
+        if (changed && mEditor != null) getEditor().mTextDisplayListIsValid = false;
     }
 
     /**
@@ -6981,11 +6556,11 @@
             // This offsets because getInterestingRect() is in terms of viewport coordinates, but
             // requestRectangleOnScreen() is in terms of content coordinates.
 
-            if (mTempRect == null) mTempRect = new Rect();
             // The offsets here are to ensure the rectangle we are using is
             // within our view bounds, in case the cursor is on the far left
             // or right.  If it isn't withing the bounds, then this request
             // will be ignored.
+            if (mTempRect == null) mTempRect = new Rect();
             mTempRect.set(x - 2, top, x + 2, bottom);
             getInterestingRect(mTempRect, line);
             mTempRect.offset(mScrollX, mScrollY);
@@ -7205,11 +6780,11 @@
      * @param singleLine
      */
     private void setInputTypeSingleLine(boolean singleLine) {
-        if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
+        if (mEditor != null && (getEditor().mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
             if (singleLine) {
-                mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+                getEditor().mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
             } else {
-                mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+                getEditor().mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
             }
         }
     }
@@ -7288,7 +6863,8 @@
      */
     @android.view.RemotableViewMethod
     public void setSelectAllOnFocus(boolean selectAllOnFocus) {
-        mSelectAllOnFocus = selectAllOnFocus;
+        createEditorIfNeeded("setSelectAllOnFocus");
+        getEditor().mSelectAllOnFocus = selectAllOnFocus;
 
         if (selectAllOnFocus && !(mText instanceof Spannable)) {
             setText(mText, BufferType.SPANNABLE);
@@ -7302,8 +6878,10 @@
      */
     @android.view.RemotableViewMethod
     public void setCursorVisible(boolean visible) {
-        if (mCursorVisible != visible) {
-            mCursorVisible = visible;
+        if (visible && mEditor == null) return; // visible is the default value with no edit data
+        createEditorIfNeeded("setCursorVisible");
+        if (getEditor().mCursorVisible != visible) {
+            getEditor().mCursorVisible = visible;
             invalidate();
 
             makeBlink();
@@ -7314,7 +6892,8 @@
     }
 
     private boolean isCursorVisible() {
-        return mCursorVisible && isTextEditable();
+        // The default value is true, even when there is no associated Editor
+        return mEditor == null ? true : (getEditor().mCursorVisible && isTextEditable());
     }
 
     private boolean canMarquee() {
@@ -7326,7 +6905,7 @@
 
     private void startMarquee() {
         // Do not ellipsize EditText
-        if (mInput != null) return;
+        if (getKeyListener() != null) return;
 
         if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
             return;
@@ -7376,142 +6955,6 @@
         }
     }
 
-    private static final class Marquee extends Handler {
-        // TODO: Add an option to configure this
-        private static final float MARQUEE_DELTA_MAX = 0.07f;
-        private static final int MARQUEE_DELAY = 1200;
-        private static final int MARQUEE_RESTART_DELAY = 1200;
-        private static final int MARQUEE_RESOLUTION = 1000 / 30;
-        private static final int MARQUEE_PIXELS_PER_SECOND = 30;
-
-        private static final byte MARQUEE_STOPPED = 0x0;
-        private static final byte MARQUEE_STARTING = 0x1;
-        private static final byte MARQUEE_RUNNING = 0x2;
-
-        private static final int MESSAGE_START = 0x1;
-        private static final int MESSAGE_TICK = 0x2;
-        private static final int MESSAGE_RESTART = 0x3;
-
-        private final WeakReference<TextView> mView;
-
-        private byte mStatus = MARQUEE_STOPPED;
-        private final float mScrollUnit;
-        private float mMaxScroll;
-        float mMaxFadeScroll;
-        private float mGhostStart;
-        private float mGhostOffset;
-        private float mFadeStop;
-        private int mRepeatLimit;
-
-        float mScroll;
-
-        Marquee(TextView v) {
-            final float density = v.getContext().getResources().getDisplayMetrics().density;
-            mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / MARQUEE_RESOLUTION;
-            mView = new WeakReference<TextView>(v);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_START:
-                    mStatus = MARQUEE_RUNNING;
-                    tick();
-                    break;
-                case MESSAGE_TICK:
-                    tick();
-                    break;
-                case MESSAGE_RESTART:
-                    if (mStatus == MARQUEE_RUNNING) {
-                        if (mRepeatLimit >= 0) {
-                            mRepeatLimit--;
-                        }
-                        start(mRepeatLimit);
-                    }
-                    break;
-            }
-        }
-
-        void tick() {
-            if (mStatus != MARQUEE_RUNNING) {
-                return;
-            }
-
-            removeMessages(MESSAGE_TICK);
-
-            final TextView textView = mView.get();
-            if (textView != null && (textView.isFocused() || textView.isSelected())) {
-                mScroll += mScrollUnit;
-                if (mScroll > mMaxScroll) {
-                    mScroll = mMaxScroll;
-                    sendEmptyMessageDelayed(MESSAGE_RESTART, MARQUEE_RESTART_DELAY);
-                } else {
-                    sendEmptyMessageDelayed(MESSAGE_TICK, MARQUEE_RESOLUTION);
-                }
-                textView.invalidate();
-            }
-        }
-
-        void stop() {
-            mStatus = MARQUEE_STOPPED;
-            removeMessages(MESSAGE_START);
-            removeMessages(MESSAGE_RESTART);
-            removeMessages(MESSAGE_TICK);
-            resetScroll();
-        }
-
-        private void resetScroll() {
-            mScroll = 0.0f;
-            final TextView textView = mView.get();
-            if (textView != null) textView.invalidate();
-        }
-
-        void start(int repeatLimit) {
-            if (repeatLimit == 0) {
-                stop();
-                return;
-            }
-            mRepeatLimit = repeatLimit;
-            final TextView textView = mView.get();
-            if (textView != null && textView.mLayout != null) {
-                mStatus = MARQUEE_STARTING;
-                mScroll = 0.0f;
-                final int textWidth = textView.getWidth() - textView.getCompoundPaddingLeft() -
-                        textView.getCompoundPaddingRight();
-                final float lineWidth = textView.mLayout.getLineWidth(0);
-                final float gap = textWidth / 3.0f;
-                mGhostStart = lineWidth - textWidth + gap;
-                mMaxScroll = mGhostStart + textWidth;
-                mGhostOffset = lineWidth + gap;
-                mFadeStop = lineWidth + textWidth / 6.0f;
-                mMaxFadeScroll = mGhostStart + lineWidth + lineWidth;
-
-                textView.invalidate();
-                sendEmptyMessageDelayed(MESSAGE_START, MARQUEE_DELAY);
-            }
-        }
-
-        float getGhostOffset() {
-            return mGhostOffset;
-        }
-
-        boolean shouldDrawLeftFade() {
-            return mScroll <= mFadeStop;
-        }
-
-        boolean shouldDrawGhost() {
-            return mStatus == MARQUEE_RUNNING && mScroll > mGhostStart;
-        }
-
-        boolean isRunning() {
-            return mStatus == MARQUEE_RUNNING;
-        }
-
-        boolean isStopped() {
-            return mStatus == MARQUEE_STOPPED;
-        }
-    }
-
     /**
      * This method is called when the text is changed, in case any subclasses
      * would like to know.
@@ -7540,7 +6983,7 @@
      */
     protected void onSelectionChanged(int selStart, int selEnd) {
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
-        mTextDisplayListIsValid = false;
+        getEditor().mTextDisplayListIsValid = false;
     }
 
     /**
@@ -7619,13 +7062,7 @@
             }
         }
 
-        updateSpellCheckSpans(start, start + after, false);
-        mTextDisplayListIsValid = false;
-
-        // Hide the controllers as soon as text is modified (typing, procedural...)
-        // We do not hide the span controllers, since they can be added when a new text is
-        // inserted into the text view (voice IME).
-        hideCursorControllers();
+        if (mEditor != null) getEditor().sendOnTextChanged(start, after);
     }
 
     /**
@@ -7647,7 +7084,7 @@
      * through a thunk.
      */
     void handleTextChanged(CharSequence buffer, int start, int before, int after) {
-        final InputMethodState ims = mInputMethodState;
+        final InputMethodState ims = mEditor == null ? null : getEditor().mInputMethodState;
         if (ims == null || ims.mBatchEditNesting == 0) {
             updateAfterEdit();
         }
@@ -7666,7 +7103,7 @@
         sendOnTextChanged(buffer, start, before, after);
         onTextChanged(buffer, start, before, after);
     }
-    
+
     /**
      * Not private so it can be called from an inner class without going
      * through a thunk.
@@ -7677,18 +7114,13 @@
 
         boolean selChanged = false;
         int newSelStart=-1, newSelEnd=-1;
-        
-        final InputMethodState ims = mInputMethodState;
-        
+
+        final InputMethodState ims = mEditor == null ? null : getEditor().mInputMethodState;
+
         if (what == Selection.SELECTION_END) {
-            mHighlightPathBogus = true;
             selChanged = true;
             newSelEnd = newStart;
 
-            if (!isFocused()) {
-                mSelectionMoved = true;
-            }
-
             if (oldStart >= 0 || newStart >= 0) {
                 invalidateCursor(Selection.getSelectionStart(buf), oldStart, newStart);
                 registerForPreDraw();
@@ -7697,14 +7129,9 @@
         }
 
         if (what == Selection.SELECTION_START) {
-            mHighlightPathBogus = true;
             selChanged = true;
             newSelStart = newStart;
 
-            if (!isFocused()) {
-                mSelectionMoved = true;
-            }
-
             if (oldStart >= 0 || newStart >= 0) {
                 int end = Selection.getSelectionEnd(buf);
                 invalidateCursor(end, oldStart, newStart);
@@ -7712,6 +7139,11 @@
         }
 
         if (selChanged) {
+            if (mEditor != null) {
+                getEditor().mHighlightPathBogus = true;
+                if (!isFocused()) getEditor().mSelectionMoved = true;
+            }
+
             if ((buf.getSpanFlags(what)&Spanned.SPAN_INTERMEDIATE) == 0) {
                 if (newSelStart < 0) {
                     newSelStart = Selection.getSelectionStart(buf);
@@ -7727,16 +7159,16 @@
                 what instanceof CharacterStyle) {
             if (ims == null || ims.mBatchEditNesting == 0) {
                 invalidate();
-                mHighlightPathBogus = true;
+                if (mEditor != null) getEditor().mHighlightPathBogus = true;
                 checkForResize();
             } else {
                 ims.mContentChanged = true;
             }
-            mTextDisplayListIsValid = false;
+            if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
         }
 
         if (MetaKeyKeyListener.isMetaTracker(buf, what)) {
-            mHighlightPathBogus = true;
+            if (mEditor != null) getEditor().mHighlightPathBogus = true;
             if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(buf, what)) {
                 ims.mSelectionModeChanged = true;
             }
@@ -7780,8 +7212,8 @@
             }
         }
 
-        if (mSpellChecker != null && newStart < 0 && what instanceof SpellCheckSpan) {
-            mSpellChecker.removeSpellCheckSpan((SpellCheckSpan) what);
+        if (mEditor != null && getEditor().mSpellChecker != null && newStart < 0 && what instanceof SpellCheckSpan) {
+            getEditor().mSpellChecker.removeSpellCheckSpan((SpellCheckSpan) what);
         }
     }
 
@@ -7790,289 +7222,16 @@
      */
     private void updateSpellCheckSpans(int start, int end, boolean createSpellChecker) {
         if (isTextEditable() && isSuggestionsEnabled() && !(this instanceof ExtractEditText)) {
-            if (mSpellChecker == null && createSpellChecker) {
-                mSpellChecker = new SpellChecker(this);
+            if (getEditor().mSpellChecker == null && createSpellChecker) {
+                getEditor().mSpellChecker = new SpellChecker(this);
             }
-            if (mSpellChecker != null) {
-                mSpellChecker.spellCheck(start, end);
+            if (getEditor().mSpellChecker != null) {
+                getEditor().mSpellChecker.spellCheck(start, end);
             }
         }
     }
 
     /**
-     * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
-     * pop-up should be displayed.
-     */
-    private class EasyEditSpanController {
-
-        private static final int DISPLAY_TIMEOUT_MS = 3000; // 3 secs
-
-        private EasyEditPopupWindow mPopupWindow;
-
-        private EasyEditSpan mEasyEditSpan;
-
-        private Runnable mHidePopup;
-
-        private void hide() {
-            if (mPopupWindow != null) {
-                mPopupWindow.hide();
-                TextView.this.removeCallbacks(mHidePopup);
-            }
-            removeSpans(mText);
-            mEasyEditSpan = null;
-        }
-
-        /**
-         * Monitors the changes in the text.
-         *
-         * <p>{@link ChangeWatcher#onSpanAdded(Spannable, Object, int, int)} cannot be used,
-         * as the notifications are not sent when a spannable (with spans) is inserted.
-         */
-        public void onTextChange(CharSequence buffer) {
-            adjustSpans(mText);
-
-            if (getWindowVisibility() != View.VISIBLE) {
-                // The window is not visible yet, ignore the text change.
-                return;
-            }
-
-            if (mLayout == null) {
-                // The view has not been layout yet, ignore the text change
-                return;
-            }
-
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (!(TextView.this instanceof ExtractEditText)
-                    && imm != null && imm.isFullscreenMode()) {
-                // The input is in extract mode. We do not have to handle the easy edit in the
-                // original TextView, as the ExtractEditText will do
-                return;
-            }
-
-            // Remove the current easy edit span, as the text changed, and remove the pop-up
-            // (if any)
-            if (mEasyEditSpan != null) {
-                if (mText instanceof Spannable) {
-                    ((Spannable) mText).removeSpan(mEasyEditSpan);
-                }
-                mEasyEditSpan = null;
-            }
-            if (mPopupWindow != null && mPopupWindow.isShowing()) {
-                mPopupWindow.hide();
-            }
-
-            // Display the new easy edit span (if any).
-            if (buffer instanceof Spanned) {
-                mEasyEditSpan = getSpan((Spanned) buffer);
-                if (mEasyEditSpan != null) {
-                    if (mPopupWindow == null) {
-                        mPopupWindow = new EasyEditPopupWindow();
-                        mHidePopup = new Runnable() {
-                            @Override
-                            public void run() {
-                                hide();
-                            }
-                        };
-                    }
-                    mPopupWindow.show(mEasyEditSpan);
-                    TextView.this.removeCallbacks(mHidePopup);
-                    TextView.this.postDelayed(mHidePopup, DISPLAY_TIMEOUT_MS);
-                }
-            }
-        }
-
-        /**
-         * Adjusts the spans by removing all of them except the last one.
-         */
-        private void adjustSpans(CharSequence buffer) {
-            // This method enforces that only one easy edit span is attached to the text.
-            // A better way to enforce this would be to listen for onSpanAdded, but this method
-            // cannot be used in this scenario as no notification is triggered when a text with
-            // spans is inserted into a text.
-            if (buffer instanceof Spannable) {
-                Spannable spannable = (Spannable) buffer;
-                EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(),
-                        EasyEditSpan.class);
-                for (int i = 0; i < spans.length - 1; i++) {
-                    spannable.removeSpan(spans[i]);
-                }
-            }
-        }
-
-        /**
-         * Removes all the {@link EasyEditSpan} currently attached.
-         */
-        private void removeSpans(CharSequence buffer) {
-            if (buffer instanceof Spannable) {
-                Spannable spannable = (Spannable) buffer;
-                EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(),
-                        EasyEditSpan.class);
-                for (int i = 0; i < spans.length; i++) {
-                    spannable.removeSpan(spans[i]);
-                }
-            }
-        }
-
-        private EasyEditSpan getSpan(Spanned spanned) {
-            EasyEditSpan[] easyEditSpans = spanned.getSpans(0, spanned.length(),
-                    EasyEditSpan.class);
-            if (easyEditSpans.length == 0) {
-                return null;
-            } else {
-                return easyEditSpans[0];
-            }
-        }
-    }
-
-    /**
-     * Displays the actions associated to an {@link EasyEditSpan}. The pop-up is controlled
-     * by {@link EasyEditSpanController}.
-     */
-    private class EasyEditPopupWindow extends PinnedPopupWindow
-            implements OnClickListener {
-        private static final int POPUP_TEXT_LAYOUT =
-                com.android.internal.R.layout.text_edit_action_popup_text;
-        private TextView mDeleteTextView;
-        private EasyEditSpan mEasyEditSpan;
-
-        @Override
-        protected void createPopupWindow() {
-            mPopupWindow = new PopupWindow(TextView.this.mContext, null,
-                    com.android.internal.R.attr.textSelectHandleWindowStyle);
-            mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-            mPopupWindow.setClippingEnabled(true);
-        }
-
-        @Override
-        protected void initContentView() {
-            LinearLayout linearLayout = new LinearLayout(TextView.this.getContext());
-            linearLayout.setOrientation(LinearLayout.HORIZONTAL);
-            mContentView = linearLayout;
-            mContentView.setBackgroundResource(
-                    com.android.internal.R.drawable.text_edit_side_paste_window);
-
-            LayoutInflater inflater = (LayoutInflater)TextView.this.mContext.
-                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-            LayoutParams wrapContent = new LayoutParams(
-                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-            mDeleteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
-            mDeleteTextView.setLayoutParams(wrapContent);
-            mDeleteTextView.setText(com.android.internal.R.string.delete);
-            mDeleteTextView.setOnClickListener(this);
-            mContentView.addView(mDeleteTextView);
-        }
-
-        public void show(EasyEditSpan easyEditSpan) {
-            mEasyEditSpan = easyEditSpan;
-            super.show();
-        }
-
-        @Override
-        public void onClick(View view) {
-            if (view == mDeleteTextView) {
-                Editable editable = (Editable) mText;
-                int start = editable.getSpanStart(mEasyEditSpan);
-                int end = editable.getSpanEnd(mEasyEditSpan);
-                if (start >= 0 && end >= 0) {
-                    deleteText_internal(start, end);
-                }
-            }
-        }
-
-        @Override
-        protected int getTextOffset() {
-            // Place the pop-up at the end of the span
-            Editable editable = (Editable) mText;
-            return editable.getSpanEnd(mEasyEditSpan);
-        }
-
-        @Override
-        protected int getVerticalLocalPosition(int line) {
-            return mLayout.getLineBottom(line);
-        }
-
-        @Override
-        protected int clipVertically(int positionY) {
-            // As we display the pop-up below the span, no vertical clipping is required.
-            return positionY;
-        }
-    }
-
-    private class ChangeWatcher implements TextWatcher, SpanWatcher {
-
-        private CharSequence mBeforeText;
-
-        private EasyEditSpanController mEasyEditSpanController;
-
-        private ChangeWatcher() {
-            mEasyEditSpanController = new EasyEditSpanController();
-        }
-
-        public void beforeTextChanged(CharSequence buffer, int start,
-                                      int before, int after) {
-            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "beforeTextChanged start=" + start
-                    + " before=" + before + " after=" + after + ": " + buffer);
-
-            if (AccessibilityManager.getInstance(mContext).isEnabled()
-                    && !isPasswordInputType(mInputType)
-                    && !hasPasswordTransformationMethod()) {
-                mBeforeText = buffer.toString();
-            }
-
-            TextView.this.sendBeforeTextChanged(buffer, start, before, after);
-        }
-
-        public void onTextChanged(CharSequence buffer, int start,
-                                  int before, int after) {
-            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onTextChanged start=" + start
-                    + " before=" + before + " after=" + after + ": " + buffer);
-            TextView.this.handleTextChanged(buffer, start, before, after);
-
-            mEasyEditSpanController.onTextChange(buffer);
-
-            if (AccessibilityManager.getInstance(mContext).isEnabled() &&
-                    (isFocused() || isSelected() && isShown())) {
-                sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
-                mBeforeText = null;
-            }
-        }
-
-        public void afterTextChanged(Editable buffer) {
-            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "afterTextChanged: " + buffer);
-            TextView.this.sendAfterTextChanged(buffer);
-
-            if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) {
-                MetaKeyKeyListener.stopSelecting(TextView.this, buffer);
-            }
-        }
-
-        public void onSpanChanged(Spannable buf,
-                                  Object what, int s, int e, int st, int en) {
-            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanChanged s=" + s + " e=" + e
-                    + " st=" + st + " en=" + en + " what=" + what + ": " + buf);
-            TextView.this.spanChange(buf, what, s, st, e, en);
-        }
-
-        public void onSpanAdded(Spannable buf, Object what, int s, int e) {
-            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanAdded s=" + s + " e=" + e
-                    + " what=" + what + ": " + buf);
-            TextView.this.spanChange(buf, what, -1, s, -1, e);
-        }
-
-        public void onSpanRemoved(Spannable buf, Object what, int s, int e) {
-            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanRemoved s=" + s + " e=" + e
-                    + " what=" + what + ": " + buf);
-            TextView.this.spanChange(buf, what, s, -1, e, -1);
-        }
-
-        private void hideControllers() {
-            mEasyEditSpanController.hide();
-        }
-    }
-
-    /**
      * @hide
      */
     @Override
@@ -8092,7 +7251,7 @@
         // Because of View recycling in ListView, there is no easy way to know when a TextView with
         // selection becomes visible again. Until a better solution is found, stop text selection
         // mode (if any) as soon as this TextView is recycled.
-        hideControllers();
+        if (mEditor != null) hideControllers();
     }
 
     @Override
@@ -8110,95 +7269,14 @@
             super.onFocusChanged(focused, direction, previouslyFocusedRect);
             return;
         }
-        
-        mShowCursor = SystemClock.uptimeMillis();
 
-        ensureEndedBatchEdit();
+        if (mEditor != null) getEditor().onFocusChanged(focused, direction);
 
         if (focused) {
-            int selStart = getSelectionStart();
-            int selEnd = getSelectionEnd();
-
-            // SelectAllOnFocus fields are highlighted and not selected. Do not start text selection
-            // mode for these, unless there was a specific selection already started.
-            final boolean isFocusHighlighted = mSelectAllOnFocus && selStart == 0 &&
-                    selEnd == mText.length();
-            mCreatedWithASelection = mFrozenWithFocus && hasSelection() && !isFocusHighlighted;
-
-            if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) {
-                // If a tap was used to give focus to that view, move cursor at tap position.
-                // Has to be done before onTakeFocus, which can be overloaded.
-                final int lastTapPosition = getLastTapPosition();
-                if (lastTapPosition >= 0) {
-                    Selection.setSelection((Spannable) mText, lastTapPosition);
-                }
-
-                if (mMovement != null) {
-                    mMovement.onTakeFocus(this, (Spannable) mText, direction);
-                }
-
-                // The DecorView does not have focus when the 'Done' ExtractEditText button is
-                // pressed. Since it is the ViewAncestor's mView, it requests focus before
-                // ExtractEditText clears focus, which gives focus to the ExtractEditText.
-                // This special case ensure that we keep current selection in that case.
-                // It would be better to know why the DecorView does not have focus at that time.
-                if (((this instanceof ExtractEditText) || mSelectionMoved) &&
-                        selStart >= 0 && selEnd >= 0) {
-                    /*
-                     * Someone intentionally set the selection, so let them
-                     * do whatever it is that they wanted to do instead of
-                     * the default on-focus behavior.  We reset the selection
-                     * here instead of just skipping the onTakeFocus() call
-                     * because some movement methods do something other than
-                     * just setting the selection in theirs and we still
-                     * need to go through that path.
-                     */
-                    Selection.setSelection((Spannable) mText, selStart, selEnd);
-                }
-
-                if (mSelectAllOnFocus) {
-                    selectAll();
-                }
-
-                mTouchFocusSelected = true;
-            }
-
-            mFrozenWithFocus = false;
-            mSelectionMoved = false;
-
             if (mText instanceof Spannable) {
                 Spannable sp = (Spannable) mText;
                 MetaKeyKeyListener.resetMetaState(sp);
             }
-
-            makeBlink();
-
-            if (mError != null) {
-                showError();
-            }
-        } else {
-            if (mError != null) {
-                hideError();
-            }
-            // Don't leave us in the middle of a batch edit.
-            onEndBatchEdit();
-
-            if (this instanceof ExtractEditText) {
-                // terminateTextSelectionMode removes selection, which we want to keep when
-                // ExtractEditText goes out of focus.
-                final int selStart = getSelectionStart();
-                final int selEnd = getSelectionEnd();
-                hideControllers();
-                Selection.setSelection((Spannable) mText, selStart, selEnd);
-            } else {
-                hideControllers();
-                downgradeEasyCorrectionSpans();
-            }
-
-            // No need to create the controller
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.resetTouchOffsets();
-            }
         }
 
         startStopMarquee(focused);
@@ -8210,48 +7288,11 @@
         super.onFocusChanged(focused, direction, previouslyFocusedRect);
     }
 
-    private int getLastTapPosition() {
-        // No need to create the controller at that point, no last tap position saved
-        if (mSelectionModifierCursorController != null) {
-            int lastTapPosition = mSelectionModifierCursorController.getMinTouchOffset();
-            if (lastTapPosition >= 0) {
-                // Safety check, should not be possible.
-                if (lastTapPosition > mText.length()) {
-                    Log.e(LOG_TAG, "Invalid tap focus position (" + lastTapPosition + " vs "
-                            + mText.length() + ")");
-                    lastTapPosition = mText.length();
-                }
-                return lastTapPosition;
-            }
-        }
-
-        return -1;
-    }
-
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
 
-        if (hasWindowFocus) {
-            if (mBlink != null) {
-                mBlink.uncancel();
-                makeBlink();
-            }
-        } else {
-            if (mBlink != null) {
-                mBlink.cancel();
-            }
-            // Don't leave us in the middle of a batch edit.
-            onEndBatchEdit();
-            if (mInputContentType != null) {
-                mInputContentType.enterDown = false;
-            }
-
-            hideControllers();
-            if (mSuggestionsPopupWindow != null) {
-                mSuggestionsPopupWindow.onParentLostFocus();
-            }
-        }
+        if (mEditor != null) getEditor().onWindowFocusChanged(hasWindowFocus);
 
         startStopMarquee(hasWindowFocus);
     }
@@ -8259,7 +7300,7 @@
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
-        if (visibility != VISIBLE) {
+        if (mEditor != null && visibility != VISIBLE) {
             hideControllers();
         }
     }
@@ -8294,23 +7335,7 @@
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
 
-        if (hasSelectionController()) {
-            getSelectionController().onTouchEvent(event);
-        }
-
-        if (mShowSuggestionRunnable != null) {
-            removeCallbacks(mShowSuggestionRunnable);
-        }
-
-        if (action == MotionEvent.ACTION_DOWN) {
-            mLastDownPositionX = event.getX();
-            mLastDownPositionY = event.getY();
-
-            // Reset this state; it will be re-set if super.onTouchEvent
-            // causes focus to move to the view.
-            mTouchFocusSelected = false;
-            mIgnoreActionUpEvent = false;
-        }
+        if (mEditor != null) getEditor().onTouchEvent(event);
 
         final boolean superResult = super.onTouchEvent(event);
 
@@ -8319,13 +7344,13 @@
          * move the selection away from whatever the menu action was
          * trying to affect.
          */
-        if (mDiscardNextActionUp && action == MotionEvent.ACTION_UP) {
-            mDiscardNextActionUp = false;
+        if (mEditor != null && getEditor().mDiscardNextActionUp && action == MotionEvent.ACTION_UP) {
+            getEditor().mDiscardNextActionUp = false;
             return superResult;
         }
 
         final boolean touchIsFinished = (action == MotionEvent.ACTION_UP) &&
-                !mIgnoreActionUpEvent && isFocused();
+                (mEditor == null || !getEditor().mIgnoreActionUpEvent) && isFocused();
 
          if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
                 && mText instanceof Spannable && mLayout != null) {
@@ -8335,7 +7360,8 @@
                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
             }
 
-            if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && mTextIsSelectable) {
+            final boolean textIsSelectable = isTextSelectable();
+            if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) {
                 // The LinkMovementMethod which should handle taps on links has not been installed
                 // on non editable text that support text selection.
                 // We reproduce its behavior here to open links for these.
@@ -8348,34 +7374,33 @@
                 }
             }
 
-            if (touchIsFinished && (isTextEditable() || mTextIsSelectable)) {
+            if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
                 // Show the IME, except when selecting in read-only text.
                 final InputMethodManager imm = InputMethodManager.peekInstance();
                 viewClicked(imm);
-                if (!mTextIsSelectable) {
+                if (!textIsSelectable) {
                     handled |= imm != null && imm.showSoftInput(this, 0);
                 }
 
-                boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect();
+                boolean selectAllGotFocus = getEditor().mSelectAllOnFocus && didTouchFocusSelect();
                 hideControllers();
                 if (!selectAllGotFocus && mText.length() > 0) {
                     // Move cursor
                     final int offset = getOffsetForPosition(event.getX(), event.getY());
                     Selection.setSelection((Spannable) mText, offset);
-                    if (mSpellChecker != null) {
+                    if (getEditor().mSpellChecker != null) {
                         // When the cursor moves, the word that was typed may need spell check
-                        mSpellChecker.onSelectionChanged();
+                        getEditor().mSpellChecker.onSelectionChanged();
                     }
                     if (!extractedTextModeWillBeStarted()) {
                         if (isCursorInsideEasyCorrectionSpan()) {
-                            if (mShowSuggestionRunnable == null) {
-                                mShowSuggestionRunnable = new Runnable() {
-                                    public void run() {
-                                        showSuggestions();
-                                    }
-                                };
-                            }
-                            postDelayed(mShowSuggestionRunnable,
+                            getEditor().mShowSuggestionRunnable = new Runnable() {
+                                public void run() {
+                                    showSuggestions();
+                                }
+                            };
+                            // removeCallbacks is performed on every touch
+                            postDelayed(getEditor().mShowSuggestionRunnable,
                                     ViewConfiguration.getDoubleTapTimeout());
                         } else if (hasInsertionController()) {
                             getInsertionController().show();
@@ -8458,6 +7483,8 @@
     }
 
     private void prepareCursorControllers() {
+        if (mEditor == null) return;
+
         boolean windowSupportsHandles = false;
 
         ViewGroup.LayoutParams params = getRootView().getLayoutParams();
@@ -8467,23 +7494,23 @@
                     || windowParams.type > WindowManager.LayoutParams.LAST_SUB_WINDOW;
         }
 
-        mInsertionControllerEnabled = windowSupportsHandles && isCursorVisible() && mLayout != null;
-        mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() &&
+        getEditor().mInsertionControllerEnabled = windowSupportsHandles && isCursorVisible() && mLayout != null;
+        getEditor().mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() &&
                 mLayout != null;
 
-        if (!mInsertionControllerEnabled) {
+        if (!getEditor().mInsertionControllerEnabled) {
             hideInsertionPointCursorController();
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onDetached();
-                mInsertionPointCursorController = null;
+            if (getEditor().mInsertionPointCursorController != null) {
+                getEditor().mInsertionPointCursorController.onDetached();
+                getEditor().mInsertionPointCursorController = null;
             }
         }
 
-        if (!mSelectionControllerEnabled) {
+        if (!getEditor().mSelectionControllerEnabled) {
             stopSelectionActionMode();
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onDetached();
-                mSelectionModifierCursorController = null;
+            if (getEditor().mSelectionModifierCursorController != null) {
+                getEditor().mSelectionModifierCursorController.onDetached();
+                getEditor().mSelectionModifierCursorController = null;
             }
         }
     }
@@ -8503,19 +7530,18 @@
      * of interest.
      */
     public boolean didTouchFocusSelect() {
-        return mTouchFocusSelected;
+        return mEditor != null && getEditor().mTouchFocusSelected;
     }
     
     @Override
     public void cancelLongPress() {
         super.cancelLongPress();
-        mIgnoreActionUpEvent = true;
+        if (mEditor != null) getEditor().mIgnoreActionUpEvent = true;
     }
 
     @Override
     public boolean onTrackballEvent(MotionEvent event) {
-        if (mMovement != null && mText instanceof Spannable &&
-            mLayout != null) {
+        if (mMovement != null && mText instanceof Spannable && mLayout != null) {
             if (mMovement.onTrackballEvent(this, (Spannable) mText, event)) {
                 return true;
             }
@@ -8528,49 +7554,11 @@
         mScroller = s;
     }
 
-    private static class Blink extends Handler implements Runnable {
-        private final WeakReference<TextView> mView;
-        private boolean mCancelled;
-
-        public Blink(TextView v) {
-            mView = new WeakReference<TextView>(v);
-        }
-
-        public void run() {
-            if (mCancelled) {
-                return;
-            }
-
-            removeCallbacks(Blink.this);
-
-            TextView tv = mView.get();
-
-            if (tv != null && tv.shouldBlink()) {
-                if (tv.mLayout != null) {
-                    tv.invalidateCursorPath();
-                }
-
-                postAtTime(this, SystemClock.uptimeMillis() + BLINK);
-            }
-        }
-
-        void cancel() {
-            if (!mCancelled) {
-                removeCallbacks(Blink.this);
-                mCancelled = true;
-            }
-        }
-
-        void uncancel() {
-            mCancelled = false;
-        }
-    }
-
     /**
      * @return True when the TextView isFocused and has a valid zero-length selection (cursor).
      */
     private boolean shouldBlink() {
-        if (!isCursorVisible() || !isFocused()) return false;
+        if (mEditor == null || !isCursorVisible() || !isFocused()) return false;
 
         final int start = getSelectionStart();
         if (start < 0) return false;
@@ -8583,12 +7571,12 @@
 
     private void makeBlink() {
         if (shouldBlink()) {
-            mShowCursor = SystemClock.uptimeMillis();
-            if (mBlink == null) mBlink = new Blink(this);
-            mBlink.removeCallbacks(mBlink);
-            mBlink.postAtTime(mBlink, mShowCursor + BLINK);
+            getEditor().mShowCursor = SystemClock.uptimeMillis();
+            if (getEditor().mBlink == null) getEditor().mBlink = new Blink(this);
+            getEditor().mBlink.removeCallbacks(getEditor().mBlink);
+            getEditor().mBlink.postAtTime(getEditor().mBlink, getEditor().mShowCursor + BLINK);
         } else {
-            if (mBlink != null) mBlink.removeCallbacks(mBlink);
+            if (mEditor != null && getEditor().mBlink != null) getEditor().mBlink.removeCallbacks(getEditor().mBlink);
         }
     }
 
@@ -8787,7 +7775,7 @@
         // If you change this condition, make sure prepareCursorController is called anywhere
         // the value of this condition might be changed.
         if (mMovement == null || !mMovement.canSelectArbitrarily()) return false;
-        return isTextEditable() || (mTextIsSelectable && mText instanceof Spannable && isEnabled());
+        return isTextEditable() || (isTextSelectable() && mText instanceof Spannable && isEnabled());
     }
 
     private boolean canCut() {
@@ -8795,7 +7783,7 @@
             return false;
         }
 
-        if (mText.length() > 0 && hasSelection() && mText instanceof Editable && mInput != null) {
+        if (mText.length() > 0 && hasSelection() && mText instanceof Editable && mEditor != null && getEditor().mKeyListener != null) {
             return true;
         }
 
@@ -8816,7 +7804,7 @@
 
     private boolean canPaste() {
         return (mText instanceof Editable &&
-                mInput != null &&
+                mEditor != null && getEditor().mKeyListener != null &&
                 getSelectionStart() >= 0 &&
                 getSelectionEnd() >= 0 &&
                 ((ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE)).
@@ -8857,8 +7845,9 @@
             return selectAll();
         }
 
-        int klass = mInputType & InputType.TYPE_MASK_CLASS;
-        int variation = mInputType & InputType.TYPE_MASK_VARIATION;
+        int inputType = getInputType();
+        int klass = inputType & InputType.TYPE_MASK_CLASS;
+        int variation = inputType & InputType.TYPE_MASK_VARIATION;
 
         // Specific text field types: select the entire text for these
         if (klass == InputType.TYPE_CLASS_NUMBER ||
@@ -8928,17 +7917,17 @@
 
     void onLocaleChanged() {
         // Will be re-created on demand in getWordIterator with the proper new locale
-        mWordIterator = null;
+        getEditor().mWordIterator = null;
     }
 
     /**
      * @hide
      */
     public WordIterator getWordIterator() {
-        if (mWordIterator == null) {
-            mWordIterator = new WordIterator(getTextServicesLocale());
+        if (getEditor().mWordIterator == null) {
+            getEditor().mWordIterator = new WordIterator(getTextServicesLocale());
         }
-        return mWordIterator;
+        return getEditor().mWordIterator;
     }
 
     private long getCharRange(int offset) {
@@ -9192,17 +8181,6 @@
         return new DragShadowBuilder(shadowView);
     }
 
-    private static class DragLocalState {
-        public TextView sourceTextView;
-        public int start, end;
-
-        public DragLocalState(TextView sourceTextView, int start, int end) {
-            this.sourceTextView = sourceTextView;
-            this.start = start;
-            this.end = end;
-        }
-    }
-
     @Override
     public boolean performLongClick() {
         boolean handled = false;
@@ -9213,9 +8191,9 @@
         }
 
         // Long press in empty space moves cursor and shows the Paste affordance if available.
-        if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY) &&
-                mInsertionControllerEnabled) {
-            final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY);
+        if (!handled && mEditor != null && !isPositionOnText(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY) &&
+                getEditor().mInsertionControllerEnabled) {
+            final int offset = getOffsetForPosition(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY);
             stopSelectionActionMode();
             Selection.setSelection((Spannable) mText, offset);
             getInsertionController().showWithActionPopup();
@@ -9223,7 +8201,7 @@
             vibrate = false;
         }
 
-        if (!handled && mSelectionActionMode != null) {
+        if (!handled && (mEditor == null || getEditor().mSelectionActionMode != null)) {
             if (touchPositionIsInSelection()) {
                 // Start a drag
                 final int start = getSelectionStart();
@@ -9250,8 +8228,8 @@
             performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         }
 
-        if (handled) {
-            mDiscardNextActionUp = true;
+        if (handled && mEditor != null) {
+            getEditor().mDiscardNextActionUp = true;
         }
 
         return handled;
@@ -9280,10 +8258,10 @@
     }
 
     private PositionListener getPositionListener() {
-        if (mPositionListener == null) {
-            mPositionListener = new PositionListener();
+        if (getEditor().mPositionListener == null) {
+            getEditor().mPositionListener = new PositionListener();
         }
-        return mPositionListener;
+        return getEditor().mPositionListener;
     }
 
     private interface TextViewPositionListener {
@@ -9291,6 +8269,1420 @@
                 boolean parentPositionChanged, boolean parentScrolled);
     }
 
+    private boolean isPositionVisible(int positionX, int positionY) {
+        synchronized (TEMP_POSITION) {
+            final float[] position = TEMP_POSITION;
+            position[0] = positionX;
+            position[1] = positionY;
+            View view = this;
+
+            while (view != null) {
+                if (view != this) {
+                    // Local scroll is already taken into account in positionX/Y
+                    position[0] -= view.getScrollX();
+                    position[1] -= view.getScrollY();
+                }
+
+                if (position[0] < 0 || position[1] < 0 ||
+                        position[0] > view.getWidth() || position[1] > view.getHeight()) {
+                    return false;
+                }
+
+                if (!view.getMatrix().isIdentity()) {
+                    view.getMatrix().mapPoints(position);
+                }
+
+                position[0] += view.getLeft();
+                position[1] += view.getTop();
+
+                final ViewParent parent = view.getParent();
+                if (parent instanceof View) {
+                    view = (View) parent;
+                } else {
+                    // We've reached the ViewRoot, stop iterating
+                    view = null;
+                }
+            }
+        }
+
+        // We've been able to walk up the view hierarchy and the position was never clipped
+        return true;
+    }
+
+    private boolean isOffsetVisible(int offset) {
+        final int line = mLayout.getLineForOffset(offset);
+        final int lineBottom = mLayout.getLineBottom(line);
+        final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset);
+        return isPositionVisible(primaryHorizontal + viewportToContentHorizontalOffset(),
+                lineBottom + viewportToContentVerticalOffset());
+    }
+
+    @Override
+    protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
+        super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
+        if (mEditor != null && getEditor().mPositionListener != null) {
+            getEditor().mPositionListener.onScrollChanged();
+        }
+    }
+
+    /**
+     * Removes the suggestion spans.
+     */
+    CharSequence removeSuggestionSpans(CharSequence text) {
+       if (text instanceof Spanned) {
+           Spannable spannable;
+           if (text instanceof Spannable) {
+               spannable = (Spannable) text;
+           } else {
+               spannable = new SpannableString(text);
+               text = spannable;
+           }
+
+           SuggestionSpan[] spans = spannable.getSpans(0, text.length(), SuggestionSpan.class);
+           for (int i = 0; i < spans.length; i++) {
+               spannable.removeSpan(spans[i]);
+           }
+       }
+       return text;
+    }
+
+    void showSuggestions() {
+        if (getEditor().mSuggestionsPopupWindow == null) {
+            getEditor().mSuggestionsPopupWindow = new SuggestionsPopupWindow();
+        }
+        hideControllers();
+        getEditor().mSuggestionsPopupWindow.show();
+    }
+
+    boolean areSuggestionsShown() {
+        return getEditor().mSuggestionsPopupWindow != null && getEditor().mSuggestionsPopupWindow.isShowing();
+    }
+
+    /**
+     * Return whether or not suggestions are enabled on this TextView. The suggestions are generated
+     * by the IME or by the spell checker as the user types. This is done by adding
+     * {@link SuggestionSpan}s to the text.
+     *
+     * When suggestions are enabled (default), this list of suggestions will be displayed when the
+     * user asks for them on these parts of the text. This value depends on the inputType of this
+     * TextView.
+     *
+     * The class of the input type must be {@link InputType#TYPE_CLASS_TEXT}.
+     *
+     * In addition, the type variation must be one of
+     * {@link InputType#TYPE_TEXT_VARIATION_NORMAL},
+     * {@link InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT},
+     * {@link InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE},
+     * {@link InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE} or
+     * {@link InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}.
+     *
+     * And finally, the {@link InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS} flag must <i>not</i> be set.
+     *
+     * @return true if the suggestions popup window is enabled, based on the inputType.
+     */
+    public boolean isSuggestionsEnabled() {
+        if (mEditor == null) return false;
+        if ((getEditor().mInputType & InputType.TYPE_MASK_CLASS) != InputType.TYPE_CLASS_TEXT) return false;
+        if ((getEditor().mInputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) > 0) return false;
+
+        final int variation = getEditor().mInputType & EditorInfo.TYPE_MASK_VARIATION;
+        return (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL ||
+                variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT ||
+                variation == EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE ||
+                variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE ||
+                variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
+    }
+
+    /**
+     * If provided, this ActionMode.Callback will be used to create the ActionMode when text
+     * selection is initiated in this View.
+     *
+     * The standard implementation populates the menu with a subset of Select All, Cut, Copy and
+     * Paste actions, depending on what this View supports.
+     *
+     * A custom implementation can add new entries in the default menu in its
+     * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
+     * default actions can also be removed from the menu using {@link Menu#removeItem(int)} and
+     * passing {@link android.R.id#selectAll}, {@link android.R.id#cut}, {@link android.R.id#copy}
+     * or {@link android.R.id#paste} ids as parameters.
+     *
+     * Returning false from
+     * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
+     * the action mode from being started.
+     *
+     * Action click events should be handled by the custom implementation of
+     * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}.
+     *
+     * Note that text selection mode is not started when a TextView receives focus and the
+     * {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in
+     * that case, to allow for quick replacement.
+     */
+    public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
+        createEditorIfNeeded("custom selection action mode set");
+        getEditor().mCustomSelectionActionModeCallback = actionModeCallback;
+    }
+
+    /**
+     * Retrieves the value set in {@link #setCustomSelectionActionModeCallback}. Default is null.
+     *
+     * @return The current custom selection callback.
+     */
+    public ActionMode.Callback getCustomSelectionActionModeCallback() {
+        return mEditor == null ? null : getEditor().mCustomSelectionActionModeCallback;
+    }
+
+    /**
+     *
+     * @return true if the selection mode was actually started.
+     */
+    private boolean startSelectionActionMode() {
+        if (getEditor().mSelectionActionMode != null) {
+            // Selection action mode is already started
+            return false;
+        }
+
+        if (!canSelectText() || !requestFocus()) {
+            Log.w(LOG_TAG, "TextView does not support text selection. Action mode cancelled.");
+            return false;
+        }
+
+        if (!hasSelection()) {
+            // There may already be a selection on device rotation
+            if (!selectCurrentWord()) {
+                // No word found under cursor or text selection not permitted.
+                return false;
+            }
+        }
+
+        boolean willExtract = extractedTextModeWillBeStarted();
+
+        // Do not start the action mode when extracted text will show up full screen, which would
+        // immediately hide the newly created action bar and would be visually distracting.
+        if (!willExtract) {
+            ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
+            getEditor().mSelectionActionMode = startActionMode(actionModeCallback);
+        }
+
+        final boolean selectionStarted = getEditor().mSelectionActionMode != null || willExtract;
+        if (selectionStarted && !isTextSelectable()) {
+            // Show the IME to be able to replace text, except when selecting non editable text.
+            final InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                imm.showSoftInput(this, 0, null);
+            }
+        }
+
+        return selectionStarted;
+    }
+
+    private boolean extractedTextModeWillBeStarted() {
+        if (!(this instanceof ExtractEditText)) {
+            final InputMethodManager imm = InputMethodManager.peekInstance();
+            return  imm != null && imm.isFullscreenMode();
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    protected void stopSelectionActionMode() {
+        if (getEditor().mSelectionActionMode != null) {
+            // This will hide the mSelectionModifierCursorController
+            getEditor().mSelectionActionMode.finish();
+        }
+    }
+
+    /**
+     * Paste clipboard content between min and max positions.
+     */
+    private void paste(int min, int max) {
+        ClipboardManager clipboard =
+            (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+        ClipData clip = clipboard.getPrimaryClip();
+        if (clip != null) {
+            boolean didFirst = false;
+            for (int i=0; i<clip.getItemCount(); i++) {
+                CharSequence paste = clip.getItemAt(i).coerceToText(getContext());
+                if (paste != null) {
+                    if (!didFirst) {
+                        long minMax = prepareSpacesAroundPaste(min, max, paste);
+                        min = extractRangeStartFromLong(minMax);
+                        max = extractRangeEndFromLong(minMax);
+                        Selection.setSelection((Spannable) mText, max);
+                        ((Editable) mText).replace(min, max, paste);
+                        didFirst = true;
+                    } else {
+                        ((Editable) mText).insert(getSelectionEnd(), "\n");
+                        ((Editable) mText).insert(getSelectionEnd(), paste);
+                    }
+                }
+            }
+            stopSelectionActionMode();
+            LAST_CUT_OR_COPY_TIME = 0;
+        }
+    }
+
+    private void setPrimaryClip(ClipData clip) {
+        ClipboardManager clipboard = (ClipboardManager) getContext().
+                getSystemService(Context.CLIPBOARD_SERVICE);
+        clipboard.setPrimaryClip(clip);
+        LAST_CUT_OR_COPY_TIME = SystemClock.uptimeMillis();
+    }
+
+    private void hideInsertionPointCursorController() {
+        // No need to create the controller to hide it.
+        if (getEditor().mInsertionPointCursorController != null) {
+            getEditor().mInsertionPointCursorController.hide();
+        }
+    }
+
+    /**
+     * Hides the insertion controller and stops text selection mode, hiding the selection controller
+     */
+    private void hideControllers() {
+        hideCursorControllers();
+        hideSpanControllers();
+    }
+
+    private void hideSpanControllers() {
+        if (mChangeWatcher != null) {
+            mChangeWatcher.hideControllers();
+        }
+    }
+
+    private void hideCursorControllers() {
+        if (getEditor().mSuggestionsPopupWindow != null && !getEditor().mSuggestionsPopupWindow.isShowingUp()) {
+            // Should be done before hide insertion point controller since it triggers a show of it
+            getEditor().mSuggestionsPopupWindow.hide();
+        }
+        hideInsertionPointCursorController();
+        stopSelectionActionMode();
+    }
+
+    /**
+     * Get the character offset closest to the specified absolute position. A typical use case is to
+     * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method.
+     *
+     * @param x The horizontal absolute position of a point on screen
+     * @param y The vertical absolute position of a point on screen
+     * @return the character offset for the character whose position is closest to the specified
+     *  position. Returns -1 if there is no layout.
+     */
+    public int getOffsetForPosition(float x, float y) {
+        if (getLayout() == null) return -1;
+        final int line = getLineAtCoordinate(y);
+        final int offset = getOffsetAtCoordinate(line, x);
+        return offset;
+    }
+
+    private float convertToLocalHorizontalCoordinate(float x) {
+        x -= getTotalPaddingLeft();
+        // Clamp the position to inside of the view.
+        x = Math.max(0.0f, x);
+        x = Math.min(getWidth() - getTotalPaddingRight() - 1, x);
+        x += getScrollX();
+        return x;
+    }
+
+    private int getLineAtCoordinate(float y) {
+        y -= getTotalPaddingTop();
+        // Clamp the position to inside of the view.
+        y = Math.max(0.0f, y);
+        y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
+        y += getScrollY();
+        return getLayout().getLineForVertical((int) y);
+    }
+
+    private int getOffsetAtCoordinate(int line, float x) {
+        x = convertToLocalHorizontalCoordinate(x);
+        return getLayout().getOffsetForHorizontal(line, x);
+    }
+
+    /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed
+     * in the view. Returns false when the position is in the empty space of left/right of text.
+     */
+    private boolean isPositionOnText(float x, float y) {
+        if (getLayout() == null) return false;
+
+        final int line = getLineAtCoordinate(y);
+        x = convertToLocalHorizontalCoordinate(x);
+
+        if (x < getLayout().getLineLeft(line)) return false;
+        if (x > getLayout().getLineRight(line)) return false;
+        return true;
+    }
+
+    @Override
+    public boolean onDragEvent(DragEvent event) {
+        switch (event.getAction()) {
+            case DragEvent.ACTION_DRAG_STARTED:
+                return hasInsertionController();
+
+            case DragEvent.ACTION_DRAG_ENTERED:
+                TextView.this.requestFocus();
+                return true;
+
+            case DragEvent.ACTION_DRAG_LOCATION:
+                final int offset = getOffsetForPosition(event.getX(), event.getY());
+                Selection.setSelection((Spannable)mText, offset);
+                return true;
+
+            case DragEvent.ACTION_DROP:
+                onDrop(event);
+                return true;
+
+            case DragEvent.ACTION_DRAG_ENDED:
+            case DragEvent.ACTION_DRAG_EXITED:
+            default:
+                return true;
+        }
+    }
+
+    private void onDrop(DragEvent event) {
+        StringBuilder content = new StringBuilder("");
+        ClipData clipData = event.getClipData();
+        final int itemCount = clipData.getItemCount();
+        for (int i=0; i < itemCount; i++) {
+            Item item = clipData.getItemAt(i);
+            content.append(item.coerceToText(TextView.this.mContext));
+        }
+
+        final int offset = getOffsetForPosition(event.getX(), event.getY());
+
+        Object localState = event.getLocalState();
+        DragLocalState dragLocalState = null;
+        if (localState instanceof DragLocalState) {
+            dragLocalState = (DragLocalState) localState;
+        }
+        boolean dragDropIntoItself = dragLocalState != null &&
+                dragLocalState.sourceTextView == this;
+
+        if (dragDropIntoItself) {
+            if (offset >= dragLocalState.start && offset < dragLocalState.end) {
+                // A drop inside the original selection discards the drop.
+                return;
+            }
+        }
+
+        final int originalLength = mText.length();
+        long minMax = prepareSpacesAroundPaste(offset, offset, content);
+        int min = extractRangeStartFromLong(minMax);
+        int max = extractRangeEndFromLong(minMax);
+
+        Selection.setSelection((Spannable) mText, max);
+        replaceText_internal(min, max, content);
+
+        if (dragDropIntoItself) {
+            int dragSourceStart = dragLocalState.start;
+            int dragSourceEnd = dragLocalState.end;
+            if (max <= dragSourceStart) {
+                // Inserting text before selection has shifted positions
+                final int shift = mText.length() - originalLength;
+                dragSourceStart += shift;
+                dragSourceEnd += shift;
+            }
+
+            // Delete original selection
+            deleteText_internal(dragSourceStart, dragSourceEnd);
+
+            // Make sure we do not leave two adjacent spaces.
+            if ((dragSourceStart == 0 ||
+                    Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) &&
+                    (dragSourceStart == mText.length() ||
+                    Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) {
+                final int pos = dragSourceStart == mText.length() ?
+                        dragSourceStart - 1 : dragSourceStart;
+                deleteText_internal(pos, pos + 1);
+            }
+        }
+    }
+
+    /**
+     * @return True if this view supports insertion handles.
+     */
+    boolean hasInsertionController() {
+        return getEditor().mInsertionControllerEnabled;
+    }
+
+    /**
+     * @return True if this view supports selection handles.
+     */
+    boolean hasSelectionController() {
+        return getEditor().mSelectionControllerEnabled;
+    }
+
+    InsertionPointCursorController getInsertionController() {
+        if (!getEditor().mInsertionControllerEnabled) {
+            return null;
+        }
+
+        if (getEditor().mInsertionPointCursorController == null) {
+            getEditor().mInsertionPointCursorController = new InsertionPointCursorController();
+
+            final ViewTreeObserver observer = getViewTreeObserver();
+            observer.addOnTouchModeChangeListener(getEditor().mInsertionPointCursorController);
+        }
+
+        return getEditor().mInsertionPointCursorController;
+    }
+
+    SelectionModifierCursorController getSelectionController() {
+        if (!getEditor().mSelectionControllerEnabled) {
+            return null;
+        }
+
+        if (getEditor().mSelectionModifierCursorController == null) {
+            getEditor().mSelectionModifierCursorController = new SelectionModifierCursorController();
+
+            final ViewTreeObserver observer = getViewTreeObserver();
+            observer.addOnTouchModeChangeListener(getEditor().mSelectionModifierCursorController);
+        }
+
+        return getEditor().mSelectionModifierCursorController;
+    }
+
+    boolean isInBatchEditMode() {
+        if (mEditor == null) return false;
+        final InputMethodState ims = getEditor().mInputMethodState;
+        if (ims != null) {
+            return ims.mBatchEditNesting > 0;
+        }
+        return getEditor().mInBatchEditControllers;
+    }
+
+    @Override
+    public void onResolveTextDirection() {
+        if (hasPasswordTransformationMethod()) {
+            mTextDir = TextDirectionHeuristics.LOCALE;
+            return;
+        }
+
+        // Always need to resolve layout direction first
+        final boolean defaultIsRtl = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL);
+
+        // Now, we can select the heuristic
+        int textDir = getResolvedTextDirection();
+        switch (textDir) {
+            default:
+            case TEXT_DIRECTION_FIRST_STRONG:
+                mTextDir = (defaultIsRtl ? TextDirectionHeuristics.FIRSTSTRONG_RTL :
+                        TextDirectionHeuristics.FIRSTSTRONG_LTR);
+                break;
+            case TEXT_DIRECTION_ANY_RTL:
+                mTextDir = TextDirectionHeuristics.ANYRTL_LTR;
+                break;
+            case TEXT_DIRECTION_LTR:
+                mTextDir = TextDirectionHeuristics.LTR;
+                break;
+            case TEXT_DIRECTION_RTL:
+                mTextDir = TextDirectionHeuristics.RTL;
+                break;
+            case TEXT_DIRECTION_LOCALE:
+                mTextDir = TextDirectionHeuristics.LOCALE;
+                break;
+        }
+    }
+
+    /**
+     * Subclasses will need to override this method to implement their own way of resolving
+     * drawables depending on the layout direction.
+     *
+     * A call to the super method will be required from the subclasses implementation.
+     */
+    protected void resolveDrawables() {
+        // No need to resolve twice
+        if (mResolvedDrawables) {
+            return;
+        }
+        // No drawable to resolve
+        if (mDrawables == null) {
+            return;
+        }
+        // No relative drawable to resolve
+        if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
+            mResolvedDrawables = true;
+            return;
+        }
+
+        Drawables dr = mDrawables;
+        switch(getResolvedLayoutDirection()) {
+            case LAYOUT_DIRECTION_RTL:
+                if (dr.mDrawableStart != null) {
+                    dr.mDrawableRight = dr.mDrawableStart;
+
+                    dr.mDrawableSizeRight = dr.mDrawableSizeStart;
+                    dr.mDrawableHeightRight = dr.mDrawableHeightStart;
+                }
+                if (dr.mDrawableEnd != null) {
+                    dr.mDrawableLeft = dr.mDrawableEnd;
+
+                    dr.mDrawableSizeLeft = dr.mDrawableSizeEnd;
+                    dr.mDrawableHeightLeft = dr.mDrawableHeightEnd;
+                }
+                break;
+
+            case LAYOUT_DIRECTION_LTR:
+            default:
+                if (dr.mDrawableStart != null) {
+                    dr.mDrawableLeft = dr.mDrawableStart;
+
+                    dr.mDrawableSizeLeft = dr.mDrawableSizeStart;
+                    dr.mDrawableHeightLeft = dr.mDrawableHeightStart;
+                }
+                if (dr.mDrawableEnd != null) {
+                    dr.mDrawableRight = dr.mDrawableEnd;
+
+                    dr.mDrawableSizeRight = dr.mDrawableSizeEnd;
+                    dr.mDrawableHeightRight = dr.mDrawableHeightEnd;
+                }
+                break;
+        }
+        mResolvedDrawables = true;
+    }
+
+    protected void resetResolvedDrawables() {
+        mResolvedDrawables = false;
+    }
+
+    /**
+     * @hide
+     */
+    protected void viewClicked(InputMethodManager imm) {
+        if (imm != null) {
+            imm.viewClicked(this);
+        }
+    }
+
+    /**
+     * Deletes the range of text [start, end[.
+     * @hide
+     */
+    protected void deleteText_internal(int start, int end) {
+        ((Editable) mText).delete(start, end);
+    }
+
+    /**
+     * Replaces the range of text [start, end[ by replacement text
+     * @hide
+     */
+    protected void replaceText_internal(int start, int end, CharSequence text) {
+        ((Editable) mText).replace(start, end, text);
+    }
+
+    /**
+     * Sets a span on the specified range of text
+     * @hide
+     */
+    protected void setSpan_internal(Object span, int start, int end, int flags) {
+        ((Editable) mText).setSpan(span, start, end, flags);
+    }
+
+    /**
+     * Moves the cursor to the specified offset position in text
+     * @hide
+     */
+    protected void setCursorPosition_internal(int start, int end) {
+        Selection.setSelection(((Editable) mText), start, end);
+    }
+
+    /**
+     * An Editor should be created as soon as any of the editable-specific fields (grouped
+     * inside the Editor object) is assigned to a non-default value.
+     * This method will create the Editor if needed.
+     *
+     * A standard TextView (as well as buttons, checkboxes...) should not qualify and hence will
+     * have a null Editor, unlike an EditText. Inconsistent in-between states will have an
+     * Editor for backward compatibility, as soon as one of these fields is assigned.
+     *
+     * Also note that for performance reasons, the mEditor is created when needed, but not
+     * reset when no more edit-specific fields are needed.
+     */
+    private void createEditorIfNeeded(String reason) {
+        if (mEditor == null) {
+            if (!(this instanceof EditText)) {
+                Log.e(LOG_TAG + " EDITOR", "Creating Editor on TextView. " + reason);
+            }
+            mEditor = new Editor();
+        } else {
+            if (!(this instanceof EditText)) {
+                Log.d(LOG_TAG + " EDITOR", "Redundant Editor creation. " + reason);
+            }
+        }
+    }
+
+    private Editor getEditor() {
+        if (mEditor == null) {
+            //createEditorIfNeeded("Problem: mEditor is not initialized!");
+            Log.e(LOG_TAG, "mEditor not initialized. Please send a bug report to debunne@");
+        }
+        return mEditor;
+    }
+
+    /**
+     * User interface state that is stored by TextView for implementing
+     * {@link View#onSaveInstanceState}.
+     */
+    public static class SavedState extends BaseSavedState {
+        int selStart;
+        int selEnd;
+        CharSequence text;
+        boolean frozenWithFocus;
+        CharSequence error;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(selStart);
+            out.writeInt(selEnd);
+            out.writeInt(frozenWithFocus ? 1 : 0);
+            TextUtils.writeToParcel(text, out, flags);
+
+            if (error == null) {
+                out.writeInt(0);
+            } else {
+                out.writeInt(1);
+                TextUtils.writeToParcel(error, out, flags);
+            }
+        }
+
+        @Override
+        public String toString() {
+            String str = "TextView.SavedState{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " start=" + selStart + " end=" + selEnd;
+            if (text != null) {
+                str += " text=" + text;
+            }
+            return str + "}";
+        }
+
+        @SuppressWarnings("hiding")
+        public static final Parcelable.Creator<SavedState> CREATOR
+                = new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+
+        private SavedState(Parcel in) {
+            super(in);
+            selStart = in.readInt();
+            selEnd = in.readInt();
+            frozenWithFocus = (in.readInt() != 0);
+            text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+
+            if (in.readInt() != 0) {
+                error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            }
+        }
+    }
+
+    private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations {
+        private char[] mChars;
+        private int mStart, mLength;
+
+        public CharWrapper(char[] chars, int start, int len) {
+            mChars = chars;
+            mStart = start;
+            mLength = len;
+        }
+
+        /* package */ void set(char[] chars, int start, int len) {
+            mChars = chars;
+            mStart = start;
+            mLength = len;
+        }
+
+        public int length() {
+            return mLength;
+        }
+
+        public char charAt(int off) {
+            return mChars[off + mStart];
+        }
+
+        @Override
+        public String toString() {
+            return new String(mChars, mStart, mLength);
+        }
+
+        public CharSequence subSequence(int start, int end) {
+            if (start < 0 || end < 0 || start > mLength || end > mLength) {
+                throw new IndexOutOfBoundsException(start + ", " + end);
+            }
+
+            return new String(mChars, start + mStart, end - start);
+        }
+
+        public void getChars(int start, int end, char[] buf, int off) {
+            if (start < 0 || end < 0 || start > mLength || end > mLength) {
+                throw new IndexOutOfBoundsException(start + ", " + end);
+            }
+
+            System.arraycopy(mChars, start + mStart, buf, off, end - start);
+        }
+
+        public void drawText(Canvas c, int start, int end,
+                             float x, float y, Paint p) {
+            c.drawText(mChars, start + mStart, end - start, x, y, p);
+        }
+
+        public void drawTextRun(Canvas c, int start, int end,
+                int contextStart, int contextEnd, float x, float y, int flags, Paint p) {
+            int count = end - start;
+            int contextCount = contextEnd - contextStart;
+            c.drawTextRun(mChars, start + mStart, count, contextStart + mStart,
+                    contextCount, x, y, flags, p);
+        }
+
+        public float measureText(int start, int end, Paint p) {
+            return p.measureText(mChars, start + mStart, end - start);
+        }
+
+        public int getTextWidths(int start, int end, float[] widths, Paint p) {
+            return p.getTextWidths(mChars, start + mStart, end - start, widths);
+        }
+
+        public float getTextRunAdvances(int start, int end, int contextStart,
+                int contextEnd, int flags, float[] advances, int advancesIndex,
+                Paint p) {
+            int count = end - start;
+            int contextCount = contextEnd - contextStart;
+            return p.getTextRunAdvances(mChars, start + mStart, count,
+                    contextStart + mStart, contextCount, flags, advances,
+                    advancesIndex);
+        }
+
+        public float getTextRunAdvances(int start, int end, int contextStart,
+                int contextEnd, int flags, float[] advances, int advancesIndex,
+                Paint p, int reserved) {
+            int count = end - start;
+            int contextCount = contextEnd - contextStart;
+            return p.getTextRunAdvances(mChars, start + mStart, count,
+                    contextStart + mStart, contextCount, flags, advances,
+                    advancesIndex, reserved);
+        }
+
+        public int getTextRunCursor(int contextStart, int contextEnd, int flags,
+                int offset, int cursorOpt, Paint p) {
+            int contextCount = contextEnd - contextStart;
+            return p.getTextRunCursor(mChars, contextStart + mStart,
+                    contextCount, flags, offset + mStart, cursorOpt);
+        }
+    }
+
+    private static class ErrorPopup extends PopupWindow {
+        private boolean mAbove = false;
+        private final TextView mView;
+        private int mPopupInlineErrorBackgroundId = 0;
+        private int mPopupInlineErrorAboveBackgroundId = 0;
+
+        ErrorPopup(TextView v, int width, int height) {
+            super(v, width, height);
+            mView = v;
+            // Make sure the TextView has a background set as it will be used the first time it is
+            // shown and positionned. Initialized with below background, which should have
+            // dimensions identical to the above version for this to work (and is more likely).
+            mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
+                    com.android.internal.R.styleable.Theme_errorMessageBackground);
+            mView.setBackgroundResource(mPopupInlineErrorBackgroundId);
+        }
+
+        void fixDirection(boolean above) {
+            mAbove = above;
+
+            if (above) {
+                mPopupInlineErrorAboveBackgroundId =
+                    getResourceId(mPopupInlineErrorAboveBackgroundId,
+                            com.android.internal.R.styleable.Theme_errorMessageAboveBackground);
+            } else {
+                mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
+                        com.android.internal.R.styleable.Theme_errorMessageBackground);
+            }
+
+            mView.setBackgroundResource(above ? mPopupInlineErrorAboveBackgroundId :
+                mPopupInlineErrorBackgroundId);
+        }
+
+        private int getResourceId(int currentId, int index) {
+            if (currentId == 0) {
+                TypedArray styledAttributes = mView.getContext().obtainStyledAttributes(
+                        R.styleable.Theme);
+                currentId = styledAttributes.getResourceId(index, 0);
+                styledAttributes.recycle();
+            }
+            return currentId;
+        }
+
+        @Override
+        public void update(int x, int y, int w, int h, boolean force) {
+            super.update(x, y, w, h, force);
+
+            boolean above = isAboveAnchor();
+            if (above != mAbove) {
+                fixDirection(above);
+            }
+        }
+    }
+
+    private class CorrectionHighlighter {
+        private final Path mPath = new Path();
+        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        private int mStart, mEnd;
+        private long mFadingStartTime;
+        private final static int FADE_OUT_DURATION = 400;
+
+        public CorrectionHighlighter() {
+            mPaint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale);
+            mPaint.setStyle(Paint.Style.FILL);
+        }
+
+        public void highlight(CorrectionInfo info) {
+            mStart = info.getOffset();
+            mEnd = mStart + info.getNewText().length();
+            mFadingStartTime = SystemClock.uptimeMillis();
+
+            if (mStart < 0 || mEnd < 0) {
+                stopAnimation();
+            }
+        }
+
+        public void draw(Canvas canvas, int cursorOffsetVertical) {
+            if (updatePath() && updatePaint()) {
+                if (cursorOffsetVertical != 0) {
+                    canvas.translate(0, cursorOffsetVertical);
+                }
+
+                canvas.drawPath(mPath, mPaint);
+
+                if (cursorOffsetVertical != 0) {
+                    canvas.translate(0, -cursorOffsetVertical);
+                }
+                invalidate(true); // TODO invalidate cursor region only
+            } else {
+                stopAnimation();
+                invalidate(false); // TODO invalidate cursor region only
+            }
+        }
+
+        private boolean updatePaint() {
+            final long duration = SystemClock.uptimeMillis() - mFadingStartTime;
+            if (duration > FADE_OUT_DURATION) return false;
+
+            final float coef = 1.0f - (float) duration / FADE_OUT_DURATION;
+            final int highlightColorAlpha = Color.alpha(mHighlightColor);
+            final int color = (mHighlightColor & 0x00FFFFFF) +
+                    ((int) (highlightColorAlpha * coef) << 24);
+            mPaint.setColor(color);
+            return true;
+        }
+
+        private boolean updatePath() {
+            final Layout layout = TextView.this.mLayout;
+            if (layout == null) return false;
+
+            // Update in case text is edited while the animation is run
+            final int length = mText.length();
+            int start = Math.min(length, mStart);
+            int end = Math.min(length, mEnd);
+
+            mPath.reset();
+            TextView.this.mLayout.getSelectionPath(start, end, mPath);
+            return true;
+        }
+
+        private void invalidate(boolean delayed) {
+            if (TextView.this.mLayout == null) return;
+
+            synchronized (TEMP_RECTF) {
+                mPath.computeBounds(TEMP_RECTF, false);
+
+                int left = getCompoundPaddingLeft();
+                int top = getExtendedPaddingTop() + getVerticalOffset(true);
+
+                if (delayed) {
+                    TextView.this.postInvalidateDelayed(16, // 60 Hz update
+                            left + (int) TEMP_RECTF.left, top + (int) TEMP_RECTF.top,
+                            left + (int) TEMP_RECTF.right, top + (int) TEMP_RECTF.bottom);
+                } else {
+                    TextView.this.postInvalidate((int) TEMP_RECTF.left, (int) TEMP_RECTF.top,
+                            (int) TEMP_RECTF.right, (int) TEMP_RECTF.bottom);
+                }
+            }
+        }
+
+        private void stopAnimation() {
+            TextView.this.getEditor().mCorrectionHighlighter = null;
+        }
+    }
+
+    private static final class Marquee extends Handler {
+        // TODO: Add an option to configure this
+        private static final float MARQUEE_DELTA_MAX = 0.07f;
+        private static final int MARQUEE_DELAY = 1200;
+        private static final int MARQUEE_RESTART_DELAY = 1200;
+        private static final int MARQUEE_RESOLUTION = 1000 / 30;
+        private static final int MARQUEE_PIXELS_PER_SECOND = 30;
+
+        private static final byte MARQUEE_STOPPED = 0x0;
+        private static final byte MARQUEE_STARTING = 0x1;
+        private static final byte MARQUEE_RUNNING = 0x2;
+
+        private static final int MESSAGE_START = 0x1;
+        private static final int MESSAGE_TICK = 0x2;
+        private static final int MESSAGE_RESTART = 0x3;
+
+        private final WeakReference<TextView> mView;
+
+        private byte mStatus = MARQUEE_STOPPED;
+        private final float mScrollUnit;
+        private float mMaxScroll;
+        float mMaxFadeScroll;
+        private float mGhostStart;
+        private float mGhostOffset;
+        private float mFadeStop;
+        private int mRepeatLimit;
+
+        float mScroll;
+
+        Marquee(TextView v) {
+            final float density = v.getContext().getResources().getDisplayMetrics().density;
+            mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / MARQUEE_RESOLUTION;
+            mView = new WeakReference<TextView>(v);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_START:
+                    mStatus = MARQUEE_RUNNING;
+                    tick();
+                    break;
+                case MESSAGE_TICK:
+                    tick();
+                    break;
+                case MESSAGE_RESTART:
+                    if (mStatus == MARQUEE_RUNNING) {
+                        if (mRepeatLimit >= 0) {
+                            mRepeatLimit--;
+                        }
+                        start(mRepeatLimit);
+                    }
+                    break;
+            }
+        }
+
+        void tick() {
+            if (mStatus != MARQUEE_RUNNING) {
+                return;
+            }
+
+            removeMessages(MESSAGE_TICK);
+
+            final TextView textView = mView.get();
+            if (textView != null && (textView.isFocused() || textView.isSelected())) {
+                mScroll += mScrollUnit;
+                if (mScroll > mMaxScroll) {
+                    mScroll = mMaxScroll;
+                    sendEmptyMessageDelayed(MESSAGE_RESTART, MARQUEE_RESTART_DELAY);
+                } else {
+                    sendEmptyMessageDelayed(MESSAGE_TICK, MARQUEE_RESOLUTION);
+                }
+                textView.invalidate();
+            }
+        }
+
+        void stop() {
+            mStatus = MARQUEE_STOPPED;
+            removeMessages(MESSAGE_START);
+            removeMessages(MESSAGE_RESTART);
+            removeMessages(MESSAGE_TICK);
+            resetScroll();
+        }
+
+        private void resetScroll() {
+            mScroll = 0.0f;
+            final TextView textView = mView.get();
+            if (textView != null) textView.invalidate();
+        }
+
+        void start(int repeatLimit) {
+            if (repeatLimit == 0) {
+                stop();
+                return;
+            }
+            mRepeatLimit = repeatLimit;
+            final TextView textView = mView.get();
+            if (textView != null && textView.mLayout != null) {
+                mStatus = MARQUEE_STARTING;
+                mScroll = 0.0f;
+                final int textWidth = textView.getWidth() - textView.getCompoundPaddingLeft() -
+                        textView.getCompoundPaddingRight();
+                final float lineWidth = textView.mLayout.getLineWidth(0);
+                final float gap = textWidth / 3.0f;
+                mGhostStart = lineWidth - textWidth + gap;
+                mMaxScroll = mGhostStart + textWidth;
+                mGhostOffset = lineWidth + gap;
+                mFadeStop = lineWidth + textWidth / 6.0f;
+                mMaxFadeScroll = mGhostStart + lineWidth + lineWidth;
+
+                textView.invalidate();
+                sendEmptyMessageDelayed(MESSAGE_START, MARQUEE_DELAY);
+            }
+        }
+
+        float getGhostOffset() {
+            return mGhostOffset;
+        }
+
+        boolean shouldDrawLeftFade() {
+            return mScroll <= mFadeStop;
+        }
+
+        boolean shouldDrawGhost() {
+            return mStatus == MARQUEE_RUNNING && mScroll > mGhostStart;
+        }
+
+        boolean isRunning() {
+            return mStatus == MARQUEE_RUNNING;
+        }
+
+        boolean isStopped() {
+            return mStatus == MARQUEE_STOPPED;
+        }
+    }
+
+    /**
+     * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
+     * pop-up should be displayed.
+     */
+    private class EasyEditSpanController {
+
+        private static final int DISPLAY_TIMEOUT_MS = 3000; // 3 secs
+
+        private EasyEditPopupWindow mPopupWindow;
+
+        private EasyEditSpan mEasyEditSpan;
+
+        private Runnable mHidePopup;
+
+        private void hide() {
+            if (mPopupWindow != null) {
+                mPopupWindow.hide();
+                TextView.this.removeCallbacks(mHidePopup);
+            }
+            removeSpans(mText);
+            mEasyEditSpan = null;
+        }
+
+        /**
+         * Monitors the changes in the text.
+         *
+         * <p>{@link ChangeWatcher#onSpanAdded(Spannable, Object, int, int)} cannot be used,
+         * as the notifications are not sent when a spannable (with spans) is inserted.
+         */
+        public void onTextChange(CharSequence buffer) {
+            adjustSpans(mText);
+
+            if (getWindowVisibility() != View.VISIBLE) {
+                // The window is not visible yet, ignore the text change.
+                return;
+            }
+
+            if (mLayout == null) {
+                // The view has not been layout yet, ignore the text change
+                return;
+            }
+
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (!(TextView.this instanceof ExtractEditText)
+                    && imm != null && imm.isFullscreenMode()) {
+                // The input is in extract mode. We do not have to handle the easy edit in the
+                // original TextView, as the ExtractEditText will do
+                return;
+            }
+
+            // Remove the current easy edit span, as the text changed, and remove the pop-up
+            // (if any)
+            if (mEasyEditSpan != null) {
+                if (mText instanceof Spannable) {
+                    ((Spannable) mText).removeSpan(mEasyEditSpan);
+                }
+                mEasyEditSpan = null;
+            }
+            if (mPopupWindow != null && mPopupWindow.isShowing()) {
+                mPopupWindow.hide();
+            }
+
+            // Display the new easy edit span (if any).
+            if (buffer instanceof Spanned) {
+                mEasyEditSpan = getSpan((Spanned) buffer);
+                if (mEasyEditSpan != null) {
+                    if (mPopupWindow == null) {
+                        mPopupWindow = new EasyEditPopupWindow();
+                        mHidePopup = new Runnable() {
+                            @Override
+                            public void run() {
+                                hide();
+                            }
+                        };
+                    }
+                    mPopupWindow.show(mEasyEditSpan);
+                    TextView.this.removeCallbacks(mHidePopup);
+                    TextView.this.postDelayed(mHidePopup, DISPLAY_TIMEOUT_MS);
+                }
+            }
+        }
+
+        /**
+         * Adjusts the spans by removing all of them except the last one.
+         */
+        private void adjustSpans(CharSequence buffer) {
+            // This method enforces that only one easy edit span is attached to the text.
+            // A better way to enforce this would be to listen for onSpanAdded, but this method
+            // cannot be used in this scenario as no notification is triggered when a text with
+            // spans is inserted into a text.
+            if (buffer instanceof Spannable) {
+                Spannable spannable = (Spannable) buffer;
+                EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(),
+                        EasyEditSpan.class);
+                for (int i = 0; i < spans.length - 1; i++) {
+                    spannable.removeSpan(spans[i]);
+                }
+            }
+        }
+
+        /**
+         * Removes all the {@link EasyEditSpan} currently attached.
+         */
+        private void removeSpans(CharSequence buffer) {
+            if (buffer instanceof Spannable) {
+                Spannable spannable = (Spannable) buffer;
+                EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(),
+                        EasyEditSpan.class);
+                for (int i = 0; i < spans.length; i++) {
+                    spannable.removeSpan(spans[i]);
+                }
+            }
+        }
+
+        private EasyEditSpan getSpan(Spanned spanned) {
+            EasyEditSpan[] easyEditSpans = spanned.getSpans(0, spanned.length(),
+                    EasyEditSpan.class);
+            if (easyEditSpans.length == 0) {
+                return null;
+            } else {
+                return easyEditSpans[0];
+            }
+        }
+    }
+
+    /**
+     * Displays the actions associated to an {@link EasyEditSpan}. The pop-up is controlled
+     * by {@link EasyEditSpanController}.
+     */
+    private class EasyEditPopupWindow extends PinnedPopupWindow
+            implements OnClickListener {
+        private static final int POPUP_TEXT_LAYOUT =
+                com.android.internal.R.layout.text_edit_action_popup_text;
+        private TextView mDeleteTextView;
+        private EasyEditSpan mEasyEditSpan;
+
+        @Override
+        protected void createPopupWindow() {
+            mPopupWindow = new PopupWindow(TextView.this.mContext, null,
+                    com.android.internal.R.attr.textSelectHandleWindowStyle);
+            mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+            mPopupWindow.setClippingEnabled(true);
+        }
+
+        @Override
+        protected void initContentView() {
+            LinearLayout linearLayout = new LinearLayout(TextView.this.getContext());
+            linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+            mContentView = linearLayout;
+            mContentView.setBackgroundResource(
+                    com.android.internal.R.drawable.text_edit_side_paste_window);
+
+            LayoutInflater inflater = (LayoutInflater)TextView.this.mContext.
+                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+            LayoutParams wrapContent = new LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+            mDeleteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
+            mDeleteTextView.setLayoutParams(wrapContent);
+            mDeleteTextView.setText(com.android.internal.R.string.delete);
+            mDeleteTextView.setOnClickListener(this);
+            mContentView.addView(mDeleteTextView);
+        }
+
+        public void show(EasyEditSpan easyEditSpan) {
+            mEasyEditSpan = easyEditSpan;
+            super.show();
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (view == mDeleteTextView) {
+                Editable editable = (Editable) mText;
+                int start = editable.getSpanStart(mEasyEditSpan);
+                int end = editable.getSpanEnd(mEasyEditSpan);
+                if (start >= 0 && end >= 0) {
+                    deleteText_internal(start, end);
+                }
+            }
+        }
+
+        @Override
+        protected int getTextOffset() {
+            // Place the pop-up at the end of the span
+            Editable editable = (Editable) mText;
+            return editable.getSpanEnd(mEasyEditSpan);
+        }
+
+        @Override
+        protected int getVerticalLocalPosition(int line) {
+            return mLayout.getLineBottom(line);
+        }
+
+        @Override
+        protected int clipVertically(int positionY) {
+            // As we display the pop-up below the span, no vertical clipping is required.
+            return positionY;
+        }
+    }
+
+    private class ChangeWatcher implements TextWatcher, SpanWatcher {
+
+        private CharSequence mBeforeText;
+
+        private EasyEditSpanController mEasyEditSpanController;
+
+        private ChangeWatcher() {
+            mEasyEditSpanController = new EasyEditSpanController();
+        }
+
+        public void beforeTextChanged(CharSequence buffer, int start,
+                                      int before, int after) {
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "beforeTextChanged start=" + start
+                    + " before=" + before + " after=" + after + ": " + buffer);
+
+            if (AccessibilityManager.getInstance(mContext).isEnabled()
+                    && !isPasswordInputType(getInputType())
+                    && !hasPasswordTransformationMethod()) {
+                mBeforeText = buffer.toString();
+            }
+
+            TextView.this.sendBeforeTextChanged(buffer, start, before, after);
+        }
+
+        public void onTextChanged(CharSequence buffer, int start,
+                                  int before, int after) {
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onTextChanged start=" + start
+                    + " before=" + before + " after=" + after + ": " + buffer);
+            TextView.this.handleTextChanged(buffer, start, before, after);
+
+            mEasyEditSpanController.onTextChange(buffer);
+
+            if (AccessibilityManager.getInstance(mContext).isEnabled() &&
+                    (isFocused() || isSelected() && isShown())) {
+                sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
+                mBeforeText = null;
+            }
+        }
+
+        public void afterTextChanged(Editable buffer) {
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "afterTextChanged: " + buffer);
+            TextView.this.sendAfterTextChanged(buffer);
+
+            if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) {
+                MetaKeyKeyListener.stopSelecting(TextView.this, buffer);
+            }
+        }
+
+        public void onSpanChanged(Spannable buf,
+                                  Object what, int s, int e, int st, int en) {
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanChanged s=" + s + " e=" + e
+                    + " st=" + st + " en=" + en + " what=" + what + ": " + buf);
+            TextView.this.spanChange(buf, what, s, st, e, en);
+        }
+
+        public void onSpanAdded(Spannable buf, Object what, int s, int e) {
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanAdded s=" + s + " e=" + e
+                    + " what=" + what + ": " + buf);
+            TextView.this.spanChange(buf, what, -1, s, -1, e);
+        }
+
+        public void onSpanRemoved(Spannable buf, Object what, int s, int e) {
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanRemoved s=" + s + " e=" + e
+                    + " what=" + what + ": " + buf);
+            TextView.this.spanChange(buf, what, s, -1, e, -1);
+        }
+
+        private void hideControllers() {
+            mEasyEditSpanController.hide();
+        }
+    }
+
+    private static class Blink extends Handler implements Runnable {
+        private final WeakReference<TextView> mView;
+        private boolean mCancelled;
+
+        public Blink(TextView v) {
+            mView = new WeakReference<TextView>(v);
+        }
+
+        public void run() {
+            if (mCancelled) {
+                return;
+            }
+
+            removeCallbacks(Blink.this);
+
+            TextView tv = mView.get();
+
+            if (tv != null && tv.shouldBlink()) {
+                if (tv.mLayout != null) {
+                    tv.invalidateCursorPath();
+                }
+
+                postAtTime(this, SystemClock.uptimeMillis() + BLINK);
+            }
+        }
+
+        void cancel() {
+            if (!mCancelled) {
+                removeCallbacks(Blink.this);
+                mCancelled = true;
+            }
+        }
+
+        void uncancel() {
+            mCancelled = false;
+        }
+    }
+
+    private static class DragLocalState {
+        public TextView sourceTextView;
+        public int start, end;
+
+        public DragLocalState(TextView sourceTextView, int start, int end) {
+            this.sourceTextView = sourceTextView;
+            this.start = start;
+            this.end = end;
+        }
+    }
+    
     private class PositionListener implements ViewTreeObserver.OnPreDrawListener {
         // 3 handles
         // 3 ActionPopup [replace, suggestion, easyedit] (suggestionsPopup first hides the others)
@@ -9303,6 +9695,7 @@
         private int mPositionX, mPositionY;
         private int mNumberOfListeners;
         private boolean mScrollHasChanged;
+        final int[] mTempCoords = new int[2];
 
         public void addSubscriber(TextViewPositionListener positionListener, boolean canMove) {
             if (mNumberOfListeners == 0) {
@@ -9381,62 +9774,6 @@
         }
     }
 
-    private boolean isPositionVisible(int positionX, int positionY) {
-        synchronized (sTmpPosition) {
-            final float[] position = sTmpPosition;
-            position[0] = positionX;
-            position[1] = positionY;
-            View view = this;
-
-            while (view != null) {
-                if (view != this) {
-                    // Local scroll is already taken into account in positionX/Y
-                    position[0] -= view.getScrollX();
-                    position[1] -= view.getScrollY();
-                }
-
-                if (position[0] < 0 || position[1] < 0 ||
-                        position[0] > view.getWidth() || position[1] > view.getHeight()) {
-                    return false;
-                }
-
-                if (!view.getMatrix().isIdentity()) {
-                    view.getMatrix().mapPoints(position);
-                }
-
-                position[0] += view.getLeft();
-                position[1] += view.getTop();
-
-                final ViewParent parent = view.getParent();
-                if (parent instanceof View) {
-                    view = (View) parent;
-                } else {
-                    // We've reached the ViewRoot, stop iterating
-                    view = null;
-                }
-            }
-        }
-
-        // We've been able to walk up the view hierarchy and the position was never clipped
-        return true;
-    }
-
-    private boolean isOffsetVisible(int offset) {
-        final int line = mLayout.getLineForOffset(offset);
-        final int lineBottom = mLayout.getLineBottom(line);
-        final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset);
-        return isPositionVisible(primaryHorizontal + viewportToContentHorizontalOffset(),
-                lineBottom + viewportToContentVerticalOffset());
-    }
-
-    @Override
-    protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
-        super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
-        if (mPositionListener != null) {
-            mPositionListener.onScrollChanged();
-        }
-    }
-
     private abstract class PinnedPopupWindow implements TextViewPositionListener {
         protected PopupWindow mPopupWindow;
         protected ViewGroup mContentView;
@@ -9564,7 +9901,7 @@
                 TextView.this.getPositionListener().removeSubscriber(SuggestionsPopupWindow.this);
 
                 // Safe cast since show() checks that mText is an Editable
-                ((Spannable) mText).removeSpan(mSuggestionRangeSpan);
+                ((Spannable) mText).removeSpan(getEditor().mSuggestionRangeSpan);
 
                 setCursorVisible(mCursorWasVisibleBeforeSuggestions);
                 if (hasInsertionController()) {
@@ -9574,7 +9911,7 @@
         }
 
         public SuggestionsPopupWindow() {
-            mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+            mCursorWasVisibleBeforeSuggestions = getEditor().mCursorVisible;
             mSuggestionSpanComparator = new SuggestionSpanComparator();
             mSpansLengths = new HashMap<SuggestionSpan, Integer>();
         }
@@ -9712,7 +10049,7 @@
             if (!(mText instanceof Editable)) return;
 
             if (updateSuggestions()) {
-                mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+                mCursorWasVisibleBeforeSuggestions = getEditor().mCursorVisible;
                 setCursorVisible(false);
                 mIsShowingUp = true;
                 super.show();
@@ -9867,17 +10204,17 @@
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             mNumberOfSuggestions++;
 
-            if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
+            if (getEditor().mSuggestionRangeSpan == null) getEditor().mSuggestionRangeSpan = new SuggestionRangeSpan();
             if (underlineColor == 0) {
                 // Fallback on the default highlight color when the first span does not provide one
-                mSuggestionRangeSpan.setBackgroundColor(mHighlightColor);
+                getEditor().mSuggestionRangeSpan.setBackgroundColor(mHighlightColor);
             } else {
                 final float BACKGROUND_TRANSPARENCY = 0.4f;
                 final int newAlpha = (int) (Color.alpha(underlineColor) * BACKGROUND_TRANSPARENCY);
-                mSuggestionRangeSpan.setBackgroundColor(
+                getEditor().mSuggestionRangeSpan.setBackgroundColor(
                         (underlineColor & 0x00FFFFFF) + (newAlpha << 24));
             }
-            spannable.setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
+            spannable.setSpan(getEditor().mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
             mSuggestionsAdapter.notifyDataSetChanged();
@@ -9910,8 +10247,8 @@
             SuggestionInfo suggestionInfo = mSuggestionInfos[position];
 
             if (suggestionInfo.suggestionIndex == DELETE_TEXT) {
-                final int spanUnionStart = editable.getSpanStart(mSuggestionRangeSpan);
-                int spanUnionEnd = editable.getSpanEnd(mSuggestionRangeSpan);
+                final int spanUnionStart = editable.getSpanStart(getEditor().mSuggestionRangeSpan);
+                int spanUnionEnd = editable.getSpanEnd(getEditor().mSuggestionRangeSpan);
                 if (spanUnionStart >= 0 && spanUnionEnd > spanUnionStart) {
                     // Do not leave two adjacent spaces after deletion, or one at beginning of text
                     if (spanUnionEnd < editable.length() &&
@@ -10011,209 +10348,6 @@
     }
 
     /**
-     * Removes the suggestion spans.
-     */
-    CharSequence removeSuggestionSpans(CharSequence text) {
-       if (text instanceof Spanned) {
-           Spannable spannable;
-           if (text instanceof Spannable) {
-               spannable = (Spannable) text;
-           } else {
-               spannable = new SpannableString(text);
-               text = spannable;
-           }
-
-           SuggestionSpan[] spans = spannable.getSpans(0, text.length(), SuggestionSpan.class);
-           for (int i = 0; i < spans.length; i++) {
-               spannable.removeSpan(spans[i]);
-           }
-       }
-       return text;
-    }
-
-    void showSuggestions() {
-        if (mSuggestionsPopupWindow == null) {
-            mSuggestionsPopupWindow = new SuggestionsPopupWindow();
-        }
-        hideControllers();
-        mSuggestionsPopupWindow.show();
-    }
-
-    boolean areSuggestionsShown() {
-        return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing();
-    }
-
-    /**
-     * Return whether or not suggestions are enabled on this TextView. The suggestions are generated
-     * by the IME or by the spell checker as the user types. This is done by adding
-     * {@link SuggestionSpan}s to the text.
-     *
-     * When suggestions are enabled (default), this list of suggestions will be displayed when the
-     * user asks for them on these parts of the text. This value depends on the inputType of this
-     * TextView.
-     *
-     * The class of the input type must be {@link InputType#TYPE_CLASS_TEXT}.
-     *
-     * In addition, the type variation must be one of
-     * {@link InputType#TYPE_TEXT_VARIATION_NORMAL},
-     * {@link InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT},
-     * {@link InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE},
-     * {@link InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE} or
-     * {@link InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}.
-     *
-     * And finally, the {@link InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS} flag must <i>not</i> be set.
-     *
-     * @return true if the suggestions popup window is enabled, based on the inputType.
-     */
-    public boolean isSuggestionsEnabled() {
-        if ((mInputType & InputType.TYPE_MASK_CLASS) != InputType.TYPE_CLASS_TEXT) return false;
-        if ((mInputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) > 0) return false;
-
-        final int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
-        return (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL ||
-                variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT ||
-                variation == EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE ||
-                variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE ||
-                variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
-    }
-
-    /**
-     * If provided, this ActionMode.Callback will be used to create the ActionMode when text
-     * selection is initiated in this View.
-     *
-     * The standard implementation populates the menu with a subset of Select All, Cut, Copy and
-     * Paste actions, depending on what this View supports.
-     *
-     * A custom implementation can add new entries in the default menu in its
-     * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
-     * default actions can also be removed from the menu using {@link Menu#removeItem(int)} and
-     * passing {@link android.R.id#selectAll}, {@link android.R.id#cut}, {@link android.R.id#copy}
-     * or {@link android.R.id#paste} ids as parameters.
-     *
-     * Returning false from 
-     * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
-     * the action mode from being started.
-     *
-     * Action click events should be handled by the custom implementation of
-     * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}.
-     *
-     * Note that text selection mode is not started when a TextView receives focus and the
-     * {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in
-     * that case, to allow for quick replacement.
-     */
-    public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
-        mCustomSelectionActionModeCallback = actionModeCallback;
-    }
-
-    /**
-     * Retrieves the value set in {@link #setCustomSelectionActionModeCallback}. Default is null.
-     *
-     * @return The current custom selection callback.
-     */
-    public ActionMode.Callback getCustomSelectionActionModeCallback() {
-        return mCustomSelectionActionModeCallback;
-    }
-
-    /**
-     *
-     * @return true if the selection mode was actually started.
-     */
-    private boolean startSelectionActionMode() {
-        if (mSelectionActionMode != null) {
-            // Selection action mode is already started
-            return false;
-        }
-
-        if (!canSelectText() || !requestFocus()) {
-            Log.w(LOG_TAG, "TextView does not support text selection. Action mode cancelled.");
-            return false;
-        }
-
-        if (!hasSelection()) {
-            // There may already be a selection on device rotation
-            if (!selectCurrentWord()) {
-                // No word found under cursor or text selection not permitted.
-                return false;
-            }
-        }
-
-        boolean willExtract = extractedTextModeWillBeStarted();
-
-        // Do not start the action mode when extracted text will show up full screen, which would
-        // immediately hide the newly created action bar and would be visually distracting.
-        if (!willExtract) {
-            ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
-            mSelectionActionMode = startActionMode(actionModeCallback);
-        }
-
-        final boolean selectionStarted = mSelectionActionMode != null || willExtract;
-        if (selectionStarted && !mTextIsSelectable) {
-            // Show the IME to be able to replace text, except when selecting non editable text.
-            final InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                imm.showSoftInput(this, 0, null);
-            }
-        }
-
-        return selectionStarted;
-    }
-
-    private boolean extractedTextModeWillBeStarted() {
-        if (!(this instanceof ExtractEditText)) {
-            final InputMethodManager imm = InputMethodManager.peekInstance();
-            return  imm != null && imm.isFullscreenMode();
-        }
-        return false;
-    }
-
-    /**
-     * @hide
-     */
-    protected void stopSelectionActionMode() {
-        if (mSelectionActionMode != null) {
-            // This will hide the mSelectionModifierCursorController
-            mSelectionActionMode.finish();
-        }
-    }
-
-    /**
-     * Paste clipboard content between min and max positions.
-     */
-    private void paste(int min, int max) {
-        ClipboardManager clipboard =
-            (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
-        ClipData clip = clipboard.getPrimaryClip();
-        if (clip != null) {
-            boolean didFirst = false;
-            for (int i=0; i<clip.getItemCount(); i++) {
-                CharSequence paste = clip.getItemAt(i).coerceToText(getContext());
-                if (paste != null) {
-                    if (!didFirst) {
-                        long minMax = prepareSpacesAroundPaste(min, max, paste);
-                        min = extractRangeStartFromLong(minMax);
-                        max = extractRangeEndFromLong(minMax);
-                        Selection.setSelection((Spannable) mText, max);
-                        ((Editable) mText).replace(min, max, paste);
-                        didFirst = true;
-                    } else {
-                        ((Editable) mText).insert(getSelectionEnd(), "\n");
-                        ((Editable) mText).insert(getSelectionEnd(), paste);
-                    }
-                }
-            }
-            stopSelectionActionMode();
-            sLastCutOrCopyTime = 0;
-        }
-    }
-
-    private void setPrimaryClip(ClipData clip) {
-        ClipboardManager clipboard = (ClipboardManager) getContext().
-                getSystemService(Context.CLIPBOARD_SERVICE);
-        clipboard.setPrimaryClip(clip);
-        sLastCutOrCopyTime = SystemClock.uptimeMillis();
-    }
-
-    /**
      * An ActionMode Callback class that is used to provide actions while in text selection mode.
      *
      * The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending
@@ -10275,8 +10409,8 @@
 
             styledAttributes.recycle();
 
-            if (mCustomSelectionActionModeCallback != null) {
-                if (!mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) {
+            if (getEditor().mCustomSelectionActionModeCallback != null) {
+                if (!getEditor().mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) {
                     // The custom mode can choose to cancel the action mode
                     return false;
                 }
@@ -10292,16 +10426,16 @@
 
         @Override
         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-            if (mCustomSelectionActionModeCallback != null) {
-                return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu);
+            if (getEditor().mCustomSelectionActionModeCallback != null) {
+                return getEditor().mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu);
             }
             return true;
         }
 
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-            if (mCustomSelectionActionModeCallback != null &&
-                 mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) {
+            if (getEditor().mCustomSelectionActionModeCallback != null &&
+                 getEditor().mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) {
                 return true;
             }
             return onTextContextMenuItem(item.getItemId());
@@ -10309,16 +10443,16 @@
 
         @Override
         public void onDestroyActionMode(ActionMode mode) {
-            if (mCustomSelectionActionModeCallback != null) {
-                mCustomSelectionActionModeCallback.onDestroyActionMode(mode);
+            if (getEditor().mCustomSelectionActionModeCallback != null) {
+                getEditor().mCustomSelectionActionModeCallback.onDestroyActionMode(mode);
             }
             Selection.setSelection((Spannable) mText, getSelectionEnd());
 
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.hide();
+            if (getEditor().mSelectionModifierCursorController != null) {
+                getEditor().mSelectionModifierCursorController.hide();
             }
 
-            mSelectionActionMode = null;
+            getEditor().mSelectionActionMode = null;
         }
     }
 
@@ -10332,7 +10466,7 @@
         protected void createPopupWindow() {
             mPopupWindow = new PopupWindow(TextView.this.mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
-            mPopupWindow.setClippingEnabled(true);
+            mPopupWindow.setClippingEnabled(true);   
         }
 
         @Override
@@ -10732,7 +10866,7 @@
         public void show() {
             super.show();
 
-            final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime;
+            final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - LAST_CUT_OR_COPY_TIME;
             if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) {
                 showActionPopupWindow(0);
             }
@@ -10746,13 +10880,14 @@
         }
 
         private void hideAfterDelay() {
-            removeHiderCallback();
             if (mHider == null) {
                 mHider = new Runnable() {
                     public void run() {
                         hide();
                     }
                 };
+            } else {
+                removeHiderCallback();
             }
             TextView.this.postDelayed(mHider, DELAY_BEFORE_HANDLE_FADES_OUT);
         }
@@ -10973,12 +11108,12 @@
         }
 
         private InsertionHandleView getHandle() {
-            if (mSelectHandleCenter == null) {
-                mSelectHandleCenter = mContext.getResources().getDrawable(
+            if (getEditor().mSelectHandleCenter == null) {
+                getEditor().mSelectHandleCenter = mContext.getResources().getDrawable(
                         mTextSelectHandleRes);
             }
             if (mHandle == null) {
-                mHandle = new InsertionHandleView(mSelectHandleCenter);
+                mHandle = new InsertionHandleView(getEditor().mSelectHandleCenter);
             }
             return mHandle;
         }
@@ -11019,12 +11154,12 @@
         }
 
         private void initDrawables() {
-            if (mSelectHandleLeft == null) {
-                mSelectHandleLeft = mContext.getResources().getDrawable(
+            if (getEditor().mSelectHandleLeft == null) {
+                getEditor().mSelectHandleLeft = mContext.getResources().getDrawable(
                         mTextSelectHandleLeftRes);
             }
-            if (mSelectHandleRight == null) {
-                mSelectHandleRight = mContext.getResources().getDrawable(
+            if (getEditor().mSelectHandleRight == null) {
+                getEditor().mSelectHandleRight = mContext.getResources().getDrawable(
                         mTextSelectHandleRightRes);
             }
         }
@@ -11032,10 +11167,10 @@
         private void initHandles() {
             // Lazy object creation has to be done before updatePosition() is called.
             if (mStartHandle == null) {
-                mStartHandle = new SelectionStartHandleView(mSelectHandleLeft, mSelectHandleRight);
+                mStartHandle = new SelectionStartHandleView(getEditor().mSelectHandleLeft, getEditor().mSelectHandleRight);
             }
             if (mEndHandle == null) {
-                mEndHandle = new SelectionEndHandleView(mSelectHandleRight, mSelectHandleLeft);
+                mEndHandle = new SelectionEndHandleView(getEditor().mSelectHandleRight, getEditor().mSelectHandleLeft);
             }
 
             mStartHandle.show();
@@ -11080,7 +11215,7 @@
 
                             if (stayedInArea && isPositionOnText(x, y)) {
                                 startSelectionActionMode();
-                                mDiscardNextActionUp = true;
+                                getEditor().mDiscardNextActionUp = true;
                             }
                         }
                     }
@@ -11169,465 +11304,501 @@
         }
     }
 
-    private void hideInsertionPointCursorController() {
-        // No need to create the controller to hide it.
-        if (mInsertionPointCursorController != null) {
-            mInsertionPointCursorController.hide();
-        }
+    static class InputContentType {
+        int imeOptions = EditorInfo.IME_NULL;
+        String privateImeOptions;
+        CharSequence imeActionLabel;
+        int imeActionId;
+        Bundle extras;
+        OnEditorActionListener onEditorActionListener;
+        boolean enterDown;
     }
 
-    /**
-     * Hides the insertion controller and stops text selection mode, hiding the selection controller
-     */
-    private void hideControllers() {
-        hideCursorControllers();
-        hideSpanControllers();
+    static class InputMethodState {
+        Rect mCursorRectInWindow = new Rect();
+        RectF mTmpRectF = new RectF();
+        float[] mTmpOffset = new float[2];
+        ExtractedTextRequest mExtracting;
+        final ExtractedText mTmpExtracted = new ExtractedText();
+        int mBatchEditNesting;
+        boolean mCursorChanged;
+        boolean mSelectionModeChanged;
+        boolean mContentChanged;
+        int mChangedStart, mChangedEnd, mChangedDelta;
     }
 
-    private void hideSpanControllers() {
-        if (mChangeWatcher != null) {
-            mChangeWatcher.hideControllers();
-        }
-    }
-
-    private void hideCursorControllers() {
-        if (mSuggestionsPopupWindow != null && !mSuggestionsPopupWindow.isShowingUp()) {
-            // Should be done before hide insertion point controller since it triggers a show of it
-            mSuggestionsPopupWindow.hide();
-        }
-        hideInsertionPointCursorController();
-        stopSelectionActionMode();
-    }
-
-    /**
-     * Get the character offset closest to the specified absolute position. A typical use case is to
-     * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method.
-     *
-     * @param x The horizontal absolute position of a point on screen
-     * @param y The vertical absolute position of a point on screen
-     * @return the character offset for the character whose position is closest to the specified
-     *  position. Returns -1 if there is no layout.
-     */
-    public int getOffsetForPosition(float x, float y) {
-        if (getLayout() == null) return -1;
-        final int line = getLineAtCoordinate(y);
-        final int offset = getOffsetAtCoordinate(line, x);
-        return offset;
-    }
-
-    private float convertToLocalHorizontalCoordinate(float x) {
-        x -= getTotalPaddingLeft();
-        // Clamp the position to inside of the view.
-        x = Math.max(0.0f, x);
-        x = Math.min(getWidth() - getTotalPaddingRight() - 1, x);
-        x += getScrollX();
-        return x;
-    }
-
-    private int getLineAtCoordinate(float y) {
-        y -= getTotalPaddingTop();
-        // Clamp the position to inside of the view.
-        y = Math.max(0.0f, y);
-        y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
-        y += getScrollY();
-        return getLayout().getLineForVertical((int) y);
-    }
-
-    private int getOffsetAtCoordinate(int line, float x) {
-        x = convertToLocalHorizontalCoordinate(x);
-        return getLayout().getOffsetForHorizontal(line, x);
-    }
-
-    /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed
-     * in the view. Returns false when the position is in the empty space of left/right of text.
-     */
-    private boolean isPositionOnText(float x, float y) {
-        if (getLayout() == null) return false;
-
-        final int line = getLineAtCoordinate(y);
-        x = convertToLocalHorizontalCoordinate(x);
-
-        if (x < getLayout().getLineLeft(line)) return false;
-        if (x > getLayout().getLineRight(line)) return false;
-        return true;
-    }
-
-    @Override
-    public boolean onDragEvent(DragEvent event) {
-        switch (event.getAction()) {
-            case DragEvent.ACTION_DRAG_STARTED:
-                return hasInsertionController();
-
-            case DragEvent.ACTION_DRAG_ENTERED:
-                TextView.this.requestFocus();
-                return true;
-
-            case DragEvent.ACTION_DRAG_LOCATION:
-                final int offset = getOffsetForPosition(event.getX(), event.getY());
-                Selection.setSelection((Spannable)mText, offset);
-                return true;
-
-            case DragEvent.ACTION_DROP:
-                onDrop(event);
-                return true;
-
-            case DragEvent.ACTION_DRAG_ENDED:
-            case DragEvent.ACTION_DRAG_EXITED:
-            default:
-                return true;
-        }
-    }
-
-    private void onDrop(DragEvent event) {
-        StringBuilder content = new StringBuilder("");
-        ClipData clipData = event.getClipData();
-        final int itemCount = clipData.getItemCount();
-        for (int i=0; i < itemCount; i++) {
-            Item item = clipData.getItemAt(i);
-            content.append(item.coerceToText(TextView.this.mContext));
+    private class Editor {
+        Editor() {
+            mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            final CompatibilityInfo compat = TextView.this.getResources().getCompatibilityInfo();
+            mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
         }
 
-        final int offset = getOffsetForPosition(event.getX(), event.getY());
+        // Cursor Controllers.
+        InsertionPointCursorController mInsertionPointCursorController;
+        SelectionModifierCursorController mSelectionModifierCursorController;
+        ActionMode mSelectionActionMode;
+        boolean mInsertionControllerEnabled;
+        boolean mSelectionControllerEnabled;
 
-        Object localState = event.getLocalState();
-        DragLocalState dragLocalState = null;
-        if (localState instanceof DragLocalState) {
-            dragLocalState = (DragLocalState) localState;
-        }
-        boolean dragDropIntoItself = dragLocalState != null &&
-                dragLocalState.sourceTextView == this;
+        // Used to highlight a word when it is corrected by the IME
+        CorrectionHighlighter mCorrectionHighlighter;
 
-        if (dragDropIntoItself) {
-            if (offset >= dragLocalState.start && offset < dragLocalState.end) {
-                // A drop inside the original selection discards the drop.
-                return;
-            }
-        }
+        InputContentType mInputContentType;
+        InputMethodState mInputMethodState;
 
-        final int originalLength = mText.length();
-        long minMax = prepareSpacesAroundPaste(offset, offset, content);
-        int min = extractRangeStartFromLong(minMax);
-        int max = extractRangeEndFromLong(minMax);
+        Path mHighlightPath;
+        boolean mHighlightPathBogus = true;
+        final Paint mHighlightPaint;
 
-        Selection.setSelection((Spannable) mText, max);
-        replaceText_internal(min, max, content);
+        DisplayList mTextDisplayList;
+        boolean mTextDisplayListIsValid;
 
-        if (dragDropIntoItself) {
-            int dragSourceStart = dragLocalState.start;
-            int dragSourceEnd = dragLocalState.end;
-            if (max <= dragSourceStart) {
-                // Inserting text before selection has shifted positions
-                final int shift = mText.length() - originalLength;
-                dragSourceStart += shift;
-                dragSourceEnd += shift;
-            }
+        boolean mFrozenWithFocus;
+        boolean mSelectionMoved;
+        boolean mTouchFocusSelected;
 
-            // Delete original selection
-            deleteText_internal(dragSourceStart, dragSourceEnd);
+        KeyListener mKeyListener;
+        int mInputType = EditorInfo.TYPE_NULL;
 
-            // Make sure we do not leave two adjacent spaces.
-            if ((dragSourceStart == 0 ||
-                    Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) &&
-                    (dragSourceStart == mText.length() ||
-                    Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) {
-                final int pos = dragSourceStart == mText.length() ?
-                        dragSourceStart - 1 : dragSourceStart;
-                deleteText_internal(pos, pos + 1);
-            }
-        }
-    }
+        boolean mDiscardNextActionUp;
+        boolean mIgnoreActionUpEvent;
 
-    /**
-     * @return True if this view supports insertion handles.
-     */
-    boolean hasInsertionController() {
-        return mInsertionControllerEnabled;
-    }
+        long mShowCursor;
+        Blink mBlink;
 
-    /**
-     * @return True if this view supports selection handles.
-     */
-    boolean hasSelectionController() {
-        return mSelectionControllerEnabled;
-    }
+        boolean mCursorVisible = true;
+        boolean mSelectAllOnFocus;
+        boolean mTextIsSelectable;
 
-    InsertionPointCursorController getInsertionController() {
-        if (!mInsertionControllerEnabled) {
-            return null;
-        }
+        CharSequence mError;
+        boolean mErrorWasChanged;
+        ErrorPopup mErrorPopup;
+        /**
+         * This flag is set if the TextView tries to display an error before it
+         * is attached to the window (so its position is still unknown).
+         * It causes the error to be shown later, when onAttachedToWindow()
+         * is called.
+         */
+        boolean mShowErrorAfterAttach;
 
-        if (mInsertionPointCursorController == null) {
-            mInsertionPointCursorController = new InsertionPointCursorController();
+        boolean mInBatchEditControllers;
 
+        SuggestionsPopupWindow mSuggestionsPopupWindow;
+        SuggestionRangeSpan mSuggestionRangeSpan;
+        Runnable mShowSuggestionRunnable;
+
+        final Drawable[] mCursorDrawable = new Drawable[2];
+        int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split)
+
+        Drawable mSelectHandleLeft;
+        Drawable mSelectHandleRight;
+        Drawable mSelectHandleCenter;
+
+        // Global listener that detects changes in the global position of the TextView
+        PositionListener mPositionListener;
+
+        float mLastDownPositionX, mLastDownPositionY;
+        Callback mCustomSelectionActionModeCallback;
+
+        // Set when this TextView gained focus with some text selected. Will start selection mode.
+        boolean mCreatedWithASelection;
+
+        WordIterator mWordIterator;
+        SpellChecker mSpellChecker;
+
+        void onAttachedToWindow() {
             final ViewTreeObserver observer = getViewTreeObserver();
-            observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+            // No need to create the controller.
+            // The get method will add the listener on controller creation.
+            if (mInsertionPointCursorController != null) {
+                observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+            }
+            if (mSelectionModifierCursorController != null) {
+                observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+            }
+            updateSpellCheckSpans(0, mText.length(), true /* create the spell checker if needed */);
         }
 
-        return mInsertionPointCursorController;
-    }
+        void onDetachedFromWindow() {
+            if (mError != null) {
+                hideError();
+            }
 
-    SelectionModifierCursorController getSelectionController() {
-        if (!mSelectionControllerEnabled) {
-            return null;
+            if (mBlink != null) {
+                mBlink.removeCallbacks(mBlink);
+            }
+
+            if (mInsertionPointCursorController != null) {
+                mInsertionPointCursorController.onDetached();
+            }
+
+            if (mSelectionModifierCursorController != null) {
+                mSelectionModifierCursorController.onDetached();
+            }
+
+            if (mShowSuggestionRunnable != null) {
+                removeCallbacks(mShowSuggestionRunnable);
+            }
+
+            if (mTextDisplayList != null) {
+                mTextDisplayList.invalidate();
+            }
+
+            if (mSpellChecker != null) {
+                mSpellChecker.closeSession();
+                // Forces the creation of a new SpellChecker next time this window is created.
+                // Will handle the cases where the settings has been changed in the meantime.
+                mSpellChecker = null;
+            }
+
+            hideControllers();
         }
 
-        if (mSelectionModifierCursorController == null) {
-            mSelectionModifierCursorController = new SelectionModifierCursorController();
-
-            final ViewTreeObserver observer = getViewTreeObserver();
-            observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
-        }
-
-        return mSelectionModifierCursorController;
-    }
-
-    boolean isInBatchEditMode() {
-        final InputMethodState ims = mInputMethodState;
-        if (ims != null) {
-            return ims.mBatchEditNesting > 0;
-        }
-        return mInBatchEditControllers;
-    }
-
-    @Override
-    protected void resolveTextDirection() {
-        if (hasPasswordTransformationMethod()) {
-            mTextDir = TextDirectionHeuristics.LOCALE;
-            return;
-        }
-
-        // Always need to resolve layout direction first
-        final boolean defaultIsRtl = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL);
-
-        // Then resolve text direction on the parent
-        super.resolveTextDirection();
-
-        // Now, we can select the heuristic
-        int textDir = getResolvedTextDirection();
-        switch (textDir) {
-            default:
-            case TEXT_DIRECTION_FIRST_STRONG:
-                mTextDir = (defaultIsRtl ? TextDirectionHeuristics.FIRSTSTRONG_RTL :
-                        TextDirectionHeuristics.FIRSTSTRONG_LTR);
-                break;
-            case TEXT_DIRECTION_ANY_RTL:
-                mTextDir = TextDirectionHeuristics.ANYRTL_LTR;
-                break;
-            case TEXT_DIRECTION_LTR:
-                mTextDir = TextDirectionHeuristics.LTR;
-                break;
-            case TEXT_DIRECTION_RTL:
-                mTextDir = TextDirectionHeuristics.RTL;
-                break;
-            case TEXT_DIRECTION_LOCALE:
-                mTextDir = TextDirectionHeuristics.LOCALE;
-                break;
-        }
-    }
-
-    /**
-     * Subclasses will need to override this method to implement their own way of resolving
-     * drawables depending on the layout direction.
-     *
-     * A call to the super method will be required from the subclasses implementation.
-     *
-     */
-    protected void resolveDrawables() {
-        // No need to resolve twice
-        if (mResolvedDrawables) {
-            return;
-        }
-        // No drawable to resolve
-        if (mDrawables == null) {
-            return;
-        }
-        // No relative drawable to resolve
-        if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
-            mResolvedDrawables = true;
-            return;
-        }
-
-        Drawables dr = mDrawables;
-        switch(getResolvedLayoutDirection()) {
-            case LAYOUT_DIRECTION_RTL:
-                if (dr.mDrawableStart != null) {
-                    dr.mDrawableRight = dr.mDrawableStart;
-
-                    dr.mDrawableSizeRight = dr.mDrawableSizeStart;
-                    dr.mDrawableHeightRight = dr.mDrawableHeightStart;
+        void adjustInputType(boolean password, boolean passwordInputType,
+                boolean webPasswordInputType, boolean numberPasswordInputType) {
+            // mInputType has been set from inputType, possibly modified by mInputMethod.
+            // Specialize mInputType to [web]password if we have a text class and the original input
+            // type was a password.
+            if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
+                if (password || passwordInputType) {
+                    mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
+                            | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
                 }
-                if (dr.mDrawableEnd != null) {
-                    dr.mDrawableLeft = dr.mDrawableEnd;
-
-                    dr.mDrawableSizeLeft = dr.mDrawableSizeEnd;
-                    dr.mDrawableHeightLeft = dr.mDrawableHeightEnd;
+                if (webPasswordInputType) {
+                    mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
+                            | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
                 }
-                break;
-
-            case LAYOUT_DIRECTION_LTR:
-            default:
-                if (dr.mDrawableStart != null) {
-                    dr.mDrawableLeft = dr.mDrawableStart;
-
-                    dr.mDrawableSizeLeft = dr.mDrawableSizeStart;
-                    dr.mDrawableHeightLeft = dr.mDrawableHeightStart;
+            } else if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_NUMBER) {
+                if (numberPasswordInputType) {
+                    mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
+                            | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD;
                 }
-                if (dr.mDrawableEnd != null) {
-                    dr.mDrawableRight = dr.mDrawableEnd;
-
-                    dr.mDrawableSizeRight = dr.mDrawableSizeEnd;
-                    dr.mDrawableHeightRight = dr.mDrawableHeightEnd;
-                }
-                break;
+            }
         }
-        mResolvedDrawables = true;
-    }
 
-    protected void resetResolvedDrawables() {
-        mResolvedDrawables = false;
-    }
+        void setFrame() {
+            if (mErrorPopup != null) {
+                TextView tv = (TextView) mErrorPopup.getContentView();
+                chooseSize(mErrorPopup, mError, tv);
+                mErrorPopup.update(TextView.this, getErrorX(), getErrorY(),
+                        mErrorPopup.getWidth(), mErrorPopup.getHeight());
+            }
+        }
 
-    /**
-     * @hide
-     */
-    protected void viewClicked(InputMethodManager imm) {
-        if (imm != null) {
-            imm.viewClicked(this);
+        void onFocusChanged(boolean focused, int direction) {
+            mShowCursor = SystemClock.uptimeMillis();
+            ensureEndedBatchEdit();
+
+            if (focused) {
+                int selStart = getSelectionStart();
+                int selEnd = getSelectionEnd();
+
+                // SelectAllOnFocus fields are highlighted and not selected. Do not start text selection
+                // mode for these, unless there was a specific selection already started.
+                final boolean isFocusHighlighted = mSelectAllOnFocus && selStart == 0 &&
+                        selEnd == mText.length();
+
+                mCreatedWithASelection = mFrozenWithFocus && hasSelection() && !isFocusHighlighted;
+
+                if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) {
+                    // If a tap was used to give focus to that view, move cursor at tap position.
+                    // Has to be done before onTakeFocus, which can be overloaded.
+                    final int lastTapPosition = getLastTapPosition();
+                    if (lastTapPosition >= 0) {
+                        Selection.setSelection((Spannable) mText, lastTapPosition);
+                    }
+
+                    // Note this may have to be moved out of the Editor class
+                    if (mMovement != null) {
+                        mMovement.onTakeFocus(TextView.this, (Spannable) mText, direction);
+                    }
+
+                    // The DecorView does not have focus when the 'Done' ExtractEditText button is
+                    // pressed. Since it is the ViewAncestor's mView, it requests focus before
+                    // ExtractEditText clears focus, which gives focus to the ExtractEditText.
+                    // This special case ensure that we keep current selection in that case.
+                    // It would be better to know why the DecorView does not have focus at that time.
+                    if (((TextView.this instanceof ExtractEditText) || mSelectionMoved) &&
+                            selStart >= 0 && selEnd >= 0) {
+                        /*
+                         * Someone intentionally set the selection, so let them
+                         * do whatever it is that they wanted to do instead of
+                         * the default on-focus behavior.  We reset the selection
+                         * here instead of just skipping the onTakeFocus() call
+                         * because some movement methods do something other than
+                         * just setting the selection in theirs and we still
+                         * need to go through that path.
+                         */
+                        Selection.setSelection((Spannable) mText, selStart, selEnd);
+                    }
+
+                    if (mSelectAllOnFocus) {
+                        selectAll();
+                    }
+
+                    mTouchFocusSelected = true;
+                }
+
+                mFrozenWithFocus = false;
+                mSelectionMoved = false;
+
+                if (mError != null) {
+                    showError();
+                }
+
+                makeBlink();
+            } else {
+                if (mError != null) {
+                    hideError();
+                }
+                // Don't leave us in the middle of a batch edit.
+                onEndBatchEdit();
+
+                if (TextView.this instanceof ExtractEditText) {
+                    // terminateTextSelectionMode removes selection, which we want to keep when
+                    // ExtractEditText goes out of focus.
+                    final int selStart = getSelectionStart();
+                    final int selEnd = getSelectionEnd();
+                    hideControllers();
+                    Selection.setSelection((Spannable) mText, selStart, selEnd);
+                } else {
+                    hideControllers();
+                    downgradeEasyCorrectionSpans();
+                }
+
+                // No need to create the controller
+                if (mSelectionModifierCursorController != null) {
+                    mSelectionModifierCursorController.resetTouchOffsets();
+                }
+            }
+        }
+
+        void sendOnTextChanged(int start, int after) {
+            updateSpellCheckSpans(start, start + after, false);
+            mTextDisplayListIsValid = false;
+
+            // Hide the controllers as soon as text is modified (typing, procedural...)
+            // We do not hide the span controllers, since they can be added when a new text is
+            // inserted into the text view (voice IME).
+            hideCursorControllers();
+        }
+
+        private int getLastTapPosition() {
+            // No need to create the controller at that point, no last tap position saved
+            if (mSelectionModifierCursorController != null) {
+                int lastTapPosition = mSelectionModifierCursorController.getMinTouchOffset();
+                if (lastTapPosition >= 0) {
+                    // Safety check, should not be possible.
+                    if (lastTapPosition > mText.length()) {
+                        Log.e(LOG_TAG, "Invalid tap focus position (" + lastTapPosition + " vs "
+                                + mText.length() + ")");
+                        lastTapPosition = mText.length();
+                    }
+                    return lastTapPosition;
+                }
+            }
+
+            return -1;
+        }
+
+        void onWindowFocusChanged(boolean hasWindowFocus) {
+            if (hasWindowFocus) {
+                if (mBlink != null) {
+                    mBlink.uncancel();
+                    makeBlink();
+                }
+            } else {
+                if (mBlink != null) {
+                    mBlink.cancel();
+                }
+                if (mInputContentType != null) {
+                    mInputContentType.enterDown = false;
+                }
+                // Order matters! Must be done before onParentLostFocus to rely on isShowingUp
+                hideControllers();
+                if (mSuggestionsPopupWindow != null) {
+                    mSuggestionsPopupWindow.onParentLostFocus();
+                }
+
+                // Don't leave us in the middle of a batch edit.
+                onEndBatchEdit();
+            }
+        }
+
+        void onTouchEvent(MotionEvent event) {
+            if (hasSelectionController()) {
+                getSelectionController().onTouchEvent(event);
+            }
+
+            if (mShowSuggestionRunnable != null) {
+                removeCallbacks(mShowSuggestionRunnable);
+                mShowSuggestionRunnable = null;
+            }
+
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mLastDownPositionX = event.getX();
+                mLastDownPositionY = event.getY();
+
+                // Reset this state; it will be re-set if super.onTouchEvent
+                // causes focus to move to the view.
+                mTouchFocusSelected = false;
+                mIgnoreActionUpEvent = false;
+            }
+        }
+
+        void onDraw(Canvas canvas, Layout layout, int cursorOffsetVertical) {
+            Path highlight = null;
+            Paint highlightPaint = null;
+
+            int selStart = -1, selEnd = -1;
+            boolean drawCursor = false;
+
+            highlightPaint = mHighlightPaint;
+            //  If there is no movement method, then there can be no selection.
+            //  Check that first and attempt to skip everything having to do with
+            //  the cursor.
+            //  XXX This is not strictly true -- a program could set the
+            //  selection manually if it really wanted to.
+            if (mMovement != null && (isFocused() || isPressed())) {
+                selStart = getSelectionStart();
+                selEnd = getSelectionEnd();
+
+                if (selStart >= 0) {
+                    if (mHighlightPath == null) mHighlightPath = new Path();
+
+                    if (selStart == selEnd) {
+                        if (isCursorVisible() &&
+                                (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) {
+                            if (mHighlightPathBogus) {
+                                mHighlightPath.reset();
+                                mLayout.getCursorPath(selStart, mHighlightPath, mText);
+                                updateCursorsPositions();
+                                mHighlightPathBogus = false;
+                            }
+
+                            // XXX should pass to skin instead of drawing directly
+                            highlightPaint.setColor(mCurTextColor);
+                            if (mCurrentAlpha != 255) {
+                                highlightPaint.setAlpha(
+                                        (mCurrentAlpha * Color.alpha(mCurTextColor)) / 255);
+                            }
+                            highlightPaint.setStyle(Paint.Style.STROKE);
+                            highlight = mHighlightPath;
+                            drawCursor = mCursorCount > 0;
+                        }
+                    } else if (textCanBeSelected()) {
+                        if (mHighlightPathBogus) {
+                            mHighlightPath.reset();
+                            mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
+                            mHighlightPathBogus = false;
+                        }
+
+                        // XXX should pass to skin instead of drawing directly
+                        highlightPaint.setColor(mHighlightColor);
+                        if (mCurrentAlpha != 255) {
+                            highlightPaint.setAlpha(
+                                    (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255);
+                        }
+                        highlightPaint.setStyle(Paint.Style.FILL);
+
+                        highlight = mHighlightPath;
+                    }
+                }
+            }
+
+            final InputMethodState ims = mInputMethodState;
+            if (ims != null && ims.mBatchEditNesting == 0) {
+                InputMethodManager imm = InputMethodManager.peekInstance();
+                if (imm != null) {
+                    if (imm.isActive(TextView.this)) {
+                        boolean reported = false;
+                        if (ims.mContentChanged || ims.mSelectionModeChanged) {
+                            // We are in extract mode and the content has changed
+                            // in some way... just report complete new text to the
+                            // input method.
+                            reported = reportExtractedText();
+                        }
+                        if (!reported && highlight != null) {
+                            int candStart = -1;
+                            int candEnd = -1;
+                            if (mText instanceof Spannable) {
+                                Spannable sp = (Spannable)mText;
+                                candStart = EditableInputConnection.getComposingSpanStart(sp);
+                                candEnd = EditableInputConnection.getComposingSpanEnd(sp);
+                            }
+                            imm.updateSelection(TextView.this, selStart, selEnd, candStart, candEnd);
+                        }
+                    }
+
+                    if (imm.isWatchingCursor(TextView.this) && highlight != null) {
+                        highlight.computeBounds(ims.mTmpRectF, true);
+                        ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0;
+
+                        canvas.getMatrix().mapPoints(ims.mTmpOffset);
+                        ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]);
+
+                        ims.mTmpRectF.offset(0, cursorOffsetVertical);
+
+                        ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5),
+                                (int)(ims.mTmpRectF.top + 0.5),
+                                (int)(ims.mTmpRectF.right + 0.5),
+                                (int)(ims.mTmpRectF.bottom + 0.5));
+
+                        imm.updateCursor(TextView.this,
+                                ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top,
+                                ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom);
+                    }
+                }
+            }
+
+            if (mCorrectionHighlighter != null) {
+                mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
+            }
+
+            if (drawCursor) {
+                drawCursor(canvas, cursorOffsetVertical);
+                // Rely on the drawable entirely, do not draw the cursor line.
+                // Has to be done after the IMM related code above which relies on the highlight.
+                highlight = null;
+            }
+
+            if (canHaveDisplayList() && canvas.isHardwareAccelerated()) {
+                final int width = mRight - mLeft;
+                final int height = mBottom - mTop;
+
+                if (mTextDisplayList == null || !mTextDisplayList.isValid() ||
+                        !mTextDisplayListIsValid) {
+                    if (mTextDisplayList == null) {
+                        mTextDisplayList = getHardwareRenderer().createDisplayList("Text");
+                    }
+
+                    final HardwareCanvas hardwareCanvas = mTextDisplayList.start();
+                    try {
+                        hardwareCanvas.setViewport(width, height);
+                        // The dirty rect should always be null for a display list
+                        hardwareCanvas.onPreDraw(null);
+                        hardwareCanvas.translate(-mScrollX, -mScrollY);
+                        layout.draw(hardwareCanvas, highlight, highlightPaint, cursorOffsetVertical);
+                        hardwareCanvas.translate(mScrollX, mScrollY);
+                    } finally {
+                        hardwareCanvas.onPostDraw();
+                        mTextDisplayList.end();
+                        mTextDisplayListIsValid = true;
+                    }
+                }
+                canvas.translate(mScrollX, mScrollY);
+                ((HardwareCanvas) canvas).drawDisplayList(mTextDisplayList, width, height, null,
+                        DisplayList.FLAG_CLIP_CHILDREN);
+                canvas.translate(-mScrollX, -mScrollY);
+            } else {
+                layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
+            }
+
+            if (mMarquee != null && mMarquee.shouldDrawGhost()) {
+                canvas.translate((int) mMarquee.getGhostOffset(), 0.0f);
+                layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
+            }
         }
     }
-
-    /**
-     * Deletes the range of text [start, end[.
-     * @hide
-     */
-    protected void deleteText_internal(int start, int end) {
-        ((Editable) mText).delete(start, end);
-    }
-
-    /**
-     * Replaces the range of text [start, end[ by replacement text
-     * @hide
-     */
-    protected void replaceText_internal(int start, int end, CharSequence text) {
-        ((Editable) mText).replace(start, end, text);
-    }
-
-    /**
-     * Sets a span on the specified range of text
-     * @hide
-     */
-    protected void setSpan_internal(Object span, int start, int end, int flags) {
-        ((Editable) mText).setSpan(span, start, end, flags);
-    }
-
-    /**
-     * Moves the cursor to the specified offset position in text
-     * @hide
-     */
-    protected void setCursorPosition_internal(int start, int end) {
-        Selection.setSelection(((Editable) mText), start, end);
-    }
-
-    @ViewDebug.ExportedProperty(category = "text")
-    private CharSequence            mText;
-    private CharSequence            mTransformed;
-    private BufferType              mBufferType = BufferType.NORMAL;
-
-    private int                     mInputType = EditorInfo.TYPE_NULL;
-    private CharSequence            mHint;
-    private Layout                  mHintLayout;
-
-    private KeyListener             mInput;
-
-    private MovementMethod          mMovement;
-    private TransformationMethod    mTransformation;
-    private boolean                 mAllowTransformationLengthChange;
-    private ChangeWatcher           mChangeWatcher;
-
-    private ArrayList<TextWatcher>  mListeners = null;
-
-    // display attributes
-    private final TextPaint         mTextPaint;
-    private boolean                 mUserSetTextScaleX;
-    private final Paint             mHighlightPaint;
-    private int                     mHighlightColor = 0x6633B5E5;
-    private Layout                  mLayout;
-
-    private long                    mShowCursor;
-    private Blink                   mBlink;
-    private boolean                 mCursorVisible = true;
-
-    // Cursor Controllers.
-    private InsertionPointCursorController mInsertionPointCursorController;
-    private SelectionModifierCursorController mSelectionModifierCursorController;
-    private ActionMode              mSelectionActionMode;
-    private boolean                 mInsertionControllerEnabled;
-    private boolean                 mSelectionControllerEnabled;
-    private boolean                 mInBatchEditControllers;
-
-    private boolean                 mSelectAllOnFocus = false;
-
-    private int                     mGravity = Gravity.TOP | Gravity.START;
-    private boolean                 mHorizontallyScrolling;
-
-    private int                     mAutoLinkMask;
-    private boolean                 mLinksClickable = true;
-
-    private float                   mSpacingMult = 1.0f;
-    private float                   mSpacingAdd = 0.0f;
-    private boolean                 mTextIsSelectable = false;
-
-    private static final int        LINES = 1;
-    private static final int        EMS = LINES;
-    private static final int        PIXELS = 2;
-
-    private int                     mMaximum = Integer.MAX_VALUE;
-    private int                     mMaxMode = LINES;
-    private int                     mMinimum = 0;
-    private int                     mMinMode = LINES;
-
-    private int                     mOldMaximum = mMaximum;
-    private int                     mOldMaxMode = mMaxMode;
-
-    private int                     mMaxWidth = Integer.MAX_VALUE;
-    private int                     mMaxWidthMode = PIXELS;
-    private int                     mMinWidth = 0;
-    private int                     mMinWidthMode = PIXELS;
-
-    private boolean                 mSingleLine;
-    private int                     mDesiredHeightAtMeasure = -1;
-    private boolean                 mIncludePad = true;
-
-    // tmp primitives, so we don't alloc them on each draw
-    private Path                    mHighlightPath;
-    private boolean                 mHighlightPathBogus = true;
-    private static final RectF      sTempRect = new RectF();
-    private static final float[]    sTmpPosition = new float[2];
-
-    // XXX should be much larger
-    private static final int        VERY_WIDE = 1024*1024;
-
-    private static final int        BLINK = 500;
-
-    private static final int ANIMATED_SCROLL_GAP = 250;
-    private long mLastScroll;
-    private Scroller mScroller = null;
-
-    private BoringLayout.Metrics mBoring;
-    private BoringLayout.Metrics mHintBoring;
-
-    private BoringLayout mSavedLayout, mSavedHintLayout;
-
-    private TextDirectionHeuristic mTextDir = null;
-
-    private static final InputFilter[] NO_FILTERS = new InputFilter[0];
-    private InputFilter[] mFilters = NO_FILTERS;
-    private static final Spanned EMPTY_SPANNED = new SpannedString("");
-    private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
-    // System wide time for last cut or copy action.
-    private static long sLastCutOrCopyTime;
-    // Used to highlight a word when it is corrected by the IME
-    private CorrectionHighlighter mCorrectionHighlighter;
-    // New state used to change background based on whether this TextView is multiline.
-    private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline };
 }
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 8f10fff..ef1d7d0 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -213,6 +213,7 @@
                     button.requestFocus();
                     mIsAm = !mIsAm;
                     updateAmPmControl();
+                    onTimeChanged();
                 }
             });
         } else {
@@ -227,6 +228,7 @@
                     picker.requestFocus();
                     mIsAm = !mIsAm;
                     updateAmPmControl();
+                    onTimeChanged();
                 }
             });
             mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input);
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index f3d891d1..02dc27b 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -501,7 +501,7 @@
 
         } else {
 
-            ViewRootImpl viewRoot = getOwnerViewRootImpl();
+            ViewRootImpl viewRoot = mOwnerView.getViewRootImpl();
             if (viewRoot != null) {
                 viewRoot.dispatchKey(event);
             }
@@ -526,20 +526,6 @@
         }
     }
 
-    private ViewRootImpl getOwnerViewRootImpl() {
-        View rootViewOfOwner = mOwnerView.getRootView();
-        if (rootViewOfOwner == null) {
-            return null;
-        }
-
-        ViewParent parentOfRootView = rootViewOfOwner.getParent();
-        if (parentOfRootView instanceof ViewRootImpl) {
-            return (ViewRootImpl) parentOfRootView;
-        } else {
-            return null;
-        }
-    }
-
     /**
      * @hide The ZoomButtonsController implements the OnTouchListener, but this
      *       does not need to be shown in its public API.
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
index c03ff1a..d443523 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -34,25 +34,23 @@
 }
 */
 
+/**
+ * Class encapsulating a Notification. Sent by the NotificationManagerService to the IStatusBar (in System UI).
+ */
 public class StatusBarNotification implements Parcelable {
-    public static int PRIORITY_JIFFY_EXPRESS = -100;
-    public static int PRIORITY_NORMAL        = 0;
-    public static int PRIORITY_ONGOING       = 100;
-    public static int PRIORITY_SYSTEM        = 200;
-
     public String pkg;
     public int id;
     public String tag;
     public int uid;
     public int initialPid;
     public Notification notification;
-    public int priority = PRIORITY_NORMAL;
-
+    public int score;
+    
     public StatusBarNotification() {
     }
 
     public StatusBarNotification(String pkg, int id, String tag,
-            int uid, int initialPid, Notification notification) {
+            int uid, int initialPid, int score, Notification notification) {
         if (pkg == null) throw new NullPointerException();
         if (notification == null) throw new NullPointerException();
 
@@ -61,9 +59,8 @@
         this.tag = tag;
         this.uid = uid;
         this.initialPid = initialPid;
+        this.score = score;
         this.notification = notification;
-
-        this.priority = PRIORITY_NORMAL;
     }
 
     public StatusBarNotification(Parcel in) {
@@ -80,7 +77,7 @@
         }
         this.uid = in.readInt();
         this.initialPid = in.readInt();
-        this.priority = in.readInt();
+        this.score = in.readInt();
         this.notification = new Notification(in);
     }
 
@@ -95,7 +92,7 @@
         }
         out.writeInt(this.uid);
         out.writeInt(this.initialPid);
-        out.writeInt(this.priority);
+        out.writeInt(this.score);
         this.notification.writeToParcel(out, flags);
     }
 
@@ -119,12 +116,12 @@
 
     public StatusBarNotification clone() {
         return new StatusBarNotification(this.pkg, this.id, this.tag,
-                this.uid, this.initialPid, this.notification.clone());
+                this.uid, this.initialPid, this.score, this.notification.clone());
     }
 
     public String toString() {
-        return "StatusBarNotification(package=" + pkg + " id=" + id + " tag=" + tag
-                + " notification=" + notification + " priority=" + priority + ")";
+        return "StatusBarNotification(pkg=" + pkg + " id=" + id + " tag=" + tag
+                + " score=" + score + " notn=" + notification + ")";
     }
 
     public boolean isOngoing() {
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
index ba0aa1a..4553f9f 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
@@ -24,7 +24,7 @@
 oneway interface ISpellCheckerSession {
     void onGetSuggestionsMultiple(
             in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords);
-    void onGetSuggestionsMultipleForSentence(in TextInfo[] textInfos, int suggestionsLimit);
+    void onGetSentenceSuggestionsMultiple(in TextInfo[] textInfos, int suggestionsLimit);
     void onCancel();
     void onClose();
 }
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
index b44dbc8..641ed8c 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.textservice;
 
+import android.view.textservice.SentenceSuggestionsInfo;
 import android.view.textservice.SuggestionsInfo;
 
 /**
@@ -23,5 +24,5 @@
  */
 oneway interface ISpellCheckerSessionListener {
     void onGetSuggestions(in SuggestionsInfo[] results);
-    void onGetSuggestionsForSentence(in SuggestionsInfo[] results);
+    void onGetSentenceSuggestions(in SentenceSuggestionsInfo[] result);
 }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 683aca5..5d80b79 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -67,6 +67,7 @@
     InputMethodSubtype getCurrentInputMethodSubtype();
     boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
     boolean switchToLastInputMethod(in IBinder token);
+    boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
     boolean setInputMethodEnabled(String id, boolean enabled);
     oneway void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
 }
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index ed02636..fa16527 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -405,8 +405,7 @@
                     View child = mMenuView.getChildAt(i);
                     child.setScaleY(0);
                     ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1);
-                    a.setDuration(100);
-                    a.setStartDelay(j * 70);
+                    a.setDuration(300);
                     b.with(a);
                 }
             }
@@ -432,8 +431,7 @@
                     View child = mMenuView.getChildAt(i);
                     child.setScaleY(0);
                     ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0);
-                    a.setDuration(100);
-                    a.setStartDelay(i * 70);
+                    a.setDuration(300);
                     b.with(a);
                 }
             }
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 517ce4e..2f325bf 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -606,6 +606,9 @@
                 ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) {
             mHomeLayout.setIcon(icon);
         }
+        if (mExpandedActionView != null) {
+            mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources()));
+        }
     }
 
     public void setIcon(int resId) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 6893ffb..acc3c1c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -857,11 +857,6 @@
      * @return Whether biometric weak lock is installed and that the front facing camera exists
      */
     public boolean isBiometricWeakInstalled() {
-        // Check that the system flag was set
-        if (!OPTION_ENABLE_FACELOCK.equals(getString(LOCKSCREEN_OPTIONS))) {
-            return false;
-        }
-
         // Check that it's installed
         PackageManager pm = mContext.getPackageManager();
         try {
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
index 2e7810f..26518eb 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -147,7 +147,7 @@
     }
 
     private void sendKeyEventsToTarget(int character) {
-        Handler handler = mTargetView.getHandler();
+        ViewRootImpl viewRootImpl = mTargetView.getViewRootImpl();
         KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getEvents(
                 new char[] { (char) character });
         if (events != null) {
@@ -156,22 +156,22 @@
                 KeyEvent event = events[i];
                 event = KeyEvent.changeFlags(event, event.getFlags()
                         | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
-                handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY, event));
+                viewRootImpl.dispatchKey(event);
             }
         }
     }
 
     public void sendDownUpKeyEvents(int keyEventCode) {
         long eventTime = SystemClock.uptimeMillis();
-        Handler handler = mTargetView.getHandler();
-        handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME,
+        ViewRootImpl viewRootImpl = mTargetView.getViewRootImpl();
+        viewRootImpl.dispatchKeyFromIme(
                 new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0,
                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
-                    KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)));
-        handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME,
+                    KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
+        viewRootImpl.dispatchKeyFromIme(
                 new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0,
                         KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
-                        KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)));
+                        KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
     }
 
     public void onKey(int primaryCode, int[] keyCodes) {
diff --git a/core/java/com/google/android/mms/ContentType.java b/core/java/com/google/android/mms/ContentType.java
index b066fad..12a1343 100644
--- a/core/java/com/google/android/mms/ContentType.java
+++ b/core/java/com/google/android/mms/ContentType.java
@@ -39,6 +39,7 @@
     public static final String IMAGE_GIF         = "image/gif";
     public static final String IMAGE_WBMP        = "image/vnd.wap.wbmp";
     public static final String IMAGE_PNG         = "image/png";
+    public static final String IMAGE_X_MS_BMP    = "image/x-ms-bmp";
 
     public static final String AUDIO_UNSPECIFIED = "audio/*";
     public static final String AUDIO_AAC         = "audio/aac";
@@ -58,6 +59,7 @@
     public static final String AUDIO_X_MPEG      = "audio/x-mpeg";
     public static final String AUDIO_X_MPG       = "audio/x-mpg";
     public static final String AUDIO_3GPP        = "audio/3gpp";
+    public static final String AUDIO_X_WAV       = "audio/x-wav";
     public static final String AUDIO_OGG         = "application/ogg";
 
     public static final String VIDEO_UNSPECIFIED = "video/*";
@@ -89,6 +91,7 @@
         sSupportedContentTypes.add(IMAGE_WBMP);
         sSupportedContentTypes.add(IMAGE_PNG);
         sSupportedContentTypes.add(IMAGE_JPG);
+        sSupportedContentTypes.add(IMAGE_X_MS_BMP);
         //supportedContentTypes.add(IMAGE_SVG); not yet supported.
 
         sSupportedContentTypes.add(AUDIO_AAC);
@@ -106,6 +109,7 @@
         sSupportedContentTypes.add(AUDIO_X_MPEG3);
         sSupportedContentTypes.add(AUDIO_X_MPEG);
         sSupportedContentTypes.add(AUDIO_X_MPG);
+        sSupportedContentTypes.add(AUDIO_X_WAV);
         sSupportedContentTypes.add(AUDIO_3GPP);
         sSupportedContentTypes.add(AUDIO_OGG);
 
@@ -127,6 +131,7 @@
         sSupportedImageTypes.add(IMAGE_WBMP);
         sSupportedImageTypes.add(IMAGE_PNG);
         sSupportedImageTypes.add(IMAGE_JPG);
+        sSupportedImageTypes.add(IMAGE_X_MS_BMP);
 
         // add supported audio types
         sSupportedAudioTypes.add(AUDIO_AAC);
@@ -145,6 +150,7 @@
         sSupportedAudioTypes.add(AUDIO_X_MPEG3);
         sSupportedAudioTypes.add(AUDIO_X_MPEG);
         sSupportedAudioTypes.add(AUDIO_X_MPG);
+        sSupportedAudioTypes.add(AUDIO_X_WAV);
         sSupportedAudioTypes.add(AUDIO_3GPP);
         sSupportedAudioTypes.add(AUDIO_OGG);
 
diff --git a/core/java/com/google/android/mms/pdu/PduComposer.java b/core/java/com/google/android/mms/pdu/PduComposer.java
index 8940945..d426f89 100644
--- a/core/java/com/google/android/mms/pdu/PduComposer.java
+++ b/core/java/com/google/android/mms/pdu/PduComposer.java
@@ -835,9 +835,7 @@
         appendOctet(PduHeaders.CONTENT_TYPE);
 
         //  Message body
-        makeMessageBody();
-
-        return PDU_COMPOSE_SUCCESS;  // Composing the message is OK
+        return makeMessageBody();
     }
 
     /**
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index 8d57e5d..7037b61 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -783,7 +783,7 @@
                         Log.v(TAG, "Saving data to: " + uri);
                     }
 
-                    byte[] buffer = new byte[256];
+                    byte[] buffer = new byte[8192];
                     for (int len = 0; (len = is.read(buffer)) != -1; ) {
                         os.write(buffer, 0, len);
                     }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 39b84bb..c6d3cee 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -126,6 +126,7 @@
 	android_media_ToneGenerator.cpp \
 	android_hardware_Camera.cpp \
 	android_hardware_SensorManager.cpp \
+	android_hardware_SerialPort.cpp \
 	android_hardware_UsbDevice.cpp \
 	android_hardware_UsbDeviceConnection.cpp \
 	android_hardware_UsbRequest.cpp \
@@ -184,6 +185,7 @@
 	libcore/include
 
 LOCAL_SHARED_LIBRARIES := \
+	libandroidfw \
 	libexpat \
 	libnativehelper \
 	libcutils \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8a3063f..3067e75 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -76,6 +76,7 @@
 
 extern int register_android_hardware_Camera(JNIEnv *env);
 extern int register_android_hardware_SensorManager(JNIEnv *env);
+extern int register_android_hardware_SerialPort(JNIEnv *env);
 extern int register_android_hardware_UsbDevice(JNIEnv *env);
 extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env);
 extern int register_android_hardware_UsbRequest(JNIEnv *env);
@@ -1157,6 +1158,7 @@
     REG_JNI(register_com_android_internal_os_ZygoteInit),
     REG_JNI(register_android_hardware_Camera),
     REG_JNI(register_android_hardware_SensorManager),
+    REG_JNI(register_android_hardware_SerialPort),
     REG_JNI(register_android_hardware_UsbDevice),
     REG_JNI(register_android_hardware_UsbDeviceConnection),
     REG_JNI(register_android_hardware_UsbRequest),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index ea35006..4b64bf3 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -15,8 +15,8 @@
 #include "JNIHelp.h"
 
 #include <android_runtime/AndroidRuntime.h>
-#include <utils/Asset.h>
-#include <utils/ResourceTypes.h>
+#include <androidfw/Asset.h>
+#include <androidfw/ResourceTypes.h>
 #include <netinet/in.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index d437929..682877a 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -37,7 +37,7 @@
 
 #include <binder/Parcel.h>
 #include <jni.h>
-#include <utils/Asset.h>
+#include <androidfw/Asset.h>
 #include <sys/stat.h>
 
 #if 0
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index c1acaa3..4f64ff8 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -5,8 +5,8 @@
 #include "SkUtils.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 
-#include <utils/Asset.h>
-#include <utils/ResourceTypes.h>
+#include <androidfw/Asset.h>
+#include <androidfw/ResourceTypes.h>
 #include <netinet/in.h>
 
 #if 0
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index f3b28a9..684b1c1 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "9patch"
 #define LOG_NDEBUG 1
 
-#include <utils/ResourceTypes.h>
+#include <androidfw/ResourceTypes.h>
 #include <utils/Log.h>
 
 #include "SkCanvas.h"
diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp
index 1d0bb506..ff0eb45 100644
--- a/core/jni/android/graphics/NinePatchImpl.cpp
+++ b/core/jni/android/graphics/NinePatchImpl.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "NinePatch"
 #define LOG_NDEBUG 1
 
-#include <utils/ResourceTypes.h>
+#include <androidfw/ResourceTypes.h>
 #include <utils/Log.h>
 
 #include "SkBitmap.h"
diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h
index 8567e23..207536c 100644
--- a/core/jni/android/graphics/NinePatchPeeker.h
+++ b/core/jni/android/graphics/NinePatchPeeker.h
@@ -18,7 +18,7 @@
 #define NinePatchPeeker_h
 
 #include "SkImageDecoder.h"
-#include <utils/ResourceTypes.h>
+#include <androidfw/ResourceTypes.h>
 
 using namespace android;
 
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index b25598a..7f4c37b 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -2,10 +2,10 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include "GraphicsJNI.h"
-#include <android_runtime/android_util_AssetManager.h>
 #include "SkStream.h"
 #include "SkTypeface.h"
-#include <utils/AssetManager.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <androidfw/AssetManager.h>
 
 using namespace android;
 
diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h
index 9a7a697..75ceaa2 100644
--- a/core/jni/android/graphics/Utils.h
+++ b/core/jni/android/graphics/Utils.h
@@ -22,7 +22,7 @@
 #include "android_util_Binder.h"
 
 #include <jni.h>
-#include <utils/Asset.h>
+#include <androidfw/Asset.h>
 
 namespace android {
 
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index ac4fe5b..4b3324b 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -27,7 +27,7 @@
 #include <android_runtime/android_util_AssetManager.h>
 #include <surfaceflinger/Surface.h>
 #include <ui/egl/android_natives.h>
-#include <ui/InputTransport.h>
+#include <androidfw/InputTransport.h>
 #include <utils/Looper.h>
 
 #include "JNIHelp.h"
@@ -130,21 +130,21 @@
 void AInputQueue::attachLooper(ALooper* looper, int ident,
         ALooper_callbackFunc callback, void* data) {
     mLooper = static_cast<android::Looper*>(looper);
-    mLooper->addFd(mConsumer.getChannel()->getReceivePipeFd(),
+    mLooper->addFd(mConsumer.getChannel()->getFd(),
             ident, ALOOPER_EVENT_INPUT, callback, data);
     mLooper->addFd(mDispatchKeyRead,
             ident, ALOOPER_EVENT_INPUT, callback, data);
 }
 
 void AInputQueue::detachLooper() {
-    mLooper->removeFd(mConsumer.getChannel()->getReceivePipeFd());
+    mLooper->removeFd(mConsumer.getChannel()->getFd());
     mLooper->removeFd(mDispatchKeyRead);
 }
 
 int32_t AInputQueue::hasEvents() {
     struct pollfd pfd[2];
 
-    pfd[0].fd = mConsumer.getChannel()->getReceivePipeFd();
+    pfd[0].fd = mConsumer.getChannel()->getFd();
     pfd[0].events = POLLIN;
     pfd[0].revents = 0;
     pfd[1].fd = mDispatchKeyRead;
@@ -172,7 +172,7 @@
             in_flight_event inflight;
             inflight.event = kevent;
             inflight.seq = -1;
-            inflight.doFinish = false;
+            inflight.finishSeq = 0;
             mInFlightEvents.push(inflight);
         }
         if (mFinishPreDispatches.size() > 0) {
@@ -200,27 +200,22 @@
             return 0;
         }
     }
-    
-    int32_t res = mConsumer.receiveDispatchSignal();
-    if (res != android::OK) {
-        ALOGE("channel '%s' ~ Failed to receive dispatch signal.  status=%d",
-                mConsumer.getChannel()->getName().string(), res);
-        return -1;
-    }
 
+    uint32_t consumerSeq;
     InputEvent* myEvent = NULL;
-    res = mConsumer.consume(this, &myEvent);
+    status_t res = mConsumer.consume(this, true /*consumeBatches*/, &consumerSeq, &myEvent);
     if (res != android::OK) {
-        ALOGW("channel '%s' ~ Failed to consume input event.  status=%d",
-                mConsumer.getChannel()->getName().string(), res);
-        mConsumer.sendFinishedSignal(false);
+        if (res != android::WOULD_BLOCK) {
+            ALOGW("channel '%s' ~ Failed to consume input event.  status=%d",
+                    mConsumer.getChannel()->getName().string(), res);
+        }
         return -1;
     }
 
     in_flight_event inflight;
     inflight.event = myEvent;
     inflight.seq = -1;
-    inflight.doFinish = true;
+    inflight.finishSeq = consumerSeq;
     mInFlightEvents.push(inflight);
 
     *outEvent = myEvent;
@@ -262,8 +257,8 @@
     for (size_t i=0; i<N; i++) {
         const in_flight_event& inflight(mInFlightEvents[i]);
         if (inflight.event == event) {
-            if (inflight.doFinish) {
-                int32_t res = mConsumer.sendFinishedSignal(handled);
+            if (inflight.finishSeq) {
+                status_t res = mConsumer.sendFinishedSignal(inflight.finishSeq, handled);
                 if (res != android::OK) {
                     ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                             mConsumer.getChannel()->getName().string(), res);
@@ -481,11 +476,6 @@
                     android_view_InputChannel_getInputChannel(env, _channel);
             if (ic != NULL) {
                 nativeInputQueue = new AInputQueue(ic, mainWorkWrite);
-                if (nativeInputQueue->getConsumer().initialize() != android::OK) {
-                    delete nativeInputQueue;
-                    nativeInputQueue = NULL;
-                    return UNKNOWN_ERROR;
-                }
             } else {
                 return UNKNOWN_ERROR;
             }
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index 066a23e..2ca645a 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -21,7 +21,7 @@
 #include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/BackupHelpers.h>
+#include <androidfw/BackupHelpers.h>
 
 #include <string.h>
 
diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp
index 2fb0076..25b0007 100644
--- a/core/jni/android_backup_BackupDataInput.cpp
+++ b/core/jni/android_backup_BackupDataInput.cpp
@@ -20,7 +20,7 @@
 #include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/BackupHelpers.h>
+#include <androidfw/BackupHelpers.h>
 
 namespace android
 {
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index f4b5dca..e8f0fb8 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -20,7 +20,7 @@
 #include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/BackupHelpers.h>
+#include <androidfw/BackupHelpers.h>
 
 namespace android
 {
@@ -52,7 +52,6 @@
     if (keyUTF == NULL) {
         return -1;
     }
-
     err = writer->WriteEntityHeader(String8(keyUTF), dataSize);
 
     env->ReleaseStringUTFChars(key, keyUTF);
diff --git a/core/jni/android_backup_FileBackupHelperBase.cpp b/core/jni/android_backup_FileBackupHelperBase.cpp
index 0dfd8db..bb3a751 100644
--- a/core/jni/android_backup_FileBackupHelperBase.cpp
+++ b/core/jni/android_backup_FileBackupHelperBase.cpp
@@ -20,7 +20,7 @@
 #include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/BackupHelpers.h>
+#include <androidfw/BackupHelpers.h>
 
 namespace android
 {
diff --git a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
old mode 100755
new mode 100644
diff --git a/core/jni/android_bluetooth_c.c b/core/jni/android_bluetooth_c.c
old mode 100755
new mode 100644
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 8837538..5d51ee2 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -18,7 +18,7 @@
 
 #include <utils/Log.h>
 #include <utils/String8.h>
-#include <utils/ObbFile.h>
+#include <androidfw/ObbFile.h>
 
 #include "jni.h"
 #include "JNIHelp.h"
diff --git a/core/jni/android_hardware_SerialPort.cpp b/core/jni/android_hardware_SerialPort.cpp
new file mode 100644
index 0000000..7f40a5c
--- /dev/null
+++ b/core/jni/android_hardware_SerialPort.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SerialPortJNI"
+
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+
+using namespace android;
+
+static jfieldID field_context;
+
+static void
+android_hardware_SerialPort_open(JNIEnv *env, jobject thiz, jobject fileDescriptor, jint speed)
+{
+    switch (speed) {
+        case 50:
+            speed = B50;
+            break;
+        case 75:
+            speed = B75;
+            break;
+        case 110:
+            speed = B110;
+            break;
+        case 134:
+            speed = B134;
+            break;
+        case 150:
+            speed = B150;
+            break;
+        case 200:
+            speed = B200;
+            break;
+        case 300:
+            speed = B300;
+            break;
+        case 600:
+            speed = B600;
+            break;
+        case 1200:
+            speed = B1200;
+            break;
+        case 1800:
+            speed = B1800;
+            break;
+        case 2400:
+            speed = B2400;
+            break;
+        case 4800:
+            speed = B4800;
+            break;
+        case 9600:
+            speed = B9600;
+            break;
+        case 19200:
+            speed = B19200;
+            break;
+        case 38400:
+            speed = B38400;
+            break;
+        case 57600:
+            speed = B57600;
+            break;
+        case 115200:
+            speed = B115200;
+            break;
+        case 230400:
+            speed = B230400;
+            break;
+        case 460800:
+            speed = B460800;
+            break;
+        case 500000:
+            speed = B500000;
+            break;
+        case 576000:
+            speed = B576000;
+            break;
+        case 921600:
+            speed = B921600;
+            break;
+        case 1000000:
+            speed = B1000000;
+            break;
+        case 1152000:
+            speed = B1152000;
+            break;
+        case 1500000:
+            speed = B1500000;
+            break;
+        case 2000000:
+            speed = B2000000;
+            break;
+        case 2500000:
+            speed = B2500000;
+            break;
+        case 3000000:
+            speed = B3000000;
+            break;
+        case 3500000:
+            speed = B3500000;
+            break;
+        case 4000000:
+            speed = B4000000;
+            break;
+        default:
+            jniThrowException(env, "java/lang/IllegalArgumentException",
+                              "Unsupported serial port speed");
+            return;
+    }
+
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
+    fd = dup(fd);
+    if (fd < 0) {
+        jniThrowException(env, "java/io/IOException", "Could not open serial port");
+        return;
+    }
+    env->SetIntField(thiz, field_context, fd);
+
+    struct termios tio;
+    if (tcgetattr(fd, &tio))
+        memset(&tio, 0, sizeof(tio));
+
+    tio.c_cflag =  speed | CS8 | CLOCAL | CREAD;
+    // Disable output processing, including messing with end-of-line characters.
+    tio.c_oflag &= ~OPOST;
+    tio.c_iflag = IGNPAR;
+    tio.c_lflag = 0; /* turn of CANON, ECHO*, etc */
+    /* no timeout but request at least one character per read */
+    tio.c_cc[VTIME] = 0;
+    tio.c_cc[VMIN] = 1;
+    tcsetattr(fd, TCSANOW, &tio);
+    tcflush(fd, TCIFLUSH);
+}
+
+static void
+android_hardware_SerialPort_close(JNIEnv *env, jobject thiz)
+{
+    int fd = env->GetIntField(thiz, field_context);
+    close(fd);
+    env->SetIntField(thiz, field_context, -1);
+}
+
+static jint
+android_hardware_SerialPort_read_array(JNIEnv *env, jobject thiz, jbyteArray buffer, jint length)
+{
+    int fd = env->GetIntField(thiz, field_context);
+    jbyte* buf = (jbyte *)malloc(length);
+    if (!buf) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return -1;
+    }
+
+    int ret = read(fd, buf, length);
+    if (ret > 0) {
+        // copy data from native buffer to Java buffer
+        env->SetByteArrayRegion(buffer, 0, ret, buf);
+    }
+
+    free(buf);
+    if (ret < 0)
+        jniThrowException(env, "java/io/IOException", NULL);
+    return ret;
+}
+
+static jint
+android_hardware_SerialPort_read_direct(JNIEnv *env, jobject thiz, jobject buffer, jint length)
+{
+    int fd = env->GetIntField(thiz, field_context);
+
+    jbyte* buf = (jbyte *)env->GetDirectBufferAddress(buffer);
+    if (!buf) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "ByteBuffer not direct");
+        return -1;
+    }
+
+    int ret = read(fd, buf, length);
+    if (ret < 0)
+        jniThrowException(env, "java/io/IOException", NULL);
+    return ret;
+}
+
+static void
+android_hardware_SerialPort_write_array(JNIEnv *env, jobject thiz, jbyteArray buffer, jint length)
+{
+    int fd = env->GetIntField(thiz, field_context);
+    jbyte* buf = (jbyte *)malloc(length);
+    if (!buf) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return;
+    }
+    env->GetByteArrayRegion(buffer, 0, length, buf);
+
+    jint ret = write(fd, buf, length);
+    free(buf);
+    if (ret < 0)
+        jniThrowException(env, "java/io/IOException", NULL);
+}
+
+static void
+android_hardware_SerialPort_write_direct(JNIEnv *env, jobject thiz, jobject buffer, jint length)
+{
+    int fd = env->GetIntField(thiz, field_context);
+
+    jbyte* buf = (jbyte *)env->GetDirectBufferAddress(buffer);
+    if (!buf) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "ByteBuffer not direct");
+        return;
+    }
+    int ret = write(fd, buf, length);
+    if (ret < 0)
+        jniThrowException(env, "java/io/IOException", NULL);
+}
+
+static void
+android_hardware_SerialPort_send_break(JNIEnv *env, jobject thiz)
+{
+    int fd = env->GetIntField(thiz, field_context);
+    tcsendbreak(fd, 0);
+}
+
+static JNINativeMethod method_table[] = {
+    {"native_open",             "(Ljava/io/FileDescriptor;I)V",
+                                        (void *)android_hardware_SerialPort_open},
+    {"native_close",            "()V",  (void *)android_hardware_SerialPort_close},
+    {"native_read_array",       "([BI)I",
+                                        (void *)android_hardware_SerialPort_read_array},
+    {"native_read_direct",      "(Ljava/nio/ByteBuffer;I)I",
+                                        (void *)android_hardware_SerialPort_read_direct},
+    {"native_write_array",      "([BI)V",
+                                        (void *)android_hardware_SerialPort_write_array},
+    {"native_write_direct",     "(Ljava/nio/ByteBuffer;I)V",
+                                        (void *)android_hardware_SerialPort_write_direct},
+    {"native_send_break",       "()V",  (void *)android_hardware_SerialPort_send_break},
+};
+
+int register_android_hardware_SerialPort(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("android/hardware/SerialPort");
+    if (clazz == NULL) {
+        ALOGE("Can't find android/hardware/SerialPort");
+        return -1;
+    }
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        ALOGE("Can't find SerialPort.mNativeContext");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env, "android/hardware/SerialPort",
+            method_table, NELEM(method_table));
+}
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 547607e..68a8de8 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -220,7 +220,7 @@
         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
         goto native_init_failure;
     }
-    // read the audio session ID back from AudioTrack in case a new session was created during set()
+    // read the audio session ID back from AudioRecord in case a new session was created during set()
     nSession[0] = lpRecorder->getSessionId();
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 6b4c5e8..ee5eb7e 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1,4 +1,4 @@
-/* //device/libs/android_runtime/android_media_AudioSystem.cpp
+/*
 **
 ** Copyright 2006, The Android Open Source Project
 **
@@ -204,6 +204,38 @@
     return index;
 }
 
+static int
+android_media_AudioSystem_setMasterVolume(JNIEnv *env, jobject thiz, jfloat value)
+{
+    return check_AudioSystem_Command(AudioSystem::setMasterVolume(value));
+}
+
+static jfloat
+android_media_AudioSystem_getMasterVolume(JNIEnv *env, jobject thiz)
+{
+    float value;
+    if (AudioSystem::getMasterVolume(&value) != NO_ERROR) {
+        value = -1.0;
+    }
+    return value;
+}
+
+static int
+android_media_AudioSystem_setMasterMute(JNIEnv *env, jobject thiz, jboolean mute)
+{
+    return check_AudioSystem_Command(AudioSystem::setMasterMute(mute));
+}
+
+static jfloat
+android_media_AudioSystem_getMasterMute(JNIEnv *env, jobject thiz)
+{
+    bool mute;
+    if (AudioSystem::getMasterMute(&mute) != NO_ERROR) {
+        mute = false;
+    }
+    return mute;
+}
+
 static jint
 android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
 {
@@ -226,6 +258,10 @@
     {"initStreamVolume",    "(III)I",   (void *)android_media_AudioSystem_initStreamVolume},
     {"setStreamVolumeIndex","(III)I",   (void *)android_media_AudioSystem_setStreamVolumeIndex},
     {"getStreamVolumeIndex","(II)I",    (void *)android_media_AudioSystem_getStreamVolumeIndex},
+    {"setMasterVolume",     "(F)I",     (void *)android_media_AudioSystem_setMasterVolume},
+    {"getMasterVolume",     "()F",      (void *)android_media_AudioSystem_getMasterVolume},
+    {"setMasterMute",       "(Z)I",     (void *)android_media_AudioSystem_setMasterMute},
+    {"getMasterMute",       "()Z",      (void *)android_media_AudioSystem_getMasterMute},
     {"getDevicesForStream", "(I)I",     (void *)android_media_AudioSystem_getDevicesForStream},
 };
 
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 26e82aa..544b4c0 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -1,4 +1,4 @@
-/* //device/libs/android_runtime/android_media_AudioSystem.cpp
+/*
  **
  ** Copyright 2008, The Android Open Source Project
  **
@@ -39,7 +39,7 @@
 static fields_t fields;
 
 static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType, jint durationMs) {
-    ALOGV("android_media_ToneGenerator_startTone: %x\n", (int)thiz);
+    ALOGV("android_media_ToneGenerator_startTone: %x", (int)thiz);
 
     ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
             fields.context);
@@ -52,12 +52,12 @@
 }
 
 static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
-    ALOGV("android_media_ToneGenerator_stopTone: %x\n", (int)thiz);
+    ALOGV("android_media_ToneGenerator_stopTone: %x", (int)thiz);
 
     ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
             fields.context);
 
-    ALOGV("ToneGenerator lpToneGen: %x\n", (unsigned int)lpToneGen);
+    ALOGV("ToneGenerator lpToneGen: %x", (unsigned int)lpToneGen);
     if (lpToneGen == NULL) {
         jniThrowRuntimeException(env, "Method called after release()");
         return;
@@ -68,13 +68,11 @@
 static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) {
     ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
             fields.context);
-    ALOGV("android_media_ToneGenerator_release lpToneGen: %x\n", (int)lpToneGen);
+    ALOGV("android_media_ToneGenerator_release lpToneGen: %x", (int)lpToneGen);
 
     env->SetIntField(thiz, fields.context, 0);
 
-    if (lpToneGen) {
-        delete lpToneGen;
-    }
+    delete lpToneGen;
 }
 
 static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz,
@@ -83,17 +81,17 @@
 
     env->SetIntField(thiz, fields.context, 0);
 
-    ALOGV("android_media_ToneGenerator_native_setup jobject: %x\n", (int)thiz);
+    ALOGV("android_media_ToneGenerator_native_setup jobject: %x", (int)thiz);
 
     if (lpToneGen == NULL) {
-        ALOGE("ToneGenerator creation failed \n");
+        ALOGE("ToneGenerator creation failed");
         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
         return;
     }
-    ALOGV("ToneGenerator lpToneGen: %x\n", (unsigned int)lpToneGen);
+    ALOGV("ToneGenerator lpToneGen: %x", (unsigned int)lpToneGen);
 
     if (!lpToneGen->isInited()) {
-        ALOGE("ToneGenerator init failed \n");
+        ALOGE("ToneGenerator init failed");
         jniThrowRuntimeException(env, "Init failed");
         return;
     }
@@ -101,18 +99,18 @@
     // Stow our new C++ ToneGenerator in an opaque field in the Java object.
     env->SetIntField(thiz, fields.context, (int)lpToneGen);
 
-    ALOGV("ToneGenerator fields.context: %x\n", env->GetIntField(thiz, fields.context));
+    ALOGV("ToneGenerator fields.context: %x", env->GetIntField(thiz, fields.context));
 }
 
 static void android_media_ToneGenerator_native_finalize(JNIEnv *env,
         jobject thiz) {
-    ALOGV("android_media_ToneGenerator_native_finalize jobject: %x\n", (int)thiz);
+    ALOGV("android_media_ToneGenerator_native_finalize jobject: %x", (int)thiz);
 
     ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
             fields.context);
 
-    if (lpToneGen) {
-        ALOGV("delete lpToneGen: %x\n", (int)lpToneGen);
+    if (lpToneGen != NULL) {
+        ALOGV("delete lpToneGen: %p", lpToneGen);
         delete lpToneGen;
     }
 }
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 2ac3ca8..c5ff16e 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -116,14 +116,9 @@
     return (jboolean)(::wifi_unload_driver() == 0);
 }
 
-static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject)
+static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject, jboolean p2pSupported)
 {
-    return (jboolean)(::wifi_start_supplicant() == 0);
-}
-
-static jboolean android_net_wifi_startP2pSupplicant(JNIEnv* env, jobject)
-{
-    return (jboolean)(::wifi_start_p2p_supplicant() == 0);
+    return (jboolean)(::wifi_start_supplicant(p2pSupported) == 0);
 }
 
 static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
@@ -207,8 +202,7 @@
     { "loadDriver", "()Z",  (void *)android_net_wifi_loadDriver },
     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded },
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
-    { "startSupplicant", "()Z",  (void *)android_net_wifi_startSupplicant },
-    { "startP2pSupplicant", "()Z",  (void *)android_net_wifi_startP2pSupplicant },
+    { "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },
     { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
     { "connectToSupplicant", "(Ljava/lang/String;)Z",
             (void *)android_net_wifi_connectToSupplicant },
diff --git a/core/jni/android_os_Power.cpp b/core/jni/android_os_Power.cpp
index dc16990..48845f6 100644
--- a/core/jni/android_os_Power.cpp
+++ b/core/jni/android_os_Power.cpp
@@ -15,13 +15,18 @@
 ** limitations under the License.
 */
 
+#define LOG_TAG "Power-JNI"
+
 #include "JNIHelp.h"
 #include "jni.h"
 #include "android_runtime/AndroidRuntime.h"
 #include <utils/misc.h>
+#include <hardware/power.h>
 #include <hardware_legacy/power.h>
 #include <cutils/android_reboot.h>
 
+static struct power_module *sPowerModule;
+
 namespace android
 {
 
@@ -65,7 +70,9 @@
 static int
 setScreenState(JNIEnv *env, jobject clazz, jboolean on)
 {
-    return set_screen_state(on);
+    if (sPowerModule)
+        sPowerModule->setInteractive(sPowerModule, on);
+    return 0;
 }
 
 static void android_os_Power_shutdown(JNIEnv *env, jobject clazz)
@@ -85,12 +92,26 @@
     jniThrowIOException(env, errno);
 }
 
+static int android_os_Power_init(JNIEnv *env, jobject clazz)
+{
+    status_t err = hw_get_module(POWER_HARDWARE_MODULE_ID,
+        (hw_module_t const**)&sPowerModule);
+    ALOGE_IF(err, "couldn't load %s module (%s)",
+             POWER_HARDWARE_MODULE_ID, strerror(-err));
+
+    if (!err)
+        sPowerModule->init(sPowerModule);
+
+    return err;
+}
+
 static JNINativeMethod method_table[] = {
     { "acquireWakeLock", "(ILjava/lang/String;)V", (void*)acquireWakeLock },
     { "releaseWakeLock", "(Ljava/lang/String;)V", (void*)releaseWakeLock },
     { "setLastUserActivityTimeout", "(J)I", (void*)setLastUserActivityTimeout },
     { "setScreenState", "(Z)I", (void*)setScreenState },
     { "shutdown", "()V", (void*)android_os_Power_shutdown },
+    { "powerInitNative", "()I", (void*)android_os_Power_init },
     { "rebootNative", "(Ljava/lang/String;)V", (void*)android_os_Power_reboot },
 };
 
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 9292fc0..8a69ba4 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -164,7 +164,6 @@
         ALOGE("%s: out of memory!", __FUNCTION__);
         return;
     }
-    memset(nat, 0, sizeof(native_data_t));
 
     pthread_mutex_init(&(nat->thread_mutex), NULL);
 
@@ -722,24 +721,20 @@
         return JNI_FALSE;
     }
 
-    nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *
-            DEFAULT_INITIAL_POLLFD_COUNT);
+    nat->pollData = (struct pollfd *)calloc(
+            DEFAULT_INITIAL_POLLFD_COUNT, sizeof(struct pollfd));
     if (!nat->pollData) {
         ALOGE("out of memory error starting EventLoop!");
         goto done;
     }
 
-    nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *
-            DEFAULT_INITIAL_POLLFD_COUNT);
+    nat->watchData = (DBusWatch **)calloc(
+            DEFAULT_INITIAL_POLLFD_COUNT, sizeof(DBusWatch *));
     if (!nat->watchData) {
         ALOGE("out of memory error starting EventLoop!");
         goto done;
     }
 
-    memset(nat->pollData, 0, sizeof(struct pollfd) *
-            DEFAULT_INITIAL_POLLFD_COUNT);
-    memset(nat->watchData, 0, sizeof(DBusWatch *) *
-            DEFAULT_INITIAL_POLLFD_COUNT);
     nat->pollDataSize = DEFAULT_INITIAL_POLLFD_COUNT;
     nat->pollMemberCount = 1;
 
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 17130af..7146667 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -31,9 +31,9 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
 
-#include <utils/Asset.h>
-#include <utils/AssetManager.h>
-#include <utils/ResourceTypes.h>
+#include <androidfw/Asset.h>
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
 
 #include <stdio.h>
 
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index 958ddb2..28746ce 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -23,7 +23,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
 
-#include <utils/ResourceTypes.h>
+#include <androidfw/ResourceTypes.h>
 
 #include <stdio.h>
 
diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp
index 45728db..ad6033b 100644
--- a/core/jni/android_util_XmlBlock.cpp
+++ b/core/jni/android_util_XmlBlock.cpp
@@ -19,12 +19,11 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
-#include <utils/misc.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <utils/AssetManager.h>
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
 #include <utils/Log.h>
-
-#include <utils/ResourceTypes.h>
+#include <utils/misc.h>
 
 #include <stdio.h>
 
diff --git a/core/jni/android_view_Display.cpp b/core/jni/android_view_Display.cpp
index 366a52e..f076cc8 100644
--- a/core/jni/android_view_Display.cpp
+++ b/core/jni/android_view_Display.cpp
@@ -28,6 +28,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 #include <utils/Log.h>
+#include <cutils/properties.h>
 
 // ----------------------------------------------------------------------------
 
@@ -44,6 +45,7 @@
     jfieldID ydpi;
 };
 static offsets_t offsets;
+static bool headless = false;
 
 // ----------------------------------------------------------------------------
 
@@ -51,10 +53,19 @@
         JNIEnv* env, jobject clazz, jint dpy)
 {
     DisplayInfo info;
-    status_t err = SurfaceComposerClient::getDisplayInfo(DisplayID(dpy), &info);
-    if (err < 0) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return;
+    if (headless) {
+        // initialize dummy display with reasonable values
+        info.pixelFormatInfo.format = 1; // RGB_8888
+        info.fps = 60;
+        info.density = 160;
+        info.xdpi = 160;
+        info.ydpi = 160;
+    } else {
+        status_t err = SurfaceComposerClient::getDisplayInfo(DisplayID(dpy), &info);
+        if (err < 0) {
+            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+            return;
+        }
     }
     env->SetIntField(clazz, offsets.pixelFormat,info.pixelFormatInfo.format);
     env->SetFloatField(clazz, offsets.fps,      info.fps);
@@ -66,6 +77,7 @@
 static jint android_view_Display_getRawWidthNative(
         JNIEnv* env, jobject clazz)
 {
+    if (headless) return 640;
     DisplayID dpy = env->GetIntField(clazz, offsets.display);
     return SurfaceComposerClient::getDisplayWidth(dpy);
 }
@@ -73,6 +85,7 @@
 static jint android_view_Display_getRawHeightNative(
         JNIEnv* env, jobject clazz)
 {
+    if (headless) return 480;
     DisplayID dpy = env->GetIntField(clazz, offsets.display);
     return SurfaceComposerClient::getDisplayHeight(dpy);
 }
@@ -80,6 +93,7 @@
 static jint android_view_Display_getOrientation(
         JNIEnv* env, jobject clazz)
 {
+    if (headless) return 0; // Surface.ROTATION_0
     DisplayID dpy = env->GetIntField(clazz, offsets.display);
     return SurfaceComposerClient::getDisplayOrientation(dpy);
 }
@@ -87,6 +101,7 @@
 static jint android_view_Display_getDisplayCount(
         JNIEnv* env, jclass clazz)
 {
+    if (headless) return 1;
     return SurfaceComposerClient::getNumberOfDisplays();
 }
 
@@ -113,6 +128,12 @@
 
 void nativeClassInit(JNIEnv* env, jclass clazz)
 {
+    char value[PROPERTY_VALUE_MAX];
+
+    property_get("ro.config.headless", value, "0");
+    if (strcmp(value, "1") == 0)
+        headless = true;
+
     offsets.display     = env->GetFieldID(clazz, "mDisplay", "I");
     offsets.pixelFormat = env->GetFieldID(clazz, "mPixelFormat", "I");
     offsets.fps         = env->GetFieldID(clazz, "mRefreshRate", "F");
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 25397b5..8a1c4a9 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -59,21 +59,20 @@
     sp<Looper> mLooper;
     DisplayEventReceiver mReceiver;
     bool mWaitingForVsync;
-    bool mFdCallbackRegistered;
 };
 
 
 NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
         jobject receiverObj, const sp<Looper>& looper) :
         mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
-        mLooper(looper), mWaitingForVsync(false), mFdCallbackRegistered(false) {
+        mLooper(looper), mWaitingForVsync(false) {
     ALOGV("receiver %p ~ Initializing input event receiver.", this);
 }
 
 NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
     ALOGV("receiver %p ~ Disposing display event receiver.", this);
 
-    if (mFdCallbackRegistered) {
+    if (!mReceiver.initCheck()) {
         mLooper->removeFd(mReceiver.getFd());
     }
 
@@ -88,6 +87,11 @@
         return result;
     }
 
+    int rc = mLooper->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
+            handleReceiveCallback, this);
+    if (rc < 0) {
+        return UNKNOWN_ERROR;
+    }
     return OK;
 }
 
@@ -113,15 +117,6 @@
             return status;
         }
 
-        if (!mFdCallbackRegistered) {
-            int rc = mLooper->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
-                    handleReceiveCallback, this);
-            if (rc < 0) {
-                return UNKNOWN_ERROR;
-            }
-            mFdCallbackRegistered = true;
-        }
-
         mWaitingForVsync = true;
     }
     return OK;
@@ -133,7 +128,6 @@
     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
         ALOGE("Display event receiver pipe was closed or an error occurred.  "
                 "events=0x%x", events);
-        r->mFdCallbackRegistered = false;
         return 0; // remove the callback
     }
 
@@ -150,7 +144,7 @@
     DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
     ssize_t n;
     while ((n = r->mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-        ALOGV("receiver %p ~ Read %d events.", this, int(n));
+        ALOGV("receiver %p ~ Read %d events.", data, int(n));
         while (n-- > 0) {
             if (buf[n].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
                 vsyncTimestamp = buf[n].header.timestamp;
@@ -161,20 +155,20 @@
     }
 
     if (vsyncTimestamp < 0) {
-        ALOGV("receiver %p ~ Woke up but there was no vsync pulse!", this);
+        ALOGV("receiver %p ~ Woke up but there was no vsync pulse!", data);
         return 1; // keep the callback, did not obtain a vsync pulse
     }
 
     ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, count=%d",
-            this, vsyncTimestamp, vsyncCount);
+            data, vsyncTimestamp, vsyncCount);
     r->mWaitingForVsync = false;
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
 
-    ALOGV("receiver %p ~ Invoking vsync handler.", this);
+    ALOGV("receiver %p ~ Invoking vsync handler.", data);
     env->CallVoidMethod(r->mReceiverObjGlobal,
             gDisplayEventReceiverClassInfo.dispatchVsync, vsyncTimestamp, vsyncCount);
-    ALOGV("receiver %p ~ Returned from vsync handler.", this);
+    ALOGV("receiver %p ~ Returned from vsync handler.", data);
 
     if (env->ExceptionCheck()) {
         ALOGE("An exception occurred while dispatching a vsync event.");
@@ -182,13 +176,7 @@
         env->ExceptionClear();
     }
 
-    // Check whether dispatchVsync called scheduleVsync reentrantly and set mWaitingForVsync.
-    // If so, keep the callback, otherwise remove it.
-    if (r->mWaitingForVsync) {
-        return 1; // keep the callback
-    }
-    r->mFdCallbackRegistered = false;
-    return 0; // remove the callback
+    return 1; // keep the callback
 }
 
 
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index e19bb38..cdce4f9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -24,7 +24,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 #include <cutils/properties.h>
-#include <utils/ResourceTypes.h>
+#include <androidfw/ResourceTypes.h>
 
 #include <gui/SurfaceTexture.h>
 
@@ -652,9 +652,9 @@
 
 static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
         jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList,
-        jint width, jint height, jobject dirty) {
+        jint width, jint height, jobject dirty, jint flags) {
     android::uirenderer::Rect bounds;
-    bool redraw = renderer->drawDisplayList(displayList, width, height, bounds);
+    bool redraw = renderer->drawDisplayList(displayList, width, height, bounds, flags);
     if (redraw && dirty != NULL) {
         env->CallVoidMethod(dirty, gRectClassInfo.set,
                 int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
@@ -901,7 +901,7 @@
     { "nGetDisplayListSize",     "(I)I",       (void*) android_view_GLES20Canvas_getDisplayListSize },
     { "nSetDisplayListName",     "(ILjava/lang/String;)V",
                                                (void*) android_view_GLES20Canvas_setDisplayListName },
-    { "nDrawDisplayList",        "(IIIILandroid/graphics/Rect;)Z",
+    { "nDrawDisplayList",        "(IIIILandroid/graphics/Rect;I)Z",
                                                (void*) android_view_GLES20Canvas_drawDisplayList },
 
     { "nCreateDisplayListRenderer", "()I",     (void*) android_view_GLES20Canvas_createDisplayListRenderer },
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index fce432b..8350e73 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -21,7 +21,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <binder/Parcel.h>
 #include <utils/Log.h>
-#include <ui/InputTransport.h>
+#include <androidfw/InputTransport.h>
 #include "android_view_InputChannel.h"
 #include "android_util_Binder.h"
 
@@ -199,32 +199,16 @@
         bool isInitialized = parcel->readInt32();
         if (isInitialized) {
             String8 name = parcel->readString8();
-            int32_t parcelAshmemFd = parcel->readFileDescriptor();
-            int32_t ashmemFd = dup(parcelAshmemFd);
-            if (ashmemFd < 0) {
-                ALOGE("Error %d dup ashmem fd %d.", errno, parcelAshmemFd);
-            }
-            int32_t parcelReceivePipeFd = parcel->readFileDescriptor();
-            int32_t receivePipeFd = dup(parcelReceivePipeFd);
-            if (receivePipeFd < 0) {
-                ALOGE("Error %d dup receive pipe fd %d.", errno, parcelReceivePipeFd);
-            }
-            int32_t parcelSendPipeFd = parcel->readFileDescriptor();
-            int32_t sendPipeFd = dup(parcelSendPipeFd);
-            if (sendPipeFd < 0) {
-                ALOGE("Error %d dup send pipe fd %d.", errno, parcelSendPipeFd);
-            }
-            if (ashmemFd < 0 || receivePipeFd < 0 || sendPipeFd < 0) {
-                if (ashmemFd >= 0) ::close(ashmemFd);
-                if (receivePipeFd >= 0) ::close(receivePipeFd);
-                if (sendPipeFd >= 0) ::close(sendPipeFd);
+            int rawFd = parcel->readFileDescriptor();
+            int dupFd = dup(rawFd);
+            if (dupFd < 0) {
+                ALOGE("Error %d dup channel fd %d.", errno, rawFd);
                 jniThrowRuntimeException(env,
                         "Could not read input channel file descriptors from parcel.");
                 return;
             }
 
-            InputChannel* inputChannel = new InputChannel(name, ashmemFd,
-                    receivePipeFd, sendPipeFd);
+            InputChannel* inputChannel = new InputChannel(name, dupFd);
             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
 
             android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
@@ -243,9 +227,7 @@
 
             parcel->writeInt32(1);
             parcel->writeString8(inputChannel->getName());
-            parcel->writeDupFileDescriptor(inputChannel->getAshmemFd());
-            parcel->writeDupFileDescriptor(inputChannel->getReceivePipeFd());
-            parcel->writeDupFileDescriptor(inputChannel->getSendPipeFd());
+            parcel->writeDupFileDescriptor(inputChannel->getFd());
         } else {
             parcel->writeInt32(0);
         }
diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h
index fa2d282..0967021 100644
--- a/core/jni/android_view_InputChannel.h
+++ b/core/jni/android_view_InputChannel.h
@@ -19,7 +19,7 @@
 
 #include "jni.h"
 
-#include <ui/InputTransport.h>
+#include <androidfw/InputTransport.h>
 
 namespace android {
 
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index ed0acce..e7d4244 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -28,7 +28,7 @@
 #include <utils/Log.h>
 #include <utils/Looper.h>
 #include <utils/threads.h>
-#include <ui/InputTransport.h>
+#include <androidfw/InputTransport.h>
 #include "android_os_MessageQueue.h"
 #include "android_view_InputChannel.h"
 #include "android_view_KeyEvent.h"
@@ -40,6 +40,7 @@
     jclass clazz;
 
     jmethodID dispatchInputEvent;
+    jmethodID dispatchBatchedInputEventPending;
 } gInputEventReceiverClassInfo;
 
 
@@ -50,7 +51,8 @@
             const sp<Looper>& looper);
 
     status_t initialize();
-    status_t finishInputEvent(bool handled);
+    status_t finishInputEvent(uint32_t seq, bool handled);
+    status_t consumeEvents(bool consumeBatches);
     static int handleReceiveCallback(int receiveFd, int events, void* data);
 
 protected:
@@ -60,8 +62,8 @@
     jobject mReceiverObjGlobal;
     InputConsumer mInputConsumer;
     sp<Looper> mLooper;
-    bool mEventInProgress;
     PreallocatedInputEventFactory mInputEventFactory;
+    bool mBatchedInputEventPending;
 
     const char* getInputChannelName() {
         return mInputConsumer.getChannel()->getName().string();
@@ -72,7 +74,8 @@
 NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
         jobject receiverObj, const sp<InputChannel>& inputChannel, const sp<Looper>& looper) :
         mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
-        mInputConsumer(inputChannel), mLooper(looper), mEventInProgress(false) {
+        mInputConsumer(inputChannel), mLooper(looper),
+        mBatchedInputEventPending(false) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
 #endif
@@ -83,45 +86,29 @@
     ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
 #endif
 
-    mLooper->removeFd(mInputConsumer.getChannel()->getReceivePipeFd());
-    if (mEventInProgress) {
-        mInputConsumer.sendFinishedSignal(false); // ignoring result
-    }
+    mLooper->removeFd(mInputConsumer.getChannel()->getFd());
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->DeleteGlobalRef(mReceiverObjGlobal);
 }
 
 status_t NativeInputEventReceiver::initialize() {
-    status_t result = mInputConsumer.initialize();
-    if (result) {
-        ALOGW("Failed to initialize input consumer for input channel '%s', status=%d",
-                getInputChannelName(), result);
-        return result;
-    }
-
-    int32_t receiveFd = mInputConsumer.getChannel()->getReceivePipeFd();
+    int receiveFd = mInputConsumer.getChannel()->getFd();
     mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     return OK;
 }
 
-status_t NativeInputEventReceiver::finishInputEvent(bool handled) {
-    if (mEventInProgress) {
+status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
 #if DEBUG_DISPATCH_CYCLE
-        ALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
+    ALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
 #endif
-        mEventInProgress = false;
 
-        status_t status = mInputConsumer.sendFinishedSignal(handled);
-        if (status) {
-            ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
-                    getInputChannelName(), status);
-        }
-        return status;
-    } else {
-        ALOGW("Ignoring attempt to finish input event while no event is in progress.");
-        return OK;
+    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
+    if (status) {
+        ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
+                getInputChannelName(), status);
     }
+    return status;
 }
 
 int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) {
@@ -139,86 +126,101 @@
         return 1;
     }
 
-    status_t status = r->mInputConsumer.receiveDispatchSignal();
-    if (status) {
-        ALOGE("channel '%s' ~ Failed to receive dispatch signal.  status=%d",
-                r->getInputChannelName(), status);
-        return 0; // remove the callback
-    }
+    status_t status = r->consumeEvents(false /*consumeBatches*/);
+    return status == OK || status == NO_MEMORY ? 1 : 0;
+}
 
-    if (r->mEventInProgress) {
-        ALOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
-                r->getInputChannelName());
-        return 1;
-    }
+status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s.", getInputChannelName(),
+            consumeBatches ? "true" : "false");
+#endif
 
-    InputEvent* inputEvent;
-    status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent);
-    if (status) {
-        ALOGW("channel '%s' ~ Failed to consume input event.  status=%d",
-                r->getInputChannelName(), status);
-        r->mInputConsumer.sendFinishedSignal(false);
-        return 1;
+    if (consumeBatches) {
+        mBatchedInputEventPending = false;
     }
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    jobject inputEventObj;
-    switch (inputEvent->getType()) {
-    case AINPUT_EVENT_TYPE_KEY:
+    for (;;) {
+        uint32_t seq;
+        InputEvent* inputEvent;
+        status_t status = mInputConsumer.consume(&mInputEventFactory,
+                consumeBatches, &seq, &inputEvent);
+        if (status) {
+            if (status == WOULD_BLOCK) {
+                if (mInputConsumer.hasPendingBatch() && !mBatchedInputEventPending) {
+                    // There is a pending batch.  Come back later.
+                    mBatchedInputEventPending = true;
 #if DEBUG_DISPATCH_CYCLE
-        ALOGD("channel '%s' ~ Received key event.",
-                r->getInputChannelName());
+                    ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
+                            getInputChannelName());
 #endif
-        inputEventObj = android_view_KeyEvent_fromNative(env,
-                static_cast<KeyEvent*>(inputEvent));
-        break;
+                    env->CallVoidMethod(mReceiverObjGlobal,
+                            gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
 
-    case AINPUT_EVENT_TYPE_MOTION:
+                    if (env->ExceptionCheck()) {
+                        ALOGE("channel '%s' ~ An exception occurred while dispatching that "
+                                "batched input events are pending.", getInputChannelName());
+                        LOGE_EX(env);
+                        env->ExceptionClear();
+                        mBatchedInputEventPending = false; // try again later
+                    }
+                }
+                return OK;
+            }
+            ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
+                    getInputChannelName(), status);
+            return status;
+        }
+        assert(inputEvent);
+
+        jobject inputEventObj;
+        switch (inputEvent->getType()) {
+        case AINPUT_EVENT_TYPE_KEY:
 #if DEBUG_DISPATCH_CYCLE
-        ALOGD("channel '%s' ~ Received motion event.",
-                r->getInputChannelName());
+            ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
 #endif
-        inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
-                static_cast<MotionEvent*>(inputEvent));
-        break;
+            inputEventObj = android_view_KeyEvent_fromNative(env,
+                    static_cast<KeyEvent*>(inputEvent));
+            break;
 
-    default:
-        assert(false); // InputConsumer should prevent this from ever happening
-        inputEventObj = NULL;
-    }
+        case AINPUT_EVENT_TYPE_MOTION:
+#if DEBUG_DISPATCH_CYCLE
+            ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
+#endif
+            inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+                    static_cast<MotionEvent*>(inputEvent));
+            break;
 
-    if (!inputEventObj) {
-        ALOGW("channel '%s' ~ Failed to obtain event object.",
-                r->getInputChannelName());
-        r->mInputConsumer.sendFinishedSignal(false);
-        return 1;
-    }
+        default:
+            assert(false); // InputConsumer should prevent this from ever happening
+            inputEventObj = NULL;
+        }
 
-    r->mEventInProgress = true;
+        if (!inputEventObj) {
+            ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
+            mInputConsumer.sendFinishedSignal(seq, false);
+            return NO_MEMORY;
+        }
 
 #if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ Invoking input handler.", r->getInputChannelName());
+        ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
 #endif
-    env->CallVoidMethod(r->mReceiverObjGlobal,
-            gInputEventReceiverClassInfo.dispatchInputEvent, inputEventObj);
-#if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ Returned from input handler.", r->getInputChannelName());
-#endif
+        env->CallVoidMethod(mReceiverObjGlobal,
+                gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
 
-    if (env->ExceptionCheck()) {
-        ALOGE("channel '%s' ~ An exception occurred while dispatching an event.",
-                r->getInputChannelName());
-        LOGE_EX(env);
-        env->ExceptionClear();
+        env->DeleteLocalRef(inputEventObj);
 
-        if (r->mEventInProgress) {
-            r->mInputConsumer.sendFinishedSignal(false);
-            r->mEventInProgress = false;
+        if (env->ExceptionCheck()) {
+            ALOGE("channel '%s' ~ An exception occurred while dispatching an event.",
+                    getInputChannelName());
+            LOGE_EX(env);
+            env->ExceptionClear();
+
+            mInputConsumer.sendFinishedSignal(seq, false);
+            return OK;
         }
     }
-
-    env->DeleteLocalRef(inputEventObj);
-    return 1;
 }
 
 
@@ -257,28 +259,41 @@
     receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
 }
 
-static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, jboolean handled) {
+static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr,
+        jint seq, jboolean handled) {
     sp<NativeInputEventReceiver> receiver =
             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
-    status_t status = receiver->finishInputEvent(handled);
-    if (status) {
+    status_t status = receiver->finishInputEvent(seq, handled);
+    if (status && status != DEAD_OBJECT) {
         String8 message;
         message.appendFormat("Failed to finish input event.  status=%d", status);
         jniThrowRuntimeException(env, message.string());
     }
 }
 
+static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr) {
+    sp<NativeInputEventReceiver> receiver =
+            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
+    status_t status = receiver->consumeEvents(true /*consumeBatches*/);
+    if (status && status != DEAD_OBJECT) {
+        String8 message;
+        message.appendFormat("Failed to consume batched input event.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
+    }
+}
+
 
 static JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit",
             "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
             (void*)nativeInit },
-    { "nativeDispose",
-            "(I)V",
+    { "nativeDispose", "(I)V",
             (void*)nativeDispose },
-    { "nativeFinishInputEvent", "(IZ)V",
-            (void*)nativeFinishInputEvent }
+    { "nativeFinishInputEvent", "(IIZ)V",
+            (void*)nativeFinishInputEvent },
+    { "nativeConsumeBatchedInputEvents", "(I)V",
+            (void*)nativeConsumeBatchedInputEvents },
 };
 
 #define FIND_CLASS(var, className) \
@@ -299,7 +314,10 @@
 
     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
             gInputEventReceiverClassInfo.clazz,
-            "dispatchInputEvent", "(Landroid/view/InputEvent;)V");
+            "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
+    GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending,
+            gInputEventReceiverClassInfo.clazz,
+            "dispatchBatchedInputEventPending", "()V");
     return 0;
 }
 
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index b9f3738..7245d9d 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
 */
 
-#include <ui/KeyCharacterMap.h>
-#include <ui/Input.h>
+#include <androidfw/KeyCharacterMap.h>
+#include <androidfw/Input.h>
 
 #include <android_runtime/AndroidRuntime.h>
 #include <nativehelper/jni.h>
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 27469a2..36a98f9 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -20,7 +20,7 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include "android_view_KeyEvent.h"
 
 namespace android {
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 2def1d1..0fb1b17 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -20,7 +20,7 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include "android_view_MotionEvent.h"
 #include "android_util_Binder.h"
 #include "android/graphics/Matrix.h"
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index bba4b47..18bcea1 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -572,7 +572,7 @@
 }
 
 static jobject Surface_screenshot(JNIEnv* env, jobject clazz, jint width, jint height,
-        jint minLayer, jint maxLayer, bool allLayers)
+        jint minLayer, jint maxLayer)
 {
     return doScreenshot(env, clazz, width, height, minLayer, maxLayer, false);
 }
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index 0da90d7..668d3bb 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -20,7 +20,7 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include "android_view_MotionEvent.h"
 
 
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index b68c97a..afbcfc2 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -21,7 +21,7 @@
 
 #include <utils/Log.h>
 #include <ScopedUtfChars.h>
-#include <utils/ZipFileRO.h>
+#include <androidfw/ZipFileRO.h>
 
 #include <zlib.h>
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 97658a1..a2b1117 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -717,6 +717,13 @@
         android:label="@string/permlab_removeTasks"
         android:description="@string/permdesc_removeTasks" />
 
+    <!-- @hide Change the screen compatibility mode of applications -->
+    <permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_setScreenCompatibility"
+        android:description="@string/permdesc_setScreenCompatibility" />
+
     <!-- Allows an application to modify the current configuration, such
          as locale. -->
     <permission android:name="android.permission.CHANGE_CONFIGURATION"
@@ -1507,6 +1514,24 @@
         android:description="@string/permdesc_bindPackageVerifier"
         android:protectionLevel="signature" />
 
+    <!-- Allows applications to access serial ports via the SerialManager.
+         @hide -->
+    <permission android:name="android.permission.SERIAL_PORT"
+        android:label="@string/permlab_serialPort"
+        android:description="@string/permdesc_serialPort"
+        android:protectionLevel="normal" />
+
+    <!-- Allows the holder to access content providers from outside an ApplicationThread.
+         This permission is enforced by the ActivityManagerService on the corresponding APIs,
+         in particular ActivityManagerService#getContentProviderExternal(String) and
+         ActivityManagerService#removeContentProviderExternal(String).
+         @hide
+    -->
+    <permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY"
+        android:label="@string/permlab_accessContentProvidersExternally"
+        android:description="@string/permdesc_accessContentProvidersExternally"
+        android:protectionLevel="signature" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/assets/webkit/youtube.html b/core/res/assets/webkit/youtube.html
index d808bcf..8e103c1 100644
--- a/core/res/assets/webkit/youtube.html
+++ b/core/res/assets/webkit/youtube.html
@@ -13,94 +13,59 @@
         height: 100%;
         padding: 0%;
         z-index: 10;
+        background-size: 100%;
+        background: no-repeat;
+        background-position: center;
+      }
+      #play {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+      }
+      #logo {
+        position: absolute;
+        bottom: 0;
+        right: 0;
       }
     </style>
   </head>
   <body id="body">
   <script type="text/javascript">
-    // Nominal original size. If the embed is smaller than this, the play and logo
-    // images get scaled appropriately. These are actually 3/4 of the sizes suggested
-    // by youtube, so the images don't get too tiny.
-    defHeight = 258;
-    defWidth = 318;
-
     function setup() {
         var width = document.body.clientWidth;
         var height = document.body.clientHeight;
-        var canvas = document.getElementById("canvas");
-        // Resize the canvas to the right size
-        canvas.width = width;
-        canvas.height = height;
-        var ctx = canvas.getContext('2d');
+        var mainElement = document.getElementById("main");
+        var playElement = document.getElementById("play");
         var loadcount = 0;
+        var POSTER = "http://img.youtube.com/vi/VIDEO_ID/0.jpg";
+
         function doload() {
-            if (++loadcount == 3) {
-                // All images are loaded, so display them.
-                // (Note that the images are loaded from javascript, so might load
-                // after document.onload fires)
-
-                playWidth = play.width;
-                playHeight = play.height;
-                logoWidth = logo.width;
-                logoHeight = logo.height;
-                var ratio = 1;
-                // If the page is smaller than it 'should' be in either dimension
-                // we scale the background, play button and logo according to the
-                // dimension that has been shrunk the most.
-                if (width / height > defWidth / defHeight && height < defHeight) {
-                    ratio = height / defHeight;
-                    // Stretch the background in this dimension only.
-                    backgroundHeight = background.height / ratio;
-                    ctx.drawImage(background, 0, 0, background.width, background.height,
-                        0, (height - backgroundHeight) / 2, width, backgroundHeight);
-                } else if (width / height < defWidth / defHeight && width < defWidth) {
-                    ratio = width / defWidth;
-                    backgroundWidth = background.width / ratio;
-                    ctx.drawImage(background, 0, 0, background.width, background.height,
-                        (width - backgroundWidth) / 2, 0, backgroundWidth, height);
-                } else {
-                    // In this case stretch the background in both dimensions to fill the space.
-                    ctx.drawImage(background, 0, 0, width, height);
-                }
-                playWidth *= ratio;
-                playHeight *= ratio;
-                logoWidth *= ratio;
-                logoHeight *= ratio;
-                playLeft = (width - playWidth) / 2;
-                playTop = (height - playHeight) / 2;
-                ctx.drawImage(play, playLeft, playTop, playWidth, playHeight);
-                ctx.globalAlpha = 0.7
-                ctx.drawImage(logo, width - logoWidth, height - logoHeight, logoWidth, logoHeight);
-                // To make it slightly easier to hit, the click target is twice the width/height of the unscaled play button
-                targetLeft = width / 2 - play.width;
-                targetRight = width / 2 + play.width;
-                targetTop = height / 2 - play.height;
-                targetBottom = height / 2 + play.height;
-
-                canvas.addEventListener("click", function(e) {
-                   var posx = e.clientX-canvas.offsetLeft;
-                   var posy = e.clientY-canvas.offsetTop;
-                   if (posx >= targetLeft && posx <= targetRight &&
-                       posy >= targetTop && posy <= targetBottom) {
-                       top.location.href = "vnd.youtube:VIDEO_ID";
-                   }
-               }, false);
+            if (++loadcount == 2) {
+                // Resize the element to the right size
+                mainElement.width = width;
+                mainElement.height = height;
+                mainElement.style.backgroundImage = "url('" + POSTER + "')";
+                // Center the play button
+                playElement.style.marginTop = "-" + play.height/2 + "px";
+                playElement.style.marginLeft = "-" + play.width/2 + "px";
+                playElement.addEventListener("click", function(e) {
+                    top.location.href = "vnd.youtube:VIDEO_ID";
+                }, false);
             }
         }
         var background = new Image();
         background.onload = doload;
-        background.src = "http://img.youtube.com/vi/VIDEO_ID/0.jpg";
+        background.src = POSTER;
         play = new Image();
         play.onload = doload;
         play.src = "play.png";
-        logo = new Image();
-        logo.onload = doload;
-        logo.src = "youtube.png";
     }
+
     window.onload = setup;
   </script>
     <div id="main">
-    <canvas id="canvas"></canvas>
+        <img src="play.png" id="play"></img>
+        <img src="youtube.png" id="logo"></img>
     </div>
   </body>
 </html>
diff --git a/core/res/res/anim/recents_fade_in.xml b/core/res/res/anim/recents_fade_in.xml
new file mode 100644
index 0000000..c516fadb
--- /dev/null
+++ b/core/res/res/anim/recents_fade_in.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/recents_fade_in.xml
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@interpolator/decelerate_quad"
+        android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="150" />
diff --git a/core/res/res/anim/recents_fade_out.xml b/core/res/res/anim/recents_fade_out.xml
new file mode 100644
index 0000000..7468cc19
--- /dev/null
+++ b/core/res/res/anim/recents_fade_out.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/recents_fade_out.xml
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/accelerate_quad"
+    android:fromAlpha="1.0"
+    android:toAlpha="0.0"
+    android:duration="150" />
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_3_fully.png b/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_3_fully.png
index c2e4b78..cb08eed 100644
--- a/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_3_fully.png
+++ b/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_3_fully.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_disconnected.png b/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_disconnected.png
index 51b839f..ea065c3 100644
--- a/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_disconnected.png
+++ b/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_disconnected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_3_fully.png b/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_3_fully.png
new file mode 100644
index 0000000..d3ba98c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_3_fully.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_disconnected.png b/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_disconnected.png
new file mode 100644
index 0000000..153c6ad
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_disconnected.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_3_fully.png b/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_3_fully.png
new file mode 100644
index 0000000..ec6bc54
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_3_fully.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_disconnected.png b/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_disconnected.png
new file mode 100644
index 0000000..9fd4f33
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_disconnected.png
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index f21c6b8..abf6676 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -215,6 +215,10 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Laat die program toe om take na die voorgrond en agtergrond te skuif. Kwaadwillige programme kan hulself sonder jou beheer na vore dwing."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"stop lopende programme"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Laat die program toe om take te verwyder en hul programme te dood. Kwaadwillige programme kan die gedrag van ander programme ontwrig."</string>
+    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
+    <skip />
+    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"aktiveer programontfouting"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Laat die program toe om ontfouting vir \'n ander program af te skakel. Kwaadwillige programme kan dit dalk gebruik om ander programme te dood."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"Verander jou UI-instellings"</string>
@@ -539,30 +543,30 @@
     <item msgid="1735177144948329370">"Tuisfaks"</item>
     <item msgid="603878674477207394">"Roeper"</item>
     <item msgid="1650824275177931637">"Ander"</item>
-    <item msgid="9192514806975898961">"Gepasmaakte"</item>
+    <item msgid="9192514806975898961">"Gepasmaak"</item>
   </string-array>
   <string-array name="emailAddressTypes">
     <item msgid="8073994352956129127">"Tuis"</item>
     <item msgid="7084237356602625604">"Werk"</item>
     <item msgid="1112044410659011023">"Ander"</item>
-    <item msgid="2374913952870110618">"Gepasmaakte"</item>
+    <item msgid="2374913952870110618">"Gepasmaak"</item>
   </string-array>
   <string-array name="postalAddressTypes">
     <item msgid="6880257626740047286">"Tuis"</item>
     <item msgid="5629153956045109251">"Werk"</item>
     <item msgid="4966604264500343469">"Ander"</item>
-    <item msgid="4932682847595299369">"Gepasmaakte"</item>
+    <item msgid="4932682847595299369">"Gepasmaak"</item>
   </string-array>
   <string-array name="imAddressTypes">
     <item msgid="1738585194601476694">"Tuis"</item>
     <item msgid="1359644565647383708">"Werk"</item>
     <item msgid="7868549401053615677">"Ander"</item>
-    <item msgid="3145118944639869809">"Gepasmaakte"</item>
+    <item msgid="3145118944639869809">"Gepasmaak"</item>
   </string-array>
   <string-array name="organizationTypes">
     <item msgid="7546335612189115615">"Werk"</item>
     <item msgid="4378074129049520373">"Ander"</item>
-    <item msgid="3455047468583965104">"Gepasmaakte"</item>
+    <item msgid="3455047468583965104">"Gepasmaak"</item>
   </string-array>
   <string-array name="imProtocols">
     <item msgid="8595261363518459565">"AIM"</item>
@@ -574,7 +578,7 @@
     <item msgid="2506857312718630823">"ICQ"</item>
     <item msgid="1648797903785279353">"Jabber"</item>
   </string-array>
-    <string name="phoneTypeCustom" msgid="1644738059053355820">"Gepasmaakte"</string>
+    <string name="phoneTypeCustom" msgid="1644738059053355820">"Gepasmaak"</string>
     <string name="phoneTypeHome" msgid="2570923463033985887">"Tuis"</string>
     <string name="phoneTypeMobile" msgid="6501463557754751037">"Mobiel"</string>
     <string name="phoneTypeWork" msgid="8863939667059911633">"Werk"</string>
@@ -595,24 +599,24 @@
     <string name="phoneTypeWorkPager" msgid="649938731231157056">"Werkroeper"</string>
     <string name="phoneTypeAssistant" msgid="5596772636128562884">"Assistent"</string>
     <string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
-    <string name="eventTypeCustom" msgid="7837586198458073404">"Gepasmaakte"</string>
+    <string name="eventTypeCustom" msgid="7837586198458073404">"Gepasmaak"</string>
     <string name="eventTypeBirthday" msgid="2813379844211390740">"Verjaardag"</string>
     <string name="eventTypeAnniversary" msgid="3876779744518284000">"Herdenking"</string>
     <string name="eventTypeOther" msgid="7388178939010143077">"Ander"</string>
-    <string name="emailTypeCustom" msgid="8525960257804213846">"Gepasmaakte"</string>
+    <string name="emailTypeCustom" msgid="8525960257804213846">"Gepasmaak"</string>
     <string name="emailTypeHome" msgid="449227236140433919">"Tuis"</string>
     <string name="emailTypeWork" msgid="3548058059601149973">"Werk"</string>
     <string name="emailTypeOther" msgid="2923008695272639549">"Ander"</string>
     <string name="emailTypeMobile" msgid="119919005321166205">"Mobiel"</string>
-    <string name="postalTypeCustom" msgid="8903206903060479902">"Gepasmaakte"</string>
+    <string name="postalTypeCustom" msgid="8903206903060479902">"Gepasmaak"</string>
     <string name="postalTypeHome" msgid="8165756977184483097">"Tuis"</string>
     <string name="postalTypeWork" msgid="5268172772387694495">"Werk"</string>
     <string name="postalTypeOther" msgid="2726111966623584341">"Ander"</string>
-    <string name="imTypeCustom" msgid="2074028755527826046">"Gepasmaakte"</string>
+    <string name="imTypeCustom" msgid="2074028755527826046">"Gepasmaak"</string>
     <string name="imTypeHome" msgid="6241181032954263892">"Tuis"</string>
     <string name="imTypeWork" msgid="1371489290242433090">"Werk"</string>
     <string name="imTypeOther" msgid="5377007495735915478">"Ander"</string>
-    <string name="imProtocolCustom" msgid="6919453836618749992">"Gepasmaakte"</string>
+    <string name="imProtocolCustom" msgid="6919453836618749992">"Gepasmaak"</string>
     <string name="imProtocolAim" msgid="7050360612368383417">"AIM"</string>
     <string name="imProtocolMsn" msgid="144556545420769442">"Windows Live"</string>
     <string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo"</string>
@@ -624,8 +628,8 @@
     <string name="imProtocolNetMeeting" msgid="8287625655986827971">"NetMeeting"</string>
     <string name="orgTypeWork" msgid="29268870505363872">"Werk"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Ander"</string>
-    <string name="orgTypeCustom" msgid="225523415372088322">"Gepasmaakte"</string>
-    <string name="relationTypeCustom" msgid="3542403679827297300">"Gepasmaakte"</string>
+    <string name="orgTypeCustom" msgid="225523415372088322">"Gepasmaak"</string>
+    <string name="relationTypeCustom" msgid="3542403679827297300">"Gepasmaak"</string>
     <string name="relationTypeAssistant" msgid="6274334825195379076">"Assistent"</string>
     <string name="relationTypeBrother" msgid="8757913506784067713">"Broer"</string>
     <string name="relationTypeChild" msgid="1890746277276881626">"Kind"</string>
@@ -640,7 +644,7 @@
     <string name="relationTypeRelative" msgid="1799819930085610271">"Familielid"</string>
     <string name="relationTypeSister" msgid="1735983554479076481">"Suster"</string>
     <string name="relationTypeSpouse" msgid="394136939428698117">"Eggenoot"</string>
-    <string name="sipAddressTypeCustom" msgid="2473580593111590945">"Gepasmaakte"</string>
+    <string name="sipAddressTypeCustom" msgid="2473580593111590945">"Gepasmaak"</string>
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Tuis"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Werk"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Ander"</string>
@@ -760,6 +764,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Laat die program toe om te verifieer dat \'n pakket installeerbaar is."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind aan \'n pakkieverifieerder"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Laat die houer toe om versoeke aan pakketverifieerders te rig. Dit moet nooit vir normale programme nodig wees nie."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"kry toegang tot reekspoorte"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Laat die houer toe om toegang te verkry tot reekspoorte wat die SerialManager API gebruik."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"verkry toegang tot inhoud ekstern"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Stel die houer in staat om toegang te verkry tot inhoudverskaffers vanuit die dop. Behoort nooit nodig te wees vir gewone programme nie."</string>
     <string name="save_password_message" msgid="767344687139195790">"Wil jy hê die blaaier moet hierdie wagwoord onthou?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Nie nou nie"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Onthou"</string>
@@ -972,6 +980,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Begin Wi-Fi Direct. Dit sal die Wi-Fi-kliënt/warmkol afskakel."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kon nie Wi-Fi Direct begin nie."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direk is aan"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Raak vir instellings"</string>
     <string name="accept" msgid="1645267259272829559">"Aanvaar"</string>
     <string name="decline" msgid="2112225451706137894">"Weier"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Uitnodiging gestuur"</string>
@@ -980,8 +990,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Aan:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer die vereiste PIN in:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direk is aan"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Raak vir instellings"</string>
     <string name="select_character" msgid="3365550120617701745">"Voeg karakter in"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Onbekend program"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Stuur SMS-boodskappe"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9b32c0d..bb175e7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"ወደ ግንባር ዎይ እና ዳራ ስራዎችን ለማንቀሳቀስ ለመተግበሪያው ይፈቅዳሉ፡፡ ያለአንተ ቁጥጥር  ተንኮል አዘል መተግበሪያዎች ራሳቸውን ወደፊት መምጣት ሊያስገድዱ ይችላሉ፡፡"</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"የአሂድ ትግበራዎች አቁም"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"ተግባሮችን ለማስወገድ እና መተግበሪያዎቻቸውን ለመግደል ለመተግበሪያ ይፈቅዳል። ጎጂ የሆኑ መተግበሪያዎች የሌሎችን መተግበሪያዎችን ባህሪ ሊያውኩ ይችላሉ።"</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"የማያ ገጽ ተኳኋኝነት መድብ"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"መተግበሪያው የሌሎች መተግበሪያዎች የማያ ገጽ ተኳኋኝነት ሁናቴ እንዲቆጣጠር ይፈቅዳል። ተንኮለኛ መተግበሪያዎች የሌሎች መተግበሪያዎች ባህሪ ሊሰብሩ ይችላሉ።"</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"የትግበራ ማረሚያ አንቃ"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"ለሌላ መተግበሪያ አርምን አብራ ለመተግበሪያው ይፈቅዳሉ። ሌሎች መተግበሪያዎች ለመግደል ተንኮል አዘል መተግበሪያዎች ይሄንን ሊጠቀሙት ይችላሉ።"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"የUI ቅንብሮችን ለውጥ"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ፓኬጅ መጫን የሚችል መሆኑን ለማረጋገጥ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"በፓኬጅ አረጋጋጭ የተወሰነ"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"የፓኬጅ አረጋጋጮችን ጥየቃ ለማድረግ ያዡ ይፈቅዳሉ። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"ተከታታይ ወደቦችን ድረስ"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Allows the holder to access serial ports using the SerialManager API. የተከታታይ አደራጅ APIን በመጠቀም ያዡ የተከታታይ ወደቦችን እንዲደርስ ይፈቅዳል።"</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ይዘት አቅራቢዎችን በውጭ በኩል ድረስባቸው"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"ያዢውን ከቀፎው ወደሚመጡ የይዘት አቅራቢዎች እንዲደርስ ይፈቅድለታል። ለመደበኛ መተግበሪያዎች በፍጹም ማስፈለግ የለባቸውም።"</string>
     <string name="save_password_message" msgid="767344687139195790">"አሳሹ ይህን ይለፍ ቃል እንዲያስታወስ ይፈልጋሉ?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"አሁን አይደለም"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"አስታውስ"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ቀጥታ"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"የWi-Fi በቀጥታ  ጀምር።ይህ የWi-Fi ደንበኛ /ድረስ ነጥብ  ያጠፋል።"</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"በቀጥታ Wi-Fi ማስጀመር አልተቻለም።"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"የWi-Fi ቀጥታ በርቷል"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"ለቅንብሮች ንካ"</string>
     <string name="accept" msgid="1645267259272829559">"ተቀበል"</string>
     <string name="decline" msgid="2112225451706137894">"ውድቅ አድርግ"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"ግብዣ ተልኳል"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"ለ፦"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"የሚፈለገውን ፒን ተይብ፦"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ፒን፦"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"የWi-Fi ቀጥታ በርቷል"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"ለቅንብሮች ንካ"</string>
     <string name="select_character" msgid="3365550120617701745">"ቁምፊ አስገባ"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"ያልታወቀ መተግበሪያ"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"የSMS መልዕክቶች መበላክ ላይ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index b8cd094..a98a7d4 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"للسماح لتطبيق ما بنقل المهام إلى المقدمة والخلفية. قد تفرض التطبيقات الضارة نفسها إلى المقدمة بدون تحكم منك."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"إيقاف التطبيقات التي قيد التشغيل"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"للسماح للتطبيق بإزالة المهام وإنهاء تطبيقاتها. قد تعطل التطبيقات الضارة عمل التطبيقات الأخرى."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"تعيين توافق الشاشة"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"السماح للتطبيق بالتحكم في وضع التوافق مع شاشة التطبيقات الأخرى. قد تتسبب التطبيقات الضارة في تعطيل سلوك التطبيقات الأخرى."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"تمكين تصحيح أخطاء التطبيق"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"للسماح للتطبيق بتشغيل تصحيح الأخطاء لتطبيق آخر. قد تستخدم التطبيقات الضارة هذا لإنهاء التطبيقات الأخرى."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"تغيير إعدادات واجهة المستخدم"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"السماح للتطبيق بالتحقق من إمكانية تثبيت حزمة."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"الالتزام بمحقق حزمة"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"السماح للمالك بإجراء طلبات محققي الحزمة. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"الدخول إلى المنافذ التسلسلية"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"يسمح لحامله بالدخول إلى المنافذ التسلسلية باستخدام واجهة برمجة التطبيقات."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"الدخول إلى مزودي المحتوى خارجيًا"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"السماح للمالك بالدخول إلى مزودي المحتوى من الوعاء. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
     <string name="save_password_message" msgid="767344687139195790">"هل تريد من المتصفح تذكر كلمة المرور هذه؟"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"ليس الآن"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"تذكّر"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"اتصال Wi-Fi مباشر"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"ابدأ Wi-Fi Direct. يؤدي هذا إلى إيقاف عميل/نقطة اتصال Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"تعذر بدء Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"تم تشغيل اتصال Wi-Fi المباشر"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"المس للحصول على الإعدادات"</string>
     <string name="accept" msgid="1645267259272829559">"قبول"</string>
     <string name="decline" msgid="2112225451706137894">"رفض"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"تم إرسال الدعوة"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"إلى:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"اكتب رقم التعريف الشخصي المطلوب:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"رقم التعريف الشخصي:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"تم تشغيل اتصال Wi-Fi المباشر"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"المس للحصول على الإعدادات"</string>
     <string name="select_character" msgid="3365550120617701745">"إدراج حرف"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"تطبيق غير معروف"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"إرسال رسائل قصيرة SMS"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 8479324..233d5a9 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Дазваляе прыкладанням перамяшчаць заданні на ​​пярэдні план і фон. Шкоднасныя прыкладанні могуць прымусова рабіць сябе асноўнымі без вашага ведама."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"спыніць запушчаныя прыкладанні"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Дазваляе прыкладанням выдаляць заданні і спыняць прыкладанні, якія іх выкарыстоўваюць. Шкоднасныя прыкладаннi могуць перашкодзiць працы іншых прыкладанняў."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"усталяваць сумяшчальнасць экранаў"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Дазваляе прыкладанню кіраваць рэжымам сумяшчальнасці экранаў іншых прыкладанняў. Шкоднаснае ПЗ можа перашкодзiць працы іншых прыкладанняў."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"уключыць адладку прыкладання"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Дазваляе прыкладанням уключаць адладку для іншага прыкладання. Шкоднасныя прыкладанні могуць выкарыстоўваць гэта, каб спыняць іншыя прыкладанні."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"змяняць налады карыстальніцкага інтэрфейса"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Дазваляе прыкладанням правяраць магчымасць усталявання пакету."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"прывязаць да верыфікатару пакету"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Дазваляе ўладальніку рабіць запыты верыфікатараў пакету. Не патрабуецца для звычайных прыкладанняў."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"атрымаць доступ да паслядоўных партоў"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Дазваляе ўладальніку атрымліваць доступ да паслядоўных партоў з дапамогай API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"знешнi доступ да кантэнт-правайдэраў"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Дае ўладальніку доступ да кантэнт-правайдэраў з абалонкi. Не патрабуецца для звычайных прыкладанняў."</string>
     <string name="save_password_message" msgid="767344687139195790">"Вы хочаце, каб браўзэр запомніў гэты пароль?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Не цяпер"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Запомніць"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Пачаць работу Wi-Fi Direct. Гэта адключыць кліента або кропку доступу Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Немагчыма запусціць Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct уключаны"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Дакраніцеся, каб наладзіць"</string>
     <string name="accept" msgid="1645267259272829559">"Прыняць"</string>
     <string name="decline" msgid="2112225451706137894">"Адхіліць"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Запрашэнне адпраўлена"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Каму:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Увядзіце патрэбны PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct уключаны"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Дакраніцеся, каб наладзіць"</string>
     <string name="select_character" msgid="3365550120617701745">"Уставіць сімвал"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Невядомае прыкладанне"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Адпраўка SMS"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 6bec103..66516b9 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Разрешава на приложението да прехвърля задачи на преден и на заден план. Злонамерените приложения могат сами да се изведат на преден план без ваша намеса."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"спиране на изпълняваните приложения"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Разрешава на приложението да премахва задачи и да прекратява приложенията им. Злонамерените приложения могат да нарушат поведението на други приложения."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"задаване на съвместимост на екрана"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Разрешава на приложението да контролира режима на съвместимост на екрана на други приложения. Злонамерените програми могат да нарушат поведението на други приложения."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"активиране на отстраняването на грешки в приложения"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Разрешава на приложението да включва отстраняването на грешки за друго приложение. Злонамерените приложения могат да използват това, за да прекратят други приложения."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"промяна на настройките ви за потребителския интерфейс"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Разрешава на приложението да провери дали пакетът може да се инсталира."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"обвързване с верификатор на пакета"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Разрешава на притежателя да прави заявки за верификатори на пакета. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"достъп до серийни портове"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Разрешава на притежателя достъп до серийни портове посредством приложния програмен интерфейс (API) SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"външен достъп до доставчиците на съдърж."</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Разрешава на притежателя достъп до доставчиците на съдържание от командния ред. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
     <string name="save_password_message" msgid="767344687139195790">"Искате ли браузърът да запомни тази парола?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Не сега"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Запомняне"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Стартиране на Wi-Fi Direct. Това ще изключи клиентската програма/точката за достъп до Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct не можа да се стартира."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct е включено"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Докоснете за настройки"</string>
     <string name="accept" msgid="1645267259272829559">"Приемам"</string>
     <string name="decline" msgid="2112225451706137894">"Отхвърлям"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Поканата е изпратена"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"До:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Въведете задължителния ПИН:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ПИН:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct е включено"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Докоснете за настройки"</string>
     <string name="select_character" msgid="3365550120617701745">"Вмъкване на знак"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Неизвестно приложение"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Изпращане на SMS съобщения"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 8dced70..b848c2c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Permet que l\'aplicació desplaci tasques en primer o segon pla. Les aplicacions malicioses poden aparèixer en primer pla sense el teu consentiment."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"atura les aplicacions que s\'estan executant"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Permet que l\'aplicació elimini tasques i finalitzi les seves aplicacions. Les aplicacions malicioses poden alterar el comportament d\'altres aplicacions."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"definició de la compatibilitat de pantalla"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permet que l\'aplicació controli el mode de compatibilitat de pantalla d\'altres aplicacions. És possible que les aplicacions malicioses interrompin el comportament d\'altres aplicacions."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"activa la depuració d\'aplicacions"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Permet que una aplicació activi la depuració per a una altra aplicació. Les aplicacions malicioses poden utilitzar aquesta funció per finalitzar altres aplicacions."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"canviar la configuració de la IU"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permet que l\'aplicació verifiqui si un paquet es pot instal·lar."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vincula a un verificador de paquets"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permet que el titular sol·liciti verificadors de paquets. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"accedeix a ports sèrie"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet que el titular accedeixi a ports sèrie amb l\'API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accedeix als proveïdors de contingut externament"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permet que el titular accedeixi als proveïdors de contingut des de l\'intèrpret d\'ordres. No és necessari per a les aplicacions normals."</string>
     <string name="save_password_message" msgid="767344687139195790">"Voleu que el navegador recordi aquesta contrasenya?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ara no"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Recorda-ho"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Inicia Wi-Fi Direct. Això desactivarà el client/la zona Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"No s\'ha pogut iniciar Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct està activat"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toca per accedir a la configuració"</string>
     <string name="accept" msgid="1645267259272829559">"Accepta"</string>
     <string name="decline" msgid="2112225451706137894">"Rebutja"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"S\'ha enviat la invitació"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Per a:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introdueix el PIN sol·licitat:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct està activat"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toca per accedir a la configuració"</string>
     <string name="select_character" msgid="3365550120617701745">"Insereix un caràcter"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicació desconeguda"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"S\'estan enviant missatges SMS"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index e21de4b..544395f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Umožňuje aplikaci přesunout úlohy na popředí nebo pozadí. Škodlivé aplikace mohou vynutit zobrazení na popředí bez vašeho svolení."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"zastavení činnosti aplikací"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Umožňuje aplikaci odstranit úlohy a ukončit jejich aplikace. Škodlivé aplikace mohou narušit chování ostatních aplikací."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nastavit kompatibilitu obrazovky"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Umožňuje aplikaci ovládat režim kompatibility obrazovky v ostatních aplikacích. Škodlivé aplikace mohou narušit chování ostatních aplikací."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"povolení ladění aplikací"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Umožňuje aplikaci zapnout ladění jiné aplikace. Škodlivé aplikace mohou toto oprávnění použít k ukončení ostatních aplikací."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"změna vašeho nastavení uživatelského rozhraní"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Umožňuje aplikaci ověřit, zda balíček lze nainstalovat."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"navázat na ověřovatele balíčků"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Umožňuje držiteli podávat žádosti o ověření balíčků. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"přístup k sériovým portům"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Umožňuje držiteli přístup k sériovým portům pomocí rozhraní SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"externí přístup k poskytovatelům obsahu"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Umožňuje držiteli získat z příkazového řádku přístup k poskytovatelům obsahu. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
     <string name="save_password_message" msgid="767344687139195790">"Chcete, aby si prohlížeč zapamatoval toto heslo?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Nyní ne"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Zapamatovat"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Přímé připojení sítě Wi-Fi"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Spustit přímé připojení sítě Wi-Fi. Tato možnost vypne provoz sítě Wi-Fi v režimu klient/hotspot."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Přímé připojení sítě Wi-Fi se nepodařilo spustit."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Přímé připojení sítě Wi-Fi je zapnuto"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Nastavení otevřete dotykem"</string>
     <string name="accept" msgid="1645267259272829559">"Přijmout"</string>
     <string name="decline" msgid="2112225451706137894">"Odmítnout"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pozvánka odeslána."</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Komu:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadejte požadovaný kód PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Přímé připojení sítě Wi-Fi je zapnuto"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Nastavení otevřete dotykem"</string>
     <string name="select_character" msgid="3365550120617701745">"Vkládání znaků"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Neznámá aplikace"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Odesílání zpráv SMS"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 1175cbb..ceefe1d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Tillader, at appen kan flytte opgaver til forgrunden og baggrunden. Ondsindede apps kan tvinge sig selv i forgrunden uden din kontrol."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"stoppe kørsel af apps"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Tillader, at en app kan fjerne opgaver og lukke deres apps. Ondsindede apps kan forstyrre adfærden for andre apps."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"indstil skærmens kompatibilitet"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Tillader, at appen kontrollerer kompatibilitetstilstanden for skærme i andre applikationer. Ondsindede applikationer kan forstyrre andre applikationers adfærd."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"aktivere fejlretning af appen"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Tillader, at appen kan slå fejlretning til for en anden app. Ondsindede apps kan bruge dette til at afslutte andre apps."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"skift indstillinger for brugergrænsefladen"</string>
@@ -755,11 +757,15 @@
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføj telefonsvarer"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Tillader, at appen kan tilføje beskeder på din telefonsvarer."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"skifte tilladelser til geografisk placering i Browser"</string>
-    <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websteder."</string>
+    <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websites."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"bekræft pakker"</string>
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Tillader, at appen kan bekræfte, at en pakke kan installeres."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind til en bekræftelse af pakker"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Tillader, at indehaveren kan sende anmodninger om bekræftelser af pakker. Dette bør aldrig være nødvendigt for almindelige apps."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"adgang til serielle porte"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Tillader, at indehaveren kan få adgang til serielle porte ved hjælp af SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"adgang til indholdsleverandører eksternt"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Giver indehaveren adgang til indholdsleverandører fra startsiden. Bør aldrig være nødvendigt for normale apps."</string>
     <string name="save_password_message" msgid="767344687139195790">"Ønsker du, at browseren skal huske denne adgangskode?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ikke nu"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Husk"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. Dette slår Wi-Fi-klient/hotspot fra."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct kunne ikke startes."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct er slået til"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tryk for indstillinger"</string>
     <string name="accept" msgid="1645267259272829559">"Accepter"</string>
     <string name="decline" msgid="2112225451706137894">"Afvis"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitationen er sendt"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Til:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv den påkrævede pinkode:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pinkode:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct er slået til"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tryk for indstillinger"</string>
     <string name="select_character" msgid="3365550120617701745">"Indsæt tegn"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Ukendt app"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sender sms-beskeder"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 953897c..3b3d08d 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Ermöglicht der App, Aufgaben in den Vorder- und Hintergrund zu verschieben. Schädliche Apps können so ohne Ihr Zutun eine Anzeige im Vordergrund erzwingen."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"Aktive Apps beenden"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Ermöglicht der App, Aufgaben zu entfernen und die entsprechenden Apps zu beenden. Schädliche Apps können das Verhalten anderer Apps stören."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Bildschirmkompatibilität festlegen"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Ermöglicht der App, den Bildschirmkompatibilitätsmodus anderer Apps zu steuern. Schädliche Apps können das Verhalten anderer Apps stören."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"Fehlerbeseitigung für App aktivieren"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Ermöglicht der App, die Fehlerbeseitigung für eine andere App zu aktivieren. Schädliche Apps können so andere Apps beenden."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI-Einstellungen ändern"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Ermöglicht der App die Überprüfung, ob ein Paket installiert werden kann"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"An Paketprüfung binden"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ermöglicht dem Halter, Anfragen für die Paketprüfung zu senden. Sollte nie für normale Apps benötigt werden."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"Zugriff auf serielle Schnittstellen"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Ermöglicht dem Inhaber den Zugriff auf serielle Schnittstellen über das SerialManager-API"</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"Extern auf Content-Anbieter zugreifen"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Berechtigt den Inhaber, extern auf Content-Anbieter zuzugreifen. Bei normalen Apps nicht notwendig"</string>
     <string name="save_password_message" msgid="767344687139195790">"Möchten Sie, dass der Browser dieses Passwort speichert?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Nicht jetzt"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Speichern"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct-Betrieb starten. Hierdurch wird der WLAN-Client-/-Hotspot-Betrieb deaktiviert."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Starten von Wi-Fi Direct nicht möglich"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ist aktiviert."</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Zum Aufrufen der Einstellungen berühren"</string>
     <string name="accept" msgid="1645267259272829559">"Akzeptieren"</string>
     <string name="decline" msgid="2112225451706137894">"Ablehnen"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Einladung gesendet"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"An:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Geben Sie die erforderliche PIN ein:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ist aktiviert."</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Zum Aufrufen der Einstellungen berühren"</string>
     <string name="select_character" msgid="3365550120617701745">"Zeichen einfügen"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Unbekannte App"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Kurznachrichten werden gesendet"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 458517c..e2bc244 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Επιτρέπει στην εφαρμογή τη μετακίνηση εργασιών στο προσκήνιο και στο φόντο. Τυχόν κακόβουλες εφαρμογές μπορούν να προωθηθούν στο προσκήνιο χωρίς να μπορείτε να τις ελέγξετε."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"διακοπή εκτέλεσης εφαρμογών"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Επιτρέπει στην εφαρμογή την κατάργηση ενεργειών και την απομάκρυνση των εφαρμογών τους. Τυχόν κακόβουλες εφαρμογές ενδέχεται να διαταράξουν τη λειτουργία άλλων εφαρμογών."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ρύθμιση συμβατότητας οθόνης"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Επιτρέπει στην εφαρμογή να ελέγξει τη λειτουργία συμβατότητας της οθόνης με άλλες εφαρμογές. Οι κακόβουλες εφαρμογές μπορεί να επηρεάσουν τη συμπεριφορά άλλων εφαρμογών."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"ενεργοποίηση εντοπισμού σφαλμάτων εφαρμογής"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Επιτρέπει στην εφαρμογή να ενεργοποιήσει τον εντοπισμό σφαλμάτων για μια άλλη εφαρμογή. Τυχόν κακόβουλες εφαρμογές ενδέχεται να χρησιμοποιήσουν αυτήν τη δυνατότητα για τον τερματισμό άλλων εφαρμογών."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"αλλαγή των ρυθμίσεων του UI"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Επιτρέπει στην εφαρμογή να επαληθεύσει τη δυνατότητα εγκατάστασης ενός πακέτου."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"δέσμευση με επαλήθευση πακέτου"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Επιτρέπει στον κάτοχο να υποβάλλει ερωτήματα σε προγράμματα επαλήθευσης πακέτου. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"πρόσβαση στις σειριακές θύρες"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Επιτρέπει στον κάτοχο την πρόσβαση στις σειριακές θύρες με τη χρήση του SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"εξωτερική πρόσβαση σε παρόχους περιεχ."</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Επιτρέπει στον κάτοχο να έχει πρόσβαση στους παρόχους περιεχομένου από το κέλυφος. Να μην απαιτείται ποτέ για τις κανονικές εφαρμογές."</string>
     <string name="save_password_message" msgid="767344687139195790">"Θέλετε το πρόγραμμα περιήγησης να διατηρήσει αυτόν τον κωδικό πρόσβασης;"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Να μην γίνει τώρα"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Διατήρηση"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Ξεκινήστε τη λειτουργία Wi-Fi Direct. Θα απενεργοποιηθεί η λειτουργία πελάτη/φορητού σημείου πρόσβασης Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Δεν ήταν δυνατή η εκκίνηση του Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Το Wi-Fi Direct έχει ενεργοποιηθεί"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Αγγίξτε για ρυθμίσεις"</string>
     <string name="accept" msgid="1645267259272829559">"Αποδοχή"</string>
     <string name="decline" msgid="2112225451706137894">"Απόρριψη"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Η πρόσκληση στάλθηκε"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Προς:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Πληκτρολογήστε τον απαιτούμενο κωδικό PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Το Wi-Fi Direct έχει ενεργοποιηθεί"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Αγγίξτε για ρυθμίσεις"</string>
     <string name="select_character" msgid="3365550120617701745">"Εισαγωγή χαρακτήρα"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Άγνωστη εφαρμογή"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Αποστολή μηνυμάτων SMS"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index d48f544..bed4de2 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Allows the app to move tasks to the foreground and background. Malicious apps may force themselves to the front without your control."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"stop running apps"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Allows the app to remove tasks and kill their apps. Malicious apps may disrupt the behaviour of other apps."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"set screen compatibility"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Allows the app to control the screen compatibility mode of other applications. Malicious applications may break the behaviour of other applications."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"enable app debugging"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Allows the app to turn on debugging for another app. Malicious apps may use this to kill other apps."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"change your UI settings"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Allows the app to verify a package is installable."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind to a package verifier"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Allows the holder to make requests of package verifiers. Should never be needed for normal apps."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"access serial ports"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Allows the holder to access serial ports using the SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"access content providers externally"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Allows the holder to access content providers from the shell. Should never be needed for normal apps."</string>
     <string name="save_password_message" msgid="767344687139195790">"Do you want the browser to remember this password?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Not now"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Remember"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. This will turn off Wi-Fi client/hotspot."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Couldn\'t start Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct is on"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Touch for settings"</string>
     <string name="accept" msgid="1645267259272829559">"Accept"</string>
     <string name="decline" msgid="2112225451706137894">"Decline"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitation sent"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"To:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Type the required PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct is on"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Touch for settings"</string>
     <string name="select_character" msgid="3365550120617701745">"Insert character"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Unknown app"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sending SMS messages"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index c66dc8e..58df147 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Permite que la aplicación mueva tareas al primero o segundo plano. Las aplicaciones maliciosas pueden forzar su paso al primer plano sin que tú las controles."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"detener las aplicaciones en ejecución"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que la aplicación elimine tareas y cierre sus aplicaciones. Las aplicaciones malintencionadas pueden usar este permiso para interferir en el comportamiento de otras aplicaciones."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Definir compatibilidad de pantalla"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permite a la aplicación controlar el modo de compatibilidad de las pantallas de otras aplicaciones. Las aplicaciones malintencionadas pueden interrumpir el funcionamiento de otras aplicaciones."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"activar depuración de aplicación"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Permite que la aplicación active la depuración de otra aplicación. Las aplicaciones malintencionadas pueden usar este permiso para interrumpir la ejecución de otras aplicaciones."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"cambiar tu configuración de la interfaz de usuario"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite que la aplicación verifique si se puede instalar un paquete."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"Vincular a un verificador de paquetes"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que el titular solicite verificadores de paquetes. Las aplicaciones normales no deberían necesitar este permiso."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"Acceder a los puertos serie"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite acceder a puertos serie a través de la API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acceder a proveedores externamente"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite acceder a los proveedores de contenido desde la interfaz. Las aplicaciones normales nunca deberían necesitarlo."</string>
     <string name="save_password_message" msgid="767344687139195790">"¿Quieres recordar esta contraseña en el navegador?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no."</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Recuerda"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar Wi-Fi Direct. Se desactivará el funcionamiento de la zona o del cliente Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"No se pudo iniciar Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Se activó Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocar para ajustar los parámetros de configuración"</string>
     <string name="accept" msgid="1645267259272829559">"Aceptar"</string>
     <string name="decline" msgid="2112225451706137894">"Rechazar"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Se envió la invitación."</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Se activó Wi-Fi Direct."</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocar para ajustar los parámetros de configuración"</string>
     <string name="select_character" msgid="3365550120617701745">"Insertar caracteres"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicación desconocida"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 0c526ff..9499214 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Permite que la aplicación mueva tareas a segundo o a primer plano. Algunas aplicaciones malintencionadas pueden aparecer en primer plano sin el control del usuario."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"detener aplicaciones en ejecución"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que la aplicación termine tareas y cierre sus aplicaciones. Las aplicaciones malintencionadas pueden usar este permiso para interferir en el comportamiento de otras aplicaciones."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"establecer compatibilidad de pantalla"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permite que la aplicación controle el modo de compatibilidad de la pantalla de otras aplicaciones. Las aplicaciones malintencionadas pueden influir de forma negativa en el funcionamiento de otras aplicaciones."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"habilitar depuración de aplicación"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Permite que la aplicación active la depuración de otra aplicación. Las aplicaciones malintencionadas pueden usar este permiso para interrumpir la ejecución de otras aplicaciones."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"cambiar la configuración de la interfaz de usuario"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite que la aplicación verifique si se puede instalar un paquete."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"enlazar con un detector de paquetes"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que se envíen solicitudes de detectores de paquetes. Las aplicaciones normales no deberían necesitar este permiso."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"acceder a puertos serie"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite acceder a puertos serie a través de SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acceder a proveedores de contenido externamente"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite acceder a los proveedores de contenido desde el shell. Las aplicaciones normales nunca deberían necesitar este permiso."</string>
     <string name="save_password_message" msgid="767344687139195790">"¿Quieres que el navegador recuerde esta contraseña?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Recordar"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar Wi-Fi Direct. Se desactivará el funcionamiento de la zona o del cliente Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"No se ha podido iniciar Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct activado"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toca para acceder a Ajustes"</string>
     <string name="accept" msgid="1645267259272829559">"Aceptar"</string>
     <string name="decline" msgid="2112225451706137894">"Rechazar"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitación enviada"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct activado"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toca para acceder a Ajustes"</string>
     <string name="select_character" msgid="3365550120617701745">"Insertar carácter"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicación desconocida"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS..."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 5591289..da06538 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Võimaldab rakendusel teisaldada ülesanded esiplaanile ja taustale. Pahatahtlikud rakendused võivad sundida end esiplaanile tulema teie loata."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"käitatud rakenduste peatamine"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Võimaldab rakendusel eemaldada ülesanded ja peatada nende rakendused. Pahatahtlikud rakendused võivad häirida teiste rakenduste käitumist."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"kuva ühilduvuse seadmine"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Võimaldab rakendusel juhtida teiste rakenduste kuva ühilduvuse režiimi. Pahatahtlikud rakendused võivad teisi rakendusi häirida."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"Rakenduse silumise lubamine"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Võimaldab rakendusel lülitada sisse teise rakenduse silumise. Pahatahtlikud rakendused võivad seda kasutada teiste rakenduste peatamiseks."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"muuda UI-seadeid"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Võimaldab rakendusel kinnitada, et paketti saab installida."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"sidumine paketi kinnitajaga"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lubab omanikul teha taotlusi paketi kinnitajate kohta. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"juurdepääs jadaportidele"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Võimaldab omanikul SerialManageri API-liidese abil jadaportidele juurde pääseda."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"väline juurdepääs sisupakkujatele"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Võimaldab valdajal hankida juurdepääsu sisupakkujatele kesta kaudu. Pole kunagi vajalik tavaliste rakenduste puhul."</string>
     <string name="save_password_message" msgid="767344687139195790">"Kas soovite, et brauser jätaks selle parooli meelde?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Mitte praegu"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Pidage meeles"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"WiFi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Käivitage WiFi otseühendus. See lülitab välja WiFi kliendi/leviala."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"WiFi otseühenduse käivitamine ebaõnnestus."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"WiFi Direct on sees"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Puuted seadete jaoks"</string>
     <string name="accept" msgid="1645267259272829559">"Nõustu"</string>
     <string name="decline" msgid="2112225451706137894">"Keeldu"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Kutse on saadetud"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Saaja:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Sisestage nõutav PIN-kood:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kood:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"WiFi Direct on sees"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Puuted seadete jaoks"</string>
     <string name="select_character" msgid="3365550120617701745">"Sisesta tähemärk"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Tundmatu rakendus"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-sõnumite saatmine"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 7097e43..9a6a293 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"به برنامه اجازه می‎دهد تا کارها را به پیش زمینه و پس زمینه منتقل کند. برنامه‎های مخرب می‎توانند بدون کنترل به اجبار خود را به جلو منتقل کنند."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"متوقف کردن برنامه‎های در حال اجرا"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"به برنامه اجازه می‎دهد تا کارها را حذف کند و برنامه‎های آنها را متوقف کند. برنامه‎های مخرب می‌توانند در اجرای برنامه‎های دیگر اختلال ایجاد ‎کنند."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"تنظیم سازگاری با صفحه نمایش"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"به برنامه کاربردی اجازه کنترل حالت سازگاری صفحه نمایش برای برنامه‌های دیگر را می‌دهد. برنامه‌های خرابکار ممکن است باعث کارکرد نادرست دیگر برنامه‌ها شوند."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"فعال کردن عیب‌یابی برنامه"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"به برنامه اجازه می‎دهد تا عیب‌یابی را برای برنامه‌ای دیگر فعال کند. برنامه‎های مخرب می‎توانند از آن استفاده کنند تا اجرای برنامه‎های دیگر را متوقف کنند."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"تغییر تنظیمات UI"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"به برنامه اجازه می‌دهد قابل نصب بودن بسته را تأیید کند."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"اتصال به یک تأیید کننده بسته"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"به دارنده اجازه می‎دهد تا تاییدکنندگان بسته را درخواست کند. برای برنامه‎های عادی نیاز نیست."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"دسترسی به درگاه‌های سریال"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"به دارنده اجازه می‌دهد با استفاده از SerialManager API به درگاه‌های سریال دسترسی داشته باشد."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"دسترسی خارجی به ارائه‌دهندگان محتوا"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"به دارنده اجازه می‌دهد تا از خارج برنامه به ارائه دهندگان محتوا دسترسی داشته باشد. هرگز برای برنامه‌های معمولی به آن نیازی نیست."</string>
     <string name="save_password_message" msgid="767344687139195790">"می خواهید مرورگر این رمز ورود را به خاطر داشته باشد؟"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"اکنون خیر"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"به خاطر سپردن"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct را شروع کنید. این کار نقطه اتصال/سرویس گیرنده Wi-Fi را غیرفعال خواهد کرد."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct شروع نشد."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct روشن است"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"لمس کردن برای تنظیمات"</string>
     <string name="accept" msgid="1645267259272829559">"پذیرش"</string>
     <string name="decline" msgid="2112225451706137894">"عدم پذیرش"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"دعوت‌نامه ارسال شد"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"به:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"پین لازم را تایپ کنید:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"پین:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct روشن است"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"لمس کردن برای تنظیمات"</string>
     <string name="select_character" msgid="3365550120617701745">"درج نویسه"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"برنامه ناشناخته"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"ارسال پیامک ها"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0a60adb..2fe9b4e 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Antaa sovelluksen siirtää tehtäviä etualalle ja taustalle. Haitalliset sovellukset voivat pakottaa itsensä etualalle ilman käyttäjän hallintaa."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"käynnissä olevien sovellusten pysäyttäminen"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Antaa sovelluksen poistaa tehtäviä ja lopettaa niiden sovelluksia. Haitalliset sovellukset voivat häiritä muiden sovellusten toimintaa."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"aseta näytön yhteensopivuus"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Sallii sovelluksen hallita toisten sovellusten näytön yhteensopivuustilaa. Haittasovellukset voivat häiritä toisten sovellusten toimintaa."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"sovellusten vianetsinnän käyttöönotto"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Antaa sovelluksen ottaa vianetsinnän käyttöön toisessa sovelluksessa. Haitalliset ohjelmat voivat lopettaa tällä muita sovelluksia."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"muuta käyttöliittymäsi asetuksia"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Antaa sovelluksen vahvistaa, että pakkaus on asennettavissa."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"sitoudu paketin vahvistajaan"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Antaa sovelluksen tehdä pakettien vahvistuspyyntöjä. Ei tavallisten sovellusten käyttöön."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"käytä sarjaportteja"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Luvan haltija voi käyttää sarjaportteja SerialManager-sovellusliittymän avulla."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"käytä ulkoisia sisällöntarjoajia"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Antaa luvan haltijan käyttää liittymän sisällöntarjoajia. Ei normaalien sovelluksien käyttöön."</string>
     <string name="save_password_message" msgid="767344687139195790">"Haluatko selaimen muistavan tämän salasanan?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ei nyt"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Muista"</string>
@@ -776,7 +782,7 @@
     <string name="searchview_description_query" msgid="5911778593125355124">"Hakulauseke"</string>
     <string name="searchview_description_clear" msgid="1330281990951833033">"Tyhjennä kysely"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"Lähetä kysely"</string>
-    <string name="searchview_description_voice" msgid="2453203695674994440">"Äänihaku"</string>
+    <string name="searchview_description_voice" msgid="2453203695674994440">"Puhehaku"</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"kuukausi sitten"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Yli kuukausi sitten"</string>
   <plurals name="num_seconds_ago">
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Suora wifi-yhteys"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Käynnistä suora wifi-yhteys. Wifi-asiakas/-hotspot poistetaan käytöstä."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Suoran wifi-yhteyden käynnistäminen epäonnistui."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct on käytössä"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tarkastele asetuksia koskettamalla"</string>
     <string name="accept" msgid="1645267259272829559">"Hyväksy"</string>
     <string name="decline" msgid="2112225451706137894">"Hylkää"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Kutsu lähetetty."</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kohde:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Kirjoita pyydetty PIN-koodi:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-koodi:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct on käytössä"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tarkastele asetuksia koskettamalla"</string>
     <string name="select_character" msgid="3365550120617701745">"Lisää merkki"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Tuntematon sovellus"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Tekstiviestien lähettäminen"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index d32f487..d6e8f30 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Permet à l\'application de faire passer les tâches de premier plan en arrière-plan. Des applications malveillantes peuvent exploiter cette fonctionnalité pour passer au premier plan sans votre consentement."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"arrêter les applications en cours d\'exécution"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Permet à l\'application de supprimer des tâches et de fermer les applications qui les exécutent. Des applications malveillantes peuvent exploiter cette fonctionnalité pour perturber le comportement des autres applications."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"définir la compatibilité de l\'écran"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permettre de contrôler le mode de compatibilité de l\'écran des autres applications. Des applications malveillantes peuvent perturber le fonctionnement d\'autres applications."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"activer le débogage des applications"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Permet à l\'application d\'activer le débogage d\'une autre application. Des applications malveillantes peuvent exploiter cette fonctionnalité pour en fermer d\'autres."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"Modification des paramètres de l\'IU"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permet à l\'application de vérifier qu\'un package peut être installé."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"associer à un vérificateur de package"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permet à l\'application autorisée d\'effectuer des requêtes de vérificateurs de package. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"accéder aux ports série"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet à l\'application autorisée d\'accéder aux ports série avec l\'API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accès externe fournisseurs de contenu"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permettre à l\'application titulaire d\'accéder à des fournisseurs de contenu depuis l\'interface. Les applications standards ne devraient jamais avoir recours à cette autorisation."</string>
     <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Pas maintenant"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Mémoriser"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Lancer le Wi-Fi Direct. Cela désactive le fonctionnement du Wi-Fi client ou via un point d\'accès."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Impossible d\'activer le Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct activé"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Appuyez pour accéder aux paramètres."</string>
     <string name="accept" msgid="1645267259272829559">"Accepter"</string>
     <string name="decline" msgid="2112225451706137894">"Refuser"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitation envoyée"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"À :"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Saisissez le code PIN requis :"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Code PIN :"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct activé"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Appuyez pour accéder aux paramètres."</string>
     <string name="select_character" msgid="3365550120617701745">"Insérer un caractère"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Application inconnue"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Envoi de messages SMS"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 7a4f706..f5a3cb7 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"एप्लिकेशन को अग्रभूमि और पृष्ठभूमि में कार्यों को ले जाने देता है. दुर्भावनापूर्ण एप्लिकेशन आपके नियंत्रण के बिना स्वयं को बलपूर्वक आगे कर सकते हैं."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"चलने वाले एप्लिकेशन रोकें"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"किसी एप्‍लिकेशन को कार्यों को निकालने और उनके एप्‍लिकेशन समाप्त करने देता है. दुर्भावनापूर्ण एप्‍लिकेशन अन्‍य एप्‍लिकेशन का व्‍यवहार बाधित कर सकते हैं."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"स्‍क्रीन संगतता सेट करें"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"एप्‍लिकेशन को अन्‍य एप्‍लिकेशन के स्‍क्रीन संगतता मोड को नियंत्रित करने देता है. दुर्भावनापूर्ण एप्‍लिकेशन अन्‍य एप्‍लिकेशन का व्‍यवहार बाधित कर सकते हैं."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"एप्‍लिकेशन डीबग करना सक्षम करें"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"एप्लिकेशन को अन्य एप्लिकेशन के लिए डीबग किया जाना चालू करने देता है. दुर्भावनापूर्ण एप्लिकेशन इसका उपयोग अन्य एप्लिकेशन को समाप्त करने के लिए कर सकते हैं."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"अपनी UI सेटिंग बदलें"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"एप्‍लि‍केशन को इंस्‍टॉल करने योग्‍य पैकेज सत्‍यापि‍त करने देता है."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"पैकेज प्रमाणक से आबद्ध करें"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"धारक को पैकेज प्रमाणक के अनुरोध की अनुमति‍ देता है. सामान्‍य एप्‍लिकेशन के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"सीरियल पोर्ट पर पहुंचें"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API का उपयोग करके धारक को सीरियल पोर्ट पर पहुंच प्रदान करता है."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"बाह्य रूप से सामग्री प्रदाताओं पर पहुंच"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"धारक को शेल से सामग्री प्रदाताओं तक पहुंचने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यकता नहीं होनी चाहिए."</string>
     <string name="save_password_message" msgid="767344687139195790">"क्‍या आप चाहते हैं कि ब्राउज़र पासवर्ड को याद रखे?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"अभी नहीं"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"याद रखें"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi प्रत्यक्ष"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi डायरेक्ट प्रारंभ करें. इससे Wi-Fi क्‍लाइंट/हॉटस्पॉट कार्यवाही बंद हो जाएगी."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi डायरेक्ट प्रारंभ नहीं किया जा सका."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi प्रत्यक्ष चालू है"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"सेटिंग के लिए स्‍पर्श करें"</string>
     <string name="accept" msgid="1645267259272829559">"स्वीकार करें"</string>
     <string name="decline" msgid="2112225451706137894">"अस्वीकार करें"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"आमंत्रण भेजा गया"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"प्रति:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"आवश्‍यक पिन लिखें:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"पिन:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi प्रत्यक्ष चालू है"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"सेटिंग के लिए स्‍पर्श करें"</string>
     <string name="select_character" msgid="3365550120617701745">"वर्ण सम्‍मिलित करें"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"अज्ञात एप्‍लिकेशन"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS संदेश भेज रहा है"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e37be22..01378666 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Omogućuje aplikaciji da premjesti zadatke u prednji plan ili pozadinu. Zlonamjerne aplikacije mogu na silu doći u prednji plan bez vašeg nadzora."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"zaustavljanje pokrenutih aplikacija"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Omogućuje aplikaciji uklanjanje zadataka i uklanjanje njihovih aplikacija. Zlonamjerne aplikacije mogu poremetiti rad drugih aplikacija."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"postavljanje kompatibilnosti sa zaslonom"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Aplikaciji omogućuje upravljanje načinom kompatibilnosti aplikacija sa zaslonom. Zlonamjerne aplikacije mogu prekinuti takvo ponašanje ostalih aplikacija."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"omogućavanje rješavanja programskih pogrešaka u aplikaciji"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Omogućuje aplikaciji uključivanje uklanjanja programskih pogrešaka za drugu aplikaciju. Zlonamjerne aplikacije mogu na taj način ukloniti druge aplikacije."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"promjena postavki korisničkog sučelja"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Aplikaciji omogućuje da provjeri je li paket moguće instalirati."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vezano uz paketnu provjeru"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Nositelju omogućuje da traži paketnu provjeru. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"pristup serijskim priključcima"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Rukovatelju omogućuje pristup serijskim priključcima pomoću značajke SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"pristup pružateljima sadržaja izvana"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Omogućuje vlasniku pristup pružateljima sadržaja iz programske ovojnice. Ne bi trebalo biti potrebno za normalne aplikacije."</string>
     <string name="save_password_message" msgid="767344687139195790">"Želite li da preglednik zapamti ovu zaporku?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ne sada"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Zapamti"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Izravni Wi-Fi"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Pokreni izravan rad s Wi-Fi mrežom. To će isključiti rad s Wi-Fi klijentom/žarišnom točkom."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Pokretanje izravne Wi-Fi veze nije moguće."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct uključen"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dodirnite za postavke"</string>
     <string name="accept" msgid="1645267259272829559">"Prihvaćam"</string>
     <string name="decline" msgid="2112225451706137894">"Odbaci"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pozivnica je poslana"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Prima:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Upišite potreban PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct uključen"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dodirnite za postavke"</string>
     <string name="select_character" msgid="3365550120617701745">"Umetni znak"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Nepoznata aplikacija"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Slanje SMS poruka"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 9b3eb54..54e8b06 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Lehetővé teszi az alkalmazás számára, hogy feladatokat helyezzen át az előtérből a háttérbe és fordítva. A rosszindulatú alkalmazások az előtérbe helyezhetik magukat az Ön engedélye nélkül."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"futó alkalmazások leállítása"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Lehetővé teszi, hogy az alkalmazás feladatokat távolítson el és leállítsa azok alkalmazásait. Rosszindulatú alkalmazások megzavarhatják más alkalmazások viselkedését."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Képernyő-kompatibilitás beállítása"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Lehetővé teszi, hogy az alkalmazás szabályozza az egyéb alkalmazások képernyő-kompatibilitási módját. A kártékony alkalmazások megzavarhatják a többi alkalmazás viselkedését."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"alkalmazások hibakeresésének bekapcsolása"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Lehetővé teszi az alkalmazás számára, hogy hibakeresést végezzen egy másik alkalmazáson. A rosszindulatú alkalmazások ezzel leállíthatnak más alkalmazásokat."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"a felhasználói felület beállításainak módosítása"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Lehetővé teszi az alkalmazás számára, hogy ellenőrizze, egy csomag telepíthető-e."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"egy csomaghitelesítőhöz kötődnek"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lehetővé teszi, hogy a tulajdonos kérelmeket nyújtson be a csomag hitelesítőivel kapcsolatban. A normál alkalmazásoknak erre soha nincs szüksége."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"soros portok elérése"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Lehetővé teszi a tulajdonos számára a soros portok elérését a SerialManager API segítségével."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"tartalomszolgáltatók külső elérése"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Lehetővé teszi, hogy a tulajdonos hozzáférjen a tartalomszolgáltatókhoz a shellből. Normál alkalmazásoknál nem szükséges."</string>
     <string name="save_password_message" msgid="767344687139195790">"Szeretné, hogy a böngésző megjegyezze a jelszót?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Most nem"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Megjegyzés"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct elindítása. A Wi-Fi kliens/hotspot ettől leáll."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Nem sikerült elindítani a Wi-Fi Direct kapcsolatot."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"A Wi-Fi Direct be van kapcsolva"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"A beállításokhoz érintse meg"</string>
     <string name="accept" msgid="1645267259272829559">"Elfogadás"</string>
     <string name="decline" msgid="2112225451706137894">"Elutasítás"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Meghívó elküldve"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Címzett:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Adja meg a szükséges PIN kódot:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kód:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"A Wi-Fi Direct be van kapcsolva"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"A beállításokhoz érintse meg"</string>
     <string name="select_character" msgid="3365550120617701745">"Karakter beszúrása"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Ismeretlen alkalmazás"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-ek küldése"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b61fb42..ac5d463 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Mengizinkan apl memindah tugas ke latar depan dan latar belakang. Apl berbahaya dapat memaksa berpindah ke depan tanpa kontrol Anda."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"menghentikan apl yang berjalan"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Memungkinkan apl menghapus tugas dan menutup aplikasinya. Apl berbahaya dapat mengganggu perilaku apl lain."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"menyetel kompatibilitas layar"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Memungkinkan apl mengontrol mode kompatibilitas layar aplikasi lain. Aplikasi berbahaya dapat merusak perilaku aplikasi lain."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"mengaktifkan debugging apl"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Mengizinkan apl mengaktifkan debugging untuk apl lain. Apl berbahaya dapat menggunakan cara ini untuk menutup apl lain."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"ubah setelan UI Anda"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Mengizinkan apl memverifikasi bahwa suatu paket dapat dipasang."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"mengikat ke pemverifikasi paket"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Mengizinkan pemegang mengajukan permintaan pemverifikasian paket. Tidak pernah dibutuhkan oleh apl normal."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"akses port serial"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Memungkinkan pemegangnya mengakses port serial menggunakan API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"mengakses penyedia konten dari luar"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Memungkinkan pemegang mengakses penyedia konten dari cangkang. Tidak pernah diperlukan untuk apl normal."</string>
     <string name="save_password_message" msgid="767344687139195790">"Apakah Anda ingin peramban menyimpan sandi ini?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Tidak sekarang"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Ingat"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Langsung"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Memulai Wi-Fi Langsung. Opsi ini akan mematikan hotspot/klien Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Tidak dapat memulai Wi-Fi Langsung."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Langsung aktif"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Sentuh untuk setelan"</string>
     <string name="accept" msgid="1645267259272829559">"Terima"</string>
     <string name="decline" msgid="2112225451706137894">"Tolak"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Undangan terkirim"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kepada:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ketik PIN yang diminta:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Langsung aktif"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Sentuh untuk setelan"</string>
     <string name="select_character" msgid="3365550120617701745">"Sisipkan huruf"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Apl tak dikenal"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Mengirim pesan SMS"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ebf7223..77733b3 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Consente all\'applicazione di spostare attività in primo piano e in background. Le applicazioni dannose potrebbero forzare la loro impostazione in primo piano senza il tuo controllo."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"interruzione applicazioni in esecuzione"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Consente all\'applicazione di rimuovere le attività e terminare le loro applicazioni. Le applicazioni dannose potrebbero interferire con il comportamento di altre applicazioni."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"impostazione compatibilità schermo"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Consente all\'applicazione di controllare la modalità di compatibilità dello schermo di altre applicazioni. Le applicazioni dannose potrebbero disturbare il comportamento di altre applicazioni."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"attivazione debug delle applicazioni"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Consente all\'applicazione di attivare il debug per un\'altra applicazione. Le applicazioni dannose potrebbero farne uso per terminare altre applicazioni."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"modifica impostazioni UI"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Consente all\'applicazione di verificare se un pacchetto è installabile."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"associazione a verifica pacchetto"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Consente al proprietario di effettuare richieste relative alle verifiche dei pacchetti. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"accesso alle porte seriali"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Permette al proprietario di accedere alle porte seriali utilizzando l\'API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accesso a fornitori di contenuti esterni"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Consente al proprietario di accedere ai fornitori di contenuti dalla shell. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
     <string name="save_password_message" msgid="767344687139195790">"Memorizzare la password nel browser?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Non ora"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Memorizza"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Avvia Wi-Fi Direct. Verrà disattivato il client/hotspot Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Avvio di Wi-Fi Direct non riuscito."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct è attivo"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocca per le impostazioni"</string>
     <string name="accept" msgid="1645267259272829559">"Accetto"</string>
     <string name="decline" msgid="2112225451706137894">"Rifiuto"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invito inviato"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"A:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Inserisci il PIN richiesto:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct è attivo"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocca per le impostazioni"</string>
     <string name="select_character" msgid="3365550120617701745">"Inserisci carattere"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Applicazione sconosciuta"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Invio SMS"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 69d506e..a232c69 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"מאפשר ליישום להעביר משימות לחזית ולרקע. יישומים זדוניים עלולים לאלץ את עצמם לעבור לחזית ללא שליטה מצדך."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"עצירת יישומים פעילים"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"הרשאה זו מאפשרת ליישום להסיר משימות ולסגור את היישומים שבהם הן פועלות. יישומים זדוניים עלולים לשבש את פעולתם של יישומים אחרים."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"הגדרת תאימות מסך"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"מאפשר ליישום לשלוט במצב תאימות המסך של יישומים אחרים. יישומים זדוניים עלולים לפגוע בהתנהגות של יישומים אחרים."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"הפעלה של ניקוי באגים ביישומים"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"הרשאה זו מאפשרת ליישום להפעיל ניקוי באגים עבור יישום אחר. יישומים זדוניים עלולים להשתמש באפשרות זו כדי לסגור יישומים אחרים."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"שנה את הגדרות ממשק המשתמש שלך"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"מאפשר ליישום לאמת שחבילה ניתנת להתקנה."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"הכפפה למאמת חבילה"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"מאפשר למשתמש להגיש בקשות של מאמתי חבילות. הרשאה זו לעולם אינה נחוצה ליישומים רגילים."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"גישה ליציאות טוריות"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"מאפשר לבעלים לגשת ליציאות טוריות באמצעות ממשק ה- API של SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"גישה לספקי תוכן באופן חיצוני"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"מאפשר לבעלים לגשת לספקי תוכן מהמעטפת. לעולם לא אמור להיות צורך עבור יישומים רגילים."</string>
     <string name="save_password_message" msgid="767344687139195790">"האם ברצונך שהדפדפן יזכור סיסמה זו?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"לא כעת"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"זכור"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ישיר"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"הפעל Wi-Fi ישיר. פעולה זו תכבה את הלקוח/הנקודה החמה של ה-Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"לא ניתן להפעיל Wi-Fi ישיר"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ישיר מופעל"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"גע עבור הגדרות"</string>
     <string name="accept" msgid="1645267259272829559">"קבל"</string>
     <string name="decline" msgid="2112225451706137894">"דחה"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"ההזמנה נשלחה"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"אל:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"הקלד את קוד ה-PIN הנדרש."</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ישיר מופעל"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"גע עבור הגדרות"</string>
     <string name="select_character" msgid="3365550120617701745">"הוסף תו"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"יישום לא ידוע"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"שולח הודעות SMS"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index cb958e4..66e847a 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリに許可します。この許可を悪意のあるアプリに利用されると、悪意のあるアプリが強制的に優先される恐れがあります。"</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"実行中のアプリの停止"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"タスクの削除とアプリの終了をアプリに許可します。この許可を悪意のあるアプリケーションに利用されると、他のアプリの動作が妨害される恐れがあります。"</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"画面互換性の設定"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"他のアプリの画面互換性モードをコントロールすることをアプリに許可します。この許可を悪意のあるアプリに利用されると、他のアプリの動作が中断される恐れがあります。"</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"アプリのデバッグの有効化"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"別のアプリをデバッグモードにすることをアプリに許可します。この許可を悪意のあるアプリに利用されると、他のアプリが強制終了される恐れがあります。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI設定の変更"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"パッケージがインストール可能かどうか確認することをアプリに許可します。"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"パッケージベリファイアにバインド"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"パッケージベリファイアのリクエストを所有者に許可します。通常のアプリでは不要です。"</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"シリアルポートへのアクセス"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager APIを使用してシリアルポートにアクセスすることを所有者に許可します。"</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"コンテンツプロバイダへの外部アクセス"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"シェルからコンテンツプロバイダにアクセスすることを権利所有者に許可します。通常のアプリでは必要ありません。"</string>
     <string name="save_password_message" msgid="767344687139195790">"このパスワードをブラウザで保存しますか?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"今は保存しない"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"保存"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Directを開始します。これによりWi-Fiクライアント/アクセスポイントがOFFになります。"</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Directを開始できませんでした。"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi DirectはONです"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"設定を表示するにはタップしてください"</string>
     <string name="accept" msgid="1645267259272829559">"同意する"</string>
     <string name="decline" msgid="2112225451706137894">"同意しない"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"招待状を送信しました"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"To:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"必要なPINを入力してください:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi DirectはONです"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"設定を表示するにはタップしてください"</string>
     <string name="select_character" msgid="3365550120617701745">"文字を挿入"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"不明なアプリ"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMSメッセージの送信中"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 216ac76..754f791 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"앱이 작업을 포그라운드나 백그라운드로 이동할 수 있도록 허용합니다. 이 경우 악성 앱이 사용자의 조작 없이 앞으로 이동할 수 있습니다."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"실행 중인 앱 중지"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"애플리케이션이 작업을 삭제하거나 다른 애플리케이션을 중지시킬 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션의 동작을 멈추게 할 수 있습니다."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"화면 호환성 설정"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"앱이 다른 애플리케이션의 화면 호환성 모드를 제어할 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션의 동작을 멈추게 할 수 있습니다."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"앱 디버깅 사용"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"애플리케이션이 다른 애플리케이션에 대해 디버깅을 사용할 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션을 중지시킬 수 있습니다."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI 설정 변경"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"앱이 패키지가 설치 가능한지 확인할 수 있도록 허용합니다."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"패키지 인증 연결"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"권한을 가진 프로그램이 패키지 인증을 요청할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"직렬 포트에 액세스"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API를 사용하여 권한을 가진 프로그램이 직렬 포트에 액세스할 수 있도록 합니다."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"외부에서 콘텐츠 제공자에 액세스"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"권한을 가진 프로그램이 셸에서 콘텐츠 제공자에 액세스하도록 허용합니다. 일반 앱에서는 필요하지 않습니다."</string>
     <string name="save_password_message" msgid="767344687139195790">"브라우저에 이 비밀번호를 저장하시겠습니까?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"나중에"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"저장"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct 작업을 시작합니다. 이 작업을 하면 Wi-Fi 클라이언트/핫스팟 작업이 중지됩니다."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct를 시작하지 못했습니다."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct 켜짐"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"설정으로 이동하려면 터치하세요."</string>
     <string name="accept" msgid="1645267259272829559">"동의"</string>
     <string name="decline" msgid="2112225451706137894">"거부"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"초대장을 보냈습니다."</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"받는사람:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"필수 PIN 입력:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct 켜짐"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"설정으로 이동하려면 터치하세요."</string>
     <string name="select_character" msgid="3365550120617701745">"문자 삽입"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"알 수 없는 앱"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS 메시지를 보내는 중"</string>
diff --git a/core/res/res/values-large/themes.xml b/core/res/res/values-large/themes.xml
index 871a131..448e7c8 100644
--- a/core/res/res/values-large/themes.xml
+++ b/core/res/res/values-large/themes.xml
@@ -33,17 +33,17 @@
  -->
 <resources>
     <style name="Theme.Holo.DialogWhenLarge"
-            parent="@android:style/Theme.Holo.Dialog.MinWidth">
+            parent="@android:style/Theme.Holo.Dialog.FixedSize">
         <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
     </style>
     <style name="Theme.Holo.DialogWhenLarge.NoActionBar"
-            parent="@android:style/Theme.Holo.Dialog.NoActionBar.MinWidth">
+            parent="@android:style/Theme.Holo.Dialog.NoActionBar.FixedSize">
         <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
     </style>
     <style name="Theme.Holo.Light.DialogWhenLarge"
-            parent="@android:style/Theme.Holo.Light.Dialog.MinWidth">
+            parent="@android:style/Theme.Holo.Light.Dialog.FixedSize">
     </style>
     <style name="Theme.Holo.Light.DialogWhenLarge.NoActionBar"
-            parent="@android:style/Theme.Holo.Light.Dialog.NoActionBar.MinWidth">
+            parent="@android:style/Theme.Holo.Light.Dialog.NoActionBar.FixedSize">
     </style>
 </resources>
diff --git a/core/res/res/values-large/themes_device_defaults.xml b/core/res/res/values-large/themes_device_defaults.xml
index 52fff5c..d57e827 100644
--- a/core/res/res/values-large/themes_device_defaults.xml
+++ b/core/res/res/values-large/themes_device_defaults.xml
@@ -32,17 +32,17 @@
  -->
 <resources>
     <style name="Theme.DeviceDefault.DialogWhenLarge"
-            parent="@android:style/Theme.DeviceDefault.Dialog.MinWidth">
+            parent="@android:style/Theme.DeviceDefault.Dialog.FixedSize">
         <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
     </style>
     <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar"
-            parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar.MinWidth">
+            parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar.FixedSize">
         <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
     </style>
     <style name="Theme.DeviceDefault.Light.DialogWhenLarge"
-            parent="@android:style/Theme.DeviceDefault.Light.Dialog.MinWidth">
+            parent="@android:style/Theme.DeviceDefault.Light.Dialog.FixedSize">
     </style>
     <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar"
-            parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth">
+            parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize">
     </style>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 7f0370f..43aa1d3 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Leidžiama programai užduotis perkelti į priekinį planą ir į foną. Kenkėjiškos programos gali priverstinai persikelti į priekį be jūsų įsikišimo."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"sustabdyti vykdomas programas"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Leidžiama programai pašalinti užduotis ir panaikinti jų programas. Kenkėjiškos programos gali trikdyti kitų programų veikimą."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nustatyti ekrano suderinamumo režimą"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Programai leidžiama valdyti kitų programų ekrano suderinamumo režimą. Kenkėjiškos programos gali kliudyti veikti kitoms programoms."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"įgalinti programos derinimą"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Leidžiama programai įjungti kitos programos derinimą. Kenkėjiškos programos gali tai naudoti, kad išjungtų kitas programas."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"keisti UI nustatymus"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Leidžiama programai patikrinti, ar paketą galima įdiegti."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"susaistyti su paketo tikrinimo programa"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Savininkui leidžiama teikti užklausas patikrinti paketą. Įprastoms programoms to neturėtų prireikti."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"pasiekti nuosekliuosius prievadus"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Leidžiama savininkui pasiekti nuosekliuosius prievadus naudojant „SerialManager“ API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"pasiekti turinio teikėjus iš išorės"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Leidžiama savininkui pasiekti turinio teikėjus naudojant apvalkalą. To niekada neturėtų prireikti naudojant įprastas programas."</string>
     <string name="save_password_message" msgid="767344687139195790">"Ar norite, kad naršyklė atsimintų šį slaptažodį?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ne dabar"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Atsiminti"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Tiesioginis „Wi-Fi“ ryšys"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Paleiskite „Wi-Fi Direct“. Bus išjungta „Wi-Fi“ programa / viešosios interneto prieigos taškas."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Nepavyko paleisti „Wi-Fi Direct“."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"„Wi-Fi Direct“ įjungta"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Jei norite peržiūrėti nustatymus, palieskite"</string>
     <string name="accept" msgid="1645267259272829559">"Sutikti"</string>
     <string name="decline" msgid="2112225451706137894">"Atmesti"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pakvietimas išsiųstas"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Skirta:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Įveskite reikiamą PIN kodą:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kodas:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"„Wi-Fi Direct“ įjungta"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Jei norite peržiūrėti nustatymus, palieskite"</string>
     <string name="select_character" msgid="3365550120617701745">"Įterpti simbolį"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Nežinoma programa"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS pranešimų siuntimas"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a052481..5f84a48 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Ļauj lietotnei pārvietot uzdevumus priekšplānā un fonā. Ļaunprātīgas lietotnes var tikt izvirzītas priekšplānā bez jūsu vadības."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"apturēt izmantoto lietotņu darbību"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Ļauj lietotnei noņemt uzdevumus un pārtraukt to lietotņu darbību. Ļaunprātīgas lietotnes var traucēt citu lietotņu darbību."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Ekrāna saderības noteikšana"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Ļauj lietotnei kontrolēt citu lietotņu ekrāna saderības režīmu. Ļaunprātīgas lietojumprogrammas var mainīt citu lietojumprogrammu darbību."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"iespējot lietotnes atkļūdošanu"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Ļauj lietotnei ieslēgt citas lietotnes atkļūdošanu. Ļaunprātīgas lietotnes to var izmantot, lai pārtrauktu citu lietotņu darbību."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"mainīt lietotāja saskarnes iestatījumus"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Ļauj lietotnei verificēt, vai pakotne ir instalējama."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"saistīšana ar pakotnes verificētāju"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ļauj īpašniekam sūtīt pakotņu verificētāju pieprasījumus. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"piekļuve seriālajiem portiem"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Ļauj īpašniekam piekļūt seriālajiem portiem, izmantojot SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ārēji piekļūt satura nodrošinātājiem"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Ļauj īpašniekam no čaulas piekļūt satura nodrošinātājiem. Nekad nav nepieciešama parastām lietotnēm."</string>
     <string name="save_password_message" msgid="767344687139195790">"Vai vēlaties, lai pārlūkprogrammā tiktu saglabāta šī parole?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ne tagad"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Atcerēties"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Palaist programmu Wi-Fi Direct. Tādējādi tiks izslēgta Wi-Fi klienta/tīklāja darbība."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Nevarēja palaist programmu Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ir ieslēgts"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Pieskarieties, lai piekļūtu iestatījumiem."</string>
     <string name="accept" msgid="1645267259272829559">"Piekrist"</string>
     <string name="decline" msgid="2112225451706137894">"Noraidīt"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Ielūgums ir nosūtīts."</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kam:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ierakstiet pieprasīto PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ir ieslēgts"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Pieskarieties, lai piekļūtu iestatījumiem."</string>
     <string name="select_character" msgid="3365550120617701745">"Ievietojiet rakstzīmi"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Nezināma lietotne"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Īsziņu sūtīšana"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index f6189f2..3f240ab 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Membenarkan apl untuk memindahkan tugasan ke latar depan dan latar belakang. Apl hasad boleh memaksa diri mereka ke hadapan tanpa kawalan anda."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"hentikan apl yang sedang dijalankan"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Membenarkan apl untuk mengalih keluar tugasan dan melupuskan aplnya. Apl hasad boleh mengganggu tingkah laku apl lain."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"tetapkan keserasian skrin"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Membenarkan apl mengawal mod keserasian skrin aplikasi lain. Aplikasi hasad mungkin mematahkan kelakuan aplikasi lain."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"dayakan penyahpepijatan apl"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Membenarkan apl untuk menghidupkan penyahpepijatan untuk apl lain. Apl hasad boleh menggunakannya untuk menghapuskan apl lain."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"tukar tetapan UI anda"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Membenarkan apl untuk mengesahkan bahawa pakej boleh dipasang."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"ikat kepada pengesah pakej"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Membenarkan pemegang membuat permintaan pengesah pakej. Tidak sekali-kali diperlukan untuk apl normal."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"akses port bersiri"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Membenarkan pemegang mengakses port bersiri menggunakan API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"akses pembekal kandungan secara luaran"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Membolehkan pemegang mengakses pembekal kandungan dari luar. Tidak akan sekali-kali diperlukan untuk apl biasa."</string>
     <string name="save_password_message" msgid="767344687139195790">"Adakah anda mahu penyemak imbas mengingati kata laluan ini?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Bukan sekarang"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Ingat"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Langsung"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Mulakan Wi-Fi Langsung. Hal ini akan mematikan pengendalian klien/liputan Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Tidak dapat memulakan Wi-Fi Langsung."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct dihidupkan"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Sentuh untuk tetapan"</string>
     <string name="accept" msgid="1645267259272829559">"Terima"</string>
     <string name="decline" msgid="2112225451706137894">"Tolak"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Jemputan dihantar"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kepada:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Taipkan PIN yang diperlukan:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct dihidupkan"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Sentuh untuk tetapan"</string>
     <string name="select_character" msgid="3365550120617701745">"Masukkan aksara"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Apl tidak diketahui"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Menghantar mesej SMS"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index e03b735..63df5f4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Lar appen flytte oppgaver til forgrunnen eller bakgrunnen. Ondsinnede apper kan tvinge seg frem til forgrunnen utenfor din kontroll."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"avslutte apper som kjører"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Lar appen fjerne oppgaver og avslutte apper. Ondsinnede apper kan forstyrre atferden til andre apper."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"angi skjermkompatibilitet"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Lar appen kontrollere modus for skjermkompatibilitet i andre apper. Skadelige apper kan ødelegge funksjoner i andre apper."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"aktivere feilsøking av app"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Lar appen slå av feilsøking for andre apper. Ondsinnede apper kan bruke dette til å avslutte andre apper."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"endre innstillingene for brukergrensesnitt"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Lar appen bekrefte om en pakke kan installeres."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind til en pakkeverifikator"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lar innehaveren sende forespørsler om pakkeverifikatorer. Skal aldri være nødvendig for normale apper."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"tilgang til serielle porter"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Gir innehaveren tilgang til serielle porter ved hjelp av SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"gå til innholdsleverandører eksternt"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Lar innehaveren gå til innholdsleverandører fra kommandovinduet. Skal aldri være nødvendig for vanlige apper."</string>
     <string name="save_password_message" msgid="767344687139195790">"Ønsker du at nettleseren skal huske dette passordet?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ikke nå"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Husk"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. Dette deaktiverer Wi-Fi-klienten/-sonen."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kunne ikke starte Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct er slått på"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Berør for å se innstillinger"</string>
     <string name="accept" msgid="1645267259272829559">"Godta"</string>
     <string name="decline" msgid="2112225451706137894">"Avslå"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitasjonen er sendt"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Til:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv inn påkrevd PIN-kode:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct er slått på"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Berør for å se innstillinger"</string>
     <string name="select_character" msgid="3365550120617701745">"Sett inn tegn"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Ukjent app"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sender SMS-meldinger"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3a43b8e..8e5e8bc 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Hiermee kan de app taken naar de voor- en achtergrond verplaatsen. Schadelijke apps kunnen zichzelf op de voorgrond plaatsen zonder dat u hier iets aan kunt doen."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"actieve apps stoppen"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Hiermee kan de app taken verwijderen en apps sluiten. Schadelijke apps kunnen het gedrag van andere apps verstoren."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"schermcompatibiliteit instellen"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Toestaan dat de app de schermcompatibiliteitsmodus van andere apps beheert. Schadelijke apps kunnen het gedrag van andere apps verstoren."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"foutopsporing in apps inschakelen"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Hiermee kan de app de foutopsporing voor een andere app inschakelen. Schadelijke apps kunnen dit gebruiken om andere apps af te sluiten."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"uw UI-instellingen wijzigen"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Hiermee kan de app controleren of een pakket kan worden geïnstalleerd."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"koppelen aan pakketcontroleprogramma"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Hiermee kan de houder verzoeken indienen voor pakketcontroles. Nooit vereist voor normale apps."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"toegang krijgen tot seriële poorten"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"De houder toestaan toegang tot seriële poorten te krijgen met de SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"externe toegang tot inhoudsproviders"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Hiermee kan de houder toegang krijgen tot inhoudsproviders via de shell. Nooit vereist voor normale apps."</string>
     <string name="save_password_message" msgid="767344687139195790">"Wilt u dat de browser dit wachtwoord onthoudt?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Niet nu"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Onthouden"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wifi Direct starten. Hierdoor wordt de wifi-client/hotspot uitgeschakeld."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kan Wifi Direct niet starten."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct is actief"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Aanraken voor instellingen"</string>
     <string name="accept" msgid="1645267259272829559">"Accepteren"</string>
     <string name="decline" msgid="2112225451706137894">"Weigeren"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Uitnodiging verzonden"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Naar:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer de gewenste pincode in:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pincode"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct is actief"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Aanraken voor instellingen"</string>
     <string name="select_character" msgid="3365550120617701745">"Teken invoegen"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Onbekende app"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-berichten verzenden"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index e73f7d6..c17da8f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Pozwala aplikacji na przenoszenie zadań między tłem i pierwszym planem. Złośliwe aplikacje mogą wymusić przeniesienie się na pierwszy plan bez Twojego udziału."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"zatrzymywanie uruchomionych aplikacji"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Umożliwia aplikacji usuwanie zadań i kończenie powiązanych z nimi aplikacji. Złośliwe aplikacje mogą zakłócić działanie innych aplikacji."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ustaw zgodność ekranu"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Pozwala aplikacji na sterowanie trybem zgodności ekranu innych aplikacji. Złośliwe aplikacje mogą zmienić zachowanie innych programów."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"włączenie debugowania aplikacji"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Pozwala aplikacji na włączenie debugowania innej aplikacji. Złośliwe aplikacje mogą to wykorzystać do kończenia pracy innych programów."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"zmienianie ustawień interfejsu użytkownika"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Pozwala aplikacji na zweryfikowanie, czy pakiet można zainstalować."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"powiązanie z weryfikatorem pakietów"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Pozwala na wysyłanie żądań weryfikacji pakietu. To uprawnienie nie powinno być potrzebne zwykłym aplikacjom."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"dostęp do portów szeregowych"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Umożliwia posiadaczowi dostęp do portów szeregowych przy użyciu interfejsu API narzędzia SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"Dostęp do dostawców treści z zewnątrz"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Pozwala na dostęp do dostawców treści z powłoki. To uprawnienie nie powinno być potrzebne zwykłym aplikacjom."</string>
     <string name="save_password_message" msgid="767344687139195790">"Czy chcesz, aby zapamiętać to hasło w przeglądarce?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Nie teraz"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Zapamiętaj"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Uruchom Wi-Fi Direct. Spowoduje to wyłączenie klienta lub punktu dostępu Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Nie można uruchomić Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct włączone"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotknij, aby zmienić ustawienia"</string>
     <string name="accept" msgid="1645267259272829559">"Akceptuj"</string>
     <string name="decline" msgid="2112225451706137894">"Odrzuć"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Wysłano zaproszenie"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Do:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Wpisz wymagany kod PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Kod PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct włączone"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotknij, aby zmienić ustawienia"</string>
     <string name="select_character" msgid="3365550120617701745">"Wstaw znak"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Nieznana aplikacja"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Wysyłanie wiadomości SMS"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 800013d..74870ff 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Permite à aplicação mover tarefas para primeiro e segundo plano. As aplicações maliciosas podem impor-se em primeiro plano sem o controlo do utilizador."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"parar aplicações em execução"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que a aplicação remova tarefas e elimine as respetivas aplicações. As aplicações maliciosas podem perturbar o comportamento de outras aplicações."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"definir compatibilidade de ecrã"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permite que a aplicação controle o modo de compatibilidade de ecrã de outras aplicações. As aplicações maliciosas poderão afetar o comportamento de outras aplicações."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"ativar depuração da aplicação"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Permite que a aplicação ative a depuração para outra aplicação. As aplicações maliciosas podem utilizar isto para eliminar outras aplicações."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"alterar definições da IU"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite que a aplicação verifique se um pacote é instalável."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vincular a um verificador de pacotes"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite ao titular solicitar verificadores de pacotes. Nunca deverá ser necessário para aplicações normais."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"aceder a portas série"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite ao titular aceder a portas de série através da API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"aceder a fornecedores de conteúdos externamente"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite ao titular aceder a fornecedores de conteúdos a partir da shell. Nunca deverá ser necessário para aplicações normais."</string>
     <string name="save_password_message" msgid="767344687139195790">"Quer que o browser memorize esta palavra-passe?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Agora não"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Lembrar"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar o Wi-Fi Direct. Esta opção desativará o cliente/zona Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Não foi possível iniciar o Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"O Wi-Fi Direct está ativado"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toque para aceder às definições"</string>
     <string name="accept" msgid="1645267259272829559">"Aceitar"</string>
     <string name="decline" msgid="2112225451706137894">"Recusar"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Convite enviado"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduza o PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"O Wi-Fi Direct está ativado"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toque para aceder às definições"</string>
     <string name="select_character" msgid="3365550120617701745">"Introduzir carácter"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicação desconhecida"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"A enviar mensagens SMS"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index c907929..efe6833 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Permite que o aplicativo mova tarefas para o primeiro plano e para o plano de fundo. Aplicativos maliciosos podem forçar-se para a primeiro plano sem que você tenha controle sobre a ação."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"parar os aplicativos em execução"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite que um aplicativo remova tarefas e elimine seus aplicativos. Aplicativos maliciosos podem interferir no comportamento de outros aplicativos."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"definir a compatibilidade de tela"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permite que o aplicativo controle o modo de compatibilidade de tela de outros aplicativos. Aplicativos maliciosos podem interromper o comportamento de outros aplicativos."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"ativar depuração do aplicativo"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Permite que o aplicativo ative a depuração para outro aplicativo. Aplicativos maliciosos podem usar esse recurso para cancelar outros aplicativos."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"alterar as suas configurações de UI"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite que o aplicativo verifique se um pacote pode ser instalado."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vincular a um verificador de pacote"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que o titular solicite verificadores de pacote. Nunca deve ser necessário para aplicativos normais."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"acessar portas seriais"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite que o detentor tenha acesso a portas seriais usando a API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acessar fornec. de conteúdo externamente"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite que o proprietário tenha acesso a fornecedores de conteúdo a partir da camada. Nunca deve ser necessário para aplicativos normais."</string>
     <string name="save_password_message" msgid="767344687139195790">"Deseja que o navegador lembre desta senha?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Agora não"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Lembrar"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar o Wi-Fi Direct. Isso desativará o ponto de acesso/cliente Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Não foi possível iniciar o Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ativado"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocar para acessar configurações"</string>
     <string name="accept" msgid="1645267259272829559">"Aceitar"</string>
     <string name="decline" msgid="2112225451706137894">"Recusar"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Convite enviado"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Digite o PIN obrigatório:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ativado"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocar para acessar configurações"</string>
     <string name="select_character" msgid="3365550120617701745">"Inserir caractere"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicativo desconhecido"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensagens SMS"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index bc8d4fb..5b8efa6 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -278,6 +278,10 @@
     <skip />
     <!-- no translation found for permdesc_removeTasks (1394714352062635493) -->
     <skip />
+    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
+    <skip />
+    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
+    <skip />
     <!-- no translation found for permlab_setDebugApp (3022107198686584052) -->
     <skip />
     <!-- no translation found for permdesc_setDebugApp (4474512416299013256) -->
@@ -1147,6 +1151,14 @@
     <skip />
     <!-- no translation found for permdesc_bindPackageVerifier (3180741773233862126) -->
     <skip />
+    <!-- no translation found for permlab_serialPort (546083327654631076) -->
+    <skip />
+    <!-- no translation found for permdesc_serialPort (2991639985224598193) -->
+    <skip />
+    <!-- no translation found for permlab_accessContentProvidersExternally (5077774297943409285) -->
+    <skip />
+    <!-- no translation found for permdesc_accessContentProvidersExternally (4544346486697853685) -->
+    <skip />
     <string name="save_password_message" msgid="767344687139195790">"Vulais Vus ch\'il navigatur memorisescha quest pled-clav?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Betg ussa"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Memorisar"</string>
@@ -1416,6 +1428,10 @@
     <skip />
     <!-- no translation found for wifi_p2p_failed_message (3763669677935623084) -->
     <skip />
+    <!-- no translation found for wifi_p2p_enabled_notification_title (2068321881673734886) -->
+    <skip />
+    <!-- no translation found for wifi_p2p_enabled_notification_message (1638949953993894335) -->
+    <skip />
     <!-- no translation found for accept (1645267259272829559) -->
     <skip />
     <!-- no translation found for decline (2112225451706137894) -->
@@ -1432,10 +1448,6 @@
     <skip />
     <!-- no translation found for wifi_p2p_show_pin_message (8530563323880921094) -->
     <skip />
-    <!-- no translation found for wifi_p2p_enabled_notification_title (2068321881673734886) -->
-    <skip />
-    <!-- no translation found for wifi_p2p_enabled_notification_message (1638949953993894335) -->
-    <skip />
     <string name="select_character" msgid="3365550120617701745">"Inserir in caracter"</string>
     <!-- no translation found for sms_control_default_app_name (3058577482636640465) -->
     <skip />
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ecee96d..8ffa16e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Permite aplicaţiei să mute activităţile în prim-plan şi în fundal. Aplicaţiile rău intenţionate pot să apară forţat în prim-plan, fără ca dvs. să puteţi controla acest lucru."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"oprire aplicaţii care rulează"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Permite aplicaţiei să elimine sarcini şi să închidă aplicaţiile corespunzătoare acestora. Aplicaţiile rău intenţionate pot perturba comportamentul altor aplicaţii."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"setaţi compatibilitatea ecranului"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permite aplicaţiei să controleze modul de compatibilitate a ecranului pentru alte aplicaţii. Aplicaţiile rău intenţionate pot afecta comportamentul altor aplicaţii."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"activare depanare aplicaţie"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Permite aplicaţiei să activeze depanarea pentru o altă aplicaţie. Aplicaţiile rău intenţionate pot să utilizeze această permisiune pentru a închide alte aplicaţii."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"modificare setări UI"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite aplicaţiei să verifice dacă un pachet poate fi instalat."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"conectare la un verificator de pachete"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite proprietarului să efectueze solicitări pentru verificatori de pachete. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"acces la porturi seriale"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite posesorului accesul la porturile serial utilizând API-ul SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accesaţi furniz. de conţin. din exterior"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite deţinătorului să acceseze furnizorii de conţinut din interfaţă. Nu ar trebui să fie necesară pentru aplicaţiile normale."</string>
     <string name="save_password_message" msgid="767344687139195790">"Doriţi ca browserul să reţină această parolă?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Nu acum"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Reţineţi"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Porniţi Wi-Fi Direct. Acest lucru va dezactiva clientul/hotspotul Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct nu a putut porni."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct este activat"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Atingeţi pentru setări"</string>
     <string name="accept" msgid="1645267259272829559">"Acceptaţi"</string>
     <string name="decline" msgid="2112225451706137894">"Refuzaţi"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitaţia a fost trimisă."</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Către:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduceţi codul PIN necesar:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Cod PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct este activat"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Atingeţi pentru setări"</string>
     <string name="select_character" msgid="3365550120617701745">"Introduceţi caracterul"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Aplicaţie necunoscută"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Se trimit mesaje SMS"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 7b95291..a6644e5 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Приложение сможет перемещать задачи в режим активного или фонового выполнения. Вредоносные программы смогут переводить себя в активный режим без вашего ведома."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"остановка запущенных приложений"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Приложение сможет удалять задачи и собственные программы. Вредоносное ПО при этом сможет нарушать работу других приложений."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"Установка режима совместимости"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Приложение сможет управлять режимом совместимости экрана других приложений. Вредоносное ПО может привести к сбоям в работе других программ."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"включение отладки приложений"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Приложение сможет включать отладку для другой программы. Вредоносное ПО сможет таким образом останавливать работу других приложений."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"изменять настройки пользовательского интерфейса"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Приложение сможет проверять возможность установки пакетов."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"связываться с верификатором пакетов"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Приложение сможет запрашивать проверку пакетов. Это разрешение не используется обычными приложениями."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"доступ к последовательным портам"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Открыть владельцу доступ к последовательным портам с помощью SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"доступ к контенту без приложения"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Владелец сможет получить доступ к контенту без использования приложения. Это разрешение не применяется в обычных приложениях."</string>
     <string name="save_password_message" msgid="767344687139195790">"Вы хотите, чтобы браузер запомнил этот пароль?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Не сейчас"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Запомнить"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Начать соединение через Wi-Fi Direct. Модуль Wi-Fi будет отключен."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Не удалось запустить Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct включен"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Нажмите, чтобы открыть настройки"</string>
     <string name="accept" msgid="1645267259272829559">"Принять"</string>
     <string name="decline" msgid="2112225451706137894">"Отклонить"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Приглашение отправлено"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Кому:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введите PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct включен"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Нажмите, чтобы открыть настройки"</string>
     <string name="select_character" msgid="3365550120617701745">"Введите символ"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Неизвестное приложение"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Отправка SMS-сообщений"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index aafaa9e..df25ac7 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Umožňuje aplikácii presúvať úlohy do popredia a pozadia. Škodlivé aplikácie sa môžu pretlačiť do popredia bez vášho vedomia."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"zastaviť spustené aplikácie"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Umožňuje aplikácii odstrániť úlohy a ukončiť ich aplikácie. Škodlivé aplikácie môžu narušiť správanie iných aplikácií."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nastaviť kompatibilitu obrazovky"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Umožňuje aplikácii ovládať režim kompatibility obrazovky v ostatných aplikáciách. Škodlivé aplikácie môžu narušiť správanie ostatných aplikácií."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"povoliť ladenie aplikácií"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Umožňuje aplikácii zapnúť ladenie inej aplikácie. Škodlivé aplikácie môžu pomocou tohto nastavenia ukončiť iné aplikácie."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"zmeny vašich nastavení používateľského rozhrania"</string>
@@ -760,6 +762,12 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Umožňuje aplikácii overiť, či je možné balík nainštalovať."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"naviazať na overovateľa balíka"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Umožňuje držiteľovi podávať žiadosti o overenie balíkov. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"prístup k sériovým portom"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Držiteľa oprávňuje na prístup k sériovým portom pomocou rozhrania API SerialManager."</string>
+    <!-- no translation found for permlab_accessContentProvidersExternally (5077774297943409285) -->
+    <skip />
+    <!-- no translation found for permdesc_accessContentProvidersExternally (4544346486697853685) -->
+    <skip />
     <string name="save_password_message" msgid="767344687139195790">"Chcete, aby si prehliadač zapamätal toto heslo?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Teraz nie"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Zapamätať"</string>
@@ -972,6 +980,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Priame pripojenie Wi-Fi"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Spustiť priame pripojenie siete Wi-Fi. Táto možnosť vypne sieť Wi-Fi v režime klient alebo hotspot."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Priame pripojenie siete Wi-Fi sa nepodarilo spustiť"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Priame pripojenie siete Wi-Fi je zapnuté"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Nastavenia otvoríte dotykom"</string>
     <string name="accept" msgid="1645267259272829559">"Prijať"</string>
     <string name="decline" msgid="2112225451706137894">"Odmietnuť"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pozvánka bola odoslaná"</string>
@@ -980,8 +990,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Komu:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadajte požadovaný kód PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Priame pripojenie siete Wi-Fi je zapnuté"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Nastavenia otvoríte dotykom"</string>
     <string name="select_character" msgid="3365550120617701745">"Vkladanie znakov"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Neznáma aplikácia"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Odosielanie správ SMS"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index aa98c80..159d504 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Programu omogoča premikanje opravil v ospredje in ozadje. Zlonamerni programi se lahko brez vašega nadzora vsilijo v ospredje."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"ustavitev programov, ki se izvajajo"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Programu omogoča odstranjevanje opravil in zapiranje njihovih programov. Zlonamerni programi lahko motijo delovanje drugih programov."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"nastavitev združljivosti zaslona"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Programu omogoča nadzor združljivostnega načina zaslona drugih programov. Zlonamerni programi lahko prekinejo delovanje drugih programov."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"omogočanje iskanja in odpravljanja napak v programu"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Programu omogoča vklop funkcije za odpravljanje napak za drug program. Zlonamerni programi lahko to uporabijo za zapiranje drugih programov."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"spreminjanje nastavitev UV"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Programu omogoča, da preveri, ali je paket mogoče namestiti."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"poveži s preverjanjem paketov"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Imetniku omogoča zahtevanje preverjanja paketov. Tega nikoli ni treba uporabiti za navadne programe."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"dostop do serijskih vrat"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Imetniku omogoča, da z API-jem za SerialManager dostopa do serijskih vrat."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"zunanji dostop do ponudnikov vsebine"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Omogoča imetniku, da dostopa do ponudnikov vsebine iz lupine. Nikoli naj ne bi bilo potrebno za običajne programe"</string>
     <string name="save_password_message" msgid="767344687139195790">"Ali želite, da si brskalnik zapomni to geslo?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Ne zdaj"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Zapomni si"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Zaženite Wi-Fi Direct. S tem boste izklopili odjemalca/dostopno točko Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct ni bilo mogoče zagnati."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct je vklopljen"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotaknite se za nastavitve"</string>
     <string name="accept" msgid="1645267259272829559">"Sprejmi"</string>
     <string name="decline" msgid="2112225451706137894">"Zavrni"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Povabilo je poslano"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Za:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Vnesite zahtevano kodo PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct je vklopljen"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotaknite se za nastavitve"</string>
     <string name="select_character" msgid="3365550120617701745">"Vstavljanje znaka"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Neznan program"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Pošiljanje sporočil SMS"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7ba8503..199d318 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Дозвољава апликацији да премешта задатке у први план и у позадину. Злонамерне апликације могу на тај начин да принудно пређу у први план без ваше контроле."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"заустављање покренутих апликација"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Дозвољава апликацији да уклања задатке и уништава њихове апликације. Злонамерне апликације могу да поремете понашање других апликација."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"подешавање компатибилности екрана"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Дозвољава апликацији да контролише режим компатибилности екрана других апликација. Злонамерне апликације могу да угрозе понашање других апликација."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"омогућавање отклањања грешака у апликацији"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Дозвољава апликацији да укључи уклањање грешака за другу апликацију. Злонамерне апликације могу то да искористе за онемогућавање других апликација."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"промена подешавања корисничког интерфејса"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Дозвољава апликацији да верификује да ли је пакет могуће инсталирати."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"обавезивање на верификатор пакета"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Омогућава да власник упућује захтеве верификаторима пакета. Уобичајене апликације никада не би требало да је користе."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"приступ серијским портовима"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Омогућава власнику да приступи серијским портовима помоћу SerialManager API-ја."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"приступ добављачима садржаја споља"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Дозвољава власнику да приступа добављачима садржаја из интерфејса. Никада не би требало да буде потребно за обичне апликације."</string>
     <string name="save_password_message" msgid="767344687139195790">"Желите ли да прегледач запамти ову лозинку?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Не сада"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Запамти"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Покрените Wi-Fi Direct. Тиме ћете искључити клијента/хотспот за Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Није могуће покренути Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct је укључен"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Додирните за подешавања"</string>
     <string name="accept" msgid="1645267259272829559">"Прихвати"</string>
     <string name="decline" msgid="2112225451706137894">"Одбиј"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Позивница је послата"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Коме:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Унесите потребни PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct је укључен"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Додирните за подешавања"</string>
     <string name="select_character" msgid="3365550120617701745">"Уметање знака"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Непозната апликација"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Слање SMS порука"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4d3cea5..be397b0 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Tillåter att appen flyttar uppgifter till förgrunden eller bakgrunden. Skadliga appar kan tvinga sig till förgrunden utan att du kan styra det."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"avsluta appar som körs"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Tillåter att appen tar bort uppgifter och avslutar appar. Skadliga appar kan störa funktionen i andra appar."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ange skärmkompatibilitet"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Tillåter att appen styr skärmkompatibilitetsläget i andra appar. Skadliga appar kan störa andra appars funktion."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"aktivera felsökning av appar"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Tillåter att appen aktiverar felsökning för en annan app. Skadliga appar kan använda detta för att avsluta andra appar."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"ändra dina gränssnittsinställningar"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Tillåter att appen kontrollerar om ett paket går att installera."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"binda till en paketverifierare"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Tillåter att innehavaren skickar förfrågningar till paketverifierare. Det ska inte behövas för vanliga appar."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"åtkomst till serieportar"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Innebär att innehavaren får åtkomst till serieportar med programmeringsgränssnittet för SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"komma åt innehållsleverantörer externt"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Innehavaren kan få åtkomst till innehållsleverantörer från skalet. Ska inte behövas för vanliga appar."</string>
     <string name="save_password_message" msgid="767344687139195790">"Vill du att webbläsaren ska komma ihåg lösenordet?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Inte nu"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Kom ihåg"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi direkt"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Starta direkt Wi-Fi-användning. Detta inaktiverar Wi-Fi-användning med klient/trådlös surfzon."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Det gick inte att starta Wi-Fi direkt."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct är aktiverat"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tryck om du vill visa inställningar"</string>
     <string name="accept" msgid="1645267259272829559">"Godkänn"</string>
     <string name="decline" msgid="2112225451706137894">"Avvisa"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Inbjudan har skickats"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Till:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct är aktiverat"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tryck om du vill visa inställningar"</string>
     <string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Okänd app"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index a9a8679..4cb3a3a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Inaruhusu programu kusongesha kazi kwenye mandhari-mbele na mandhari-nyuma. Programu hasidi zinaweza kujilazimisha mbele bila udhibiti wako."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"Komesha programu zinazoendeshwa"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Huruhusu programu kuondoa majukumu na kuua programu zao. Programu hasidi zinaweza kutatiza tabia ya programu zingine."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"weka utangamano wa skrini"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Huruhusu programu kudhibiti hali ya utangamano wa skrini ya programu zingine. Programu hasidi zinaweza kuvunja mwenendo wa programu zingine."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"wezesha utatuaji wa programu"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Huruhusu programu kuwasha kueua cha programu nyingine. Programu hasidi huenda zikatumia hii ili kuua programu nyingine."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"badilisha mipangilio yako ya onyesho"</string>
@@ -335,9 +337,9 @@
     <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"Inaruhusu programu kusoma maelezo mafupi ya kibinafsi yaliyohifadhiwa kwenye kifaa chako, kama vile jina lako na taarifa ya kuwasiliana. Hii ina maanisha programu inaweza kukutambua na kutuma taarifa yako fupi ya kibinafsi kwa wengine."</string>
     <string name="permlab_writeProfile" msgid="4679878325177177400">"andika kwenye data ya maelezo yako mafupi"</string>
     <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"Inaruhusu programu kubadilisha au kuongeza taarifa fupi ya kibinafsi iliyohifadhiwa katika kifaa chako, kama vile jina lako na maelezo ya kuwasiliana. Hii ina maanisha kwamba programu zingine zinaweza kukutambua na kutuma taarifa yako fupi kwa wengine."</string>
-    <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"soma mkondo wako wa kijamii"</string>
+    <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"soma mipasho yako wa kijamii"</string>
     <string name="permdesc_readSocialStream" product="default" msgid="3419050808547335320">"Inaruhusu programu kufikia na kulandanisha sasisho za kijamii kutoka kwako na marafiki zako. Programu hasidi zinaweza kutumia hii kusoma mawasiliano ya kibinafsi kati yako na marafiki zako kwenye mitandao ya kijamii."</string>
-    <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"andika kwa mkondo wako wa kijamii"</string>
+    <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"andika kwa mipasho yako wa kijamii"</string>
     <string name="permdesc_writeSocialStream" product="default" msgid="3496277176955721451">"Inaruhusu programu kuonyesha sasisho za kijamii kutoka kwa marafiki zako. Programu hasidi zinaweza kutumia hii kujifanya kuwa rafiki na kukuhadaa kuonyesha nenosiri au taarifa zingine za siri."</string>
     <string name="permlab_readCalendar" msgid="5972727560257612398">"soma matukio ya kalenda pamoja na maelezo ya siri"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="2338414551004122687">"Inaruhusu programu kusoma matukio yote ya kalenda yaliyohifadhiwa kwa kompyuta yako kibao pamoja na za marafiki au wafanyakazi wenzako. Programu hasidi zinaweza kutoa maelezo ya kibinafsi kutoka kwa kalenda hizi bila wamiliki kujua."</string>
@@ -637,7 +639,7 @@
     <string name="relationTypeParent" msgid="4755635567562925226">"Mzazi"</string>
     <string name="relationTypePartner" msgid="7266490285120262781">"Mshirika"</string>
     <string name="relationTypeReferredBy" msgid="101573059844135524">"Alipendekezwa na"</string>
-    <string name="relationTypeRelative" msgid="1799819930085610271">"Ndugu"</string>
+    <string name="relationTypeRelative" msgid="1799819930085610271">"Jamaa"</string>
     <string name="relationTypeSister" msgid="1735983554479076481">"Dada"</string>
     <string name="relationTypeSpouse" msgid="394136939428698117">"Mwenzi wa Ndoa"</string>
     <string name="sipAddressTypeCustom" msgid="2473580593111590945">"Maalum"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Inaruhusu programu kuthibitisha kuwa furushi linaweza kusakinishwa."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"Funga kwa kithibitishaji cha furushi"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Inaruhusu mmiliki kutuma maombi ya vibainishi furushi. Kamwe hazitahitajika kwa programu za kawaida."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"kituo tambulishi cha ufikivu"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Inaruhusu mmiliki kufikia vituo tambulishi kwa kutumia KisimamiziTambulishi cha API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"fikia watoa huduma nje"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Inaruhusu mmiliki kufikia watoa huduma  kutoka kwa onyesho. Haifai kuhitajika kamwe kwa programu za kawaida."</string>
     <string name="save_password_message" msgid="767344687139195790">"Unataka kuvinjari ili ukumbuke nenosiri hili?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Si Sasa"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Kumbuka"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Mtandao hewa Moja kwa moja"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Anzisha Wi-Fi Moja kwa Moja. Hii itazima mteja/mtandao-hewa wa Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Haikuweza kuanzisha Wi-Fi Moja kwa Moja."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ya Moja kwa Moja imewashwa"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Gusa kwa ajili ya mipangilio"</string>
     <string name="accept" msgid="1645267259272829559">"Kubali"</string>
     <string name="decline" msgid="2112225451706137894">"Kataa"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Mwaliko umetumwa"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kwa:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Charaza PIN inayohitajika:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ya Moja kwa Moja imewashwa"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Gusa kwa ajili ya mipangilio"</string>
     <string name="select_character" msgid="3365550120617701745">"Ingiza kibambo"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Programu isiyojulikana"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Inatuma ujumbe wa SMS"</string>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 431a502..13acdd6 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -68,5 +68,19 @@
 
     <!-- Minimum width for an action button in the menu area of an action bar -->
     <dimen name="action_button_min_width">64dip</dimen>
+
+    <!-- The platform's desired fixed width for a dialog along the major axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_width_major">50%</item>
+    <!-- The platform's desired fixed width for a dialog along the minor axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_width_minor">70%</item>
+    <!-- The platform's desired fixed height for a dialog along the major axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_height_major">60%</item>
+    <!-- The platform's desired fixed height for a dialog along the minor axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_height_minor">90%</item>
+
 </resources>
 
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 6a8431c..41c56f4 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"อนุญาตให้แอปพลิเคชันย้ายงานไปยังส่วนหน้าและพื้นหลัง แอปพลิเคชันที่เป็นอันตรายอาจบังคับตัวเองให้ไปที่ส่วนหน้าโดยไม่มีการควบคุมจากคุณ"</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"หยุดแอปพลิเคชันที่ทำงานอยู่"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"อนุญาตให้แอปพลิเคชันลบงานออกและยุติแอปพลิเคชันต่างๆ ของงานนั้น แอปพลิเคชันที่เป็นอันตรายอาจทำให้แอปพลิเคชันอื่นๆ ทำงานได้ไม่ถูกต้อง"</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ตั้งค่าความเข้ากันได้ของหน้าจอ"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"อนุญาตให้แอปพลิเคชันควบคุมโหมดความเข้ากันได้ของหน้าจอของแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจทำให้แอปพลิเคชันอื่นทำงานผิดพลาด"</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"เปิดใช้งานการแก้ไขบกพร่องของแอปพลิเคชัน"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"อนุญาตให้แอปพลิเคชันเปิดการแก้ไขข้อบกพร่องสำหรับแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ยุติการทำงานของแอปพลิเคชันอื่นๆ ได้"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"เปลี่ยนการตั้งค่า UI ของคุณ"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"อนุญาตให้แอปพลิเคชันยืนยันว่าแพคเกจสามารถติดตั้งได้หรือไม่"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"เชื่อมโยงกับการยืนยันแพคเกจ"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"อนุญาตให้ผู้ใช้ส่งคำขอให้มีการยืนยันแพคเกจ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"เข้าถึงพอร์ตอนุกรม"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"อนุญาตให้ผู้ถือสามารถเข้าถึงพอร์ตอนุกรมโดยใช้ SerialManager API"</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"เข้าถึงผู้ให้บริการเนื้อหาจากภายนอก"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"ช่วยให้เจ้าของสามารถเข้าถึงผู้ให้บริการเนื้อหาจากหน้าจอรับคำสั่งเชลล์ แอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string>
     <string name="save_password_message" msgid="767344687139195790">"คุณต้องการให้เบราว์เซอร์จำรหัสผ่านนี้หรือไม่"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"ยังไม่ใช้งานขณะนี้"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"จำไว้"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"เริ่มการทำงาน WiFi Direct ซึ่งจะเป็นการปิดการทำงาน WiFi ไคลเอ็นต์/ฮอตสปอต"</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"ไม่สามารถเริ่ม WiFi Direct ได้"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"เปิด Wi-Fi Direct อยู่"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"แตะเพื่อตั้งค่า"</string>
     <string name="accept" msgid="1645267259272829559">"ยอมรับ"</string>
     <string name="decline" msgid="2112225451706137894">"ปฏิเสธ"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"ส่งข้อความเชิญแล้ว"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"ถึง:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"พิมพ์ PIN ที่ต้องการ:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"เปิด Wi-Fi Direct อยู่"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"แตะเพื่อตั้งค่า"</string>
     <string name="select_character" msgid="3365550120617701745">"ใส่อักขระ"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"แอปพลิเคชันที่ไม่รู้จัก"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"กำลังส่งข้อความ SMS"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 04635c8..9548fe9 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Pinapayagan ang app na ilipat ang mga gawain sa foreground at background. Maaaring puwersahin ng nakakahamak na apps ang mga sarili nito sa harapan nang wala ang iyong pagkontrol."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"ihinto ang pagpapatakbo ng apps"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Pinapayagan ang app na mag-alis ng mga gawain at i-off ang apps nito. Maaaring maantala ng nakakahamak na apps ang pagkilos ng iba pang apps."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"itakda ang pagkakatugma ng screen"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Pinapayagan ang app na kontrolin ang mode ng pagkakatugma ng screen ng iba pang mga application. Maaaring sirain ng mga nakakahamak na application ang pag-uugali ng iba pang mga application."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"paganahin ang pag-debug ng app"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Pinapayagan ang app na i-on ang pag-debug para sa isa pang app. Maaari itong gamitin ng nakakahamak na apps upang i-off ang iba pang apps."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"baguhin ang iyong mga setting ng UI"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Pinapayagan ang app na i-verify kung ang isang package ay nai-install."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"sumailalim sa taga-verify ng package"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Pinapayagan ang may-ari na gumawa ng mga kahilingan ng mga taga-verify ng package. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"mag-access sa mga serial port"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Binibigyang-daan ang may-ari na mag-access ng mga serial port gamit ang SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"panlabas na mag-access ng mga provider ng nilalaman"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Binibigyang-daan ang may-ari na ma-access ang mga provider ng nilalaman mula sa shell. Hindi kailanman dapat kailanganin para sa karaniwang apps."</string>
     <string name="save_password_message" msgid="767344687139195790">"Gusto mo bang tandaan ng browser ang password na ito?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Hindi ngayon"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Tandaan"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Simulan ang Wi-Fi Direct. I-o-off nito ang client/hotspot ng Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Hindi masimulan ang Wi-Fi Direct"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Ang Wi-Fi Direct ay naka-on"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Pindutin para sa mga setting"</string>
     <string name="accept" msgid="1645267259272829559">"Tanggapin"</string>
     <string name="decline" msgid="2112225451706137894">"Tanggihan"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Naipadala ang imbitasyon"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kay:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"I-type ang kinakailangang PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Ang Wi-Fi Direct ay naka-on"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Pindutin para sa mga setting"</string>
     <string name="select_character" msgid="3365550120617701745">"Magpasok ng character"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Hindi kilalang app"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Nagpapadala ng mga SMS na mensahe"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bfe9c0a..6da7fcd 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Uygulamaya, görevleri ön plana ve arka plana taşıma izni verir. Kötü amaçlı uygulamalar kendilerini sizin denetiminiz dışında ön plana taşıyabilir."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"çalışan uygulamaları durdur"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Uygulamaya, görevleri kaldırma ve bunlara ait uygulamaları kapatma izni verir. Kötü amaçlı uygulamalar diğer uygulamaların çalışmasını bozabilir."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ekran uyumluluğunu ayarla"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Uygulamaya diğer uygulamaların ekran uyumluluk modunu denetleme izni verir. Kötü amaçlı uygulamalar diğer uygulamaların çalışma şeklini bozabilir."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"uygulama hata ayıklamayı etkinleştir"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Uygulamaya, başka bir uygulama için hata ayıklamayı açma izni verir. Kötü amaçlı uygulamalar diğer uygulamaları kaldırmak için bunu kullanabilir."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"kullanıcı arayüzü ayarlarınızı değiştirin"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Uygulamaya, bir paketin yüklenebilir olduğunu doğrulama izni verir."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"paket doğrulayıcıya bağlan"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Cihazın sahibine, paket doğrulayıcıları için istek yapma izni verir. Normal uygulamalar için gerekli olmaz."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"seri bağlantı noktalarına eriş"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"İzin sahibinin, SerialManager API\'sını kullanarak seri bağlantı noktalarına erişmesine olanak sağlar."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"içerik sağlayıcılara harici olarak eriş"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"İzin verilen uygulamaya, Uygulama İş Parçacığının dışından içerik sağlayıcılara erişebilme olanağı verir."</string>
     <string name="save_password_message" msgid="767344687139195790">"Tarayıcının bu şifreyi anımsamasını istiyor musunuz?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Şimdi değil"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Anımsa"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Kablosuz Doğrudan Bağlantı"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Kablosuz Doğrudan Bağlantıyı başlat. Bu işlem, Kablosuz istemci/hotspot kullanımını kapatacak."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kablosuz Doğrudan bağlantı başlatılamadı."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Kablosuz Doğrudan özelliği açık"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Ayarlar için dokunun"</string>
     <string name="accept" msgid="1645267259272829559">"Kabul et"</string>
     <string name="decline" msgid="2112225451706137894">"Reddet"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Davetiye gönderildi"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Alıcı:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Gerekli PIN\'i yazın:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Kablosuz Doğrudan özelliği açık"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Ayarlar için dokunun"</string>
     <string name="select_character" msgid="3365550120617701745">"Karakter ekle"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Bilinmeyen uygulama"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS mesajları gönderiliyor"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 6708893..3fd6264 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Дозволяє програмі переміщувати завдання в активні чи фонові вікна. Шкідливі програми можуть примусово ставати активними без вашого відома."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"зупиняти запущені програми"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Дозволяє програмі видаляти завдання та примусово припиняти роботу відповідних програм. Шкідливі програми можуть переривати роботу інших програм."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"установити сумісність екрана"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Дозволяє програмі контролювати режим сумісності екрана інших програм. Шкідливі програми можуть переривати роботу інших програм."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"вмикати налагодження програми"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Дозволяє програмі вмикати налагодження для іншої програми. Шкідливі програми можуть використовувати це для примусового припинення роботи інших програм."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"змін. налашт. інтерф. кор."</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Дозволяє програмі перевіряти можливість встановлення пакета."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"прив’язуватися до програми перевірки пакетів"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Дозволяє власникові робити запити на програми перевірки пакетів. Ніколи не застосовується для звичайних програм."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"отримувати доступ до послідовних портів"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Дозволяє власнику отримувати доступ до послідовних портів за допомогою API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"отримув. ззовні доступ до постач. вмісту"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Дозволяє власнику отримувати ззовні доступ до постачальників вмісту. Ніколи не застосовується для звичайних програм."</string>
     <string name="save_password_message" msgid="767344687139195790">"Хочете, щоб переглядач запам\'ятав цей пароль?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Не зараз"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Запам\'ятати"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Запустити Wi-Fi Direct. Це вимкне з’єднання Wi-Fi клієнт/точка доступу."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Не вдалося запустити Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct увімкнено"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Торкніться, щоб побачити налаштування"</string>
     <string name="accept" msgid="1645267259272829559">"Прийняти"</string>
     <string name="decline" msgid="2112225451706137894">"Відхилити"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Запрошення надіслано"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Кому:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введіть потрібний PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct увімкнено"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Торкніться, щоб побачити налаштування"</string>
     <string name="select_character" msgid="3365550120617701745">"Вставл-ня символу"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Невідома програма"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Надсил. SMS повідомлень"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 6da402ce..8503dc5 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Cho phép ứng dụng di chuyển công việc sang nền trước và nền sau. Ứng dụng độc hại có thể tự hiển thị ở nền trước mà không chịu sự kiểm soát của bạn."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"dừng các ứng dụng đang chạy"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Cho phép ứng dụng xóa công việc và loại bỏ các ứng dụng của chúng. Ứng dụng độc hại có thể làm gián đoạn hoạt động của các ứng dụng khác."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"đặt độ tương thích màn hình"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Cho phép ứng dụng kiểm soát chế độ tương thích màn hình của ứng dụng khác. Các ứng dụng độc hại có thể phá vỡ hoạt động của các ứng dụng khác."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"bật gỡ lỗi ứng dụng"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Cho phép ứng dụng bật gỡ lỗi cho một ứng dụng khác. Ứng dụng độc hại có thể sử dụng quyền này để loại bỏ những ứng dụng khác."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"thay đổi cài đặt giao diện người dùng của bạn"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Cho phép ứng dụng xác minh gói có thể cài đặt."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"liên kết với trình xác minh gói"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Cho phép chủ sở hữu yêu cầu trình xác minh gói. Không cần thiết cho các ứng dụng thông thường."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"truy cập cổng nối tiếp"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Cho phép chủ sở hữu truy cập cổng nối tiếp sử dụng API SerialManager."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"truy cập vào nhà cung cấp nội dung từ bên ngoài"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Cho phép chủ sở hữu truy cập vào nhà cung cấp nội dung từ bên ngoài. Không bao giờ cần cho ứng dụng thông thường."</string>
     <string name="save_password_message" msgid="767344687139195790">"Bạn có muốn trình duyệt nhớ mật khẩu này không?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Không phải bây giờ"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Nhớ"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Khởi động Wi-Fi Direct. Việc này sẽ tắt hoạt động của ứng dụng khách/điểm phát sóng Wi-Fi."</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Không thể khởi động Wi-Fi Direct."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct được bật"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Chạm để cài đặt"</string>
     <string name="accept" msgid="1645267259272829559">"Đồng ý"</string>
     <string name="decline" msgid="2112225451706137894">"Từ chối"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Đã gửi thư mời"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Người nhận:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Nhập PIN bắt buộc:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Mã PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct được bật"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Chạm để cài đặt"</string>
     <string name="select_character" msgid="3365550120617701745">"Chèn ký tự"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"Ứng dụng không xác định"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Đang gửi tin nhắn SMS"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6c40202..bc74518 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"允许应用程序将任务移动到前台和后台。恶意应用程序可能会不受您的控制,强行让自己处于前台。"</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"停止正在运行的应用程序"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"允许该应用程序删除任务并终止这些任务的应用程序。恶意应用程序可以籍此影响其他应用程序的行为。"</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"设置屏幕兼容性"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"允许该应用程序控制其他应用程序的屏幕兼容模式。恶意应用程序可以籍此影响其他应用程序的行为。"</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"启用应用程序调试"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"允许该应用程序对其他应用程序启用调试。恶意应用程序可以籍此终止其他的应用程序。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"更改用户界面设置"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"允许应用程序验证程序包是否可以安装。"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"绑定到软件包验证程序"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允许用户请求使用程序包验证程序。普通应用程序绝不需要此权限。"</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"访问串行端口"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"允许持有人使用 SerialManager API 访问串行端口。"</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"从外部访问内容提供程序"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"允许持有者通过界面访问内容提供程序。普通应用程序绝不需要此权限。"</string>
     <string name="save_password_message" msgid="767344687139195790">"是否希望浏览器记住此密码?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"暂不保存"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"记住"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"启动 Wi-Fi Direct。此操作将会关闭 Wi-Fi 客户端/热点。"</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"无法启动 Wi-Fi Direct。"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"已启用 Wi-Fi Direct"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"通过触摸进行设置"</string>
     <string name="accept" msgid="1645267259272829559">"接受"</string>
     <string name="decline" msgid="2112225451706137894">"拒绝"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"邀请已发送"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"收件人:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"键入所需的 PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"已启用 Wi-Fi Direct"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"通过触摸进行设置"</string>
     <string name="select_character" msgid="3365550120617701745">"插入字符"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"未知应用程序"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"正在发送短信"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 131c2f7..5c89004 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"允許應用程式將工作移至前景或背景。請注意,惡意應用程式可能利用此功能自行移動至前景。"</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"停止執行中的應用程式"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"允許應用程式移除工作並終止執行工作的應用程式。請注意,惡意應用程式可能利用此功能干擾其他應用程式的行為。"</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"設定螢幕相容性"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"允許應用程式控制其他應用程式的螢幕相容性模式。惡意應用程式可能藉此破壞其他應用程式的正常運作。"</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"啟用應用程式偵錯"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"允許應用程式為其他程式開啟偵錯功能。提醒您,惡意應用程式可能會利用這個功能終止其他應用程式。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"變更介面設定"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"允許應用程式驗證是否可安裝特定套件。"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"繫結至套件驗證程序"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允許應用程式要求驗證套件 (一般應用程式不需使用)。"</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"存取序列埠"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"允許應用程式使用 SerialManager API 存取序列埠。"</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"從外部存取內容供應端"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"允許應用程式透過文字指令介面存取內容供應端 (一般應用程式不需這項權限)。"</string>
     <string name="save_password_message" msgid="767344687139195790">"是否記住此密碼?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"現在不要"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"記住"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"啟動 WiFi Direct 作業,這會關閉 WiFi 用戶端/無線基地台作業。"</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"無法啟動 WiFi Direct。"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct 已開啟"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"輕觸即可設定"</string>
     <string name="accept" msgid="1645267259272829559">"接受"</string>
     <string name="decline" msgid="2112225451706137894">"拒絕"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"邀請函已傳送"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"收件者:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"請輸入必要的 PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct 已開啟"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"輕觸即可設定"</string>
     <string name="select_character" msgid="3365550120617701745">"插入字元"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"不明的應用程式"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"傳送 SMS 簡訊"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 56af9aa..282fb91 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -215,6 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Ivumela insiza ukuthi ihambise izenzo ziye ngaphambili kanye nasemumva. Izinsiza ezinobungozi zingaziphoqelela ukuth iziye phambili ngaphandle kokulawula kwakho."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"misa izinsiza ezisebenzayo"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Vumela ukuthi insiza isuse okumele kwenziwe ibulale nezinsiza zakho. Izinsiza eziwubungozi zingaphazamisa ukusebenza kwezinye izinsiza."</string>
+    <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"setha ukuhambelana kwesikrini"</string>
+    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Ivumela uhlelo lokusebenza ukulawula imodi yokuhambelana kwesikrini kwezinye izinhlelo zokusebenza. Izinhlelo zokusebenza ezinonya zingase zephule ukuziphatha kwezinye izinhlelo zokusebenza."</string>
     <string name="permlab_setDebugApp" msgid="3022107198686584052">"vumela insiza ilungise inkinga"</string>
     <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Ivumela insiza ukuthi ivule uhlelo lokulungisa lwenye insiza. Izinsiza ezinobungozi zingasebenzisa lokhu ukubulala ezinye izinsiza."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"shintsha izilungiselelo zakho ze-UI"</string>
@@ -760,6 +762,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Ivumela ukuthi isisetshenziswa siqinisekise ukuthi ngabe iphakheji iyafakeka."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bopha okokuqinisekisa iphakheji"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ivumela umnikazi ukuthi enze izicelo zezinsiza eziqinisekisa iphakheji. Akumele kudingeke ekusetshenzisweni okujwayelekile."</string>
+    <string name="permlab_serialPort" msgid="546083327654631076">"finyelela kuma- serial port"</string>
+    <string name="permdesc_serialPort" msgid="2991639985224598193">"Ivumela umnikai ukuthi athole inombolo ye-serial ukue angene kwiindawo ze-serial esebenzisa i-SerialManager API."</string>
+    <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"finyelela abahlinzeki bokuqukethwe ngaphandle"</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Ivumela umphathi ukufinyelela abahlinzeki bokuqukethwe kusuka kumasistimu asebenzayo. Akusoze kwadingeka kwizinhlelo zokusebenza ezivamile."</string>
     <string name="save_password_message" msgid="767344687139195790">"Ingabe ufuna ukuba isiphequluli sikhumbule lephasiwedi?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Hha yi manje"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Khumbula"</string>
@@ -972,6 +978,8 @@
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"I-WiFi Eqondile"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Qala ukusebenza kwe-WiFi Okuqondile. Lokhu kuzocima ikhasimende le-WiFi/Ukusebenza okwe-hotspot"</string>
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Yehlulekile ukuqala i-Wi-Fi Ngqo"</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"I-Wi-Fi Direct ivulekile"</string>
+    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Thinta ukuze uthole izilungiselelo"</string>
     <string name="accept" msgid="1645267259272829559">"Yamukela"</string>
     <string name="decline" msgid="2112225451706137894">"Nqaba"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Isimemo sithunyelwe"</string>
@@ -980,8 +988,6 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Ku:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Faka i-PIN edingekayo:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"I-Wi-Fi Direct ivulekile"</string>
-    <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Thinta ukuze uthole izilungiselelo"</string>
     <string name="select_character" msgid="3365550120617701745">"Faka uhlamvu"</string>
     <string name="sms_control_default_app_name" msgid="3058577482636640465">"insiza engaziwa"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Ithumela imiyalezo ye-SMS"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 16b7ff3..9375730 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1556,6 +1556,24 @@
              an absolute dimension or a fraction of the screen size in that
              dimension. -->
         <attr name="windowMinWidthMinor" format="dimension|fraction" />
+
+        <!-- A fixed width for the window along the major axis of the screen,
+             that is, when in landscape. Can be either an absolute dimension
+             or a fraction of the screen size in that dimension. -->
+        <attr name="windowFixedWidthMajor" format="dimension|fraction" />
+        <!-- A fixed height for the window along the minor axis of the screen,
+             that is, when in landscape. Can be either an absolute dimension
+             or a fraction of the screen size in that dimension. -->
+        <attr name="windowFixedHeightMinor" format="dimension|fraction" />
+
+        <!-- A fixed width for the window along the minor axis of the screen,
+             that is, when in portrait. Can be either an absolute dimension
+             or a fraction of the screen size in that dimension. -->
+        <attr name="windowFixedWidthMinor" format="dimension|fraction" />
+        <!-- A fixed height for the window along the major axis of the screen,
+             that is, when in portrait. Can be either an absolute dimension
+             or a fraction of the screen size in that dimension. -->
+        <attr name="windowFixedHeightMajor" format="dimension|fraction" />
     </declare-styleable>
 
     <!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -2981,7 +2999,10 @@
         <attr name="textColorLink" />
         <!-- Makes the cursor visible (the default) or invisible. -->
         <attr name="cursorVisible" format="boolean" />
-        <!-- Makes the TextView be at most this many lines tall. -->
+        <!-- Makes the TextView be at most this many lines tall.
+
+        When used on an editable text, the <code>inputType</code> attribute's value must be
+        combined with the <code>textMultiLine</code> flag for the maxLines attribute to apply. -->
         <attr name="maxLines" format="integer" min="0" />
         <!-- Makes the TextView be at most this many pixels tall. -->
         <attr name="maxHeight" />
@@ -2991,7 +3012,10 @@
              You could get the same effect by specifying this number in the
              layout parameters. -->
         <attr name="height" format="dimension" />
-        <!-- Makes the TextView be at least this many lines tall. -->
+        <!-- Makes the TextView be at least this many lines tall.
+
+        When used on an editable text, the <code>inputType</code> attribute's value must be
+        combined with the <code>textMultiLine</code> flag for the minLines attribute to apply. -->
         <attr name="minLines" format="integer" min="0" />
         <!-- Makes the TextView be at least this many pixels tall. -->
         <attr name="minHeight" />
@@ -3022,21 +3046,20 @@
         <!-- Constrains the text to a single horizontally scrolling line
              instead of letting it wrap onto multiple lines, and advances
              focus instead of inserting a newline when you press the
-             enter key.  Note: for editable text views, it is better
-             to control this using the textMultiLine flag in the inputType
-             attribute.  (If both singleLine and inputType are supplied,
-             the inputType flags will override the value of singleLine.)
-             {@deprecated This attribute is deprecated and is replaced by the textMultiLine flag
-             in the inputType attribute.  Use caution when altering existing layouts, as the
-             default value of singeLine is false (multi-line mode), but if you specify any
-             value for inputType, the default is single-line mode.  (If both singleLine and
-             inputType attributes are found,  the inputType flags will override the value of
-             singleLine.) } -->
+             enter key.
+
+             The default value is false (multi-line wrapped text mode) for non-editable text, but if
+             you specify any value for inputType, the default is true (single-line input field mode).
+
+             {@deprecated This attribute is deprecated. Use <code>maxLines</code> instead to change
+             the layout of a static text, and use the <code>textMultiLine</code> flag in the
+             inputType attribute instead for editable text views (if both singleLine and inputType
+             are supplied, the inputType flags will override the value of singleLine). } -->
         <attr name="singleLine" format="boolean" />
         <!-- Specifies whether the TextView is enabled or not. {@deprecated Use state_enabled instead}. -->
         <attr name="enabled" format="boolean" />
         <!-- If the text is selectable, select it all when the view takes
-             focus instead of moving the cursor to the start or end. -->
+             focus. -->
         <attr name="selectAllOnFocus" format="boolean" />
         <!-- Leave enough room for ascenders and descenders instead of
              using the font ascent and descent strictly.  (Normally true). -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index dbd49fb..92c59ab 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1235,6 +1235,10 @@
              when the user remove a task rooted in an activity owned by
              the application.  The default is false. -->
         <attr name="stopWithTask" format="boolean" />
+        <!-- If set to true, this service will run under a special process
+             that is isolated from the rest of the system.  The only communication
+             with it is through the Service API (binding and starting). -->
+        <attr name="isolatedProcess" format="boolean" />
     </declare-styleable>
     
     <!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0511b75..1eab01a 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -61,6 +61,25 @@
          As of Honeycomb, blurring is not supported anymore. -->
     <bool name="config_sf_slowBlur">true</bool>
 
+    <!-- Flag indicating that the media framework should allow changing
+         master volume stream and nothing else . -->
+    <bool name="config_useMasterVolume">false</bool>
+
+    <!-- Array of integer pairs controlling the rate at which the master volume changes
+         in response to volume up and down key events.
+         The first integer of each pair is compared against the current master volume
+         (in range 0 to 100).
+         The last pair with first integer <= the current volume is chosen,
+         and the second integer of the pair indicates the amount to increase the master volume
+         when volume up is pressed. -->
+    <integer-array name="config_masterVolumeRamp">
+        <item>0</item>  <item>5</item>  <!-- default: always increase volume by 5% -->
+    </integer-array>
+
+    <!-- Flag indicating whether the AUDIO_BECOMING_NOISY notification should
+         be sent during an change to the audio output device. -->
+    <bool name="config_sendAudioBecomingNoisy">true</bool>
+
     <!-- The duration (in milliseconds) of a short animation. -->
     <integer name="config_shortAnimTime">200</integer>
 
@@ -227,6 +246,13 @@
         <item>4</item>
     </integer-array>
 
+    <!-- If the DUN connection for this CDMA device supports more than just DUN -->
+    <!-- traffic you should list them here. -->
+    <!-- If this device is not CDMA this is ignored.  If this list is empty on -->
+    <!-- a DUN-requiring CDMA device, the DUN APN will just support just DUN. -->
+    <string-array translatable="false" name="config_cdma_dun_supported_types">
+    </string-array>
+
     <!-- String containing the apn value for tethering.  May be overriden by secure settings
          TETHER_DUN_APN.  Value is a comma separated series of strings:
          "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
@@ -364,6 +390,12 @@
     <string-array name="config_usbHostBlacklist" translatable="false">
     </string-array>
 
+    <!-- List of paths to serial ports that are available to the serial manager.
+         for example, /dev/ttyUSB0
+    -->
+    <string-array translatable="false" name="config_serialPorts">
+    </string-array>
+
     <!-- Vibrator pattern for feedback about a long screen/key press -->
     <integer-array name="config_longPressVibePattern">
         <item>0</item>
@@ -535,6 +567,9 @@
          specified -->
     <string name="default_wallpaper_component" translatable="false">@null</string>
 
+    <!-- True if WallpaperService is enabled -->
+    <bool name="config_enableWallpaperService">true</bool>
+
     <!-- Component name of the service providing network location support. -->
     <string name="config_networkLocationProvider" translatable="false">@null</string>
 
@@ -760,4 +795,10 @@
          movement threshold where scrolling should begin. -->
     <dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
 
+    <!-- Array of OEM specific USB mode override config.
+         OEM can override a certain USB mode depending on ro.bootmode.
+         Specify an array of below items to set override rule.
+         [bootmode]:[original USB mode]:[USB mode used]-->
+    <integer-array translatable="false" name="config_oemUsbModeOverride">
+    </integer-array>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 82ef68a..6d6b86b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -108,6 +108,20 @@
          is along the major axis (that is the screen is landscape).  This may
          be either a fraction or a dimension. -->
     <item type="dimen" name="dialog_min_width_major">65%</item>
+
+    <!-- The platform's desired fixed width for a dialog along the major axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_width_major">320dp</item>
+    <!-- The platform's desired fixed width for a dialog along the minor axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_width_minor">320dp</item>
+    <!-- The platform's desired fixed height for a dialog along the major axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_height_major">80%</item>
+    <!-- The platform's desired fixed height for a dialog along the minor axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_height_minor">100%</item>
+
     <!-- Preference activity, vertical padding for the header list -->
     <dimen name="preference_screen_header_vertical_padding">0dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e3c2bd8..0950bdb 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -212,6 +212,10 @@
   <java-symbol type="attr" name="textAppearanceMisspelledSuggestion" />
   <java-symbol type="attr" name="textColorSearchUrl" />
   <java-symbol type="attr" name="timePickerStyle" />
+  <java-symbol type="attr" name="windowFixedWidthMajor" />
+  <java-symbol type="attr" name="windowFixedWidthMinor" />
+  <java-symbol type="attr" name="windowFixedHeightMajor" />
+  <java-symbol type="attr" name="windowFixedHeightMinor" />
 
   <java-symbol type="bool" name="action_bar_embed_tabs" />
   <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
@@ -234,6 +238,9 @@
   <java-symbol type="bool" name="preferences_prefer_dual_pane" />
   <java-symbol type="bool" name="skip_restoring_network_selection" />
   <java-symbol type="bool" name="split_action_bar_is_narrow" />
+  <java-symbol type="bool" name="config_useMasterVolume" />
+  <java-symbol type="bool" name="config_enableWallpaperService" />
+  <java-symbol type="bool" name="config_sendAudioBecomingNoisy" />
 
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_longPressOnPowerBehavior" />
@@ -859,6 +866,8 @@
   <java-symbol type="array" name="preloaded_drawables" />
   <java-symbol type="array" name="special_locale_codes" />
   <java-symbol type="array" name="special_locale_names" />
+  <java-symbol type="array" name="config_masterVolumeRamp" />
+  <java-symbol type="array" name="config_cdma_dun_supported_types" />
 
   <java-symbol type="drawable" name="default_wallpaper" />
   <java-symbol type="drawable" name="ic_suggestions_add" />
@@ -953,6 +962,7 @@
   <java-symbol type="drawable" name="tab_bottom_right_v4" />
   <java-symbol type="drawable" name="tab_indicator_v4" />
   <java-symbol type="drawable" name="text_select_handle_left" />
+  <java-symbol type="drawable" name="text_select_handle_middle" />
   <java-symbol type="drawable" name="text_select_handle_right" />
   <java-symbol type="drawable" name="unknown_image" />
   <java-symbol type="drawable" name="unlock_default" />
@@ -1288,7 +1298,9 @@
   <java-symbol type="array" name="config_tether_usb_regexs" />
   <java-symbol type="array" name="config_tether_wifi_regexs" />
   <java-symbol type="array" name="config_usbHostBlacklist" />
+  <java-symbol type="array" name="config_serialPorts" />
   <java-symbol type="array" name="radioAttributes" />
+  <java-symbol type="array" name="config_oemUsbModeOverride" />
   <java-symbol type="bool" name="config_animateScreenLights" />
   <java-symbol type="bool" name="config_automatic_brightness_available" />
   <java-symbol type="bool" name="config_sf_limitedAlpha" />
@@ -1432,6 +1444,7 @@
   <java-symbol type="anim" name="push_down_out" />
   <java-symbol type="anim" name="push_up_in" />
   <java-symbol type="anim" name="push_up_out" />
+  <java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
   <java-symbol type="dimen" name="status_bar_icon_size" />
   <java-symbol type="dimen" name="system_bar_icon_size" />
   <java-symbol type="drawable" name="list_selector_pressed_holo_dark" />
@@ -1469,6 +1482,7 @@
   <java-symbol type="string" name="usb_storage_stop_notification_title" />
   <java-symbol type="string" name="usb_storage_stop_title" />
   <java-symbol type="string" name="usb_storage_title" />
+  <java-symbol type="style" name="Animation.RecentApplications" />
 
   <!-- ImfTest -->
   <java-symbol type="layout" name="auto_complete_list" />
@@ -1528,6 +1542,21 @@
   <java-symbol type="drawable" name="ic_volume" />
   <java-symbol type="drawable" name="stat_notify_sim_toolkit" />
 
+  <!-- From maps library -->
+  <java-symbol type="array" name="maps_starting_lat_lng" />
+  <java-symbol type="array" name="maps_starting_zoom" />
+  <java-symbol type="attr" name="mapViewStyle" />
+  <java-symbol type="attr" name="state_focused" />
+  <java-symbol type="attr" name="state_selected" />
+  <java-symbol type="attr" name="state_pressed" />
+  <java-symbol type="drawable" name="compass_arrow" />
+  <java-symbol type="drawable" name="compass_base" />
+  <java-symbol type="drawable" name="ic_maps_indicator_current_position_anim" />
+  <java-symbol type="drawable" name="loading_tile_android" />
+  <java-symbol type="drawable" name="maps_google_logo" />
+  <java-symbol type="drawable" name="no_tile_256" />
+  <java-symbol type="drawable" name="reticle" />
+
   <!-- From PinyinIME(!!!) -->
   <java-symbol type="string" name="inputMethod" />
 
@@ -3482,4 +3511,18 @@
   <public type="color" name="holo_orange_dark" id="0x01060019" />
   <public type="color" name="holo_purple" id="0x0106001a" />
   <public type="color" name="holo_blue_bright" id="0x0106001b" />
+
+<!-- ===============================================================
+     Resources added in version 16 of the platform (Jelly Bean)
+     =============================================================== -->
+  <public type="attr" name="isolatedProcess" id="0x010103a7" />
+
+  <public type="attr" name="textDirection"/>
+
+  <public type="attr" name="paddingStart"/>
+  <public type="attr" name="paddingEnd"/>
+
+  <public type="attr" name="layout_marginStart"/>
+  <public type="attr" name="layout_marginEnd"/>
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dc45c408..3c1f50d 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -532,6 +532,13 @@
         tasks and kill their apps. Malicious apps may disrupt
         the behavior of other apps.</string>
 
+    <!-- Title of an application permission, allowing control of app screen compatibility mode -->
+    <string name="permlab_setScreenCompatibility">set screen compatibility</string>
+    <!-- Description of an application permission, allowing control of app screen compatibility mode -->
+    <string name="permdesc_setScreenCompatibility">Allows the app to control the
+        screen compatibility mode of other applications.  Malicious applications may
+        break the behavior of other applications.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setDebugApp">enable app debugging</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2236,6 +2243,19 @@
     <string name="permdesc_bindPackageVerifier">Allows the holder to make requests of
         package verifiers. Should never be needed for normal apps.</string>
 
+    <!-- Title of an application permission which allows the application to access serial ports via the SerialManager. [CHAR LIMIT=40] -->
+    <string name="permlab_serialPort">access serial ports</string>
+    <!-- Description of an application permission which allows the application access serial ports via the SerialManager. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_serialPort">Allows the holder to access serial ports using the SerialManager API.</string>
+
+    <!-- Title of an application permission which allows the holder to access content
+         providers from outside an ApplicationThread. [CHAR LIMIT=40] -->
+    <string name="permlab_accessContentProvidersExternally">access content providers externally</string>
+    <!-- Description of an application permission which allows the holder to access
+         content providers from outside an ApplicationThread. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_accessContentProvidersExternally">Allows the holder to access content
+     providers from the shell. Should never be needed for normal apps.</string>
+
     <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Text in the save password dialog, asking if the browser should remember a password. -->
     <string name="save_password_message">Do you want the browser to remember this password?</string>
     <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Button in the save password dialog, saying not to remember this password. -->
@@ -2696,10 +2716,11 @@
     <!-- Do not translate. Default access point SSID used for tethering -->
     <string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
 
-    <!-- Wi-Fi p2p dialog title-->
     <string name="wifi_p2p_dialog_title">Wi-Fi Direct</string>
     <string name="wifi_p2p_turnon_message">Start Wi-Fi Direct. This will turn off Wi-Fi client/hotspot.</string>
     <string name="wifi_p2p_failed_message">Couldn\'t start Wi-Fi Direct.</string>
+    <string name="wifi_p2p_enabled_notification_title">Wi-Fi Direct is on</string>
+    <string name="wifi_p2p_enabled_notification_message">Touch for settings</string>
 
     <string name="accept">Accept</string>
     <string name="decline">Decline</string>
@@ -2711,9 +2732,6 @@
     <string name="wifi_p2p_enter_pin_message">Type the required PIN: </string>
     <string name="wifi_p2p_show_pin_message">PIN: </string>
 
-    <string name="wifi_p2p_enabled_notification_title">Wi-Fi Direct is on</string>
-    <string name="wifi_p2p_enabled_notification_message">Touch for settings</string>
-
     <!-- Name of the dialog that lets the user choose an accented character to insert -->
     <string name="select_character">Insert character</string>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 571c4ad..610bad8 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -198,8 +198,10 @@
     <!-- A special animation we can use for recent applications,
          for devices that can support it (do alpha transformations). -->
     <style name="Animation.RecentApplications">
-        <item name="windowEnterAnimation">@anim/fade_in</item>
-        <item name="windowExitAnimation">@anim/fade_out</item>
+        <item name="windowEnterAnimation">@anim/recents_fade_in</item>
+        <item name="windowShowAnimation">@anim/recents_fade_in</item>
+        <item name="windowExitAnimation">@anim/recents_fade_out</item>
+        <item name="windowHideAnimation">@anim/recents_fade_out</item>
     </style>
 
     <!-- A special animation value used internally for popup windows. -->
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 7046fc5..55438b2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -1580,6 +1580,22 @@
         <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
     </style>
 
+    <!-- Variant of Theme.Holo.Dialog that has a fixed size. -->
+    <style name="Theme.Holo.Dialog.FixedSize">
+        <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item>
+    </style>
+
+    <!-- Variant of Theme.Holo.Dialog.NoActionBar that has a fixed size. -->
+    <style name="Theme.Holo.Dialog.NoActionBar.FixedSize">
+        <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item>
+    </style>
+
     <!-- Variant of Theme.Holo.Dialog that does not include a frame (or background).
          The view hierarchy of the dialog is responsible for drawing all of
          its pixels. -->
@@ -1672,6 +1688,22 @@
         <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
     </style>
 
+    <!-- Variant of Theme.Holo.Light.Dialog that has a fixed size. -->
+    <style name="Theme.Holo.Light.Dialog.FixedSize">
+        <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item>
+    </style>
+
+    <!-- Variant of Theme.Holo.Light.Dialog.NoActionBar that has a fixed size. -->
+    <style name="Theme.Holo.Light.Dialog.NoActionBar.FixedSize">
+        <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item>
+    </style>
+
     <!-- Theme for a window that will be displayed either full-screen on
          smaller screens (small, normal) or as a dialog on larger screens
          (large, xlarge). -->
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index abe4aad..7fd981c 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -379,6 +379,23 @@
     <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Holo.Dialog.NoActionBar.MinWidth" >
 
     </style>
+
+    <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
+    <style name="Theme.DeviceDefault.Dialog.FixedSize">
+        <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item>
+    </style>
+
+    <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
+    <style name="Theme.DeviceDefault.Dialog.NoActionBar.FixedSize">
+        <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item>
+    </style>
+
     <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
     floating (not fill the entire screen), and puts a frame around its contents. You can set this
     theme on an activity if you would like to make an activity that looks like a Dialog.-->
@@ -406,6 +423,23 @@
     <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Holo.Light.Dialog.NoActionBar.MinWidth" >
 
     </style>
+
+    <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
+    <style name="Theme.DeviceDefault.Light.Dialog.FixedSize">
+        <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item>
+    </style>
+
+    <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
+    <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize">
+        <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item>
+    </style>
+
     <!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
     screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
     <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Holo.DialogWhenLarge" >
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
index 9819c54..9c1922f 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
@@ -46,8 +46,7 @@
             // create a new test suite
             suite.setName("ConnectivityManagerWifiOnlyFunctionalTests");
             String[] methodNames = {"testConnectToWifi", "testConnectToWifWithKnownAP",
-                    "testDisconnectWifi", "testDataConnectionOverAMWithWifi",
-                    "testDataConnectionWithWifiToAMToWifi", "testWifiStateChange"};
+                    "testDisconnectWifi", "testWifiStateChange"};
             Class<ConnectivityManagerMobileTest> testClass = ConnectivityManagerMobileTest.class;
             for (String method: methodNames) {
                 suite.addTest(TestSuite.createTest(testClass, method));
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index a781472..76b702e 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -90,6 +90,7 @@
      */
     @LargeTest
     public void testWifiDownload() throws Exception {
+        mConnectionUtil.wifiTestInit();
         assertTrue("Could not connect to wifi!", setDeviceWifiAndAirplaneMode(mSsid));
         downloadFile();
     }
@@ -143,6 +144,7 @@
      */
     @LargeTest
     public void testWifiUpload() throws Exception {
+        mConnectionUtil.wifiTestInit();
         assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
         uploadFile();
     }
@@ -197,6 +199,7 @@
      */
     @LargeTest
     public void testWifiDownloadWithDownloadManager() throws Exception {
+        mConnectionUtil.wifiTestInit();
         assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
         downloadFileUsingDownloadManager();
     }
@@ -286,6 +289,8 @@
      * @return true if we successfully connect to mobile data.
      */
     public boolean hasMobileData() {
+        assertTrue(mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
+                State.CONNECTED, ConnectionUtil.LONG_TIMEOUT));
         assertTrue("Not connected to mobile", mConnectionUtil.isConnectedToMobile());
         assertFalse("Still connected to wifi.", mConnectionUtil.isConnectedToWifi());
         return mConnectionUtil.hasData();
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index dfcbba9..8d778c4 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -44,6 +44,8 @@
 import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
 import com.android.internal.util.AsyncChannel;
 
+import junit.framework.Assert;
+
 import java.io.IOException;
 import java.net.UnknownHostException;
 import java.util.List;
@@ -57,7 +59,7 @@
     private static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; // 10 seconds
     private static final int WIFI_SCAN_TIMEOUT = 50 * 1000;
     public static final int SHORT_TIMEOUT = 5 * 1000;
-    public static final int LONG_TIMEOUT = 10 * 1000;
+    public static final int LONG_TIMEOUT = 120 * 1000; // 2 minutes
     private ConnectivityReceiver mConnectivityReceiver = null;
     private WifiReceiver mWifiReceiver = null;
     private DownloadReceiver mDownloadReceiver = null;
@@ -118,8 +120,14 @@
 
         initializeNetworkStates();
 
-        mWifiManager.setWifiEnabled(true);
 
+    }
+
+    /**
+     * Additional initialization needed for wifi related tests.
+     */
+    public void wifiTestInit() {
+        mWifiManager.setWifiEnabled(true);
         Log.v(LOG_TAG, "Clear Wifi before we start the test.");
         sleep(SHORT_TIMEOUT);
         removeConfiguredNetworksAndDisableWifi();
@@ -447,6 +455,11 @@
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
+                if (mNetworkInfo == null) {
+                    Log.v(LOG_TAG, "Do not have networkInfo! Force fetch of network info.");
+                    mNetworkInfo = mCM.getActiveNetworkInfo();
+                    Assert.assertNotNull(mNetworkInfo);
+                }
                 if ((mNetworkInfo.getType() != networkType) ||
                         (mNetworkInfo.getState() != expectedState)) {
                     Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() +
@@ -525,7 +538,7 @@
     /**
      * Connect to Wi-Fi with the given configuration.
      * @param config
-     * @return true if we ar connected to a given
+     * @return true if we are connected to a given AP.
      */
     public boolean connectToWifiWithConfiguration(WifiConfiguration config) {
         //  The SSID in the configuration is a pure string, need to convert it to a quoted string.
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index b81f774..88f3f34 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -22,7 +22,7 @@
 	$(call all-java-files-under, EnabledTestApp/src)
 
 LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava littlemock
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
index 2fb4237..3dc140b 100644
--- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -50,6 +50,12 @@
     // Timeout for the accessibility state of an Activity to be fully initialized.
     private static final int TIMEOUT_PROPAGATE_ACCESSIBILITY_EVENT_MILLIS = 5000;
 
+    // Timeout for which non getting accessibility events considers the app idle.
+    private static final long IDLE_EVENT_TIME_DELTA_MILLIS = 200;
+
+    // Timeout in which to wait for idle device.
+    private static final long GLOBAL_IDLE_DETECTION_TIMEOUT_MILLIS = 1000;
+
     // Handle to a connection to the AccessibilityManagerService
     private UiTestAutomationBridge mUiTestAutomationBridge;
 
@@ -62,6 +68,8 @@
         super.setUp();
         mUiTestAutomationBridge = new UiTestAutomationBridge();
         mUiTestAutomationBridge.connect();
+        mUiTestAutomationBridge.waitForIdle(IDLE_EVENT_TIME_DELTA_MILLIS,
+                GLOBAL_IDLE_DETECTION_TIMEOUT_MILLIS);
         mUiTestAutomationBridge.executeCommandAndWaitForAccessibilityEvent(new Runnable() {
                 // wait for the first accessibility event
                 @Override
diff --git a/core/tests/coretests/src/android/net/http/HttpResponseCacheTest.java b/core/tests/coretests/src/android/net/http/HttpResponseCacheTest.java
index 4d65588..9015a6f 100644
--- a/core/tests/coretests/src/android/net/http/HttpResponseCacheTest.java
+++ b/core/tests/coretests/src/android/net/http/HttpResponseCacheTest.java
@@ -16,6 +16,8 @@
 
 package android.net.http;
 
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
 import java.io.File;
 import java.net.CacheRequest;
 import java.net.CacheResponse;
@@ -30,6 +32,7 @@
 public final class HttpResponseCacheTest extends TestCase {
 
     private File cacheDir;
+    private MockWebServer server = new MockWebServer();
 
     @Override public void setUp() throws Exception {
         super.setUp();
@@ -39,6 +42,7 @@
 
     @Override protected void tearDown() throws Exception {
         ResponseCache.setDefault(null);
+        server.shutdown();
         super.tearDown();
     }
 
@@ -100,4 +104,32 @@
         cache.delete();
         assertNull(ResponseCache.getDefault());
     }
+
+    /**
+     * Make sure that statistics tracking are wired all the way through the
+     * wrapper class. http://code.google.com/p/android/issues/detail?id=25418
+     */
+    public void testStatisticsTracking() throws Exception {
+        HttpResponseCache cache = HttpResponseCache.install(cacheDir, 10 * 1024 * 1024);
+
+        server.enqueue(new MockResponse()
+                .addHeader("Cache-Control: max-age=60")
+                .setBody("A"));
+        server.play();
+
+        URLConnection c1 = server.getUrl("/").openConnection();
+        assertEquals('A', c1.getInputStream().read());
+        assertEquals(1, cache.getRequestCount());
+        assertEquals(1, cache.getNetworkCount());
+        assertEquals(0, cache.getHitCount());
+
+        URLConnection c2 = server.getUrl("/").openConnection();
+        assertEquals('A', c2.getInputStream().read());
+
+        URLConnection c3 = server.getUrl("/").openConnection();
+        assertEquals('A', c3.getInputStream().read());
+        assertEquals(3, cache.getRequestCount());
+        assertEquals(1, cache.getNetworkCount());
+        assertEquals(2, cache.getHitCount());
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index baf587e..f2eba23 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -16,6 +16,9 @@
 
 package android.widget.focus;
 
+import static com.google.testing.littlemock.LittleMock.inOrder;
+import static com.google.testing.littlemock.LittleMock.mock;
+
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
@@ -28,9 +31,7 @@
 import android.widget.Button;
 
 import com.android.frameworks.coretests.R;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.google.testing.littlemock.LittleMock.InOrder;
 
 /**
  * {@link RequestFocusTest} is set up to exercise cases where the views that
@@ -105,43 +106,47 @@
      * This tests checks the case in which the first focusable View clears focus.
      * In such a case the framework tries to give the focus to another View starting
      * from the top. Hence, the framework will try to give focus to the view that
-     * wants to clear its focus. From a client perspective, the view does not loose
-     * focus after the call, therefore no callback for focus change should be invoked.
+     * wants to clear its focus.
      *
      * @throws Exception If an error occurs.
      */
     @UiThreadTest
-    public void testOnFocusChangeNotCalledIfFocusDoesNotMove() throws Exception {
+    public void testOnFocusChangeCallbackOrderWhenClearingFocusOfFirstFocusable()
+            throws Exception {
         // Get the first focusable.
-        Button button = mTopLeftButton;
+        Button clearingFocusButton = mTopLeftButton;
+        Button gainingFocusButton = mTopLeftButton;
 
-        // Make sure that the button is the first focusable and focus it.
-        button.getRootView().requestFocus(View.FOCUS_DOWN);
-        assertTrue(button.hasFocus());
+        // Make sure that the clearing focus View is the first focusable.
+        View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
+                View.FOCUS_FORWARD);
+        assertSame("The clearing focus button is the first focusable.",
+                clearingFocusButton, focusCandidate);
+        assertSame("The gaining focus button is the first focusable.",
+                gainingFocusButton, focusCandidate);
 
-        // Attach on focus change listener that should not be called.
-        button.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                throw new IllegalStateException("Unexpeced call to"
-                        + "OnFocusChangeListener#onFocusChange");
-            }
-        });
+        // Focus the clearing focus button.
+        clearingFocusButton.requestFocus();
+        assertTrue(clearingFocusButton.hasFocus());
 
-        // Attach on global focus change listener that should not be called.
-        button.getViewTreeObserver().addOnGlobalFocusChangeListener(
-                new OnGlobalFocusChangeListener() {
-            @Override
-            public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-                throw new IllegalStateException("Unexpeced call to"
-                        + "OnFocusChangeListener#onFocusChange");
-            }
-        });
+        // Register the invocation order checker.
+        CombinedListeners mock = mock(CombinedListeners.class);
+        clearingFocusButton.setOnFocusChangeListener(mock);
+        gainingFocusButton.setOnFocusChangeListener(mock);
+        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(mock);
 
         // Try to clear focus.
-        button.clearFocus();
+        clearingFocusButton.clearFocus();
+
+        // Check that no callback was invoked since focus did not move.
+        InOrder inOrder = inOrder(mock);
+        inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
+        inOrder.verify(mock).onGlobalFocusChanged(clearingFocusButton, gainingFocusButton);
+        inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
     }
 
+    public interface CombinedListeners extends OnFocusChangeListener, OnGlobalFocusChangeListener {}
+
     /**
      * This tests check whether the on focus change callbacks are invoked in
      * the proper order when a View loses focus and the framework gives it to
@@ -150,12 +155,12 @@
      * @throws Exception
      */
     @UiThreadTest
-    public void testOnFocusChangeCallbackOrder() throws Exception {
-        // Get the first focusable.
+    public void testOnFocusChangeCallbackOrderWhenClearingFocusOfNotFirstFocusable()
+            throws Exception {
         Button clearingFocusButton = mTopRightButton;
         Button gainingFocusButton = mTopLeftButton;
 
-        // Make sure that the clearing focus is not the first focusable.
+        // Make sure that the clearing focus View is not the first focusable.
         View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
                 View.FOCUS_FORWARD);
         assertNotSame("The clearing focus button is not the first focusable.",
@@ -168,77 +173,18 @@
         assertTrue(clearingFocusButton.hasFocus());
 
         // Register the invocation order checker.
-        CallbackOrderChecker checker = new CallbackOrderChecker(clearingFocusButton,
-                gainingFocusButton);
-        clearingFocusButton.setOnFocusChangeListener(checker);
-        gainingFocusButton.setOnFocusChangeListener(checker);
-        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(checker);
+        CombinedListeners mock = mock(CombinedListeners.class);
+        clearingFocusButton.setOnFocusChangeListener(mock);
+        gainingFocusButton.setOnFocusChangeListener(mock);
+        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(mock);
 
         // Try to clear focus.
         clearingFocusButton.clearFocus();
 
         // Check that no callback was invoked since focus did not move.
-        checker.verify();
-    }
-
-    /**
-     * This class check whether the focus change callback are invoked in order.
-     */
-    private class CallbackOrderChecker implements OnFocusChangeListener,
-            OnGlobalFocusChangeListener {
-
-        private class CallbackInvocation {
-            final String mMethodName;
-            final Object[] mArguments;
-
-            CallbackInvocation(String methodName, Object[] arguments) {
-                mMethodName = methodName;
-                mArguments = arguments;
-            }
-        }
-
-        private final View mClearingFocusView;
-        private final View mGainingFocusView;
-
-        private final List<CallbackInvocation> mInvocations = new ArrayList<CallbackInvocation>();
-
-        public CallbackOrderChecker(View clearingFocusView, View gainingFocusView) {
-            mClearingFocusView = clearingFocusView;
-            mGainingFocusView = gainingFocusView;
-        }
-
-        @Override
-        public void onFocusChange(View view, boolean hasFocus) {
-            CallbackInvocation invocation = new CallbackInvocation(
-                    "OnFocusChangeListener#onFocusChange", new Object[] {view, hasFocus});
-            mInvocations.add(invocation);
-        }
-
-        @Override
-        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-            CallbackInvocation invocation = new CallbackInvocation(
-                    "OnFocusChangeListener#onFocusChange", new Object[] {oldFocus, newFocus});
-            mInvocations.add(invocation);
-        }
-
-        public void verify() {
-            assertSame("All focus change callback should be invoked.", 3, mInvocations.size());
-            assertInvioked("Callback for View clearing focus explected.", 0,
-                    "OnFocusChangeListener#onFocusChange",
-                    new Object[] {mClearingFocusView, false});
-            assertInvioked("Callback for View global focus change explected.", 1,
-                    "OnFocusChangeListener#onFocusChange", new Object[] {mClearingFocusView,
-                    mGainingFocusView});
-            assertInvioked("Callback for View gaining focus explected.", 2,
-                    "OnFocusChangeListener#onFocusChange", new Object[] {mGainingFocusView, true});
-        }
-
-        private void assertInvioked(String message, int order, String methodName,
-                Object[] arguments) {
-            CallbackInvocation invocation = mInvocations.get(order);
-            assertEquals(message, methodName, invocation.mMethodName);
-            assertEquals(message, arguments[0], invocation.mArguments[0]);
-            assertEquals(message, arguments[1], invocation.mArguments[1]);
-        }
+        InOrder inOrder = inOrder(mock);
+        inOrder.verify(mock).onFocusChange(clearingFocusButton, false);
+        inOrder.verify(mock).onGlobalFocusChanged(clearingFocusButton, gainingFocusButton);
+        inOrder.verify(mock).onFocusChange(gainingFocusButton, true);
     }
 }
diff --git a/data/etc/android.hardware.bluetooth.xml b/data/etc/android.hardware.bluetooth.xml
new file mode 100644
index 0000000..4aa1744
--- /dev/null
+++ b/data/etc/android.hardware.bluetooth.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<!-- Adds the feature indicating support for the Bluetooth API -->
+<permissions>
+    <feature name="android.hardware.bluetooth" />
+</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 6cd07a3..8be1db2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -158,6 +158,7 @@
     <assign-permission name="android.permission.BACKUP" uid="shell" />
     <assign-permission name="android.permission.FORCE_STOP_PACKAGES" uid="shell" />
     <assign-permission name="android.permission.STOP_APP_SWITCHES" uid="shell" />
+    <assign-permission name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" uid="shell" />
 
     <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
     <assign-permission name="android.permission.ACCESS_DRM" uid="media" />
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 02d2f3d..ef38a60 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -51,6 +51,11 @@
     $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(font_symlink)
 
 ################################
+# On space-constrained devices, we include a subset of fonts:
+ifeq ($(SMALLER_FONT_FOOTPRINT),true)
+droidsans_fallback_src := DroidSansFallback.ttf
+extra_droidsans_fonts := DroidSans.ttf DroidSans-Bold.ttf
+else
 include $(CLEAR_VARS)
 LOCAL_MODULE := DroidSansEthiopic-Regular.ttf
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
@@ -59,15 +64,11 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
 include $(BUILD_PREBUILT)
 
-################################
-ifeq ($(SMALLER_FONT_FOOTPRINT),true)
-droidsans_fallback_src := DroidSansFallback.ttf
-extra_droidsans_fonts := DroidSans.ttf DroidSans-Bold.ttf
-else
 droidsans_fallback_src := DroidSansFallbackFull.ttf
 extra_droidsans_fonts := DroidSans.ttf DroidSans-Bold.ttf DroidSansEthiopic-Regular.ttf
 endif  # SMALLER_FONT_FOOTPRINT
 
+################################
 include $(CLEAR_VARS)
 LOCAL_MODULE := DroidSansFallback.ttf
 LOCAL_SRC_FILES := $(droidsans_fallback_src)
@@ -81,3 +82,46 @@
 font_symlink :=
 droidsans_fallback_src :=
 extra_droidsans_fonts :=
+
+################################
+# Build the rest font files as prebuilt.
+
+# $(1): The source file name in LOCAL_PATH.
+#       It also serves as the module name and the dest file name.
+define build-one-font-module
+$(eval include $(CLEAR_VARS))\
+$(eval LOCAL_MODULE := $(1))\
+$(eval LOCAL_SRC_FILES := $(1))\
+$(eval LOCAL_MODULE_CLASS := ETC)\
+$(eval LOCAL_MODULE_TAGS := optional)\
+$(eval LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts)\
+$(eval include $(BUILD_PREBUILT))
+endef
+
+font_src_files := \
+    Roboto-Regular.ttf \
+    Roboto-Bold.ttf \
+    Roboto-Italic.ttf \
+    Roboto-BoldItalic.ttf \
+    DroidSansArabic.ttf \
+    DroidNaskh-Regular.ttf \
+    DroidSansHebrew-Regular.ttf \
+    DroidSansHebrew-Bold.ttf \
+    DroidSansThai.ttf \
+    DroidSerif-Regular.ttf \
+    DroidSerif-Bold.ttf \
+    DroidSerif-Italic.ttf \
+    DroidSerif-BoldItalic.ttf \
+    DroidSansMono.ttf \
+    DroidSansArmenian.ttf \
+    DroidSansGeorgian.ttf \
+    AndroidEmoji.ttf \
+    Clockopia.ttf \
+    AndroidClock.ttf \
+    AndroidClock_Highlight.ttf \
+    AndroidClock_Solid.ttf \
+
+$(foreach f, $(font_src_files), $(call build-one-font-module, $(f)))
+
+build-one-font-module :=
+font_src_files :=
diff --git a/data/fonts/DroidNaskh-Bold.ttf b/data/fonts/DroidNaskh-Bold.ttf
index 6b7d4f0..692b796 100644
--- a/data/fonts/DroidNaskh-Bold.ttf
+++ b/data/fonts/DroidNaskh-Bold.ttf
Binary files differ
diff --git a/data/fonts/DroidNaskh-Regular.ttf b/data/fonts/DroidNaskh-Regular.ttf
index d11e1ae..da9a45f 100644
--- a/data/fonts/DroidNaskh-Regular.ttf
+++ b/data/fonts/DroidNaskh-Regular.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
index 2379b2d..cfbc66a 100644
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackFull.ttf b/data/fonts/DroidSansFallbackFull.ttf
index 41b015d..0cacabe 100644
--- a/data/fonts/DroidSansFallbackFull.ttf
+++ b/data/fonts/DroidSansFallbackFull.ttf
Binary files differ
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 6a9ed53..458f85b 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -14,34 +14,30 @@
 
 # Warning: this is actually a product definition, to be inherited from
 
-# On space-constrained devices, we include a subset of fonts:
-# First, core/required fonts
 PRODUCT_COPY_FILES := \
-    frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \
-    frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
-    frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
-    frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
-    frameworks/base/data/fonts/DroidSansArabic.ttf:system/fonts/DroidSansArabic.ttf \
-    frameworks/base/data/fonts/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \
-    frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \
-    frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \
-    frameworks/base/data/fonts/DroidSansThai.ttf:system/fonts/DroidSansThai.ttf \
-    frameworks/base/data/fonts/DroidSerif-Regular.ttf:system/fonts/DroidSerif-Regular.ttf \
-    frameworks/base/data/fonts/DroidSerif-Bold.ttf:system/fonts/DroidSerif-Bold.ttf \
-    frameworks/base/data/fonts/DroidSerif-Italic.ttf:system/fonts/DroidSerif-Italic.ttf \
-    frameworks/base/data/fonts/DroidSerif-BoldItalic.ttf:system/fonts/DroidSerif-BoldItalic.ttf \
-    frameworks/base/data/fonts/DroidSansMono.ttf:system/fonts/DroidSansMono.ttf \
-    frameworks/base/data/fonts/DroidSansArmenian.ttf:system/fonts/DroidSansArmenian.ttf \
-    frameworks/base/data/fonts/DroidSansGeorgian.ttf:system/fonts/DroidSansGeorgian.ttf \
-    frameworks/base/data/fonts/AndroidEmoji.ttf:system/fonts/AndroidEmoji.ttf \
-    frameworks/base/data/fonts/Clockopia.ttf:system/fonts/Clockopia.ttf \
-    frameworks/base/data/fonts/AndroidClock.ttf:system/fonts/AndroidClock.ttf \
-    frameworks/base/data/fonts/AndroidClock_Highlight.ttf:system/fonts/AndroidClock_Highlight.ttf \
-    frameworks/base/data/fonts/AndroidClock_Solid.ttf:system/fonts/AndroidClock_Solid.ttf \
     frameworks/base/data/fonts/system_fonts.xml:system/etc/system_fonts.xml \
     frameworks/base/data/fonts/fallback_fonts.xml:system/etc/fallback_fonts.xml
 
-# Next, include additional fonts, depending on how much space we have.
-# Details see module definitions in Android.mk.
 PRODUCT_PACKAGES := \
-    DroidSansFallback.ttf
+    DroidSansFallback.ttf \
+    Roboto-Regular.ttf \
+    Roboto-Bold.ttf \
+    Roboto-Italic.ttf \
+    Roboto-BoldItalic.ttf \
+    DroidSansArabic.ttf \
+    DroidNaskh-Regular.ttf \
+    DroidSansHebrew-Regular.ttf \
+    DroidSansHebrew-Bold.ttf \
+    DroidSansThai.ttf \
+    DroidSerif-Regular.ttf \
+    DroidSerif-Bold.ttf \
+    DroidSerif-Italic.ttf \
+    DroidSerif-BoldItalic.ttf \
+    DroidSansMono.ttf \
+    DroidSansArmenian.ttf \
+    DroidSansGeorgian.ttf \
+    AndroidEmoji.ttf \
+    Clockopia.ttf \
+    AndroidClock.ttf \
+    AndroidClock_Highlight.ttf \
+    AndroidClock_Solid.ttf \
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index d390ec1..d22dca1 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -51,19 +51,13 @@
 
 <ol>
   <li>Declare your application as "debuggable" in your Android Manifest.
-    <p>In Eclipse, you can do this from the <b>Application</b> tab when viewing the Manifest
-    (on the right side, set <b>Debuggable</b> to <em>true</em>). Otherwise, in the
-<code>AndroidManifest.xml</code>
-    file, add <code>android:debuggable="true"</code> to the <code>&lt;application></code>
-element.</p>
-  </li>
-  <li>Set up your device to allow installation of non-Market applications. <p>On
-the device, go to <strong>Settings > Applications</strong> and enable 
-
-<strong>Unknown sources</strong> (on an Android 4.0 device, the setting is
-located in <strong>Settings > Security</strong>).</p>
-  
-  </li>
+    <p>When using Eclipse, you can skip this step, because running your app directly from
+the Eclipse IDE automatically enables debugging.</p>
+    <p>In the <code>AndroidManifest.xml</code> file, add <code>android:debuggable="true"</code> to
+the <code>&lt;application></code> element.</p>
+    <p class="note"><strong>Note:</strong> If you manually enable debugging in the manifest
+ file, be sure to disable it before you build for release (your published application
+should usually <em>not</em> be debuggable).</p></li>
   <li>Turn on "USB Debugging" on your device.
     <p>On the device, go to <strong>Settings > Applications > Development</strong> 
     and enable <strong>USB debugging</strong> 
@@ -72,13 +66,10 @@
   </li>
   <li>Set up your system to detect your device.
     <ul>
-      <li>If you're developing on Windows, you need to install a USB driver
-      for adb. If you're using an Android Developer Phone (ADP), Nexus One, or Nexus S,
-      see the <a href="{@docRoot}sdk/win-usb.html">Google Windows USB
-      Driver</a>. Otherwise, you can find a link to the appropriate OEM driver in the
-  <a href="{@docRoot}sdk/oem-usb.html">OEM USB Drivers</a> document.</li>
+      <li>If you're developing on Windows, you need to install a USB driver for adb. For an
+installation guide and links to OEM drivers, see the <a href="{@docRoot}sdk/oem-usb.html">OEM USB
+Drivers</a> document.</li>
       <li>If you're developing on Mac OS X, it just works. Skip this step.</li>
-      
       <li>If you're developing on Ubuntu Linux, you need to add a
 <code>udev</code> rules file that contains a USB configuration for each type of device
 you want to use for development. In the rules file, each device manufacturer
@@ -114,7 +105,7 @@
   </li>
 </ol>
 
-<p>You can verify that your device is connected by executing <code>adb
+<p>When plugged in over USB, can verify that your device is connected by executing <code>adb
 devices</code> from your SDK {@code platform-tools/} directory. If connected,
 you'll see the device name listed as a "device."</p>
 
diff --git a/docs/html/guide/developing/devices/managing-avds.jd b/docs/html/guide/developing/devices/managing-avds.jd
index 1817ce7..e70a0bb 100644
--- a/docs/html/guide/developing/devices/managing-avds.jd
+++ b/docs/html/guide/developing/devices/managing-avds.jd
@@ -233,13 +233,5 @@
 
       <td>hw.lcd.density</td>
     </tr>
-
-    <tr>
-      <td>Trackball support</td>
-
-      <td>Whether there is a trackball present.</td>
-
-      <td>hw.trackBall</td>
-    </tr>
   </table>
 
diff --git a/docs/html/guide/developing/projects/index.jd b/docs/html/guide/developing/projects/index.jd
index ac8a1a5..63e67cd 100644
--- a/docs/html/guide/developing/projects/index.jd
+++ b/docs/html/guide/developing/projects/index.jd
@@ -179,8 +179,9 @@
 
     <dd>Customizable computer-specific properties for the build system. If you use Ant to build
     the project, this contains the path to the SDK installation. Because the content of the file
-    is specific to the local installation of the SDK, maintained it in a source
-    revision control system. If you use Eclipse, this file is not used.</dd>
+    is specific to the local installation of the SDK, the <code>local.properties</code> should not
+be maintained in a source revision control system. If you use Eclipse, this file is not
+used.</dd>
 
     <dt><code>ant.properties</code></dt>
 
diff --git a/docs/html/guide/developing/projects/projects-eclipse.jd b/docs/html/guide/developing/projects/projects-eclipse.jd
index 40c17ed..90f7820 100644
--- a/docs/html/guide/developing/projects/projects-eclipse.jd
+++ b/docs/html/guide/developing/projects/projects-eclipse.jd
@@ -201,7 +201,7 @@
   priority. The application itself has highest priority and its resources are always used in
   preference to identical resource IDs defined in libraries.</p>
 
-  <h3>Declaring library components in the the manifest file</h3>
+  <h3>Declaring library components in the manifest file</h3>
 
   <p>In the manifest file of the application project, you must add declarations of all components
   that the application will use that are imported from a library project. For example, you must
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index 661f5cb..d1a3786 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -243,7 +243,7 @@
 android.content.ContentResolver} object. This leaves a layer of abstraction between the content
 provider and the component requesting information (for security).</p>
 
-<p>There are separate methods for activiting each type of component:</p>
+<p>There are separate methods for activating each type of component:</p>
 <ul>
   <li>You can start an activity (or give it something new to do) by
 passing an {@link android.content.Intent} to {@link android.content.Context#startActivity
@@ -400,7 +400,7 @@
 requirements in your manifest file. That way, devices that do <em>not</em> have a camera and have an
 Android version <em>lower</em> than 2.1 cannot install your application from Android Market.</p>
 
-<p>However, you can also declare that your applicaiton uses the camera, but does not
+<p>However, you can also declare that your application uses the camera, but does not
 <em>require</em> it. In that case, your application must perform a check at runtime to determine
 if the device has a camera and disable any features that use the camera if one is not available.</p>
 
diff --git a/docs/html/guide/topics/fundamentals/loaders.jd b/docs/html/guide/topics/fundamentals/loaders.jd
index 3aad204..ddd513b 100644
--- a/docs/html/guide/topics/fundamentals/loaders.jd
+++ b/docs/html/guide/topics/fundamentals/loaders.jd
@@ -491,7 +491,7 @@
 LoaderCursor</a> &#8212; A complete version of the
 snippet shown above.</li>
   <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> &#8212; An example of how to use throttling to
-reduce the number of queries a content provider does then its data changes.</li>
+reduce the number of queries a content provider does when its data changes.</li>
 </ul>
 
 <p>For information on downloading and installing the SDK samples, see <a
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index 4d9603f..f44901b 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -272,10 +272,11 @@
 </p></dd>
 
 <dt><a name="exclude"></a>{@code android:excludeFromRecents}</dt>
-<dd>Whether or not the activity should be excluded from the list of recently 
-launched activities that can be displayed to users &mdash; "{@code true}" if 
-it should be excluded, and "{@code false}" if it should be included.  
-The default value is "{@code false}".
+<dd>Whether or not the task initiated by this activity should be excluded from the list of recently
+used applications ("recent apps"). That is, when this activity is the root activity of a new task,
+this attribute determines whether the task should not appear in the list of recent apps. "{@code
+true}" if the task should be <em>excluded</em> from the list; "{@code false}" if it should be
+<em>included</em>. The default value is "{@code false}".
 </p></dd>
 
 <dt><a name="exported"></a>{@code android:exported}</dt>
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index 80de9f9..a34ed6c 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -1248,7 +1248,6 @@
     android:drawable="@drawable/android"
     android:clipOrientation="horizontal"
     android:gravity="left" /&gt;
-&lt;/clip>
 </pre>
     <p>The following layout XML applies the clip drawable to a View:</p>
 <pre>
diff --git a/docs/html/guide/topics/resources/more-resources.jd b/docs/html/guide/topics/resources/more-resources.jd
index 972eab9..d37b9f8 100644
--- a/docs/html/guide/topics/resources/more-resources.jd
+++ b/docs/html/guide/topics/resources/more-resources.jd
@@ -216,27 +216,29 @@
 For example: 10px, 2in, 5sp. The following units of measure are supported by Android:</p>
 <dl>
   <dt>{@code dp}</dt>
-    <dd>Density-independent Pixels - an abstract unit that is based on the physical density of the
-screen. These units are relative to a 160 dpi (dots per inch) screen, so <em>{@code 160dp} is
-always one inch</em> regardless of the screen density. The ratio of dp-to-pixel will change with the
-screen density, but not necessarily in direct proportion. You should use these units when specifying
-view dimensions in your layout, so the UI properly scales to render at the same actual size on
-different screens. (The compiler accepts both "dip" and "dp", though "dp" is more consistent with
-"sp".)</dd>
+    <dd>Density-independent Pixels - An abstract unit that is based on the physical density of the
+screen. These units are relative to a 160 dpi (dots per inch) screen, on which 1dp is roughly equal
+to 1px. When running on a higher density screen, the number of pixels used to draw 1dp is scaled up
+by a factor appropriate for the screen's dpi. Likewise, when on a lower density screen, the number
+of pixels used for 1dp is scaled down. The ratio of dp-to-pixel will change with the screen density,
+but not necessarily in direct proportion. Using dp units (instead of px units) is a simple solution
+to making the view dimensions in your layout resize properly for different screen densities. In
+other words, it provides consistency for the real-world sizes of your UI elements across different
+devices.</dd>
   <dt>{@code sp}</dt>
-    <dd>Scale-independent Pixels - this is like the dp unit, but it is also scaled by the user's font
+    <dd>Scale-independent Pixels - This is like the dp unit, but it is also scaled by the user's font
     size preference. It is recommend you use this unit when specifying font sizes, so they will be adjusted
     for both the screen density and the user's preference.</dd>
   <dt>{@code pt}</dt>
     <dd>Points - 1/72 of an inch based on the physical size of the screen.</dd>
   <dt>{@code px}</dt>
-    <dd>Pixels - corresponds to actual pixels on the screen. This unit of measure is not recommended because
+    <dd>Pixels - Corresponds to actual pixels on the screen. This unit of measure is not recommended because
     the actual representation can vary across devices; each devices may have a different number of pixels
     per inch and may have more or fewer total pixels available on the screen.</dd>
   <dt>{@code mm}</dt>
-    <dd>Millimeters - based on the physical size of the screen.</dd>
+    <dd>Millimeters - Based on the physical size of the screen.</dd>
   <dt>{@code in}</dt>
-    <dd>Inches - based on the physical size of the screen.</dd>
+    <dd>Inches - Based on the physical size of the screen.</dd>
 </dl>
 
 <p class="note"><strong>Note:</strong> A dimension is a simple resource that is referenced
diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd
index a2313b3..d51a378 100644
--- a/docs/html/guide/topics/ui/menus.jd
+++ b/docs/html/guide/topics/ui/menus.jd
@@ -17,7 +17,7 @@
   <li><a href="#context-menu">Creating Contextual Menus</a>
     <ol>
       <li><a href="#FloatingContextMenu">Creating a floating context menu</a></li>
-      <li><a href="#CAB">Using the contextual action bar</a></li>
+      <li><a href="#CAB">Using the contextual action mode</a></li>
     </ol>
   </li>
   <li><a href="#PopupMenu">Creating a Popup Menu</a>
diff --git a/docs/html/images/training/lint_icon.png b/docs/html/images/training/lint_icon.png
new file mode 100644
index 0000000..118a741
--- /dev/null
+++ b/docs/html/images/training/lint_icon.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index b9d6758..431a7d2 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -154,7 +154,7 @@
 + "href='https://plus.google.com/108967384991768947849'>+Android Developers</a>. "
 + "We'll use it to host Hangouts for developers, talk about the latest releases, "
 + "development and design tips, and much more.</p>"
-+ "<div style='margin:.7em 0 0 -1.2em'><g:plus href='https://plus.google.com/108967384991768947849' "
++ "<div style='margin:.7em 0 0 0'><g:plus href='https://plus.google.com/108967384991768947849' "
 + "size=\"smallbadge\" width=\"275\"></g:plus></div>"
     },
 
diff --git a/docs/html/resources/articles/faster-screen-orientation-change.jd b/docs/html/resources/articles/faster-screen-orientation-change.jd
index 52531bb..e7b73bf 100644
--- a/docs/html/resources/articles/faster-screen-orientation-change.jd
+++ b/docs/html/resources/articles/faster-screen-orientation-change.jd
@@ -58,7 +58,7 @@
 
 <p>When your application displays a lot of data, or data that is expensive to fetch,
 the automatic destruction/creation of the activities can be lead to a
-painful user experience. Take the example of <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/">Photostream</a>,
+painful user experience. Take the example of <a href="http://code.google.com/p/apps-for-android/source/browse/#git%2FPhotostream%2Fsrc%2Fcom%2Fgoogle%2Fandroid%2Fphotostream">Photostream</a>,
 a simple Flickr browsing application. After you launch the application and choose a Flickr account, the
 application downloads a set of 6 photos (on a T-Mobile G1) from the
 Flickr servers and displays them on screen. To improve the user
@@ -80,9 +80,9 @@
 
 <p>The Activity class has a special method called 
 {@link android.app.Activity#onRetainNonConfigurationInstance()}. This method 
-can be used to pass an arbitrary object <em>your future self</em> and Android 
+can be used to pass an arbitrary object to <em>your future self</em> and Android 
 is smart enough to call this method only when needed. In the case of Photostream, 
-the application <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/PhotostreamActivity.java#226">used this method</a>
+the application used this method
 to pass the downloaded images to the future activity on orientation change. 
 The implementation can be summarized like so:</p>
 
@@ -96,7 +96,7 @@
 
 <p>In the new activity, in <code>onCreate()</code>, all you have to do to 
 get your object back is to call {@link android.app.Activity#getLastNonConfigurationInstance()}. 
-In Photostream, <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/PhotostreamActivity.java#251">this method is invoked</a> 
+In Photostream, this method is invoked 
 and if the returned value is not null, the grid is loaded with the list of 
 photos from the previous activity:</p>
 
@@ -128,3 +128,6 @@
 <code>onRetainNonConfigurationChange()</code> should be used only to retain 
 data that is expensive to load. Otherwise, keep it simple and let Android 
 do everything.</p>
+
+<p>Also read the guide to <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime
+Changes</a>.</p>
diff --git a/docs/html/resources/faq/troubleshooting.jd b/docs/html/resources/faq/troubleshooting.jd
index 05a7ddab..f19f5ec 100644
--- a/docs/html/resources/faq/troubleshooting.jd
+++ b/docs/html/resources/faq/troubleshooting.jd
@@ -222,8 +222,8 @@
 
 <ol>
   <li>In a terminal, change to the tools directory of the SDK.</li>
-  <li>If no emulator instance is running, start an emulator using using the command <code>emulator &</code>.</li>
-  <li>Uninstall the preinstalled app using the command <code>adb uninstall com.android.samples</code>.</li>
+  <li>If no emulator instance is running, start an emulator using using the command <code>emulator</code>.</li>
+  <li>Uninstall the preinstalled app using the command <code>adb uninstall com.example.android.apis</code>.</li>
   <li>Reinstall the app using the command <code>adb install &lt;path to the ApiDemos.apk&gt;</code>. If you are 
   working in Eclipse/ADT, you can just compile and run the app in the normal way. </li>
 </ol>
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex2.jd b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
index 7e3288f1..ed06778 100644
--- a/docs/html/resources/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
@@ -87,8 +87,7 @@
     menu callback used for the options menu. Here, we add just one line, which will add a menu item
     to delete a note. Call <code>menu.add()</code> like so:
       <pre>
-public void onCreateContextMenu(Menu menu, View v,
-        ContextMenu.ContextMenuInfo menuInfo) {
+public void onCreateContextMenu(Menu menu, View v, ContextMenuInfo menuInfo) {
     super.onCreateContextMenu(menu, v, menuInfo);
     menu.add(0, DELETE_ID, 0, R.string.menu_delete);
 }</pre>
diff --git a/docs/html/resources/tutorials/opengl/opengl-es10.jd b/docs/html/resources/tutorials/opengl/opengl-es10.jd
index 3570766..2b44620 100644
--- a/docs/html/resources/tutorials/opengl/opengl-es10.jd
+++ b/docs/html/resources/tutorials/opengl/opengl-es10.jd
@@ -58,7 +58,7 @@
 needs. For more information, see
 <a href="{@docRoot}guide/topics/graphics/opengl.html#choosing-version">Choosing an OpenGL API
 Version</a>. If you would prefer to use OpenGL ES 2.0, see the <a
-href="{@docRoot}resources/tutorials/opengl/opengl-es20.jd">OpenGL ES 2.0 tutorial</a>.</p>
+href="{@docRoot}resources/tutorials/opengl/opengl-es20.html">OpenGL ES 2.0 tutorial</a>.</p>
 
 <p>Before you start, you should understand how to create a basic Android application. If you do not
 know how to create an app, follow the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello
diff --git a/docs/html/resources/tutorials/opengl/opengl-es20.jd b/docs/html/resources/tutorials/opengl/opengl-es20.jd
index 889dd50..dd23dbf 100644
--- a/docs/html/resources/tutorials/opengl/opengl-es20.jd
+++ b/docs/html/resources/tutorials/opengl/opengl-es20.jd
@@ -57,7 +57,7 @@
 needs. For more information, see
 <a href="{@docRoot}guide/topics/graphics/opengl.html#choosing-version">Choosing an OpenGL API
 Version</a>. If you would prefer to use OpenGL ES 1.0, see the <a
-href="{@docRoot}resources/tutorials/opengl/opengl-es10.jd">OpenGL ES 1.0 tutorial</a>.</p>
+href="{@docRoot}resources/tutorials/opengl/opengl-es10.html">OpenGL ES 1.0 tutorial</a>.</p>
 
 <p>Before you start, you should understand how to create a basic Android application. If you do not
 know how to create an app, follow the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello
diff --git a/docs/html/resources/tutorials/views/hello-mapview.jd b/docs/html/resources/tutorials/views/hello-mapview.jd
index 836d22c..ac5e826 100644
--- a/docs/html/resources/tutorials/views/hello-mapview.jd
+++ b/docs/html/resources/tutorials/views/hello-mapview.jd
@@ -255,7 +255,7 @@
 <pre>
 List&lt;Overlay> mapOverlays = mapView.getOverlays();
 Drawable drawable = this.getResources().getDrawable(R.drawable.androidmarker);
-HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable);</pre>
+HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable, this);</pre>
 
     <p>All overlay elements on a map are held by the {@code MapView}, so when you want to add some,
     you have to get a list from the <code>getOverlays()</code> method. Then instantiate the {@link
diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd
index 44fe5e4..af25609 100644
--- a/docs/html/sdk/download.jd
+++ b/docs/html/sdk/download.jd
@@ -52,7 +52,7 @@
   <script language="javascript">
     var loc = window.location.href;
     if (loc.indexOf('?v=') != -1) {
-      var filename = loc.substring(loc.indexOf('=')+1,loc.length);
+      var filename = loc.substring(loc.indexOf('=')+1,loc.length).replace(/</g,"&lt;").replace(/>/g,"&gt;");
       document.write("File: " + filename);
     }
   </script>
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index afbad57..29f0749 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -1,16 +1,16 @@
 ndk=true
 
-ndk.win_download=android-ndk-r7-windows.zip
-ndk.win_bytes=81270552
-ndk.win_checksum=55483482cf2b75e8dd1a5d9a7caeb6e5
+ndk.win_download=android-ndk-r7b-windows.zip
+ndk.win_bytes=80346206
+ndk.win_checksum=c42b0c9c14428397337421d5e4999380
 
-ndk.mac_download=android-ndk-r7-darwin-x86.tar.bz2
-ndk.mac_bytes=71262092
-ndk.mac_checksum=817ca5675a1dd44078098e43070f19b6
+ndk.mac_download=android-ndk-r7b-darwin-x86.tar.bz2
+ndk.mac_bytes=73817184
+ndk.mac_checksum=6daa82ca6b73bc0614c9997430079c7a
 
-ndk.linux_download=android-ndk-r7-linux-x86.tar.bz2
-ndk.linux_bytes=64884365
-ndk.linux_checksum=bf15e6b47bf50824c4b96849bf003ca3
+ndk.linux_download=android-ndk-r7b-linux-x86.tar.bz2
+ndk.linux_bytes=64349733
+ndk.linux_checksum=0eb8af18796cdaa082df8f7c54ad7f9a
 
 page.title=Android NDK
 
@@ -62,6 +62,116 @@
 <div class="toggleable open">
   <a href="#" onclick="return toggleDiv(this)"><img src=
   "{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px">
+  Android NDK, Revision 7b</a> <em>(February 2012)</em>
+
+  <div class="toggleme">
+    <p>This release of the NDK includes fixes for native Windows builds, Cygwin and many other
+      improvements:</p>
+
+    <dl>
+      <dt>Important bug fixes:</dt>
+
+      <dd>
+        <ul>
+          <li>Updated {@code sys/atomics.h} to avoid correctness issues
+            on some multi-core ARM-based devices. Rebuild your unmodified sources with this
+            version of the NDK and this problem should be completely eliminated.
+            For more details, read {@code docs/ANDROID-ATOMICS.html}.</li>
+          <li>Reverted to {@code binutils} 2.19 to fix debugging issues that
+            appeared in NDK r7 (which switched to {@code binutils} 2.20.1).</li>
+          <li>Fixed {@code ndk-build} on 32-bit Linux. A packaging error put a 64-bit version
+            of the {@code awk} executable under {@code prebuilt/linux-x86/bin} in NDK r7.</li>
+          <li>Fixed native Windows build ({@code ndk-build.cmd}). Other build modes were not
+            affected. The fixes include:
+            <ul>
+              <li>Removed an infinite loop / stack overflow bug that happened when trying
+                to call {@code ndk-build.cmd} from a directory that was <em>not</em> the top of
+                your project path (e.g., in any sub-directory of it).</li>
+              <li>Fixed a problem where the auto-generated dependency files were ignored. This
+                meant that updating a header didn't trigger recompilation of sources that included
+                it.</li>
+              <li>Fixed a problem where special characters in files or paths, other than spaces and
+                quotes, were not correctly handled.</li>
+            </ul>
+          </li>
+          <li>Fixed the standalone toolchain to generate proper binaries when using
+            {@code -lstdc++} (i.e., linking against the GNU {@code libstdc++} C++ runtime). You
+            should use {@code -lgnustl_shared} if you want to link against the shared library
+            version or {@code -lstdc++} for the static version.
+
+            <p>See {@code docs/STANDALONE-TOOLCHAIN.html} for more details about this fix.</p>
+          </li>
+          <li>Fixed {@code gnustl_shared} on Cygwin. The linker complained that it couldn't find
+            {@code libsupc++.a} even though the file was at the right location.</li>
+          <li>Fixed Cygwin C++ link when not using any specific C++ runtime through
+            {@code APP_STL}.</li>
+        </ul>
+      </dd>
+    </dl>
+
+    <dl>
+      <dt>Other changes:</dt>
+
+      <dd>
+        <ul>
+          <li>When your application uses the GNU {@code libstdc++} runtime, the compiler will
+            no longer forcibly enable exceptions and RTTI. This change results in smaller code.
+            <p>If you need these features, you must do one of the following:</p>
+            <ul>
+              <li>Enable exceptions and/or RTTI explicitly in your modules or
+                {@code Application.mk}. (recommended)</li>
+              <li>Define {@code APP_GNUSTL_FORCE_CPP_FEATURES} to {@code 'exceptions'},
+                {@code 'rtti'} or both in your {@code Application.mk}. See
+                {@code docs/APPLICATION-MK.html} for more details.</li>
+            </ul>
+          </li>
+          <li>{@code ndk-gdb} now works properly when your application has private services
+            running in independent processes. It debugs the main application process, instead of the
+            first process listed by {@code ps}, which is usually a service process.</li>
+          <li>Fixed a rare bug where NDK r7 would fail to honor the {@code LOCAL_ARM_MODE} value
+            and always compile certain source files (but not all) to 32-bit instructions.</li>
+          <li>{@code stlport}: Refresh the sources to match the Android platform version. This
+            update fixes a few minor bugs:
+            <ul>
+               <li>Fixed instantiation of an incomplete type</li>
+               <li>Fixed minor "==" versus "=" typo</li>
+               <li>Used {@code memmove} instead of {@code memcpy} in {@code string::assign}</li>
+               <li>Added better handling of {@code IsNANorINF}, {@code IsINF}, {@code IsNegNAN},
+                 etc.</li>
+             </ul>
+             <p>For complete details, see the commit log.</p>
+          </li>
+          <li>{@code stlport}: Removed 5 unnecessary static initializers from the library.</li>
+          <li>The GNU libstdc++ libraries for armeabi-v7a were mistakenly compiled for
+            armeabi instead. This change had no impact on correctness, but using the right
+            ABI should provide slightly better performance.</li>
+          <li>The {@code cpu-features} helper library was updated to report three optional
+            x86 CPU features ({@code SSSE3}, {@code MOVBE} and {@code POPCNT}). See
+            {@code docs/CPU-FEATURES.html} for more details.</li>
+          <li>{@code docs/NDK-BUILD.html} was updated to mention {@code NDK_APPLICATION_MK} instead
+            of {@code NDK_APP_APPLICATION_MK} to select a custom {@code Application.mk} file.</li>
+          <li>Cygwin: {@code ndk-build} no longer creates an empty "NUL" file in the current
+            directory when invoked.</li>
+          <li>Cygwin: Added better automatic dependency detection. In the previous version, it
+            didn't work properly in the following cases:
+            <ul>
+              <li>When the Cygwin drive prefix was not {@code /cygdrive}.</li>
+              <li>When using drive-less mounts, for example, when Cygwin would translate
+                {@code /home} to {@code \\server\subdir} instead of {@code C:\Some\Dir}.</li>
+            </ul>
+          </li>
+          <li>Cygwin: {@code ndk-build} does not try to use the native Windows tools under
+            {@code $NDK/prebuilt/windows/bin} with certain versions of Cygwin and/or GNU Make.</li>
+        </ul>
+      </dd>
+    </dl>
+  </div>
+</div>
+
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)"><img src=
+  "{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px">
   Android NDK, Revision 7</a> <em>(November 2011)</em>
 
   <div class="toggleme">
diff --git a/docs/html/sdk/ndk/overview.jd b/docs/html/sdk/ndk/overview.jd
index 85599f7..e969f5d 100644
--- a/docs/html/sdk/ndk/overview.jd
+++ b/docs/html/sdk/ndk/overview.jd
@@ -46,7 +46,7 @@
     <li>Documentation, samples, and tutorials</li>
   </ul>
 
-  <p>The latest release of the NDK supports these ARM instruction sets:</p>
+  <p>The latest release of the NDK supports the following instruction sets:</p>
 
   <ul>
     <li>ARMv5TE (including Thumb-1 instructions)</li>
diff --git a/docs/html/sdk/oem-usb.jd b/docs/html/sdk/oem-usb.jd
index b81be71..f98257d 100644
--- a/docs/html/sdk/oem-usb.jd
+++ b/docs/html/sdk/oem-usb.jd
@@ -3,9 +3,21 @@
 
 <div id="qv-wrapper">
 <div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#InstallingDriver">Installing a USB Driver</a>
+      <ol>
+        <li><a href="#Win7">Windows 7</a></li>
+        <li><a href="#WinXp">Windows XP</a></li>
+        <li><a href="#WinVista">Windows Vista</a></li>
+      </ol>
+    </li>
+    <li><a href="#Drivers">OEM Drivers</a></li>
+  </ol>
+
   <h2>See also</h2>
   <ol>
-    <li><a href="{@docRoot}guide/developing/device.html">Developing on a Device</a></li>
+    <li><a href="{@docRoot}guide/developing/device.html">Using Hardware Devices</a></li>
     <li><a href="{@docRoot}sdk/win-usb.html">Google USB Driver</a></li>
   </ol>
 </div>
@@ -18,8 +30,8 @@
 not exhaustive for all available Android-powered devices.</p>
 
 <p>If you're developing on Mac OS X or Linux, then you probably don't need to install a USB driver.
-Refer to <a href="{@docRoot}guide/developing/device.html#setting-up">Setting up a Device</a> to
-start development with a device.</p>
+To start developing with your device, read <a
+href="{@docRoot}guide/developing/device.html">Using Hardware Devices</a>.</p>
 
 <p class="note"><strong>Note:</strong> If your device is one of the Android Developer Phones
 (purchased from the Android Market publisher site), a Nexus One, or a Nexus S, then you need
@@ -28,10 +40,184 @@
 href="http://www.samsung.com/us/support/downloads/verizon-wireless/SCH-I515MSAVZW">Samsung</a>
 (listed as model SCH-I515).</p>
 
-<p>For instructions about how to install the driver on Windows, follow the guide for <a
- href="{@docRoot}sdk/win-usb.html#InstallingDriver">Installing the USB Driver</a>.</p>
 
-<p class="table-caption"><strong>Table 1.</strong> Links to OEM USB drivers</p>
+<h2 id="InstallingDriver">Installing a USB Driver</h2>
+
+<p>First, find the appropriate driver for your device from the <a href="#Drivers">OEM drivers</a>
+table below.</p>
+
+<p>Once you've downloaded your USB driver, follow the instructions below to install or upgrade the
+driver, based on your version of Windows and whether you're installing for the first time
+or upgrading an existing driver.</p>
+
+<p class="note"><strong>Tip:</strong> When you finish the USB driver installation,
+see <a
+href="{@docRoot}guide/developing/device.html">Using Hardware Devices</a> for
+other important information about using an Android-powered device for
+development.</p>
+
+<ol class="nolist">
+  <li><a href="#Win7">Windows 7</a></li>
+  <li><a href="#WinXp">Windows XP</a></li>
+  <li><a href="#WinVista">Windows Vista</a></li>
+</ol>
+
+
+<p class="caution"><strong>Caution:</strong>
+You may make changes to <code>android_winusb.inf</code> file found inside
+<code>usb_driver\</code> (for example, to add support for other devices),
+however, this will lead to security warnings when you install or upgrade the
+driver. Making any other changes to the driver files may break the installation
+process.</p>
+
+
+<h3 id="Win7">Windows 7</h3>
+
+
+<p>To install the Android USB driver on Windows 7 for the first time:</p>
+<ol>
+  <li>Connect your Android-powered device to your computer's USB port.</li>
+  <li>Right-click on <em>Computer</em> from your desktop or Windows Explorer,
+    and select <strong>Manage</strong>.</li>
+  <li>Select <strong>Devices</strong> in the left pane.</li>
+  <li>Locate and expand <em>Other device</em> in the right pane.</li>
+  <li>Right-click the device name (such as <em>Nexus S</em>) and select <strong>Update
+  Driver Software</strong>.
+    This will launch the Hardware Update Wizard.</li>
+  <li>Select <strong>Browse my computer for driver software</strong> and click
+    <strong>Next</strong>.</li>
+  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
+Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.)</li>
+  <li>Click <strong>Next</strong> to install the driver.</li>
+</ol>
+
+<p>Or, to <em>upgrade</em> an existing Android USB driver on Windows 7 with the new
+driver:</p>
+
+<ol>
+  <li>Connect your Android-powered device to your computer's USB port.</li>
+  <li>Right-click on <em>Computer</em> from your desktop or Windows Explorer,
+    and select <strong>Manage</strong>.</li>
+  <li>Select <strong>Device Manager</strong> in the left pane of the Computer Management
+  window.</li>
+  <li>Locate and expand <em>Android Phone</em> in the right pane.</li>
+  <li>Right-click <em>Android Composite ADB Interface</em> and select <strong>Update
+  Driver</strong>.
+    This will launch the Hardware Update Wizard.</li>
+  <li>Select <strong>Install from a list or specific location</strong> and click
+    <strong>Next</strong>.</li>
+  <li>Select <strong>Search for the best driver in these locations</strong>; un-check
+<strong>Search removable media</strong>; and check <strong>Include this location in the
+search</strong>.</li>
+  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
+Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.)</li>
+  <li>Click <strong>Next</strong> to upgrade the driver.</li>
+</ol>
+
+
+
+
+
+<h3 id="WinXp">Windows XP</h3>
+
+<p>To install the Android USB driver on Windows XP for the first time:</p>
+
+<ol>
+  <li>Connect your Android-powered device to your computer's USB port. Windows 
+    will detect the device and launch the Hardware Update Wizard.</li>
+  <li>Select <strong>Install from a list or specific location</strong> and click
+    <strong>Next</strong>.</li>
+  <li>Select <strong>Search for the best driver in these locations</strong>; un-check
+<strong>Search
+    removable media</strong>; and check <strong>Include
+this location in the search</strong>.</li>
+  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
+Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.)</li>
+  <li>Click <strong>Next</strong> to install the driver.</li>
+</ol>
+
+<p>Or, to <em>upgrade</em> an existing Android USB driver on Windows XP with the new
+driver:</p>
+
+<ol>
+  <li>Connect your Android-powered device to your computer's USB port.</li>
+  <li>Right-click on <em>My Computer</em> from your desktop or Windows Explorer,
+    and select <strong>Manage</strong>.</li>
+  <li>Select <strong>Device Manager</strong> in the left pane.</li>
+  <li>Locate and expand <em>Android Phone</em> in the right pane.</li>
+  <li>Right-click <em>Android Composite ADB Interface</em> and select <strong>Update
+  Driver</strong>.
+    This will launch the Hardware Update Wizard.</li>
+  <li>Select <strong>Install from a list or specific location</strong> and click
+    <strong>Next</strong>.</li>
+  <li>Select <strong>Search for the best driver in these locations</strong>; un-check <strong>Search
+    removable media</strong>; and check <strong>Include
+this location in the search</strong>.</li>
+  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
+Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.)</li>
+  <li>Click <strong>Next</strong> to upgrade the driver.</li>
+</ol>
+
+
+
+<h3 id="WinVista">Windows Vista</h3>
+
+<p>To install the Android USB driver on Windows Vista for the first time:</p>
+
+<ol>
+  <li>Connect your Android-powered device to your computer's USB port. Windows
+  will detect the device and launch the Found New Hardware wizard.</li>
+  <li>Select <strong>Locate and install driver software</strong>.</li>
+  <li>Select <strong>Don't search online</strong>.</li>
+  <li>Select <strong>I don't have the disk. Show me other options</strong>.</li>
+  <li>Select <strong>Browse my computer for driver software</strong>.</li>
+  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
+Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.) As long as you specified the
+exact location of the 
+    installation package, you may leave <strong>Include subfolders</strong> checked or
+  unchecked&mdash;it doesn't matter.</li>
+  <li>Click <strong>Next</strong>. Vista may prompt you to confirm the privilege elevation
+  required for driver installation. Confirm it.</li>
+  <li>When Vista asks if you'd like to install the Google ADB Interface device,
+  click <strong>Install</strong> to install the driver.</li>
+</ol>
+
+<p>Or, to <em>upgrade</em> an existing Android USB driver on Windows Vista with the new
+driver:</p>
+
+<ol>
+  <li>Connect your Android-powered device to your computer's USB port.</li>
+  <li>Right-click on <em>Computer</em> from your desktop or Windows Explorer,
+    and select <strong>Manage</strong>.</li>
+  <li>Select <strong>Device Manager</strong> in the left pane.</li>
+  <li>Locate and expand <em>ADB Interface</em> in the right pane.</li>
+  <li>Right-click on <em>HTC Dream Composite ADB Interface</em>, and select <strong>Update
+  Driver Software</strong>.</li>
+  <li>When Vista starts updating the driver, a prompt will ask how you want to
+  search for the driver
+    software. Select <strong>Browse my computer for driver software</strong>.</li>
+  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
+Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.) As long as you specified the
+exact location of the 
+    installation package, you may leave <strong>Include subfolders</strong> checked or
+    unchecked&mdash;it doesn't matter.</li>
+  <li>Click <strong>Next</strong>. Vista might prompt you to confirm the privilege elevation
+  required for driver installation. Confirm it.</li>
+  <li>When Vista asks if you'd like to install the Google ADB Interface device,
+  click <strong>Install</strong> to upgrade the driver.</li>
+</ol>
+  
+
+<h2 id="Drivers">OEM Drivers</h2>
+
+<p class="note"><strong>Note:</strong> If your device is one of the Android Developer Phones
+(purchased from the Android Market publisher site), a Nexus One, or a Nexus S, then you need
+the <a href="{@docRoot}sdk/win-usb.html">Google USB Driver</a>, instead of an OEM driver. The Galaxy
+Nexus driver, however, is distributed by <a
+href="http://www.samsung.com/us/support/downloads/verizon-wireless/SCH-I515MSAVZW">Samsung</a>
+(listed as model SCH-I515).</p>
+
+
 <table><tr>
     <th>OEM</th>
     <th>Driver URL</th></tr>
@@ -92,7 +278,7 @@
 </tr>
 
 <tr><td>KT Tech</td>	<td><a
-href="http://www.kttech.co.kr/cscenter/download05.asp">http://www.kttech.co.kr/cscenter/download05.asp</a> for EV-S100(Take)</td>
+href="http://www.kttech.co.kr/cscenter/download05.asp">http://www.kttech.co.kr/cscenter/download05.asp</a> for EV-S100 (Take)</td>
 </tr>
   <tr>
     <td>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 0de477a..f7541f7 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -195,7 +195,7 @@
       <span style="display:none" class="zh-TW"></span>
     </span>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r7</a>
+      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r7b</a>
         <span class="new">new!</span>
         </li>
       <li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li>
diff --git a/docs/html/sdk/win-usb.jd b/docs/html/sdk/win-usb.jd
index 2d1435b..2bd031e 100644
--- a/docs/html/sdk/win-usb.jd
+++ b/docs/html/sdk/win-usb.jd
@@ -7,19 +7,12 @@
   <ol>
     <li><a href="#notes">Revisions</a></li>
     <li><a href="#WinUsbDriver">Downloading the Google USB Driver</a></li>
-    <li><a href="#InstallingDriver">Installing the USB Driver</a>
-      <ol>
-        <li><a href="#Win7">Windows 7</a></li>
-        <li><a href="#WinXp">Windows XP</a></li>
-        <li><a href="#WinVista">Windows Vista</a></li>
-      </ol>
-    </li>
   </ol>
   <h2>See also</h2>
   <ol>
-    <li><a href="{@docRoot}guide/developing/device.html">Developing on a Device</a></li>
+    <li><a href="{@docRoot}sdk/oem-usb.html#InstallingDriver">Installing a USB Driver</a></li>
+    <li><a href="{@docRoot}guide/developing/device.html">Using Hardware Devices</a></li>
     <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
-    <li><a href="{@docRoot}sdk/oem-usb.html">OEM USB Drivers</a></li>
   </ol>
 </div>
 </div>
@@ -43,9 +36,9 @@
 (listed as model SCH-I515).</p>
 
 <p class="note"><strong>Note:</strong>
-If you're developing on Mac OS X or Linux, then you do not need to install a USB driver. Refer to <a
-href="{@docRoot}guide/developing/device.html#setting-up">Setting up a Device</a> to start
-development with a device.</p>
+If you're developing on Mac OS X or Linux, then you do not need to install a USB driver. To start
+developing with your device, also read <a href="{@docRoot}guide/developing/device.html">Using
+Hardware Devices</a>.</p>
 
 <p>The sections below provide instructions on how to download and install the Google USB Driver
 for Windows. </p>
@@ -170,174 +163,10 @@
 <ol>
   <li>Launch the SDK and AVD Manager by double-clicking <code>SDK Manager.exe</code>,
   at the root of your SDK directory.</li>
-  <li>Expand the <em>Third party Add-ons</em> and <em>Google Inc. add-ons</em>.</li>
-  <li>Check <strong>Google Usb Driver package</strong> and click <strong>Install selected</strong>.</li>
+  <li>Expand <em>Extras</em>.</li>
+  <li>Check <strong>Google USB Driver package</strong> and click <strong>Install</strong>.</li>
   <li>Proceed to install the package. When done, the driver files are
 downloaded into the <code>&lt;sdk&gt;\extras\google\usb_driver\</code> directory.</li>
 </ol>
 
-
-
-<h2 id="InstallingDriver">Installing the USB Driver</h2>
-
-<p>Once you've downloaded your USB driver, follow the instructions below to install or upgrade the
-driver, based on your version of Windows and whether you're installing for the first time
-or upgrading an existing driver.</p>
-
-<p class="note"><strong>Tip:</strong> When you finish the USB driver installation,
-see <a
-href="{@docRoot}guide/developing/device.html">Developing on a Device</a> for
-other important information about using an Android-powered device for
-development.</p>
-
-<ol class="nolist">
-  <li><a href="#Win7">Windows 7</a></li>
-  <li><a href="#WinXp">Windows XP</a></li>
-  <li><a href="#WinVista">Windows Vista</a></li>
-</ol>
-
-
-<p class="caution"><strong>Caution:</strong>
-You may make changes to <code>android_winusb.inf</code> file found inside
-<code>usb_driver\</code> (for example, to add support for other devices),
-however, this will lead to security warnings when you install or upgrade the
-driver. Making any other changes to the driver files may break the installation
-process.</p>
-
-
-<h3 id="Win7">Windows 7</h3>
-
-
-<p>To install the Android USB driver on Windows 7 for the first time:</p>
-<ol>
-  <li>Connect your Android-powered device to your computer's USB port.</li>
-  <li>Right-click on <em>Computer</em> from your desktop or Windows Explorer,
-    and select <strong>Manage</strong>.</li>
-  <li>Select <strong>Devices</strong> in the left pane.</li>
-  <li>Locate and expand <em>Other device</em> in the right pane.</li>
-  <li>Right-click the device name (such as <em>Nexus S</em>) and select <strong>Update
-  Driver Software</strong>.
-    This will launch the Hardware Update Wizard.</li>
-  <li>Select <strong>Browse my computer for driver software</strong> and click
-    <strong>Next</strong>.</li>
-  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
-Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.)</li>
-  <li>Click <strong>Next</strong> to install the driver.</li>
-</ol>
-
-<p>Or, to <em>upgrade</em> an existing Android USB driver on Windows 7 with the new
-driver:</p>
-
-<ol>
-  <li>Connect your Android-powered device to your computer's USB port.</li>
-  <li>Right-click on <em>Computer</em> from your desktop or Windows Explorer,
-    and select <strong>Manage</strong>.</li>
-  <li>Select <strong>Device Manager</strong> in the left pane of the Computer Management
-  window.</li>
-  <li>Locate and expand <em>Android Phone</em> in the right pane.</li>
-  <li>Right-click <em>Android Composite ADB Interface</em> and select <strong>Update
-  Driver</strong>.
-    This will launch the Hardware Update Wizard.</li>
-  <li>Select <strong>Install from a list or specific location</strong> and click
-    <strong>Next</strong>.</li>
-  <li>Select <strong>Search for the best driver in these locations</strong>; un-check
-<strong>Search removable media</strong>; and check <strong>Include this location in the
-search</strong>.</li>
-  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
-Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.)</li>
-  <li>Click <strong>Next</strong> to upgrade the driver.</li>
-</ol>
-
-
-
-
-
-<h3 id="WinXp">Windows XP</h3>
-
-<p>To install the Android USB driver on Windows XP for the first time:</p>
-
-<ol>
-  <li>Connect your Android-powered device to your computer's USB port. Windows 
-    will detect the device and launch the Hardware Update Wizard.</li>
-  <li>Select <strong>Install from a list or specific location</strong> and click
-    <strong>Next</strong>.</li>
-  <li>Select <strong>Search for the best driver in these locations</strong>; un-check
-<strong>Search
-    removable media</strong>; and check <strong>Include
-this location in the search</strong>.</li>
-  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
-Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.)</li>
-  <li>Click <strong>Next</strong> to install the driver.</li>
-</ol>
-
-<p>Or, to <em>upgrade</em> an existing Android USB driver on Windows XP with the new
-driver:</p>
-
-<ol>
-  <li>Connect your Android-powered device to your computer's USB port.</li>
-  <li>Right-click on <em>My Computer</em> from your desktop or Windows Explorer,
-    and select <strong>Manage</strong>.</li>
-  <li>Select <strong>Device Manager</strong> in the left pane.</li>
-  <li>Locate and expand <em>Android Phone</em> in the right pane.</li>
-  <li>Right-click <em>Android Composite ADB Interface</em> and select <strong>Update
-  Driver</strong>.
-    This will launch the Hardware Update Wizard.</li>
-  <li>Select <strong>Install from a list or specific location</strong> and click
-    <strong>Next</strong>.</li>
-  <li>Select <strong>Search for the best driver in these locations</strong>; un-check <strong>Search
-    removable media</strong>; and check <strong>Include
-this location in the search</strong>.</li>
-  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
-Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.)</li>
-  <li>Click <strong>Next</strong> to upgrade the driver.</li>
-</ol>
-
-
-
-<h3 id="WinVista">Windows Vista</h3>
-
-<p>To install the Android USB driver on Windows Vista for the first time:</p>
-
-<ol>
-  <li>Connect your Android-powered device to your computer's USB port. Windows
-  will detect the device and launch the Found New Hardware wizard.</li>
-  <li>Select <strong>Locate and install driver software</strong>.</li>
-  <li>Select <strong>Don't search online</strong>.</li>
-  <li>Select <strong>I don't have the disk. Show me other options</strong>.</li>
-  <li>Select <strong>Browse my computer for driver software</strong>.</li>
-  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
-Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.) As long as you specified the
-exact location of the 
-    installation package, you may leave <strong>Include subfolders</strong> checked or
-  unchecked&mdash;it doesn't matter.</li>
-  <li>Click <strong>Next</strong>. Vista may prompt you to confirm the privilege elevation
-  required for driver installation. Confirm it.</li>
-  <li>When Vista asks if you'd like to install the Google ADB Interface device,
-  click <strong>Install</strong> to install the driver.</li>
-</ol>
-
-<p>Or, to <em>upgrade</em> an existing Android USB driver on Windows Vista with the new
-driver:</p>
-
-<ol>
-  <li>Connect your Android-powered device to your computer's USB port.</li>
-  <li>Right-click on <em>Computer</em> from your desktop or Windows Explorer,
-    and select <strong>Manage</strong>.</li>
-  <li>Select <strong>Device Manager</strong> in the left pane.</li>
-  <li>Locate and expand <em>ADB Interface</em> in the right pane.</li>
-  <li>Right-click on <em>HTC Dream Composite ADB Interface</em>, and select <strong>Update
-  Driver Software</strong>.</li>
-  <li>When Vista starts updating the driver, a prompt will ask how you want to
-  search for the driver
-    software. Select <strong>Browse my computer for driver software</strong>.</li>
-  <li>Click <strong>Browse</strong> and locate the USB driver folder. (The Google USB
-Driver is located in {@code &lt;sdk&gt;\extras\google\usb_driver\}.) As long as you specified the
-exact location of the 
-    installation package, you may leave <strong>Include subfolders</strong> checked or
-    unchecked&mdash;it doesn't matter.</li>
-  <li>Click <strong>Next</strong>. Vista might prompt you to confirm the privilege elevation
-  required for driver installation. Confirm it.</li>
-  <li>When Vista asks if you'd like to install the Google ADB Interface device,
-  click <strong>Install</strong> to upgrade the driver.</li>
-</ol>
-  
+<p>For installation information, read <a href="{@docRoot}sdk/oem-usb.html#InstallingDriver">Installing a USB Driver</a>.</p>
diff --git a/docs/html/training/improving-layouts/optimizing-layout.jd b/docs/html/training/improving-layouts/optimizing-layout.jd
index 65c8af7..0eaf199 100644
--- a/docs/html/training/improving-layouts/optimizing-layout.jd
+++ b/docs/html/training/improving-layouts/optimizing-layout.jd
@@ -18,7 +18,7 @@
 <ol>
   <li><a href="#Inspect">Inspect Your Layout</a></li>
   <li><a href="#Revise">Revise Your Layout</a></li>
-  <li><a href="#Layoutopt">Use Layoutopt</a></li>
+  <li><a href="#Lint">Use Lint</a></li>
 </ol>
 
 <!-- other docs (NOT javadocs) -->
@@ -44,7 +44,7 @@
 android.widget.GridView}.</p>
 
 <p>In this lesson you'll learn to use <a
-href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">Heirachy Viewer</a> and <a
+href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">Hierarchy Viewer</a> and <a
 href="{@docRoot}guide/developing/tools/layoutopt.html">Layoutopt</a> to examine and optimize your
 layout.</p>
 
@@ -53,7 +53,7 @@
 <h2 id="Inspect">Inspect Your Layout</h2>
 
 <p>The Android SDK tools include a tool called <a
-href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">Heirachy Viewer</a> that allows
+href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">Hierarchy Viewer</a> that allows
 you to analyze your layout while your application is running. Using this tool helps you discover
 bottlenecks in the layout performance.</p>
 
@@ -130,27 +130,28 @@
 layout weight is necessary.</p>
 
 
-<h2 id="Layoutopt">Use Layoutopt</h2>
+<h2 id="Lint">Use Lint</h2>
 
-<p>It is always good practice to also run the <a
-href="{@docRoot}guide/developing/tools/layoutopt.html">layoutopt</a> tool on your final layout files
-to search for places in your view hierarchy that may be optimized. Layoutopt is also in your SDK
-{@code tools/} directory and takes a layout directory name or a space-separated list of layout files
-that you'd like to inspect.</p>
+<p>It is always good practice to run the <a href="http://tools.android.com/tips/lint">Lint</a> tool on your layout files to search for possible view hierarchy optimizations. Lint has replaced the Layoutopt tool and has much greater functionality. Some examples of Lint <a
+href="http://tools.android.com/tips/lint-checks">rules</a> are:</p>
 
-<p>When you run {@code layoutopt} on a layout file, it prints a line number for each issue found, a
-description of the issue, and for some types of issues it also suggests a resolution. For
-example:</p>
+<ul>
+<li>Use compound drawables - A {@link android.widget.LinearLayout} which contains an {@link android.widget.ImageView} and a {@link android.widget.TextView} can be more efficiently handled as a compound drawable.</li>
+<li>Merge root frame - If a {@link android.widget.FrameLayout} is the root of a layout and does not provide background or padding etc, it can be replaced with a merge tag which is slightly more efficient.</li>
+<li>Useless leaf - A layout that has no children or no background can often be removed (since it is invisible) for a flatter and more efficient layout hierarchy.</li>
+<li>Useless parent - A layout with children that has no siblings, is not a {@link android.widget.ScrollView} or a root layout, and does not have a background, can be removed and have its children moved directly into the parent for a flatter and more efficient layout hierarchy.</li>
+<li>Deep layouts - Layouts with too much nesting are bad for performance. Consider using flatter layouts such as {@link android.widget.RelativeLayout} or {@link android.widget.GridLayout} to improve performance. The default maximum depth is 10.</li>
+</ul>
 
-<pre class="no-pretty-print classic">
-$ layoutopt samples/
-samples/compound.xml
-   7:23 The root-level &lt;FrameLayout/&gt; can be replaced with &lt;merge/&gt;
-   11:21 This LinearLayout layout or its FrameLayout parent is useless
-samples/simple.xml
-   7:7 The root-level &lt;FrameLayout/&gt; can be replaced with &lt;merge/&gt;
-</pre>
+<p>Another benefit of Lint is that it is integrated into the Android Development Tools for Eclipse (ADT 16+). Lint automatically runs whenever you export an APK, edit and save an XML file or use the Layout Editor. To manually force Lint to run press the Lint button in the Eclipse toolbar.</p>
 
-<p>After you apply the suggested layout optimizations, run Hierarchy Viewer again to inspect the
-performance changes.</p>
+<img src="{@docRoot}images/training/lint_icon.png" alt="" />
+
+<p>When used inside Eclipse, Lint has the ability to automatically fix some issues, provide suggestions for others and jump directly to the offending code for review. If you don’t use Eclipse for your development, Lint can also be run from the command line. More information about Lint is available at <a href="http://tools.android.com/tips/lint">tools.android.com</a>.</p>
+
+
+
+
+
+
 
diff --git a/drm/java/android/drm/DrmInfo.java b/drm/java/android/drm/DrmInfo.java
index 8812bfe..22d06c7 100755
--- a/drm/java/android/drm/DrmInfo.java
+++ b/drm/java/android/drm/DrmInfo.java
@@ -49,6 +49,13 @@
         mInfoType = infoType;
         mMimeType = mimeType;
         mData = data;
+        if (!isValid()) {
+            final String msg = "infoType: " + infoType + "," +
+                               "mimeType: " + mimeType + "," +
+                               "data: " + data;
+
+            throw new IllegalArgumentException(msg);
+        }
     }
 
     /**
@@ -69,6 +76,13 @@
             // call would fail with IllegalArgumentException because of mData = null
             mData = null;
         }
+        if (!isValid()) {
+            final String msg = "infoType: " + infoType + "," +
+                               "mimeType: " + mimeType + "," +
+                               "data: " + mData;
+
+            throw new IllegalArgumentException();
+        }
     }
 
     /**
diff --git a/drm/java/android/drm/DrmInfoRequest.java b/drm/java/android/drm/DrmInfoRequest.java
index 1429fa5..621da41 100755
--- a/drm/java/android/drm/DrmInfoRequest.java
+++ b/drm/java/android/drm/DrmInfoRequest.java
@@ -67,6 +67,11 @@
     public DrmInfoRequest(int infoType, String mimeType) {
         mInfoType = infoType;
         mMimeType = mimeType;
+        if (!isValid()) {
+            final String msg = "infoType: " + infoType + "," +
+                               "mimeType: " + mimeType;
+            throw new IllegalArgumentException(msg);
+        }
     }
 
     /**
diff --git a/drm/java/android/drm/DrmInfoStatus.java b/drm/java/android/drm/DrmInfoStatus.java
index 5c12ae3..2fe0a78 100755
--- a/drm/java/android/drm/DrmInfoStatus.java
+++ b/drm/java/android/drm/DrmInfoStatus.java
@@ -56,6 +56,10 @@
      * @param _mimeType The MIME type.
      */
     public DrmInfoStatus(int _statusCode, int _infoType, ProcessedData _data, String _mimeType) {
+        if (!DrmInfoRequest.isValidType(_infoType)) {
+            throw new IllegalArgumentException("infoType: " + _infoType);
+        }
+
         statusCode = _statusCode;
         infoType = _infoType;
         data = _data;
diff --git a/drm/java/android/drm/DrmRights.java b/drm/java/android/drm/DrmRights.java
index ef9c21d..d4afed1 100755
--- a/drm/java/android/drm/DrmRights.java
+++ b/drm/java/android/drm/DrmRights.java
@@ -103,6 +103,11 @@
         }
 
         mMimeType = mimeType;
+        if (!isValid()) {
+            final String msg = "mimeType: " + mMimeType + "," +
+                               "data: " + mData;
+            throw new IllegalArgumentException(msg);
+        }
     }
 
     /**
@@ -117,17 +122,25 @@
             mData = data.getData();
 
             String accountId = data.getAccountId();
-            if (null != accountId && !accountId.equals("")) {
-                mAccountId = accountId;
+            if (null == accountId || !accountId.equals("")) {
+                throw new IllegalArgumentException("accountId: " + accountId);
             }
+            mAccountId = accountId;
 
             String subscriptionId = data.getSubscriptionId();
-            if (null != subscriptionId && !subscriptionId.equals("")) {
-                mSubscriptionId = subscriptionId;
+            if (null == subscriptionId || !subscriptionId.equals("")) {
+                throw new IllegalArgumentException(
+                            "subscriptionId: " + subscriptionId);
             }
+            mSubscriptionId = subscriptionId;
         }
 
         mMimeType = mimeType;
+        if (!isValid()) {
+            final String msg = "mimeType: " + mMimeType + "," +
+                               "data: " + mData;
+            throw new IllegalArgumentException(msg);
+        }
     }
 
     /**
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 380b3d8..6f939be 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -408,16 +408,19 @@
     }
 
     /**
-     * Creates a new bitmap, scaled from an existing bitmap.
+     * Creates a new bitmap, scaled from an existing bitmap, when possible. If the
+     * specified width and height are the same as the current width and height of 
+     * the source btimap, the source bitmap is returned and now new bitmap is
+     * created.
      *
      * @param src       The source bitmap.
      * @param dstWidth  The new bitmap's desired width.
      * @param dstHeight The new bitmap's desired height.
      * @param filter    true if the source should be filtered.
-     * @return the new scaled bitmap.
+     * @return The new scaled bitmap or the source bitmap if no scaling is required.
      */
-    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
-            int dstHeight, boolean filter) {
+    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight,
+            boolean filter) {
         Matrix m;
         synchronized (Bitmap.class) {
             // small pool of just 1 matrix
@@ -458,14 +461,15 @@
     /**
      * Returns an immutable bitmap from the specified subset of the source
      * bitmap. The new bitmap may be the same object as source, or a copy may
-     * have been made.  It is
-     * initialized with the same density as the original bitmap.
+     * have been made. It is initialized with the same density as the original
+     * bitmap.
      *
      * @param source   The bitmap we are subsetting
      * @param x        The x coordinate of the first pixel in source
      * @param y        The y coordinate of the first pixel in source
      * @param width    The number of pixels in each row
      * @param height   The number of rows
+     * @return A copy of a subset of the source bitmap or the source bitmap itself.
      */
     public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {
         return createBitmap(source, x, y, width, height, null, false);
@@ -473,8 +477,13 @@
 
     /**
      * Returns an immutable bitmap from subset of the source bitmap,
-     * transformed by the optional matrix.  It is
+     * transformed by the optional matrix. The new bitmap may be the
+     * same object as source, or a copy may have been made. It is
      * initialized with the same density as the original bitmap.
+     * 
+     * If the source bitmap is immutable and the requested subset is the
+     * same as the source bitmap itself, then the source bitmap is
+     * returned and no new bitmap is created.
      *
      * @param source   The bitmap we are subsetting
      * @param x        The x coordinate of the first pixel in source
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 8d17561..bff2a76 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -518,15 +518,16 @@
         byte[] np = bm.getNinePatchChunk();
         final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
         if (opts.inScaled || isNinePatch) {
-            float scale = targetDensity / (float)density;
-            // TODO: This is very inefficient and should be done in native by Skia
-            final Bitmap oldBitmap = bm;
-            bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
-                    (int) (bm.getHeight() * scale + 0.5f), true);
-            oldBitmap.recycle();
+            float scale = targetDensity / (float) density;
+            if (scale != 1.0f) {
+                final Bitmap oldBitmap = bm;
+                bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
+                        (int) (bm.getHeight() * scale + 0.5f), true);
+                if (bm != oldBitmap) oldBitmap.recycle();
+            }
 
             if (isNinePatch) {
-                np = nativeScaleNinePatch(np, scale, outPadding);
+                if (scale != 1.0f) np = nativeScaleNinePatch(np, scale, outPadding);
                 bm.setNinePatchChunk(np);
             }
             bm.setDensity(targetDensity);
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 0ada1fb..ec911b0 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -352,8 +352,7 @@
                // check for empty first
         return this.left < this.right && this.top < this.bottom
                // now check for containment
-               && left <= r.left && top <= r.top
-               && right >= r.right && bottom >= r.bottom;
+               && left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom;
     }
 
     /**
@@ -375,20 +374,11 @@
      *              return false and do not change this rectangle.
      */
     public boolean intersect(int left, int top, int right, int bottom) {
-        if (this.left < right && left < this.right
-                && this.top < bottom && top < this.bottom) {
-            if (this.left < left) {
-                this.left = left;
-            }
-            if (this.top < top) {
-                this.top = top;
-            }
-            if (this.right > right) {
-                this.right = right;
-            }
-            if (this.bottom > bottom) {
-                this.bottom = bottom;
-            }
+        if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) {
+            if (this.left < left) this.left = left;
+            if (this.top < top) this.top = top;
+            if (this.right > right) this.right = right;
+            if (this.bottom > bottom) this.bottom = bottom;
             return true;
         }
         return false;
@@ -422,8 +412,7 @@
      *              false and do not change this rectangle.
      */
     public boolean setIntersect(Rect a, Rect b) {
-        if (a.left < b.right && b.left < a.right
-                && a.top < b.bottom && b.top < a.bottom) {
+        if (a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom) {
             left = Math.max(a.left, b.left);
             top = Math.max(a.top, b.top);
             right = Math.min(a.right, b.right);
@@ -448,8 +437,7 @@
      *              no event is this rectangle modified.
      */
     public boolean intersects(int left, int top, int right, int bottom) {
-        return this.left < right && left < this.right
-               && this.top < bottom && top < this.bottom;
+        return this.left < right && left < this.right && this.top < bottom && top < this.bottom;
     }
 
     /**
@@ -463,8 +451,7 @@
      *              either of the rectangles modified.
      */
     public static boolean intersects(Rect a, Rect b) {
-        return a.left < b.right && b.left < a.right
-               && a.top < b.bottom && b.top < a.bottom;
+        return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
     }
 
     /**
@@ -480,14 +467,10 @@
     public void union(int left, int top, int right, int bottom) {
         if ((left < right) && (top < bottom)) {
             if ((this.left < this.right) && (this.top < this.bottom)) {
-                if (this.left > left)
-                    this.left = left;
-                if (this.top > top)
-                    this.top = top;
-                if (this.right < right)
-                    this.right = right;
-                if (this.bottom < bottom)
-                    this.bottom = bottom;
+                if (this.left > left) this.left = left;
+                if (this.top > top) this.top = top;
+                if (this.right < right) this.right = right;
+                if (this.bottom < bottom) this.bottom = bottom;
             } else {
                 this.left = left;
                 this.top = top;
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 11c2427..6539ff3 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2012 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.
@@ -22,6 +22,7 @@
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.SurfaceTexture;
 import android.util.Log;
 import android.util.TypedValue;
 
@@ -78,6 +79,8 @@
     boolean mConstrainedFace;
     boolean mConstrainedY;
     boolean mConstrainedZ;
+    boolean mReadAllowed = true;
+    boolean mWriteAllowed = true;
     int mSelectedY;
     int mSelectedZ;
     int mSelectedLOD;
@@ -99,7 +102,7 @@
     public static final int USAGE_SCRIPT = 0x0001;
 
     /**
-     * GRAPHICS_TEXTURE The allcation will be used as a texture
+     * GRAPHICS_TEXTURE The allocation will be used as a texture
      * source by one or more graphics programs.
      *
      */
@@ -121,12 +124,38 @@
     public static final int USAGE_GRAPHICS_CONSTANTS = 0x0008;
 
     /**
-     * USAGE_GRAPHICS_RENDER_TARGET The allcation will be used as a
+     * USAGE_GRAPHICS_RENDER_TARGET The allocation will be used as a
      * target for offscreen rendering
      *
      */
     public static final int USAGE_GRAPHICS_RENDER_TARGET = 0x0010;
 
+    /**
+     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE The allocation
+     * will be used as a SurfaceTexture graphics consumer. This
+     * usage may only be used with USAGE_GRAPHICS_TEXTURE.
+     *
+     * @hide
+     */
+    public static final int USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE = 0x0020;
+
+    /**
+     * USAGE_IO_INPUT The allocation will be used as SurfaceTexture
+     * consumer.  This usage will cause the allocation to be created
+     * read only.
+     *
+     * @hide
+     */
+    public static final int USAGE_IO_INPUT = 0x0040;
+
+    /**
+     * USAGE_IO_OUTPUT The allocation will be used as a
+     * SurfaceTexture producer.  The dimensions and format of the
+     * SurfaceTexture will be forced to those of the allocation.
+     *
+     * @hide
+     */
+    public static final int USAGE_IO_OUTPUT = 0x0080;
 
     /**
      * Controls mipmap behavior when using the bitmap creation and
@@ -187,10 +216,26 @@
                        USAGE_GRAPHICS_TEXTURE |
                        USAGE_GRAPHICS_VERTEX |
                        USAGE_GRAPHICS_CONSTANTS |
-                       USAGE_GRAPHICS_RENDER_TARGET)) != 0) {
+                       USAGE_GRAPHICS_RENDER_TARGET |
+                       USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE |
+                       USAGE_IO_INPUT |
+                       USAGE_IO_OUTPUT)) != 0) {
             throw new RSIllegalArgumentException("Unknown usage specified.");
         }
+
+        if ((usage & (USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE | USAGE_IO_INPUT)) != 0) {
+            mWriteAllowed = false;
+
+            if ((usage & ~(USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE |
+                           USAGE_IO_INPUT |
+                           USAGE_GRAPHICS_TEXTURE |
+                           USAGE_SCRIPT)) != 0) {
+                throw new RSIllegalArgumentException("Invalid usage combination.");
+            }
+        }
+
         mType = t;
+        mUsage = usage;
 
         if (t != null) {
             updateCacheInfo(t);
@@ -278,6 +323,37 @@
         mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
     }
 
+    /**
+     * Send a buffer to the output stream.  The contents of the
+     * Allocation will be undefined after this operation.
+     *
+     * @hide
+     *
+     */
+    public void ioSendOutput() {
+        if ((mUsage & USAGE_IO_OUTPUT) == 0) {
+            throw new RSIllegalArgumentException(
+                "Can only send buffer if IO_OUTPUT usage specified.");
+        }
+        mRS.validate();
+        mRS.nAllocationIoSend(getID());
+    }
+
+    /**
+     * Receive the latest input into the Allocation.
+     *
+     * @hide
+     *
+     */
+    public void ioGetInput() {
+        if ((mUsage & USAGE_IO_INPUT) == 0) {
+            throw new RSIllegalArgumentException(
+                "Can only send buffer if IO_OUTPUT usage specified.");
+        }
+        mRS.validate();
+        mRS.nAllocationIoReceive(getID());
+    }
+
     public void copyFrom(BaseObj[] d) {
         mRS.validate();
         validateIsObject();
@@ -458,6 +534,7 @@
      * @param fp
      */
     public void setFromFieldPacker(int xoff, FieldPacker fp) {
+        mRS.validate();
         int eSize = mType.mElement.getSizeBytes();
         final byte[] data = fp.getData();
 
@@ -478,6 +555,7 @@
      * @param fp
      */
     public void setFromFieldPacker(int xoff, int component_number, FieldPacker fp) {
+        mRS.validate();
         if (component_number >= mType.mElement.mElements.length) {
             throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
         }
@@ -842,17 +920,37 @@
         updateCacheInfo(mType);
     }
 
-    /*
+    /**
+     * Resize a 2D allocation.  The contents of the allocation are
+     * preserved.  If new elements are allocated objects are created
+     * with null contents and the new region is otherwise undefined.
+     *
+     * If the new region is smaller the references of any objects
+     * outside the new region will be released.
+     *
+     * A new type will be created with the new dimension.
+     *
+     * @hide
+     * @param dimX The new size of the allocation.
+     * @param dimY The new size of the allocation.
+     */
     public void resize(int dimX, int dimY) {
-        if ((mType.getZ() > 0) || mType.getFaces() || mType.getLOD()) {
-            throw new RSIllegalStateException("Resize only support for 2D allocations at this time.");
+        if ((mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
+            throw new RSInvalidStateException(
+                "Resize only support for 2D allocations at this time.");
         }
         if (mType.getY() == 0) {
-            throw new RSIllegalStateException("Resize only support for 2D allocations at this time.");
+            throw new RSInvalidStateException(
+                "Resize only support for 2D allocations at this time.");
         }
         mRS.nAllocationResize2D(getID(), dimX, dimY);
+        mRS.finish();  // Necessary because resize is fifoed and update is async.
+
+        int typeID = mRS.nAllocationGetType(getID());
+        mType = new Type(typeID, mRS);
+        mType.updateFromNative();
+        updateCacheInfo(mType);
     }
-    */
 
 
 
@@ -876,7 +974,31 @@
         if (type.getID() == 0) {
             throw new RSInvalidStateException("Bad Type");
         }
-        int id = rs.nAllocationCreateTyped(type.getID(), mips.mID, usage);
+        int id = rs.nAllocationCreateTyped(type.getID(), mips.mID, usage, 0);
+        if (id == 0) {
+            throw new RSRuntimeException("Allocation creation failed.");
+        }
+        return new Allocation(id, rs, type, usage);
+    }
+
+    /**
+     * @hide
+     * This API is hidden and only intended to be used for
+     * transitional purposes.
+     *
+     * @param type renderscript type describing data layout
+     * @param mips specifies desired mipmap behaviour for the
+     *             allocation
+     * @param usage bit field specifying how the allocation is
+     *              utilized
+     */
+    static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips,
+                                         int usage, int pointer) {
+        rs.validate();
+        if (type.getID() == 0) {
+            throw new RSInvalidStateException("Bad Type");
+        }
+        int id = rs.nAllocationCreateTyped(type.getID(), mips.mID, usage, pointer);
         if (id == 0) {
             throw new RSRuntimeException("Allocation creation failed.");
         }
@@ -931,7 +1053,7 @@
         b.setX(count);
         Type t = b.create();
 
-        int id = rs.nAllocationCreateTyped(t.getID(), MipmapControl.MIPMAP_NONE.mID, usage);
+        int id = rs.nAllocationCreateTyped(t.getID(), MipmapControl.MIPMAP_NONE.mID, usage, 0);
         if (id == 0) {
             throw new RSRuntimeException("Allocation creation failed.");
         }
@@ -1006,6 +1128,35 @@
     }
 
     /**
+     *
+     *
+     * @hide
+     *
+     */
+    public SurfaceTexture getSurfaceTexture() {
+        if ((mUsage & USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE) == 0) {
+            throw new RSInvalidStateException("Allocation is not a surface texture.");
+        }
+
+        int id = mRS.nAllocationGetSurfaceTextureID(getID());
+        return new SurfaceTexture(id);
+
+    }
+
+    /**
+     * @hide
+     */
+    public void setSurfaceTexture(SurfaceTexture sur) {
+        if ((mUsage & USAGE_IO_OUTPUT) == 0) {
+            throw new RSInvalidStateException("Allocation is not USAGE_IO_OUTPUT.");
+        }
+
+        mRS.validate();
+        mRS.nAllocationSetSurfaceTexture(getID(), sur);
+    }
+
+
+    /**
      * Creates a non-mipmapped renderscript allocation to use as a
      * graphics texture
      *
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 22ef7bb..3d4951f 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2012 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.
@@ -54,26 +54,59 @@
     int[] mArraySizes;
     int[] mOffsetInBytes;
 
+    int[] mVisibleElementMap;
+
     DataType mType;
     DataKind mKind;
     boolean mNormalized;
     int mVectorSize;
 
+    private void updateVisibleSubElements() {
+        if (mElements == null) {
+            return;
+        }
+
+        int noPaddingFieldCount = 0;
+        int fieldCount = mElementNames.length;
+        // Find out how many elements are not padding
+        for (int ct = 0; ct < fieldCount; ct ++) {
+            if (mElementNames[ct].charAt(0) != '#') {
+                noPaddingFieldCount ++;
+            }
+        }
+        mVisibleElementMap = new int[noPaddingFieldCount];
+
+        // Make a map that points us at non-padding elements
+        for (int ct = 0, ctNoPadding = 0; ct < fieldCount; ct ++) {
+            if (mElementNames[ct].charAt(0) != '#') {
+                mVisibleElementMap[ctNoPadding ++] = ct;
+            }
+        }
+    }
+
     /**
     * @hide
     * @return element size in bytes
     */
     public int getSizeBytes() {return mSize;}
 
+    /**
+    * @hide
+    * @return element vector size
+    */
+    public int getVectorSize() {return mVectorSize;}
+
 
     /**
      * DataType represents the basic type information for a basic element.  The
-     * naming convention follows.  For numeric types its FLOAT, SIGNED, UNSIGNED
-     * followed by the _BITS where BITS is the size of the data.  BOOLEAN is a
-     * true / false (1,0) represented in an 8 bit container.  The UNSIGNED
-     * variants with multiple bit definitions are for packed graphical data
-     * formats and represents vectors with per vector member sizes which are
-     * treated as a single unit for packing and alignment purposes.
+     * naming convention follows.  For numeric types it is FLOAT,
+     * SIGNED, or UNSIGNED followed by the _BITS where BITS is the
+     * size of the data.  BOOLEAN is a true / false (1,0)
+     * represented in an 8 bit container.  The UNSIGNED variants
+     * with multiple bit definitions are for packed graphical data
+     * formats and represent vectors with per vector member sizes
+     * which are treated as a single unit for packing and alignment
+     * purposes.
      *
      * MATRIX the three matrix types contain FLOAT_32 elements and are treated
      * as 32 bits for alignment purposes.
@@ -81,6 +114,11 @@
      * RS_* objects.  32 bit opaque handles.
      */
     public enum DataType {
+        /**
+        * @hide
+        * new enum
+        */
+        NONE (0, 0),
         //FLOAT_16 (1, 2),
         FLOAT_32 (2, 4),
         FLOAT_64 (3, 8),
@@ -167,10 +205,10 @@
     * @return number of sub-elements in this element
     */
     public int getSubElementCount() {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             return 0;
         }
-        return mElements.length;
+        return mVisibleElementMap.length;
     }
 
     /**
@@ -179,13 +217,13 @@
     * @return sub-element in this element at given index
     */
     public Element getSubElement(int index) {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             throw new RSIllegalArgumentException("Element contains no sub-elements");
         }
-        if (index < 0 || index >= mElements.length) {
+        if (index < 0 || index >= mVisibleElementMap.length) {
             throw new RSIllegalArgumentException("Illegal sub-element index");
         }
-        return mElements[index];
+        return mElements[mVisibleElementMap[index]];
     }
 
     /**
@@ -194,13 +232,13 @@
     * @return sub-element in this element at given index
     */
     public String getSubElementName(int index) {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             throw new RSIllegalArgumentException("Element contains no sub-elements");
         }
-        if (index < 0 || index >= mElements.length) {
+        if (index < 0 || index >= mVisibleElementMap.length) {
             throw new RSIllegalArgumentException("Illegal sub-element index");
         }
-        return mElementNames[index];
+        return mElementNames[mVisibleElementMap[index]];
     }
 
     /**
@@ -209,13 +247,13 @@
     * @return array size of sub-element in this element at given index
     */
     public int getSubElementArraySize(int index) {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             throw new RSIllegalArgumentException("Element contains no sub-elements");
         }
-        if (index < 0 || index >= mElements.length) {
+        if (index < 0 || index >= mVisibleElementMap.length) {
             throw new RSIllegalArgumentException("Illegal sub-element index");
         }
-        return mArraySizes[index];
+        return mArraySizes[mVisibleElementMap[index]];
     }
 
     /**
@@ -224,13 +262,29 @@
     * @return offset in bytes of sub-element in this element at given index
     */
     public int getSubElementOffsetBytes(int index) {
-        if (mElements == null) {
+        if (mVisibleElementMap == null) {
             throw new RSIllegalArgumentException("Element contains no sub-elements");
         }
-        if (index < 0 || index >= mElements.length) {
+        if (index < 0 || index >= mVisibleElementMap.length) {
             throw new RSIllegalArgumentException("Illegal sub-element index");
         }
-        return mOffsetInBytes[index];
+        return mOffsetInBytes[mVisibleElementMap[index]];
+    }
+
+    /**
+    * @hide
+    * @return element data type
+    */
+    public DataType getDataType() {
+        return mType;
+    }
+
+    /**
+    * @hide
+    * @return element data kind
+    */
+    public DataKind getDataKind() {
+        return mKind;
     }
 
     /**
@@ -681,14 +735,18 @@
     Element(int id, RenderScript rs, Element[] e, String[] n, int[] as) {
         super(id, rs);
         mSize = 0;
+        mVectorSize = 1;
         mElements = e;
         mElementNames = n;
         mArraySizes = as;
+        mType = DataType.NONE;
+        mKind = DataKind.USER;
         mOffsetInBytes = new int[mElements.length];
         for (int ct = 0; ct < mElements.length; ct++ ) {
             mOffsetInBytes[ct] = mSize;
             mSize += mElements[ct].mSize * mArraySizes[ct];
         }
+        updateVisibleSubElements();
     }
 
     Element(int id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
@@ -753,7 +811,7 @@
                 mSize += mElements[i].mSize * mArraySizes[i];
             }
         }
-
+        updateVisibleSubElements();
     }
 
     /**
@@ -774,10 +832,12 @@
 
     /**
      * Create a custom vector element of the specified DataType and vector size.
-     *  DataKind will be set to USER.
+     * DataKind will be set to USER. Only primitive types (FLOAT_32, FLOAT_64,
+     * SIGNED_8, SIGNED_16, SIGNED_32, SIGNED_64, UNSIGNED_8, UNSIGNED_16,
+     * UNSIGNED_32, UNSIGNED_64, BOOLEAN) are supported.
      *
      * @param rs The context associated with the new Element.
-     * @param dt The DataType for the new element.
+     * @param dt The DataType for the new Element.
      * @param size Vector size for the new Element.  Range 2-4 inclusive
      *             supported.
      *
@@ -787,10 +847,31 @@
         if (size < 2 || size > 4) {
             throw new RSIllegalArgumentException("Vector size out of range 2-4.");
         }
-        DataKind dk = DataKind.USER;
-        boolean norm = false;
-        int id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
-        return new Element(id, rs, dt, dk, norm, size);
+
+        switch (dt) {
+        // Support only primitive integer/float/boolean types as vectors.
+        case FLOAT_32:
+        case FLOAT_64:
+        case SIGNED_8:
+        case SIGNED_16:
+        case SIGNED_32:
+        case SIGNED_64:
+        case UNSIGNED_8:
+        case UNSIGNED_16:
+        case UNSIGNED_32:
+        case UNSIGNED_64:
+        case BOOLEAN: {
+            DataKind dk = DataKind.USER;
+            boolean norm = false;
+            int id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
+            return new Element(id, rs, dt, dk, norm, size);
+        }
+
+        default: {
+            throw new RSIllegalArgumentException("Cannot create vector of " +
+                "non-primitive type.");
+        }
+        }
     }
 
     /**
@@ -875,10 +956,10 @@
 
         // Ignore mKind because it is allowed to be different (user vs. pixel).
         // We also ignore mNormalized because it can be different. The mType
-        // field must be non-null since we require name equivalence for
-        // user-created Elements.
+        // field must not be NONE since we require name equivalence for
+        // all user-created Elements.
         return ((mSize == e.mSize) &&
-                (mType != null) &&
+                (mType != DataType.NONE) &&
                 (mType == e.mType) &&
                 (mVectorSize == e.mVectorSize));
     }
diff --git a/graphics/java/android/renderscript/Path.java b/graphics/java/android/renderscript/Path.java
new file mode 100644
index 0000000..83ae150
--- /dev/null
+++ b/graphics/java/android/renderscript/Path.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.util.Vector;
+import android.util.Log;
+
+/**
+ * @hide
+ *
+ */
+public class Path extends BaseObj {
+
+    public enum Primitive {
+        QUADRATIC_BEZIER(0),
+        CUBIC_BEZIER(1);
+
+        int mID;
+        Primitive(int id) {
+            mID = id;
+        }
+    }
+
+    Allocation mVertexBuffer;
+    Allocation mLoopBuffer;
+    Primitive mPrimitive;
+    float mQuality;
+    boolean mCoverageToAlpha;
+
+    Path(int id, RenderScript rs, Primitive p, Allocation vtx, Allocation loop, float q) {
+        super(id, rs);
+        mVertexBuffer = vtx;
+        mLoopBuffer = loop;
+        mPrimitive = p;
+        mQuality = q;
+    }
+
+    public Allocation getVertexAllocation() {
+        return mVertexBuffer;
+    }
+
+    public Allocation getLoopAllocation() {
+        return mLoopBuffer;
+    }
+
+    public Primitive getPrimitive() {
+        return mPrimitive;
+    }
+
+    @Override
+    void updateFromNative() {
+    }
+
+
+    public static Path createStaticPath(RenderScript rs, Primitive p, float quality, Allocation vtx) {
+        int id = rs.nPathCreate(p.mID, false, vtx.getID(), 0, quality);
+        Path newPath = new Path(id, rs, p, null, null, quality);
+        return newPath;
+    }
+
+    public static Path createStaticPath(RenderScript rs, Primitive p, float quality, Allocation vtx, Allocation loops) {
+        return null;
+    }
+
+    public static Path createDynamicPath(RenderScript rs, Primitive p, float quality, Allocation vtx) {
+        return null;
+    }
+
+    public static Path createDynamicPath(RenderScript rs, Primitive p, float quality, Allocation vtx, Allocation loops) {
+        return null;
+    }
+
+
+}
+
+
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index a1b1ba3..4d60ac8 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -69,6 +69,7 @@
     Element mOutputs[];
     Type mConstants[];
     TextureType mTextures[];
+    String mTextureNames[];
     int mTextureCount;
     String mShader;
 
@@ -77,6 +78,50 @@
     }
 
     /**
+     * @hide
+     */
+    public int getConstantCount() {
+        return mConstants != null ? mConstants.length : 0;
+    }
+
+    /**
+     * @hide
+     */
+    public Type getConstant(int slot) {
+        if (slot < 0 || slot >= mConstants.length) {
+            throw new IllegalArgumentException("Slot ID out of range.");
+        }
+        return mConstants[slot];
+    }
+
+    /**
+     * @hide
+     */
+    public int getTextureCount() {
+        return mTextureCount;
+    }
+
+    /**
+     * @hide
+     */
+    public TextureType getTextureType(int slot) {
+        if ((slot < 0) || (slot >= mTextureCount)) {
+            throw new IllegalArgumentException("Slot ID out of range.");
+        }
+        return mTextures[slot];
+    }
+
+    /**
+     * @hide
+     */
+    public String getTextureName(int slot) {
+        if ((slot < 0) || (slot >= mTextureCount)) {
+            throw new IllegalArgumentException("Slot ID out of range.");
+        }
+        return mTextureNames[slot];
+    }
+
+    /**
      * Binds a constant buffer to be used as uniform inputs to the
      * program
      *
@@ -146,6 +191,7 @@
         Type mConstants[];
         Type mTextures[];
         TextureType mTextureTypes[];
+        String mTextureNames[];
         int mInputCount;
         int mOutputCount;
         int mConstantCount;
@@ -163,6 +209,7 @@
             mConstantCount = 0;
             mTextureCount = 0;
             mTextureTypes = new TextureType[MAX_TEXTURE];
+            mTextureNames = new String[MAX_TEXTURE];
         }
 
         /**
@@ -266,10 +313,28 @@
          * @return  self
          */
         public BaseProgramBuilder addTexture(TextureType texType) throws IllegalArgumentException {
+            addTexture(texType, "Tex" + mTextureCount);
+            return this;
+        }
+
+        /**
+         * @hide
+         * Adds a texture input to the Program
+         *
+         * @param texType describes that the texture to append it (2D,
+         *                Cubemap, etc.)
+         * @param texName what the texture should be called in the
+         *                shader
+         * @return  self
+         */
+        public BaseProgramBuilder addTexture(TextureType texType, String texName)
+            throws IllegalArgumentException {
             if(mTextureCount >= MAX_TEXTURE) {
                 throw new IllegalArgumentException("Max texture count exceeded.");
             }
-            mTextureTypes[mTextureCount ++] = texType;
+            mTextureTypes[mTextureCount] = texType;
+            mTextureNames[mTextureCount] = texName;
+            mTextureCount ++;
             return this;
         }
 
@@ -283,6 +348,8 @@
             p.mTextureCount = mTextureCount;
             p.mTextures = new TextureType[mTextureCount];
             System.arraycopy(mTextureTypes, 0, p.mTextures, 0, mTextureCount);
+            p.mTextureNames = new String[mTextureCount];
+            System.arraycopy(mTextureNames, 0, p.mTextureNames, 0, mTextureCount);
         }
     }
 
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index 21bace8..ebc15e5 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -59,6 +59,7 @@
         public ProgramFragment create() {
             mRS.validate();
             int[] tmp = new int[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2];
+            String[] texNames = new String[mTextureCount];
             int idx = 0;
 
             for (int i=0; i < mInputCount; i++) {
@@ -76,9 +77,10 @@
             for (int i=0; i < mTextureCount; i++) {
                 tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID;
                 tmp[idx++] = mTextureTypes[i].mID;
+                texNames[i] = mTextureNames[i];
             }
 
-            int id = mRS.nProgramFragmentCreate(mShader, tmp);
+            int id = mRS.nProgramFragmentCreate(mShader, texNames, tmp);
             ProgramFragment pf = new ProgramFragment(id, mRS);
             initProgram(pf);
             return pf;
diff --git a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
index 0ab73c1..cd31db3 100644
--- a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
+++ b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
@@ -47,6 +47,7 @@
         public ProgramFragmentFixedFunction create() {
             mRS.validate();
             int[] tmp = new int[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2];
+            String[] texNames = new String[mTextureCount];
             int idx = 0;
 
             for (int i=0; i < mInputCount; i++) {
@@ -64,9 +65,10 @@
             for (int i=0; i < mTextureCount; i++) {
                 tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID;
                 tmp[idx++] = mTextureTypes[i].mID;
+                texNames[i] = mTextureNames[i];
             }
 
-            int id = mRS.nProgramFragmentCreate(mShader, tmp);
+            int id = mRS.nProgramFragmentCreate(mShader, texNames, tmp);
             ProgramFragmentFixedFunction pf = new ProgramFragmentFixedFunction(id, mRS);
             initProgram(pf);
             return pf;
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 56bb836..a6cd15b 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -55,6 +55,23 @@
     }
 
     /**
+     * @hide
+     */
+    public int getInputCount() {
+        return mInputs != null ? mInputs.length : 0;
+    }
+
+    /**
+     * @hide
+     */
+    public Element getInput(int slot) {
+        if (slot < 0 || slot >= mInputs.length) {
+            throw new IllegalArgumentException("Slot ID out of range.");
+        }
+        return mInputs[slot];
+    }
+
+    /**
     * Builder class for creating ProgramVertex objects.
     * The builder starts empty and the user must minimally provide
     * the GLSL shader code, and the varying inputs. Constant, or
@@ -99,6 +116,7 @@
         public ProgramVertex create() {
             mRS.validate();
             int[] tmp = new int[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2];
+            String[] texNames = new String[mTextureCount];
             int idx = 0;
 
             for (int i=0; i < mInputCount; i++) {
@@ -116,9 +134,10 @@
             for (int i=0; i < mTextureCount; i++) {
                 tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID;
                 tmp[idx++] = mTextureTypes[i].mID;
+                texNames[i] = mTextureNames[i];
             }
 
-            int id = mRS.nProgramVertexCreate(mShader, tmp);
+            int id = mRS.nProgramVertexCreate(mShader, texNames, tmp);
             ProgramVertex pv = new ProgramVertex(id, mRS);
             initProgram(pv);
             return pv;
diff --git a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
index 740d6a5..9a43943 100644
--- a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
+++ b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
@@ -70,6 +70,7 @@
         public ProgramVertexFixedFunction create() {
             mRS.validate();
             int[] tmp = new int[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2];
+            String[] texNames = new String[mTextureCount];
             int idx = 0;
 
             for (int i=0; i < mInputCount; i++) {
@@ -87,9 +88,10 @@
             for (int i=0; i < mTextureCount; i++) {
                 tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID;
                 tmp[idx++] = mTextureTypes[i].mID;
+                texNames[i] = mTextureNames[i];
             }
 
-            int id = mRS.nProgramVertexCreate(mShader, tmp);
+            int id = mRS.nProgramVertexCreate(mShader, texNames, tmp);
             ProgramVertexFixedFunction pv = new ProgramVertexFixedFunction(id, mRS);
             initProgram(pv);
             return pv;
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index bfe412c..9517513 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -231,10 +231,10 @@
         rsnTypeGetNativeData(mContext, id, typeData);
     }
 
-    native int  rsnAllocationCreateTyped(int con, int type, int mip, int usage);
-    synchronized int nAllocationCreateTyped(int type, int mip, int usage) {
+    native int  rsnAllocationCreateTyped(int con, int type, int mip, int usage, int pointer);
+    synchronized int nAllocationCreateTyped(int type, int mip, int usage, int pointer) {
         validate();
-        return rsnAllocationCreateTyped(mContext, type, mip, usage);
+        return rsnAllocationCreateTyped(mContext, type, mip, usage, pointer);
     }
     native int  rsnAllocationCreateFromBitmap(int con, int type, int mip, Bitmap bmp, int usage);
     synchronized int nAllocationCreateFromBitmap(int type, int mip, Bitmap bmp, int usage) {
@@ -269,6 +269,28 @@
         validate();
         rsnAllocationSyncAll(mContext, alloc, src);
     }
+    native int rsnAllocationGetSurfaceTextureID(int con, int alloc);
+    synchronized int nAllocationGetSurfaceTextureID(int alloc) {
+        validate();
+        return rsnAllocationGetSurfaceTextureID(mContext, alloc);
+    }
+    native void rsnAllocationSetSurfaceTexture(int con, int alloc, SurfaceTexture sur);
+    synchronized void nAllocationSetSurfaceTexture(int alloc, SurfaceTexture sur) {
+        validate();
+        rsnAllocationSetSurfaceTexture(mContext, alloc, sur);
+    }
+    native void rsnAllocationIoSend(int con, int alloc);
+    synchronized void nAllocationIoSend(int alloc) {
+        validate();
+        rsnAllocationIoSend(mContext, alloc);
+    }
+    native void rsnAllocationIoReceive(int con, int alloc);
+    synchronized void nAllocationIoReceive(int alloc) {
+        validate();
+        rsnAllocationIoReceive(mContext, alloc);
+    }
+
+
     native void rsnAllocationGenerateMipmaps(int con, int alloc);
     synchronized void nAllocationGenerateMipmaps(int alloc) {
         validate();
@@ -547,15 +569,15 @@
         validate();
         rsnProgramBindSampler(mContext, vpf, slot, s);
     }
-    native int  rsnProgramFragmentCreate(int con, String shader, int[] params);
-    synchronized int nProgramFragmentCreate(String shader, int[] params) {
+    native int  rsnProgramFragmentCreate(int con, String shader, String[] texNames, int[] params);
+    synchronized int nProgramFragmentCreate(String shader, String[] texNames, int[] params) {
         validate();
-        return rsnProgramFragmentCreate(mContext, shader, params);
+        return rsnProgramFragmentCreate(mContext, shader, texNames, params);
     }
-    native int  rsnProgramVertexCreate(int con, String shader, int[] params);
-    synchronized int nProgramVertexCreate(String shader, int[] params) {
+    native int  rsnProgramVertexCreate(int con, String shader, String[] texNames, int[] params);
+    synchronized int nProgramVertexCreate(String shader, String[] texNames, int[] params) {
         validate();
-        return rsnProgramVertexCreate(mContext, shader, params);
+        return rsnProgramVertexCreate(mContext, shader, texNames, params);
     }
 
     native int  rsnMeshCreate(int con, int[] vtx, int[] idx, int[] prim);
@@ -584,6 +606,11 @@
         rsnMeshGetIndices(mContext, id, idxIds, primitives, vtxIdCount);
     }
 
+    native int  rsnPathCreate(int con, int prim, boolean isStatic, int vtx, int loop, float q);
+    synchronized int nPathCreate(int prim, boolean isStatic, int vtx, int loop, float q) {
+        validate();
+        return rsnPathCreate(mContext, prim, isStatic, vtx, loop, q);
+    }
 
     int     mDev;
     int     mContext;
diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk
index 652189f..ca69e1e 100644
--- a/graphics/jni/Android.mk
+++ b/graphics/jni/Android.mk
@@ -6,6 +6,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libandroid_runtime \
+        libandroidfw \
         libnativehelper \
         libRS \
         libcutils \
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 07bf7bf..27ea8f6 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -31,9 +31,9 @@
 #include <core/SkTemplates.h>
 #include <images/SkImageDecoder.h>
 
-#include <utils/Asset.h>
-#include <utils/AssetManager.h>
-#include <utils/ResourceTypes.h>
+#include <androidfw/Asset.h>
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
 
 #include "jni.h"
 #include "JNIHelp.h"
@@ -41,8 +41,8 @@
 #include "android_runtime/android_view_Surface.h"
 #include "android_runtime/android_util_AssetManager.h"
 
-#include <RenderScript.h>
-#include <RenderScriptEnv.h>
+#include <rs.h>
+#include <rsEnv.h>
 #include <gui/SurfaceTexture.h>
 #include <gui/SurfaceTextureClient.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
@@ -54,13 +54,11 @@
 
 class AutoJavaStringToUTF8 {
 public:
-    AutoJavaStringToUTF8(JNIEnv* env, jstring str) : fEnv(env), fJStr(str)
-    {
+    AutoJavaStringToUTF8(JNIEnv* env, jstring str) : fEnv(env), fJStr(str) {
         fCStr = env->GetStringUTFChars(str, NULL);
         fLength = env->GetStringUTFLength(str);
     }
-    ~AutoJavaStringToUTF8()
-    {
+    ~AutoJavaStringToUTF8() {
         fEnv->ReleaseStringUTFChars(fJStr, fCStr);
     }
     const char* c_str() const { return fCStr; }
@@ -73,6 +71,42 @@
     jsize       fLength;
 };
 
+class AutoJavaStringArrayToUTF8 {
+public:
+    AutoJavaStringArrayToUTF8(JNIEnv* env, jobjectArray strings, jsize stringsLength)
+    : mEnv(env), mStrings(strings), mStringsLength(stringsLength) {
+        mCStrings = NULL;
+        mSizeArray = NULL;
+        if (stringsLength > 0) {
+            mCStrings = (const char **)calloc(stringsLength, sizeof(char *));
+            mSizeArray = (size_t*)calloc(stringsLength, sizeof(size_t));
+            for (jsize ct = 0; ct < stringsLength; ct ++) {
+                jstring s = (jstring)mEnv->GetObjectArrayElement(mStrings, ct);
+                mCStrings[ct] = mEnv->GetStringUTFChars(s, NULL);
+                mSizeArray[ct] = mEnv->GetStringUTFLength(s);
+            }
+        }
+    }
+    ~AutoJavaStringArrayToUTF8() {
+        for (jsize ct=0; ct < mStringsLength; ct++) {
+            jstring s = (jstring)mEnv->GetObjectArrayElement(mStrings, ct);
+            mEnv->ReleaseStringUTFChars(s, mCStrings[ct]);
+        }
+        free(mCStrings);
+        free(mSizeArray);
+    }
+    const char **c_str() const { return mCStrings; }
+    size_t *c_str_len() const { return mSizeArray; }
+    jsize length() const { return mStringsLength; }
+
+private:
+    JNIEnv      *mEnv;
+    jobjectArray mStrings;
+    const char **mCStrings;
+    size_t      *mSizeArray;
+    jsize        mStringsLength;
+};
+
 // ---------------------------------------------------------------------------
 
 static jfieldID gContextId = 0;
@@ -322,33 +356,27 @@
 }
 
 static jint
-nElementCreate2(JNIEnv *_env, jobject _this, RsContext con, jintArray _ids, jobjectArray _names, jintArray _arraySizes)
+nElementCreate2(JNIEnv *_env, jobject _this, RsContext con,
+                jintArray _ids, jobjectArray _names, jintArray _arraySizes)
 {
     int fieldCount = _env->GetArrayLength(_ids);
     LOG_API("nElementCreate2, con(%p)", con);
 
     jint *ids = _env->GetIntArrayElements(_ids, NULL);
     jint *arraySizes = _env->GetIntArrayElements(_arraySizes, NULL);
-    const char ** nameArray = (const char **)calloc(fieldCount, sizeof(char *));
-    size_t* sizeArray = (size_t*)calloc(fieldCount, sizeof(size_t));
 
-    for (int ct=0; ct < fieldCount; ct++) {
-        jstring s = (jstring)_env->GetObjectArrayElement(_names, ct);
-        nameArray[ct] = _env->GetStringUTFChars(s, NULL);
-        sizeArray[ct] = _env->GetStringUTFLength(s);
-    }
+    AutoJavaStringArrayToUTF8 names(_env, _names, fieldCount);
+
+    const char **nameArray = names.c_str();
+    size_t *sizeArray = names.c_str_len();
+
     jint id = (jint)rsElementCreate2(con,
                                      (RsElement *)ids, fieldCount,
                                      nameArray, fieldCount * sizeof(size_t),  sizeArray,
                                      (const uint32_t *)arraySizes, fieldCount);
-    for (int ct=0; ct < fieldCount; ct++) {
-        jstring s = (jstring)_env->GetObjectArrayElement(_names, ct);
-        _env->ReleaseStringUTFChars(s, nameArray[ct]);
-    }
+
     _env->ReleaseIntArrayElements(_ids, ids, JNI_ABORT);
     _env->ReleaseIntArrayElements(_arraySizes, arraySizes, JNI_ABORT);
-    free(nameArray);
-    free(sizeArray);
     return (jint)id;
 }
 
@@ -430,10 +458,10 @@
 // -----------------------------------
 
 static jint
-nAllocationCreateTyped(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mips, jint usage)
+nAllocationCreateTyped(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mips, jint usage, jint pointer)
 {
-    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i)", con, (RsElement)type, mips, usage);
-    return (jint) rsAllocationCreateTyped(con, (RsType)type, (RsAllocationMipmapControl)mips, (uint32_t)usage);
+    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)", con, (RsElement)type, mips, usage, (void *)pointer);
+    return (jint) rsAllocationCreateTyped(con, (RsType)type, (RsAllocationMipmapControl)mips, (uint32_t)usage, (uint32_t)pointer);
 }
 
 static void
@@ -443,6 +471,44 @@
     rsAllocationSyncAll(con, (RsAllocation)a, (RsAllocationUsageType)bits);
 }
 
+static jint
+nAllocationGetSurfaceTextureID(JNIEnv *_env, jobject _this, RsContext con, jint a)
+{
+    LOG_API("nAllocationGetSurfaceTextureID, con(%p), a(%p)", con, (RsAllocation)a);
+    return rsAllocationGetSurfaceTextureID(con, (RsAllocation)a);
+}
+
+static void
+nAllocationSetSurfaceTexture(JNIEnv *_env, jobject _this, RsContext con,
+                             RsAllocation alloc, jobject sur)
+{
+    LOG_API("nAllocationSetSurfaceTexture, con(%p), alloc(%p), surface(%p)",
+            con, alloc, (Surface *)sur);
+
+    sp<ANativeWindow> window;
+    if (sur != 0) {
+        sp<SurfaceTexture> st = SurfaceTexture_getSurfaceTexture(_env, sur);
+        window = new SurfaceTextureClient(st);
+    }
+
+    rsAllocationSetSurface(con, alloc, window.get());
+}
+
+static void
+nAllocationIoSend(JNIEnv *_env, jobject _this, RsContext con, RsAllocation alloc)
+{
+    LOG_API("nAllocationIoSend, con(%p), alloc(%p)", con, alloc);
+    rsAllocationIoSend(con, alloc);
+}
+
+static void
+nAllocationIoReceive(JNIEnv *_env, jobject _this, RsContext con, RsAllocation alloc)
+{
+    LOG_API("nAllocationIoReceive, con(%p), alloc(%p)", con, alloc);
+    rsAllocationIoReceive(con, alloc);
+}
+
+
 static void
 nAllocationGenerateMipmaps(JNIEnv *_env, jobject _this, RsContext con, jint alloc)
 {
@@ -623,7 +689,7 @@
                         jint srcAlloc, jint srcXoff, jint srcYoff,
                         jint srcMip, jint srcFace)
 {
-    LOG_API("nAllocation2DData_s, con(%p), dstAlloc(%p), dstXoff, dstYoff,"
+    LOG_API("nAllocation2DData_s, con(%p), dstAlloc(%p), dstXoff(%i), dstYoff(%i),"
             " dstMip(%i), dstFace(%i), width(%i), height(%i),"
             " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i), srcFace(%i)",
             con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
@@ -1026,15 +1092,24 @@
 // ---------------------------------------------------------------------------
 
 static jint
-nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params)
+nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader,
+                       jobjectArray texNames, jintArray params)
 {
     AutoJavaStringToUTF8 shaderUTF(_env, shader);
     jint *paramPtr = _env->GetIntArrayElements(params, NULL);
     jint paramLen = _env->GetArrayLength(params);
 
+    int texCount = _env->GetArrayLength(texNames);
+    AutoJavaStringArrayToUTF8 names(_env, texNames, texCount);
+    const char ** nameArray = names.c_str();
+    size_t* sizeArray = names.c_str_len();
+
     LOG_API("nProgramFragmentCreate, con(%p), paramLen(%i)", con, paramLen);
 
-    jint ret = (jint)rsProgramFragmentCreate(con, shaderUTF.c_str(), shaderUTF.length(), (uint32_t *)paramPtr, paramLen);
+    jint ret = (jint)rsProgramFragmentCreate(con, shaderUTF.c_str(), shaderUTF.length(),
+                                             nameArray, texCount, sizeArray,
+                                             (uint32_t *)paramPtr, paramLen);
+
     _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT);
     return ret;
 }
@@ -1043,7 +1118,8 @@
 // ---------------------------------------------------------------------------
 
 static jint
-nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params)
+nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader,
+                     jobjectArray texNames, jintArray params)
 {
     AutoJavaStringToUTF8 shaderUTF(_env, shader);
     jint *paramPtr = _env->GetIntArrayElements(params, NULL);
@@ -1051,7 +1127,15 @@
 
     LOG_API("nProgramVertexCreate, con(%p), paramLen(%i)", con, paramLen);
 
-    jint ret = (jint)rsProgramVertexCreate(con, shaderUTF.c_str(), shaderUTF.length(), (uint32_t *)paramPtr, paramLen);
+    int texCount = _env->GetArrayLength(texNames);
+    AutoJavaStringArrayToUTF8 names(_env, texNames, texCount);
+    const char ** nameArray = names.c_str();
+    size_t* sizeArray = names.c_str_len();
+
+    jint ret = (jint)rsProgramVertexCreate(con, shaderUTF.c_str(), shaderUTF.length(),
+                                           nameArray, texCount, sizeArray,
+                                           (uint32_t *)paramPtr, paramLen);
+
     _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT);
     return ret;
 }
@@ -1122,6 +1206,17 @@
 
 // ---------------------------------------------------------------------------
 
+//native int  rsnPathCreate(int con, int prim, boolean isStatic, int vtx, int loop, float q);
+static jint
+nPathCreate(JNIEnv *_env, jobject _this, RsContext con, jint prim, jboolean isStatic, jint _vtx, jint _loop, jfloat q) {
+    LOG_API("nPathCreate, con(%p)", con);
+
+    int id = (int)rsPathCreate(con, (RsPathPrimitive)prim, isStatic,
+                               (RsAllocation)_vtx,
+                               (RsAllocation)_loop, q);
+    return id;
+}
+
 static jint
 nMeshCreate(JNIEnv *_env, jobject _this, RsContext con, jintArray _vtx, jintArray _idx, jintArray _prim)
 {
@@ -1250,7 +1345,7 @@
 {"rsnTypeCreate",                    "(IIIIIZZ)I",                            (void*)nTypeCreate },
 {"rsnTypeGetNativeData",             "(II[I)V",                               (void*)nTypeGetNativeData },
 
-{"rsnAllocationCreateTyped",         "(IIII)I",                               (void*)nAllocationCreateTyped },
+{"rsnAllocationCreateTyped",         "(IIIII)I",                               (void*)nAllocationCreateTyped },
 {"rsnAllocationCreateFromBitmap",    "(IIILandroid/graphics/Bitmap;I)I",      (void*)nAllocationCreateFromBitmap },
 {"rsnAllocationCubeCreateFromBitmap","(IIILandroid/graphics/Bitmap;I)I",      (void*)nAllocationCubeCreateFromBitmap },
 
@@ -1258,6 +1353,10 @@
 {"rsnAllocationCopyToBitmap",        "(IILandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyToBitmap },
 
 {"rsnAllocationSyncAll",             "(III)V",                                (void*)nAllocationSyncAll },
+{"rsnAllocationGetSurfaceTextureID", "(II)I",                                 (void*)nAllocationGetSurfaceTextureID },
+{"rsnAllocationSetSurfaceTexture",   "(IILandroid/graphics/SurfaceTexture;)V", (void*)nAllocationSetSurfaceTexture },
+{"rsnAllocationIoSend",              "(II)V",                                 (void*)nAllocationIoSend },
+{"rsnAllocationIoReceive",           "(II)V",                                 (void*)nAllocationIoReceive },
 {"rsnAllocationData1D",              "(IIIII[II)V",                           (void*)nAllocationData1D_i },
 {"rsnAllocationData1D",              "(IIIII[SI)V",                           (void*)nAllocationData1D_s },
 {"rsnAllocationData1D",              "(IIIII[BI)V",                           (void*)nAllocationData1D_b },
@@ -1298,9 +1397,9 @@
 {"rsnProgramBindTexture",            "(IIII)V",                               (void*)nProgramBindTexture },
 {"rsnProgramBindSampler",            "(IIII)V",                               (void*)nProgramBindSampler },
 
-{"rsnProgramFragmentCreate",         "(ILjava/lang/String;[I)I",              (void*)nProgramFragmentCreate },
+{"rsnProgramFragmentCreate",         "(ILjava/lang/String;[Ljava/lang/String;[I)I",              (void*)nProgramFragmentCreate },
 {"rsnProgramRasterCreate",           "(IZI)I",                                (void*)nProgramRasterCreate },
-{"rsnProgramVertexCreate",           "(ILjava/lang/String;[I)I",              (void*)nProgramVertexCreate },
+{"rsnProgramVertexCreate",           "(ILjava/lang/String;[Ljava/lang/String;[I)I",              (void*)nProgramVertexCreate },
 
 {"rsnContextBindRootScript",         "(II)V",                                 (void*)nContextBindRootScript },
 {"rsnContextBindProgramStore",       "(II)V",                                 (void*)nContextBindProgramStore },
@@ -1310,6 +1409,7 @@
 
 {"rsnSamplerCreate",                 "(IIIIIIF)I",                            (void*)nSamplerCreate },
 
+{"rsnPathCreate",                    "(IIZIIF)I",                             (void*)nPathCreate },
 {"rsnMeshCreate",                    "(I[I[I[I)I",                            (void*)nMeshCreate },
 
 {"rsnMeshGetVertexBufferCount",      "(II)I",                                 (void*)nMeshGetVertexBufferCount },
diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h
index 990143b..a59677a 100644
--- a/include/android_runtime/android_app_NativeActivity.h
+++ b/include/android_runtime/android_app_NativeActivity.h
@@ -17,7 +17,7 @@
 #ifndef _ANDROID_APP_NATIVEACTIVITY_H
 #define _ANDROID_APP_NATIVEACTIVITY_H
 
-#include <ui/InputTransport.h>
+#include <androidfw/InputTransport.h>
 #include <utils/Looper.h>
 
 #include <android/native_activity.h>
@@ -114,8 +114,8 @@
 
     struct in_flight_event {
         android::InputEvent* event;
-        int seq;
-        bool doFinish;
+        int seq; // internal sequence number for synthetic pre-dispatch events
+        uint32_t finishSeq; // sequence number for sendFinishedSignal, or 0 if finish not required
     };
 
     struct finish_pre_dispatch {
diff --git a/include/android_runtime/android_content_res_Configuration.h b/include/android_runtime/android_content_res_Configuration.h
index 2f5a982..34c4439 100644
--- a/include/android_runtime/android_content_res_Configuration.h
+++ b/include/android_runtime/android_content_res_Configuration.h
@@ -17,7 +17,7 @@
 #ifndef _ANDROID_CONTENT_RES_CONFIGURATION_H
 #define _ANDROID_CONTENT_RES_CONFIGURATION_H
 
-#include <utils/ResourceTypes.h>
+#include <androidfw/ResourceTypes.h>
 #include <android/configuration.h>
 
 #include "jni.h"
diff --git a/include/android_runtime/android_util_AssetManager.h b/include/android_runtime/android_util_AssetManager.h
index b6dd9c9..8dd9337 100644
--- a/include/android_runtime/android_util_AssetManager.h
+++ b/include/android_runtime/android_util_AssetManager.h
@@ -17,7 +17,7 @@
 #ifndef android_util_AssetManager_H
 #define android_util_AssetManager_H
 
-#include <utils/AssetManager.h>
+#include <androidfw/AssetManager.h>
 
 #include "jni.h"
 
diff --git a/include/utils/Asset.h b/include/androidfw/Asset.h
similarity index 100%
rename from include/utils/Asset.h
rename to include/androidfw/Asset.h
diff --git a/include/utils/AssetDir.h b/include/androidfw/AssetDir.h
similarity index 100%
rename from include/utils/AssetDir.h
rename to include/androidfw/AssetDir.h
diff --git a/include/utils/AssetManager.h b/include/androidfw/AssetManager.h
similarity index 98%
rename from include/utils/AssetManager.h
rename to include/androidfw/AssetManager.h
index a8c7ddb..d95b45e 100644
--- a/include/utils/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -20,14 +20,15 @@
 #ifndef __LIBS_ASSETMANAGER_H
 #define __LIBS_ASSETMANAGER_H
 
-#include <utils/Asset.h>
-#include <utils/AssetDir.h>
+#include <androidfw/Asset.h>
+#include <androidfw/AssetDir.h>
+#include <androidfw/ZipFileRO.h>
 #include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
+#include <utils/SortedVector.h>
 #include <utils/String16.h>
-#include <utils/ZipFileRO.h>
+#include <utils/String8.h>
 #include <utils/threads.h>
+#include <utils/Vector.h>
 
 /*
  * Native-app access is via the opaque typedef struct AAssetManager in the C namespace.
diff --git a/include/utils/BackupHelpers.h b/include/androidfw/BackupHelpers.h
similarity index 100%
rename from include/utils/BackupHelpers.h
rename to include/androidfw/BackupHelpers.h
diff --git a/include/ui/Input.h b/include/androidfw/Input.h
similarity index 99%
rename from include/ui/Input.h
rename to include/androidfw/Input.h
index c2cbe1d..f5db6e24 100644
--- a/include/ui/Input.h
+++ b/include/androidfw/Input.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_H
-#define _UI_INPUT_H
+#ifndef _ANDROIDFW_INPUT_H
+#define _ANDROIDFW_INPUT_H
 
 /**
  * Native input event structures.
@@ -894,4 +894,4 @@
 
 } // namespace android
 
-#endif // _UI_INPUT_H
+#endif // _ANDROIDFW_INPUT_H
diff --git a/include/androidfw/InputTransport.h b/include/androidfw/InputTransport.h
new file mode 100644
index 0000000..a846e65
--- /dev/null
+++ b/include/androidfw/InputTransport.h
@@ -0,0 +1,336 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROIDFW_INPUT_TRANSPORT_H
+#define _ANDROIDFW_INPUT_TRANSPORT_H
+
+/**
+ * Native input transport.
+ *
+ * The InputChannel provides a mechanism for exchanging InputMessage structures across processes.
+ *
+ * The InputPublisher and InputConsumer each handle one end-point of an input channel.
+ * The InputPublisher is used by the input dispatcher to send events to the application.
+ * The InputConsumer is used by the application to receive events from the input dispatcher.
+ */
+
+#include <androidfw/Input.h>
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+/*
+ * Intermediate representation used to send input events and related signals.
+ */
+struct InputMessage {
+    enum {
+        TYPE_KEY = 1,
+        TYPE_MOTION = 2,
+        TYPE_FINISHED = 3,
+    };
+
+    struct Header {
+        uint32_t type;
+        uint32_t padding; // 8 byte alignment for the body that follows
+    } header;
+
+    union Body {
+        struct Key {
+            uint32_t seq;
+            nsecs_t eventTime;
+            int32_t deviceId;
+            int32_t source;
+            int32_t action;
+            int32_t flags;
+            int32_t keyCode;
+            int32_t scanCode;
+            int32_t metaState;
+            int32_t repeatCount;
+            nsecs_t downTime;
+
+            inline size_t size() const {
+                return sizeof(Key);
+            }
+        } key;
+
+        struct Motion {
+            uint32_t seq;
+            nsecs_t eventTime;
+            int32_t deviceId;
+            int32_t source;
+            int32_t action;
+            int32_t flags;
+            int32_t metaState;
+            int32_t buttonState;
+            int32_t edgeFlags;
+            nsecs_t downTime;
+            float xOffset;
+            float yOffset;
+            float xPrecision;
+            float yPrecision;
+            size_t pointerCount;
+            struct Pointer {
+                PointerProperties properties;
+                PointerCoords coords;
+            } pointers[MAX_POINTERS];
+
+            inline size_t size() const {
+                return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS
+                        + sizeof(Pointer) * pointerCount;
+            }
+        } motion;
+
+        struct Finished {
+            uint32_t seq;
+            bool handled;
+
+            inline size_t size() const {
+                return sizeof(Finished);
+            }
+        } finished;
+    } body;
+
+    bool isValid(size_t actualSize) const;
+    size_t size() const;
+};
+
+/*
+ * An input channel consists of a local unix domain socket used to send and receive
+ * input messages across processes.  Each channel has a descriptive name for debugging purposes.
+ *
+ * Each endpoint has its own InputChannel object that specifies its file descriptor.
+ *
+ * The input channel is closed when all references to it are released.
+ */
+class InputChannel : public RefBase {
+protected:
+    virtual ~InputChannel();
+
+public:
+    InputChannel(const String8& name, int fd);
+
+    /* Creates a pair of input channels.
+     *
+     * Returns OK on success.
+     */
+    static status_t openInputChannelPair(const String8& name,
+            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
+
+    inline String8 getName() const { return mName; }
+    inline int getFd() const { return mFd; }
+
+    /* Sends a message to the other endpoint.
+     *
+     * If the channel is full then the message is guaranteed not to have been sent at all.
+     * Try again after the consumer has sent a finished signal indicating that it has
+     * consumed some of the pending messages from the channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t sendMessage(const InputMessage* msg);
+
+    /* Receives a message sent by the other endpoint.
+     *
+     * If there is no message present, try again after poll() indicates that the fd
+     * is readable.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if there is no message present.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t receiveMessage(InputMessage* msg);
+
+private:
+    String8 mName;
+    int mFd;
+};
+
+/*
+ * Publishes input events to an input channel.
+ */
+class InputPublisher {
+public:
+    /* Creates a publisher associated with an input channel. */
+    explicit InputPublisher(const sp<InputChannel>& channel);
+
+    /* Destroys the publisher and releases its input channel. */
+    ~InputPublisher();
+
+    /* Gets the underlying input channel. */
+    inline sp<InputChannel> getChannel() { return mChannel; }
+
+    /* Publishes a key event to the input channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Returns BAD_VALUE if seq is 0.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t publishKeyEvent(
+            uint32_t seq,
+            int32_t deviceId,
+            int32_t source,
+            int32_t action,
+            int32_t flags,
+            int32_t keyCode,
+            int32_t scanCode,
+            int32_t metaState,
+            int32_t repeatCount,
+            nsecs_t downTime,
+            nsecs_t eventTime);
+
+    /* Publishes a motion event to the input channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t publishMotionEvent(
+            uint32_t seq,
+            int32_t deviceId,
+            int32_t source,
+            int32_t action,
+            int32_t flags,
+            int32_t edgeFlags,
+            int32_t metaState,
+            int32_t buttonState,
+            float xOffset,
+            float yOffset,
+            float xPrecision,
+            float yPrecision,
+            nsecs_t downTime,
+            nsecs_t eventTime,
+            size_t pointerCount,
+            const PointerProperties* pointerProperties,
+            const PointerCoords* pointerCoords);
+
+    /* Receives the finished signal from the consumer in reply to the original dispatch signal.
+     * If a signal was received, returns the message sequence number,
+     * and whether the consumer handled the message.
+     *
+     * The returned sequence number is never 0 unless the operation failed.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if there is no signal present.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
+
+private:
+    sp<InputChannel> mChannel;
+};
+
+/*
+ * Consumes input events from an input channel.
+ */
+class InputConsumer {
+public:
+    /* Creates a consumer associated with an input channel. */
+    explicit InputConsumer(const sp<InputChannel>& channel);
+
+    /* Destroys the consumer and releases its input channel. */
+    ~InputConsumer();
+
+    /* Gets the underlying input channel. */
+    inline sp<InputChannel> getChannel() { return mChannel; }
+
+    /* Consumes an input event from the input channel and copies its contents into
+     * an InputEvent object created using the specified factory.
+     *
+     * Tries to combine a series of move events into larger batches whenever possible.
+     *
+     * If consumeBatches is false, then defers consuming pending batched events if it
+     * is possible for additional samples to be added to them later.  Call hasPendingBatch()
+     * to determine whether a pending batch is available to be consumed.
+     *
+     * If consumeBatches is true, then events are still batched but they are consumed
+     * immediately as soon as the input channel is exhausted.
+     *
+     * The returned sequence number is never 0 unless the operation failed.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if there is no event present.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Returns NO_MEMORY if the event could not be created.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
+            uint32_t* outSeq, InputEvent** outEvent);
+
+    /* Sends a finished signal to the publisher to inform it that the message
+     * with the specified sequence number has finished being process and whether
+     * the message was handled by the consumer.
+     *
+     * Returns OK on success.
+     * Returns BAD_VALUE if seq is 0.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t sendFinishedSignal(uint32_t seq, bool handled);
+
+    /* Returns true if there is a pending batch. */
+    bool hasPendingBatch() const;
+
+private:
+    sp<InputChannel> mChannel;
+
+    // The current input message.
+    InputMessage mMsg;
+
+    // True if mMsg contains a valid input message that was deferred from the previous
+    // call to consume and that still needs to be handled.
+    bool mMsgDeferred;
+
+    // Batched motion events per device and source.
+    struct Batch {
+        uint32_t seq; // sequence number of last input message batched in the event
+        MotionEvent event;
+    };
+    Vector<Batch> mBatches;
+
+    // Chain of batched sequence numbers.  When multiple input messages are combined into
+    // a batch, we append a record here that associates the last sequence number in the
+    // batch with the previous one.  When the finished signal is sent, we traverse the
+    // chain to individually finish all input messages that were part of the batch.
+    struct SeqChain {
+        uint32_t seq;   // sequence number of batched input message
+        uint32_t chain; // sequence number of previous batched input message
+    };
+    Vector<SeqChain> mSeqChains;
+
+    ssize_t findBatch(int32_t deviceId, int32_t source) const;
+    status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
+
+    static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
+    static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
+    static bool canAppendSamples(const MotionEvent* event, const InputMessage* msg);
+    static void appendSamples(MotionEvent* event, const InputMessage* msg);
+};
+
+} // namespace android
+
+#endif // _ANDROIDFW_INPUT_TRANSPORT_H
diff --git a/include/ui/KeyCharacterMap.h b/include/androidfw/KeyCharacterMap.h
similarity index 97%
rename from include/ui/KeyCharacterMap.h
rename to include/androidfw/KeyCharacterMap.h
index be14432..679dd2c 100644
--- a/include/ui/KeyCharacterMap.h
+++ b/include/androidfw/KeyCharacterMap.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef _UI_KEY_CHARACTER_MAP_H
-#define _UI_KEY_CHARACTER_MAP_H
+#ifndef _ANDROIDFW_KEY_CHARACTER_MAP_H
+#define _ANDROIDFW_KEY_CHARACTER_MAP_H
 
 #include <stdint.h>
 
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/Tokenizer.h>
@@ -196,4 +196,4 @@
 
 } // namespace android
 
-#endif // _UI_KEY_CHARACTER_MAP_H
+#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H
diff --git a/include/ui/KeyLayoutMap.h b/include/androidfw/KeyLayoutMap.h
similarity index 95%
rename from include/ui/KeyLayoutMap.h
rename to include/androidfw/KeyLayoutMap.h
index d82d0c8..5a6f550 100644
--- a/include/ui/KeyLayoutMap.h
+++ b/include/androidfw/KeyLayoutMap.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _UI_KEY_LAYOUT_MAP_H
-#define _UI_KEY_LAYOUT_MAP_H
+#ifndef _ANDROIDFW_KEY_LAYOUT_MAP_H
+#define _ANDROIDFW_KEY_LAYOUT_MAP_H
 
 #include <stdint.h>
 #include <utils/Errors.h>
@@ -96,4 +96,4 @@
 
 } // namespace android
 
-#endif // _UI_KEY_LAYOUT_MAP_H
+#endif // _ANDROIDFW_KEY_LAYOUT_MAP_H
diff --git a/include/ui/Keyboard.h b/include/androidfw/Keyboard.h
similarity index 96%
rename from include/ui/Keyboard.h
rename to include/androidfw/Keyboard.h
index 274f526..ae65198 100644
--- a/include/ui/Keyboard.h
+++ b/include/androidfw/Keyboard.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef _UI_KEYBOARD_H
-#define _UI_KEYBOARD_H
+#ifndef _ANDROIDFW_KEYBOARD_H
+#define _ANDROIDFW_KEYBOARD_H
 
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include <utils/Errors.h>
 #include <utils/String8.h>
 #include <utils/PropertyMap.h>
@@ -116,4 +116,4 @@
 
 } // namespace android
 
-#endif // _UI_KEYBOARD_H
+#endif // _ANDROIDFW_KEYBOARD_H
diff --git a/include/ui/KeycodeLabels.h b/include/androidfw/KeycodeLabels.h
similarity index 98%
rename from include/ui/KeycodeLabels.h
rename to include/androidfw/KeycodeLabels.h
index c5bd0c5..9e4dfcb 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/androidfw/KeycodeLabels.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _UI_KEYCODE_LABELS_H
-#define _UI_KEYCODE_LABELS_H
+#ifndef _ANDROIDFW_KEYCODE_LABELS_H
+#define _ANDROIDFW_KEYCODE_LABELS_H
 
 #include <android/keycodes.h>
 
@@ -307,4 +307,4 @@
     { NULL, -1 }
 };
 
-#endif // _UI_KEYCODE_LABELS_H
+#endif // _ANDROIDFW_KEYCODE_LABELS_H
diff --git a/include/utils/ObbFile.h b/include/androidfw/ObbFile.h
similarity index 100%
rename from include/utils/ObbFile.h
rename to include/androidfw/ObbFile.h
diff --git a/include/ui/PowerManager.h b/include/androidfw/PowerManager.h
similarity index 88%
rename from include/ui/PowerManager.h
rename to include/androidfw/PowerManager.h
index dd80318..59e993a 100644
--- a/include/ui/PowerManager.h
+++ b/include/androidfw/PowerManager.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _UI_POWER_MANAGER_H
-#define _UI_POWER_MANAGER_H
+#ifndef _ANDROIDFW_POWER_MANAGER_H
+#define _ANDROIDFW_POWER_MANAGER_H
 
 
 namespace android {
@@ -30,4 +30,4 @@
 
 } // namespace android
 
-#endif // _UI_POWER_MANAGER_H
+#endif // _ANDROIDFW_POWER_MANAGER_H
diff --git a/include/utils/ResourceTypes.h b/include/androidfw/ResourceTypes.h
similarity index 99%
rename from include/utils/ResourceTypes.h
rename to include/androidfw/ResourceTypes.h
index c496da6..23bca3e 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -20,7 +20,7 @@
 #ifndef _LIBS_UTILS_RESOURCE_TYPES_H
 #define _LIBS_UTILS_RESOURCE_TYPES_H
 
-#include <utils/Asset.h>
+#include <androidfw/Asset.h>
 #include <utils/ByteOrder.h>
 #include <utils/Errors.h>
 #include <utils/String16.h>
diff --git a/include/utils/StreamingZipInflater.h b/include/androidfw/StreamingZipInflater.h
similarity index 100%
rename from include/utils/StreamingZipInflater.h
rename to include/androidfw/StreamingZipInflater.h
diff --git a/include/ui/VirtualKeyMap.h b/include/androidfw/VirtualKeyMap.h
similarity index 92%
rename from include/ui/VirtualKeyMap.h
rename to include/androidfw/VirtualKeyMap.h
index 7813d9d..66340e3 100644
--- a/include/ui/VirtualKeyMap.h
+++ b/include/androidfw/VirtualKeyMap.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef _UI_VIRTUAL_KEY_MAP_H
-#define _UI_VIRTUAL_KEY_MAP_H
+#ifndef _ANDROIDFW_VIRTUAL_KEY_MAP_H
+#define _ANDROIDFW_VIRTUAL_KEY_MAP_H
 
 #include <stdint.h>
 
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/Tokenizer.h>
@@ -76,4 +76,4 @@
 
 } // namespace android
 
-#endif // _UI_KEY_CHARACTER_MAP_H
+#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H
diff --git a/include/utils/ZipFileCRO.h b/include/androidfw/ZipFileCRO.h
similarity index 100%
rename from include/utils/ZipFileCRO.h
rename to include/androidfw/ZipFileCRO.h
diff --git a/include/utils/ZipFileRO.h b/include/androidfw/ZipFileRO.h
similarity index 100%
rename from include/utils/ZipFileRO.h
rename to include/androidfw/ZipFileRO.h
diff --git a/include/utils/ZipUtils.h b/include/androidfw/ZipUtils.h
similarity index 100%
rename from include/utils/ZipUtils.h
rename to include/androidfw/ZipUtils.h
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
index 2316fef..ca594d3 100644
--- a/include/binder/BinderService.h
+++ b/include/binder/BinderService.h
@@ -34,15 +34,15 @@
 class BinderService
 {
 public:
-    static status_t publish() {
+    static status_t publish(bool allowIsolated = false) {
         sp<IServiceManager> sm(defaultServiceManager());
-        return sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
+        return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);
     }
 
-    static void publishAndJoinThreadPool() {
+    static void publishAndJoinThreadPool(bool allowIsolated = false) {
         sp<ProcessState> proc(ProcessState::self());
         sp<IServiceManager> sm(defaultServiceManager());
-        sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
+        sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);
         ProcessState::self()->startThreadPool();
         IPCThreadState::self()->joinThreadPool();
     }
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
index 24e9e99..2c297d6 100644
--- a/include/binder/IServiceManager.h
+++ b/include/binder/IServiceManager.h
@@ -47,7 +47,8 @@
      * Register a service.
      */
     virtual status_t            addService( const String16& name,
-                                            const sp<IBinder>& service) = 0;
+                                            const sp<IBinder>& service,
+                                            bool allowIsolated = false) = 0;
 
     /**
      * Return list of all existing services.
diff --git a/include/common_time/ICommonClock.h b/include/common_time/ICommonClock.h
new file mode 100644
index 0000000..d7073f1
--- /dev/null
+++ b/include/common_time/ICommonClock.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_ICOMMONCLOCK_H
+#define ANDROID_ICOMMONCLOCK_H
+
+#include <stdint.h>
+#include <linux/socket.h>
+
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+class ICommonClockListener : public IInterface {
+  public:
+    DECLARE_META_INTERFACE(CommonClockListener);
+
+    virtual void onTimelineChanged(uint64_t timelineID) = 0;
+};
+
+class BnCommonClockListener : public BnInterface<ICommonClockListener> {
+  public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+                                Parcel* reply, uint32_t flags = 0);
+};
+
+class ICommonClock : public IInterface {
+  public:
+    DECLARE_META_INTERFACE(CommonClock);
+
+    // Name of the ICommonClock service registered with the service manager.
+    static const String16 kServiceName;
+
+    // a reserved invalid timeline ID
+    static const uint64_t kInvalidTimelineID;
+
+    // a reserved invalid error estimate
+    static const int32_t kErrorEstimateUnknown;
+
+    enum State {
+        // the device just came up and is trying to discover the master
+        STATE_INITIAL,
+
+        // the device is a client of a master
+        STATE_CLIENT,
+
+        // the device is acting as master
+        STATE_MASTER,
+
+        // the device has lost contact with its master and needs to participate
+        // in the election of a new master
+        STATE_RONIN,
+
+        // the device is waiting for announcement of the newly elected master
+        STATE_WAIT_FOR_ELECTION,
+    };
+
+    virtual status_t isCommonTimeValid(bool* valid, uint32_t* timelineID) = 0;
+    virtual status_t commonTimeToLocalTime(int64_t commonTime,
+                                           int64_t* localTime) = 0;
+    virtual status_t localTimeToCommonTime(int64_t localTime,
+                                           int64_t* commonTime) = 0;
+    virtual status_t getCommonTime(int64_t* commonTime) = 0;
+    virtual status_t getCommonFreq(uint64_t* freq) = 0;
+    virtual status_t getLocalTime(int64_t* localTime) = 0;
+    virtual status_t getLocalFreq(uint64_t* freq) = 0;
+    virtual status_t getEstimatedError(int32_t* estimate) = 0;
+    virtual status_t getTimelineID(uint64_t* id) = 0;
+    virtual status_t getState(State* state) = 0;
+    virtual status_t getMasterAddr(struct sockaddr_storage* addr) = 0;
+
+    virtual status_t registerListener(
+            const sp<ICommonClockListener>& listener) = 0;
+    virtual status_t unregisterListener(
+            const sp<ICommonClockListener>& listener) = 0;
+
+    // Simple helper to make it easier to connect to the CommonClock service.
+    static inline sp<ICommonClock> getInstance() {
+        sp<IBinder> binder = defaultServiceManager()->checkService(
+                ICommonClock::kServiceName);
+        sp<ICommonClock> clk = interface_cast<ICommonClock>(binder);
+        return clk;
+    }
+};
+
+class BnCommonClock : public BnInterface<ICommonClock> {
+  public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+                                Parcel* reply, uint32_t flags = 0);
+};
+
+};  // namespace android
+
+#endif  // ANDROID_ICOMMONCLOCK_H
diff --git a/include/common_time/ICommonTimeConfig.h b/include/common_time/ICommonTimeConfig.h
new file mode 100644
index 0000000..497b666
--- /dev/null
+++ b/include/common_time/ICommonTimeConfig.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_ICOMMONTIMECONFIG_H
+#define ANDROID_ICOMMONTIMECONFIG_H
+
+#include <stdint.h>
+#include <linux/socket.h>
+
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+class String16;
+
+class ICommonTimeConfig : public IInterface {
+  public:
+    DECLARE_META_INTERFACE(CommonTimeConfig);
+
+    // Name of the ICommonTimeConfig service registered with the service
+    // manager.
+    static const String16 kServiceName;
+
+    virtual status_t getMasterElectionPriority(uint8_t *priority) = 0;
+    virtual status_t setMasterElectionPriority(uint8_t priority) = 0;
+    virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr) = 0;
+    virtual status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr) = 0;
+    virtual status_t getMasterElectionGroupId(uint64_t *id) = 0;
+    virtual status_t setMasterElectionGroupId(uint64_t id) = 0;
+    virtual status_t getInterfaceBinding(String16& ifaceName) = 0;
+    virtual status_t setInterfaceBinding(const String16& ifaceName) = 0;
+    virtual status_t getMasterAnnounceInterval(int *interval) = 0;
+    virtual status_t setMasterAnnounceInterval(int interval) = 0;
+    virtual status_t getClientSyncInterval(int *interval) = 0;
+    virtual status_t setClientSyncInterval(int interval) = 0;
+    virtual status_t getPanicThreshold(int *threshold) = 0;
+    virtual status_t setPanicThreshold(int threshold) = 0;
+    virtual status_t getAutoDisable(bool *autoDisable) = 0;
+    virtual status_t setAutoDisable(bool autoDisable) = 0;
+    virtual status_t forceNetworklessMasterMode() = 0;
+
+    // Simple helper to make it easier to connect to the CommonTimeConfig service.
+    static inline sp<ICommonTimeConfig> getInstance() {
+        sp<IBinder> binder = defaultServiceManager()->checkService(
+                ICommonTimeConfig::kServiceName);
+        sp<ICommonTimeConfig> clk = interface_cast<ICommonTimeConfig>(binder);
+        return clk;
+    }
+};
+
+class BnCommonTimeConfig : public BnInterface<ICommonTimeConfig> {
+  public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+                                Parcel* reply, uint32_t flags = 0);
+};
+
+};  // namespace android
+
+#endif  // ANDROID_ICOMMONTIMECONFIG_H
diff --git a/include/common_time/cc_helper.h b/include/common_time/cc_helper.h
new file mode 100644
index 0000000..8c4d5c0
--- /dev/null
+++ b/include/common_time/cc_helper.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef __CC_HELPER_H__
+#define __CC_HELPER_H__
+
+#include <stdint.h>
+#include <common_time/ICommonClock.h>
+#include <utils/threads.h>
+
+namespace android {
+
+// CCHelper is a simple wrapper class to help with centralizing access to the
+// Common Clock service and implementing lifetime managment, as well as to
+// implement a simple policy of making a basic attempt to reconnect to the
+// common clock service when things go wrong.
+//
+// On platforms which run the native common_time service in auto-disable mode,
+// the service will go into networkless mode whenever it has no active clients.
+// It tracks active clients using registered CommonClockListeners (the callback
+// interface for onTimelineChanged) since this provides a convienent death
+// handler notification for when the service's clients die unexpectedly.  This
+// means that users of the common time service should really always have a
+// CommonClockListener, unless they know that the time service is not running in
+// auto disabled mode, or that there is at least one other registered listener
+// active in the system.  The CCHelper makes this a little easier by sharing a
+// ref counted ICommonClock interface across all clients and automatically
+// registering and unregistering a listener whenever there are CCHelper
+// instances active in the process.
+class CCHelper {
+  public:
+    CCHelper();
+    ~CCHelper();
+
+    status_t isCommonTimeValid(bool* valid, uint32_t* timelineID);
+    status_t commonTimeToLocalTime(int64_t commonTime, int64_t* localTime);
+    status_t localTimeToCommonTime(int64_t localTime, int64_t* commonTime);
+    status_t getCommonTime(int64_t* commonTime);
+    status_t getCommonFreq(uint64_t* freq);
+    status_t getLocalTime(int64_t* localTime);
+    status_t getLocalFreq(uint64_t* freq);
+
+  private:
+    class CommonClockListener : public BnCommonClockListener {
+      public:
+        void onTimelineChanged(uint64_t timelineID);
+    };
+
+    static bool verifyClock_l();
+
+    static Mutex lock_;
+    static sp<ICommonClock> common_clock_;
+    static sp<ICommonClockListener> common_clock_listener_;
+    static uint32_t ref_count_;
+};
+
+
+}  // namespace android
+#endif  // __CC_HELPER_H__
diff --git a/include/common_time/local_clock.h b/include/common_time/local_clock.h
new file mode 100644
index 0000000..845d1c21
--- /dev/null
+++ b/include/common_time/local_clock.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+
+#ifndef __LOCAL_CLOCK_H__
+#define __LOCAL_CLOCK_H__
+
+#include <stdint.h>
+
+#include <hardware/local_time_hal.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class LocalClock {
+  public:
+     LocalClock();
+
+    bool initCheck();
+
+    int64_t  getLocalTime();
+    uint64_t getLocalFreq();
+    status_t setLocalSlew(int16_t rate);
+    int32_t  getDebugLog(struct local_time_debug_event* records,
+                         int max_records);
+
+  private:
+    static Mutex dev_lock_;
+    static local_time_hw_device_t* dev_;
+};
+
+}  // namespace android
+#endif  // __LOCAL_CLOCK_H__
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
new file mode 100644
index 0000000..ae99160
--- /dev/null
+++ b/include/gui/BufferQueue.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_GUI_BUFFERQUEUE_H
+#define ANDROID_GUI_BUFFERQUEUE_H
+
+#include <EGL/egl.h>
+
+#include <gui/ISurfaceTexture.h>
+
+#include <surfaceflinger/IGraphicBufferAlloc.h>
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class BufferQueue : public BnSurfaceTexture {
+public:
+    enum { MIN_UNDEQUEUED_BUFFERS = 2 };
+    enum {
+        MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
+        MIN_SYNC_BUFFER_SLOTS  = MIN_UNDEQUEUED_BUFFERS
+    };
+    enum { NUM_BUFFER_SLOTS = 32 };
+    enum { NO_CONNECTED_API = 0 };
+
+    struct FrameAvailableListener : public virtual RefBase {
+        // onFrameAvailable() is called from queueBuffer() each time an
+        // additional frame becomes available for consumption. This means that
+        // frames that are queued while in asynchronous mode only trigger the
+        // callback if no previous frames are pending. Frames queued while in
+        // synchronous mode always trigger the callback.
+        //
+        // This is called without any lock held and can be called concurrently
+        // by multiple threads.
+        virtual void onFrameAvailable() = 0;
+    };
+
+    // BufferQueue manages a pool of gralloc memory slots to be used
+    // by producers and consumers.
+    // allowSynchronousMode specifies whether or not synchronous mode can be
+    // enabled.
+    BufferQueue(bool allowSynchronousMode = true);
+    virtual ~BufferQueue();
+
+    virtual int query(int what, int* value);
+
+    // setBufferCount updates the number of available buffer slots.  After
+    // calling this all buffer slots are both unallocated and owned by the
+    // BufferQueue object (i.e. they are not owned by the client).
+    virtual status_t setBufferCount(int bufferCount);
+
+    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+
+    // dequeueBuffer gets the next buffer slot index for the client to use. If a
+    // buffer slot is available then that slot index is written to the location
+    // pointed to by the buf argument and a status of OK is returned.  If no
+    // slot is available then a status of -EBUSY is returned and buf is
+    // unmodified.
+    // The width and height parameters must be no greater than the minimum of
+    // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+    // An error due to invalid dimensions might not be reported until
+    // updateTexImage() is called.
+    virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height,
+            uint32_t format, uint32_t usage);
+
+    // queueBuffer returns a filled buffer to the BufferQueue. In addition, a
+    // timestamp must be provided for the buffer. The timestamp is in
+    // nanoseconds, and must be monotonically increasing. Its other semantics
+    // (zero point, etc) are client-dependent and should be documented by the
+    // client.
+    virtual status_t queueBuffer(int buf, int64_t timestamp,
+            uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
+    virtual void cancelBuffer(int buf);
+    virtual status_t setCrop(const Rect& reg);
+    virtual status_t setTransform(uint32_t transform);
+    virtual status_t setScalingMode(int mode);
+
+    // setSynchronousMode set whether dequeueBuffer is synchronous or
+    // asynchronous. In synchronous mode, dequeueBuffer blocks until
+    // a buffer is available, the currently bound buffer can be dequeued and
+    // queued buffers will be retired in order.
+    // The default mode is asynchronous.
+    virtual status_t setSynchronousMode(bool enabled);
+
+    // connect attempts to connect a producer client API to the BufferQueue.
+    // This must be called before any other ISurfaceTexture methods are called
+    // except for getAllocator.
+    //
+    // This method will fail if the connect was previously called on the
+    // BufferQueue and no corresponding disconnect call was made.
+    virtual status_t connect(int api,
+            uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
+
+    // disconnect attempts to disconnect a producer client API from the
+    // BufferQueue. Calling this method will cause any subsequent calls to other
+    // ISurfaceTexture methods to fail except for getAllocator and connect.
+    // Successfully calling connect after this will allow the other methods to
+    // succeed again.
+    //
+    // This method will fail if the the BufferQueue is not currently
+    // connected to the specified client API.
+    virtual status_t disconnect(int api);
+
+protected:
+
+    // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
+    // for the given slot.
+    void freeBufferLocked(int index);
+
+    // freeAllBuffersLocked frees the resources (both GraphicBuffer and
+    // EGLImage) for all slots.
+    void freeAllBuffersLocked();
+
+    // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer
+    // and EGLImage) for all slots except the head of mQueue
+    void freeAllBuffersExceptHeadLocked();
+
+    // drainQueueLocked drains the buffer queue if we're in synchronous mode
+    // returns immediately otherwise. It returns NO_INIT if the BufferQueue
+    // became abandoned or disconnected during this call.
+    status_t drainQueueLocked();
+
+    // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in
+    // synchronous mode and free all buffers. In asynchronous mode, all buffers
+    // are freed except the current buffer.
+    status_t drainQueueAndFreeBuffersLocked();
+
+    status_t setBufferCountServerLocked(int bufferCount);
+
+    enum { INVALID_BUFFER_SLOT = -1 };
+
+    struct BufferSlot {
+
+        BufferSlot()
+        : mEglImage(EGL_NO_IMAGE_KHR),
+          mEglDisplay(EGL_NO_DISPLAY),
+          mBufferState(BufferSlot::FREE),
+          mRequestBufferCalled(false),
+          mTransform(0),
+          mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+          mTimestamp(0),
+          mFrameNumber(0),
+          mFence(EGL_NO_SYNC_KHR) {
+            mCrop.makeInvalid();
+        }
+
+        // mGraphicBuffer points to the buffer allocated for this slot or is NULL
+        // if no buffer has been allocated.
+        sp<GraphicBuffer> mGraphicBuffer;
+
+        // mEglImage is the EGLImage created from mGraphicBuffer.
+        EGLImageKHR mEglImage;
+
+        // mEglDisplay is the EGLDisplay used to create mEglImage.
+        EGLDisplay mEglDisplay;
+
+        // BufferState represents the different states in which a buffer slot
+        // can be.
+        enum BufferState {
+            // FREE indicates that the buffer is not currently being used and
+            // will not be used in the future until it gets dequeued and
+            // subsequently queued by the client.
+            FREE = 0,
+
+            // DEQUEUED indicates that the buffer has been dequeued by the
+            // client, but has not yet been queued or canceled. The buffer is
+            // considered 'owned' by the client, and the server should not use
+            // it for anything.
+            //
+            // Note that when in synchronous-mode (mSynchronousMode == true),
+            // the buffer that's currently attached to the texture may be
+            // dequeued by the client.  That means that the current buffer can
+            // be in either the DEQUEUED or QUEUED state.  In asynchronous mode,
+            // however, the current buffer is always in the QUEUED state.
+            DEQUEUED = 1,
+
+            // QUEUED indicates that the buffer has been queued by the client,
+            // and has not since been made available for the client to dequeue.
+            // Attaching the buffer to the texture does NOT transition the
+            // buffer away from the QUEUED state. However, in Synchronous mode
+            // the current buffer may be dequeued by the client under some
+            // circumstances. See the note about the current buffer in the
+            // documentation for DEQUEUED.
+            QUEUED = 2,
+        };
+
+        // mBufferState is the current state of this buffer slot.
+        BufferState mBufferState;
+
+        // mRequestBufferCalled is used for validating that the client did
+        // call requestBuffer() when told to do so. Technically this is not
+        // needed but useful for debugging and catching client bugs.
+        bool mRequestBufferCalled;
+
+        // mCrop is the current crop rectangle for this buffer slot. This gets
+        // set to mNextCrop each time queueBuffer gets called for this buffer.
+        Rect mCrop;
+
+        // mTransform is the current transform flags for this buffer slot. This
+        // gets set to mNextTransform each time queueBuffer gets called for this
+        // slot.
+        uint32_t mTransform;
+
+        // mScalingMode is the current scaling mode for this buffer slot. This
+        // gets set to mNextScalingMode each time queueBuffer gets called for
+        // this slot.
+        uint32_t mScalingMode;
+
+        // mTimestamp is the current timestamp for this buffer slot. This gets
+        // to set by queueBuffer each time this slot is queued.
+        int64_t mTimestamp;
+
+        // mFrameNumber is the number of the queued frame for this slot.
+        uint64_t mFrameNumber;
+
+        // mFence is the EGL sync object that must signal before the buffer
+        // associated with this buffer slot may be dequeued. It is initialized
+        // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
+        // on a compile-time option) set to a new sync object in updateTexImage.
+        EGLSyncKHR mFence;
+    };
+
+    // mSlots is the array of buffer slots that must be mirrored on the client
+    // side. This allows buffer ownership to be transferred between the client
+    // and server without sending a GraphicBuffer over binder. The entire array
+    // is initialized to NULL at construction time, and buffers are allocated
+    // for a slot when requestBuffer is called with that slot's index.
+    BufferSlot mSlots[NUM_BUFFER_SLOTS];
+
+
+    // mDefaultWidth holds the default width of allocated buffers. It is used
+    // in requestBuffers() if a width and height of zero is specified.
+    uint32_t mDefaultWidth;
+
+    // mDefaultHeight holds the default height of allocated buffers. It is used
+    // in requestBuffers() if a width and height of zero is specified.
+    uint32_t mDefaultHeight;
+
+    // mPixelFormat holds the pixel format of allocated buffers. It is used
+    // in requestBuffers() if a format of zero is specified.
+    uint32_t mPixelFormat;
+
+    // mBufferCount is the number of buffer slots that the client and server
+    // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed
+    // by calling setBufferCount or setBufferCountServer
+    int mBufferCount;
+
+    // mClientBufferCount is the number of buffer slots requested by the client.
+    // The default is zero, which means the client doesn't care how many buffers
+    // there is.
+    int mClientBufferCount;
+
+    // mServerBufferCount buffer count requested by the server-side
+    int mServerBufferCount;
+
+    // mCurrentTexture is the buffer slot index of the buffer that is currently
+    // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
+    // indicating that no buffer slot is currently bound to the texture. Note,
+    // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+    // that no buffer is bound to the texture. A call to setBufferCount will
+    // reset mCurrentTexture to INVALID_BUFFER_SLOT.
+    int mCurrentTexture;
+
+    // mNextCrop is the crop rectangle that will be used for the next buffer
+    // that gets queued. It is set by calling setCrop.
+    Rect mNextCrop;
+
+    // mNextTransform is the transform identifier that will be used for the next
+    // buffer that gets queued. It is set by calling setTransform.
+    uint32_t mNextTransform;
+
+    // mNextScalingMode is the scaling mode that will be used for the next
+    // buffers that get queued. It is set by calling setScalingMode.
+    int mNextScalingMode;
+
+    // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
+    // allocate new GraphicBuffer objects.
+    sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
+
+    // mFrameAvailableListener is the listener object that will be called when a
+    // new frame becomes available. If it is not NULL it will be called from
+    // queueBuffer.
+    sp<FrameAvailableListener> mFrameAvailableListener;
+
+    // mSynchronousMode whether we're in synchronous mode or not
+    bool mSynchronousMode;
+
+    // mAllowSynchronousMode whether we allow synchronous mode or not
+    const bool mAllowSynchronousMode;
+
+    // mConnectedApi indicates the API that is currently connected to this
+    // BufferQueue.  It defaults to NO_CONNECTED_API (= 0), and gets updated
+    // by the connect and disconnect methods.
+    int mConnectedApi;
+
+    // mDequeueCondition condition used for dequeueBuffer in synchronous mode
+    mutable Condition mDequeueCondition;
+
+    // mQueue is a FIFO of queued buffers used in synchronous mode
+    typedef Vector<int> Fifo;
+    Fifo mQueue;
+
+    // mAbandoned indicates that the BufferQueue will no longer be used to
+    // consume images buffers pushed to it using the ISurfaceTexture interface.
+    // It is initialized to false, and set to true in the abandon method.  A
+    // BufferQueue that has been abandoned will return the NO_INIT error from
+    // all ISurfaceTexture methods capable of returning an error.
+    bool mAbandoned;
+
+    // mName is a string used to identify the BufferQueue in log messages.
+    // It is set by the setName method.
+    String8 mName;
+
+    // mMutex is the mutex used to prevent concurrent access to the member
+    // variables of BufferQueue objects. It must be locked whenever the
+    // member variables are accessed.
+    mutable Mutex mMutex;
+
+    // mFrameCounter is the free running counter, incremented for every buffer queued
+    // with the surface Texture.
+    uint64_t mFrameCounter;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_BUFFERQUEUE_H
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index a8c7672..dcab049 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -23,6 +23,7 @@
 #include <GLES2/gl2ext.h>
 
 #include <gui/ISurfaceTexture.h>
+#include <gui/BufferQueue.h>
 
 #include <ui/GraphicBuffer.h>
 
@@ -35,30 +36,11 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-class IGraphicBufferAlloc;
+
 class String8;
 
-class SurfaceTexture : public BnSurfaceTexture {
+class SurfaceTexture : public BufferQueue {
 public:
-    enum { MIN_UNDEQUEUED_BUFFERS = 2 };
-    enum {
-        MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
-        MIN_SYNC_BUFFER_SLOTS  = MIN_UNDEQUEUED_BUFFERS
-    };
-    enum { NUM_BUFFER_SLOTS = 32 };
-    enum { NO_CONNECTED_API = 0 };
-
-    struct FrameAvailableListener : public virtual RefBase {
-        // onFrameAvailable() is called from queueBuffer() each time an
-        // additional frame becomes available for consumption. This means that
-        // frames that are queued while in asynchronous mode only trigger the
-        // callback if no previous frames are pending. Frames queued while in
-        // synchronous mode always trigger the callback.
-        //
-        // This is called without any lock held and can be called concurrently
-        // by multiple threads.
-        virtual void onFrameAvailable() = 0;
-    };
 
     // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
     // name of the OpenGL ES texture to which images are to be streamed. This
@@ -73,64 +55,7 @@
 
     virtual ~SurfaceTexture();
 
-    // setBufferCount updates the number of available buffer slots.  After
-    // calling this all buffer slots are both unallocated and owned by the
-    // SurfaceTexture object (i.e. they are not owned by the client).
-    virtual status_t setBufferCount(int bufferCount);
 
-    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
-
-    // dequeueBuffer gets the next buffer slot index for the client to use. If a
-    // buffer slot is available then that slot index is written to the location
-    // pointed to by the buf argument and a status of OK is returned.  If no
-    // slot is available then a status of -EBUSY is returned and buf is
-    // unmodified.
-    // The width and height parameters must be no greater than the minimum of
-    // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
-    // An error due to invalid dimensions might not be reported until
-    // updateTexImage() is called.
-    virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height,
-            uint32_t format, uint32_t usage);
-
-    // queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a
-    // timestamp must be provided for the buffer. The timestamp is in
-    // nanoseconds, and must be monotonically increasing. Its other semantics
-    // (zero point, etc) are client-dependent and should be documented by the
-    // client.
-    virtual status_t queueBuffer(int buf, int64_t timestamp,
-            uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
-    virtual void cancelBuffer(int buf);
-    virtual status_t setCrop(const Rect& reg);
-    virtual status_t setTransform(uint32_t transform);
-    virtual status_t setScalingMode(int mode);
-
-    virtual int query(int what, int* value);
-
-    // setSynchronousMode set whether dequeueBuffer is synchronous or
-    // asynchronous. In synchronous mode, dequeueBuffer blocks until
-    // a buffer is available, the currently bound buffer can be dequeued and
-    // queued buffers will be retired in order.
-    // The default mode is asynchronous.
-    virtual status_t setSynchronousMode(bool enabled);
-
-    // connect attempts to connect a client API to the SurfaceTexture.  This
-    // must be called before any other ISurfaceTexture methods are called except
-    // for getAllocator.
-    //
-    // This method will fail if the connect was previously called on the
-    // SurfaceTexture and no corresponding disconnect call was made.
-    virtual status_t connect(int api,
-            uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
-
-    // disconnect attempts to disconnect a client API from the SurfaceTexture.
-    // Calling this method will cause any subsequent calls to other
-    // ISurfaceTexture methods to fail except for getAllocator and connect.
-    // Successfully calling connect after this will allow the other methods to
-    // succeed again.
-    //
-    // This method will fail if the the SurfaceTexture is not currently
-    // connected to the specified client API.
-    virtual status_t disconnect(int api);
 
     // updateTexImage sets the image contents of the target texture to that of
     // the most recently queued buffer.
@@ -233,28 +158,6 @@
 
 protected:
 
-    // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
-    // for the given slot.
-    void freeBufferLocked(int index);
-
-    // freeAllBuffersLocked frees the resources (both GraphicBuffer and
-    // EGLImage) for all slots.
-    void freeAllBuffersLocked();
-
-    // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer
-    // and EGLImage) for all slots except the head of mQueue
-    void freeAllBuffersExceptHeadLocked();
-
-    // drainQueueLocked drains the buffer queue if we're in synchronous mode
-    // returns immediately otherwise. return NO_INIT if SurfaceTexture
-    // became abandoned or disconnected during this call.
-    status_t drainQueueLocked();
-
-    // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in
-    // synchronous mode and free all buffers. In asynchronous mode, all buffers
-    // are freed except the current buffer.
-    status_t drainQueueAndFreeBuffersLocked();
-
     static bool isExternalFormat(uint32_t format);
 
 private:
@@ -263,146 +166,11 @@
     EGLImageKHR createImage(EGLDisplay dpy,
             const sp<GraphicBuffer>& graphicBuffer);
 
-    status_t setBufferCountServerLocked(int bufferCount);
-
     // computeCurrentTransformMatrix computes the transform matrix for the
     // current texture.  It uses mCurrentTransform and the current GraphicBuffer
     // to compute this matrix and stores it in mCurrentTransformMatrix.
     void computeCurrentTransformMatrix();
 
-    enum { INVALID_BUFFER_SLOT = -1 };
-
-    struct BufferSlot {
-
-        BufferSlot()
-            : mEglImage(EGL_NO_IMAGE_KHR),
-              mEglDisplay(EGL_NO_DISPLAY),
-              mBufferState(BufferSlot::FREE),
-              mRequestBufferCalled(false),
-              mTransform(0),
-              mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
-              mTimestamp(0),
-              mFrameNumber(0),
-              mFence(EGL_NO_SYNC_KHR) {
-            mCrop.makeInvalid();
-        }
-
-        // mGraphicBuffer points to the buffer allocated for this slot or is NULL
-        // if no buffer has been allocated.
-        sp<GraphicBuffer> mGraphicBuffer;
-
-        // mEglImage is the EGLImage created from mGraphicBuffer.
-        EGLImageKHR mEglImage;
-
-        // mEglDisplay is the EGLDisplay used to create mEglImage.
-        EGLDisplay mEglDisplay;
-
-        // BufferState represents the different states in which a buffer slot
-        // can be.
-        enum BufferState {
-            // FREE indicates that the buffer is not currently being used and
-            // will not be used in the future until it gets dequeued and
-            // subsequently queued by the client.
-            FREE = 0,
-
-            // DEQUEUED indicates that the buffer has been dequeued by the
-            // client, but has not yet been queued or canceled. The buffer is
-            // considered 'owned' by the client, and the server should not use
-            // it for anything.
-            //
-            // Note that when in synchronous-mode (mSynchronousMode == true),
-            // the buffer that's currently attached to the texture may be
-            // dequeued by the client.  That means that the current buffer can
-            // be in either the DEQUEUED or QUEUED state.  In asynchronous mode,
-            // however, the current buffer is always in the QUEUED state.
-            DEQUEUED = 1,
-
-            // QUEUED indicates that the buffer has been queued by the client,
-            // and has not since been made available for the client to dequeue.
-            // Attaching the buffer to the texture does NOT transition the
-            // buffer away from the QUEUED state. However, in Synchronous mode
-            // the current buffer may be dequeued by the client under some
-            // circumstances. See the note about the current buffer in the
-            // documentation for DEQUEUED.
-            QUEUED = 2,
-        };
-
-        // mBufferState is the current state of this buffer slot.
-        BufferState mBufferState;
-
-        // mRequestBufferCalled is used for validating that the client did
-        // call requestBuffer() when told to do so. Technically this is not
-        // needed but useful for debugging and catching client bugs.
-        bool mRequestBufferCalled;
-
-        // mCrop is the current crop rectangle for this buffer slot. This gets
-        // set to mNextCrop each time queueBuffer gets called for this buffer.
-        Rect mCrop;
-
-        // mTransform is the current transform flags for this buffer slot. This
-        // gets set to mNextTransform each time queueBuffer gets called for this
-        // slot.
-        uint32_t mTransform;
-
-        // mScalingMode is the current scaling mode for this buffer slot. This
-        // gets set to mNextScalingMode each time queueBuffer gets called for
-        // this slot.
-        uint32_t mScalingMode;
-
-        // mTimestamp is the current timestamp for this buffer slot. This gets
-        // to set by queueBuffer each time this slot is queued.
-        int64_t mTimestamp;
-
-        // mFrameNumber is the number of the queued frame for this slot.
-        uint64_t mFrameNumber;
-
-        // mFence is the EGL sync object that must signal before the buffer
-        // associated with this buffer slot may be dequeued. It is initialized
-        // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
-        // on a compile-time option) set to a new sync object in updateTexImage.
-        EGLSyncKHR mFence;
-    };
-
-    // mSlots is the array of buffer slots that must be mirrored on the client
-    // side. This allows buffer ownership to be transferred between the client
-    // and server without sending a GraphicBuffer over binder. The entire array
-    // is initialized to NULL at construction time, and buffers are allocated
-    // for a slot when requestBuffer is called with that slot's index.
-    BufferSlot mSlots[NUM_BUFFER_SLOTS];
-
-    // mDefaultWidth holds the default width of allocated buffers. It is used
-    // in requestBuffers() if a width and height of zero is specified.
-    uint32_t mDefaultWidth;
-
-    // mDefaultHeight holds the default height of allocated buffers. It is used
-    // in requestBuffers() if a width and height of zero is specified.
-    uint32_t mDefaultHeight;
-
-    // mPixelFormat holds the pixel format of allocated buffers. It is used
-    // in requestBuffers() if a format of zero is specified.
-    uint32_t mPixelFormat;
-
-    // mBufferCount is the number of buffer slots that the client and server
-    // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed
-    // by calling setBufferCount or setBufferCountServer
-    int mBufferCount;
-
-    // mClientBufferCount is the number of buffer slots requested by the client.
-    // The default is zero, which means the client doesn't care how many buffers
-    // there is.
-    int mClientBufferCount;
-
-    // mServerBufferCount buffer count requested by the server-side
-    int mServerBufferCount;
-
-    // mCurrentTexture is the buffer slot index of the buffer that is currently
-    // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
-    // indicating that no buffer slot is currently bound to the texture. Note,
-    // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
-    // that no buffer is bound to the texture. A call to setBufferCount will
-    // reset mCurrentTexture to INVALID_BUFFER_SLOT.
-    int mCurrentTexture;
-
     // mCurrentTextureBuf is the graphic buffer of the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
@@ -429,72 +197,17 @@
     // gets set each time updateTexImage is called.
     int64_t mCurrentTimestamp;
 
-    // mNextCrop is the crop rectangle that will be used for the next buffer
-    // that gets queued. It is set by calling setCrop.
-    Rect mNextCrop;
-
-    // mNextTransform is the transform identifier that will be used for the next
-    // buffer that gets queued. It is set by calling setTransform.
-    uint32_t mNextTransform;
-
-    // mNextScalingMode is the scaling mode that will be used for the next
-    // buffers that get queued. It is set by calling setScalingMode.
-    int mNextScalingMode;
-
     // mTexName is the name of the OpenGL texture to which streamed images will
     // be bound when updateTexImage is called. It is set at construction time
     // changed with a call to setTexName.
     const GLuint mTexName;
 
-    // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
-    // allocate new GraphicBuffer objects.
-    sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
-
-    // mFrameAvailableListener is the listener object that will be called when a
-    // new frame becomes available. If it is not NULL it will be called from
-    // queueBuffer.
-    sp<FrameAvailableListener> mFrameAvailableListener;
-
-    // mSynchronousMode whether we're in synchronous mode or not
-    bool mSynchronousMode;
-
-    // mAllowSynchronousMode whether we allow synchronous mode or not
-    const bool mAllowSynchronousMode;
-
-    // mConnectedApi indicates the API that is currently connected to this
-    // SurfaceTexture.  It defaults to NO_CONNECTED_API (= 0), and gets updated
-    // by the connect and disconnect methods.
-    int mConnectedApi;
-
-    // mDequeueCondition condition used for dequeueBuffer in synchronous mode
-    mutable Condition mDequeueCondition;
-
-    // mQueue is a FIFO of queued buffers used in synchronous mode
-    typedef Vector<int> Fifo;
-    Fifo mQueue;
-
-    // mAbandoned indicates that the SurfaceTexture will no longer be used to
-    // consume images buffers pushed to it using the ISurfaceTexture interface.
-    // It is initialized to false, and set to true in the abandon method.  A
-    // SurfaceTexture that has been abandoned will return the NO_INIT error from
-    // all ISurfaceTexture methods capable of returning an error.
-    bool mAbandoned;
-
-    // mName is a string used to identify the SurfaceTexture in log messages.
-    // It is set by the setName method.
-    String8 mName;
-
     // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
     // extension should be used to prevent buffers from being dequeued before
     // it's safe for them to be written. It gets set at construction time and
     // never changes.
     const bool mUseFenceSync;
 
-    // mMutex is the mutex used to prevent concurrent access to the member
-    // variables of SurfaceTexture objects. It must be locked whenever the
-    // member variables are accessed.
-    mutable Mutex mMutex;
-
     // mTexTarget is the GL texture target with which the GL texture object is
     // associated.  It is set in the constructor and never changed.  It is
     // almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android
@@ -504,11 +217,6 @@
     // browser's tile cache exceeds.
     const GLenum mTexTarget;
 
-    // mFrameCounter is the free running counter, incremented for every buffer queued
-    // with the surface Texture.
-    uint64_t mFrameCounter;
-
-
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 7b0b443..02dfc1b 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -108,7 +108,8 @@
      * Returned value
      *   *descriptor updated with effect descriptor
      */
-    static status_t getEffectDescriptor(effect_uuid_t *uuid, effect_descriptor_t *descriptor);
+    static status_t getEffectDescriptor(const effect_uuid_t *uuid,
+                                        effect_descriptor_t *descriptor) /*const*/;
 
 
     /*
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index c8c5dba..437a89c 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -341,10 +341,9 @@
     private:
         friend class AudioRecord;
         virtual bool        threadLoop();
-        virtual status_t    readyToRun() { return NO_ERROR; }
+        virtual status_t    readyToRun();
         virtual void        onFirstRef() {}
         AudioRecord& mReceiver;
-        Mutex       mLock;
     };
 
             bool processAudioBuffer(const sp<ClientRecordThread>& thread);
@@ -360,7 +359,9 @@
     sp<IAudioRecord>        mAudioRecord;
     sp<IMemory>             mCblkMemory;
     sp<ClientRecordThread>  mClientRecordThread;
+    status_t                mReadyToRun;
     Mutex                   mLock;
+    Condition               mCondition;
 
     uint32_t                mFrameCount;
 
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 74a1e62..1916ac5 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -55,8 +55,10 @@
     static status_t getMasterMute(bool* mute);
 
     // set/get stream volume on specified output
-    static status_t setStreamVolume(audio_stream_type_t stream, float value, int output);
-    static status_t getStreamVolume(audio_stream_type_t stream, float* volume, int output);
+    static status_t setStreamVolume(audio_stream_type_t stream, float value,
+                                    audio_io_handle_t output);
+    static status_t getStreamVolume(audio_stream_type_t stream, float* volume,
+                                    audio_io_handle_t output);
 
     // mute/unmute stream
     static status_t setStreamMute(audio_stream_type_t stream, bool mute);
@@ -129,7 +131,7 @@
         NUM_CONFIG_EVENTS
     };
 
-    // audio output descritor used to cache output configurations in client process to avoid frequent calls
+    // audio output descriptor used to cache output configurations in client process to avoid frequent calls
     // through IAudioFlinger
     class OutputDescriptor {
     public:
@@ -217,7 +219,7 @@
 
         // indicate a change in the configuration of an output or input: keeps the cached
         // values for output/input parameters upto date in client process
-        virtual void ioConfigChanged(int event, int ioHandle, void *param2);
+        virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, void *param2);
     };
 
     class AudioPolicyServiceClient: public IBinder::DeathRecipient
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 02c85cd..9f2bd3a 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -58,8 +58,8 @@
         EVENT_BUFFER_END = 5        // Playback head is at the end of the buffer.
     };
 
-    /* Create Buffer on the stack and pass it to obtainBuffer()
-     * and releaseBuffer().
+    /* Client should declare Buffer on the stack and pass address to obtainBuffer()
+     * and releaseBuffer().  See also callback_t for EVENT_MORE_DATA.
      */
 
     class Buffer
@@ -68,12 +68,16 @@
         enum {
             MUTE    = 0x00000001
         };
-        uint32_t    flags;
+        uint32_t    flags;        // 0 or MUTE
         audio_format_t format; // but AUDIO_FORMAT_PCM_8_BIT -> AUDIO_FORMAT_PCM_16_BIT
         // accessed directly by WebKit ANP callback
         int         channelCount; // will be removed in the future, do not use
-        size_t      frameCount;
-        size_t      size;
+
+        size_t      frameCount;   // number of sample frames corresponding to size;
+                                  // on input it is the number of frames desired,
+                                  // on output is the number of frames actually filled
+
+        size_t      size;         // input/output in byte units
         union {
             void*       raw;
             short*      i16;    // signed 16-bit
@@ -84,15 +88,15 @@
 
     /* As a convenience, if a callback is supplied, a handler thread
      * is automatically created with the appropriate priority. This thread
-     * invokes the callback when a new buffer becomes available or an underrun condition occurs.
+     * invokes the callback when a new buffer becomes available or various conditions occur.
      * Parameters:
      *
      * event:   type of event notified (see enum AudioTrack::event_type).
      * user:    Pointer to context for use by the callback receiver.
      * info:    Pointer to optional parameter according to event type:
      *          - EVENT_MORE_DATA: pointer to AudioTrack::Buffer struct. The callback must not write
-     *          more bytes than indicated by 'size' field and update 'size' if less bytes are
-     *          written.
+     *            more bytes than indicated by 'size' field and update 'size' if fewer bytes are
+     *            written.
      *          - EVENT_UNDERRUN: unused.
      *          - EVENT_LOOP_END: pointer to an int indicating the number of loops remaining.
      *          - EVENT_MARKER: pointer to an uint32_t containing the marker position in frames.
@@ -225,7 +229,7 @@
      */
             uint32_t     latency() const;
 
-    /* getters, see constructor */
+    /* getters, see constructors and set() */
 
             audio_stream_type_t streamType() const;
             audio_format_t format() const;
@@ -273,18 +277,18 @@
      * left and right volumes. Levels must be >= 0.0 and <= 1.0.
      */
             status_t    setVolume(float left, float right);
-            void        getVolume(float* left, float* right);
+            void        getVolume(float* left, float* right) const;
 
     /* Set the send level for this track. An auxiliary effect should be attached
      * to the track with attachEffect(). Level must be >= 0.0 and <= 1.0.
      */
             status_t    setAuxEffectSendLevel(float level);
-            void        getAuxEffectSendLevel(float* level);
+            void        getAuxEffectSendLevel(float* level) const;
 
     /* Set sample rate for this track, mostly used for games' sound effects
      */
             status_t    setSampleRate(int sampleRate);
-            uint32_t    getSampleRate();
+            uint32_t    getSampleRate() const;
 
     /* Enables looping and sets the start and end points of looping.
      *
@@ -299,7 +303,6 @@
      *          (loopEnd-loopStart) <= framecount()
      */
             status_t    setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount);
-            status_t    getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount);
 
     /* Sets marker position. When playback reaches the number of frames specified, a callback with
      * event type EVENT_MARKER is called. Calling setMarkerPosition with marker == 0 cancels marker
@@ -315,7 +318,7 @@
      *  - INVALID_OPERATION: the AudioTrack has no callback installed.
      */
             status_t    setMarkerPosition(uint32_t marker);
-            status_t    getMarkerPosition(uint32_t *marker);
+            status_t    getMarkerPosition(uint32_t *marker) const;
 
 
     /* Sets position update period. Every time the number of frames specified has been played,
@@ -333,7 +336,7 @@
      *  - INVALID_OPERATION: the AudioTrack has no callback installed.
      */
             status_t    setPositionUpdatePeriod(uint32_t updatePeriod);
-            status_t    getPositionUpdatePeriod(uint32_t *updatePeriod);
+            status_t    getPositionUpdatePeriod(uint32_t *updatePeriod) const;
 
     /* Sets playback head position within AudioTrack buffer. The new position is specified
      * in number of frames.
@@ -384,7 +387,7 @@
      * Returned value:
      *  AudioTrack session ID.
      */
-            int    getSessionId();
+            int    getSessionId() const;
 
     /* Attach track auxiliary output to specified effect. Use effectId = 0
      * to detach track from effect.
@@ -401,13 +404,19 @@
             status_t    attachAuxEffect(int effectId);
 
     /* Obtains a buffer of "frameCount" frames. The buffer must be
-     * filled entirely. If the track is stopped, obtainBuffer() returns
+     * filled entirely, and then released with releaseBuffer().
+     * If the track is stopped, obtainBuffer() returns
      * STOPPED instead of NO_ERROR as long as there are buffers available,
      * at which point NO_MORE_BUFFERS is returned.
      * Buffers will be returned until the pool (buffercount())
      * is exhausted, at which point obtainBuffer() will either block
      * or return WOULD_BLOCK depending on the value of the "blocking"
      * parameter.
+     *
+     * Interpretation of waitCount:
+     *  +n  limits wait time to n * WAIT_PERIOD_MS,
+     *  -1  causes an (almost) infinite wait time,
+     *   0  non-blocking.
      */
 
         enum {
@@ -416,12 +425,19 @@
         };
 
             status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount);
+
+    /* Release a filled buffer of "frameCount" frames for AudioFlinger to process. */
             void        releaseBuffer(Buffer* audioBuffer);
 
     /* As a convenience we provide a write() interface to the audio buffer.
-     * This is implemented on top of lockBuffer/unlockBuffer. For best
-     * performance use callbacks. Return actual number of bytes written.
-     *
+     * This is implemented on top of obtainBuffer/releaseBuffer. For best
+     * performance use callbacks. Returns actual number of bytes written >= 0,
+     * or one of the following negative status codes:
+     *      INVALID_OPERATION   AudioTrack is configured for shared buffer mode
+     *      BAD_VALUE           size is invalid
+     *      STOPPED             AudioTrack was stopped during the write
+     *      NO_MORE_BUFFERS     when obtainBuffer() returns same
+     *      or any other error code returned by IAudioTrack::start() or restoreTrack_l().
      */
             ssize_t     write(const void* buffer, size_t size);
 
@@ -430,7 +446,7 @@
      */
             status_t dump(int fd, const Vector<String16>& args) const;
 
-private:
+protected:
     /* copying audio tracks is not allowed */
                         AudioTrack(const AudioTrack& other);
             AudioTrack& operator = (const AudioTrack& other);
@@ -446,9 +462,9 @@
         virtual status_t    readyToRun();
         virtual void        onFirstRef();
         AudioTrack& mReceiver;
-        Mutex       mLock;
     };
 
+            // body of AudioTrackThread::threadLoop()
             bool processAudioBuffer(const sp<AudioTrackThread>& thread);
             status_t createTrack_l(audio_stream_type_t streamType,
                                  uint32_t sampleRate,
@@ -485,7 +501,7 @@
 
     bool                    mActive;                // protected by mLock
 
-    callback_t              mCbf;
+    callback_t              mCbf;                   // callback handler for events, or NULL
     void*                   mUserData;
     uint32_t                mNotificationFramesReq; // requested number of frames between each notification callback
     uint32_t                mNotificationFramesAct; // actual number of frames between each notification callback
@@ -502,10 +518,33 @@
     int                     mAuxEffectId;
     mutable Mutex           mLock;
     status_t                mRestoreStatus;
+    bool                    mIsTimed;
     int                     mPreviousPriority;          // before start()
     int                     mPreviousSchedulingGroup;
 };
 
+class TimedAudioTrack : public AudioTrack
+{
+public:
+    TimedAudioTrack();
+
+    /* allocate a shared memory buffer that can be passed to queueTimedBuffer */
+    status_t allocateTimedBuffer(size_t size, sp<IMemory>* buffer);
+
+    /* queue a buffer obtained via allocateTimedBuffer for playback at the
+       given timestamp.  PTS units a microseconds on the media time timeline.
+       The media time transform (set with setMediaTimeTransform) set by the
+       audio producer will handle converting from media time to local time
+       (perhaps going through the common time timeline in the case of
+       synchronized multiroom audio case) */
+    status_t queueTimedBuffer(const sp<IMemory>& buffer, int64_t pts);
+
+    /* define a transform between media time and either common time or
+       local time */
+    enum TargetTimeline {LOCAL_TIME, COMMON_TIME};
+    status_t setMediaTimeTransform(const LinearTransform& xform,
+                                   TargetTimeline target);
+};
 
 }; // namespace android
 
diff --git a/include/media/EffectsFactoryApi.h b/include/media/EffectsFactoryApi.h
index 8ae13cc..65c26f47 100644
--- a/include/media/EffectsFactoryApi.h
+++ b/include/media/EffectsFactoryApi.h
@@ -87,7 +87,7 @@
 //    Description:    Creates an effect engine of the specified type and returns an
 //          effect control interface on this engine. The function will allocate the
 //          resources for an instance of the requested effect engine and return
-//          a handler on the effect control interface.
+//          a handle on the effect control interface.
 //
 //    Input:
 //          pEffectUuid:    pointer to the effect uuid.
@@ -109,23 +109,23 @@
 //        *pHandle:         updated with the effect handle.
 //
 ////////////////////////////////////////////////////////////////////////////////
-int EffectCreate(effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle);
+int EffectCreate(const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
 //    Function:       EffectRelease
 //
-//    Description:    Releases the effect engine whose handler is given as argument.
+//    Description:    Releases the effect engine whose handle is given as argument.
 //          All resources allocated to this particular instance of the effect are
 //          released.
 //
 //    Input:
-//          handle:    handler on the effect interface to be released.
+//          handle:    handle on the effect interface to be released.
 //
 //    Output:
 //        returned value:    0          successful operation.
 //                          -ENODEV     factory failed to initialize
-//                          -EINVAL     invalid interface handler
+//                          -EINVAL     invalid interface handle
 //
 ////////////////////////////////////////////////////////////////////////////////
 int EffectRelease(effect_handle_t handle);
@@ -151,7 +151,7 @@
 //        *pDescriptor:     updated with the effect descriptor.
 //
 ////////////////////////////////////////////////////////////////////////////////
-int EffectGetDescriptor(effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor);
+int EffectGetDescriptor(const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
@@ -167,7 +167,7 @@
 //                           1 if uuid is equal to EFFECT_UUID_NULL.
 //
 ////////////////////////////////////////////////////////////////////////////////
-int EffectIsNullUuid(effect_uuid_t *pEffectUuid);
+int EffectIsNullUuid(const effect_uuid_t *pEffectUuid);
 
 #if __cplusplus
 }  // extern "C"
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 760595c..7a2ada0 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -27,6 +27,7 @@
 #include <media/IAudioTrack.h>
 #include <media/IAudioRecord.h>
 #include <media/IAudioFlingerClient.h>
+#include <system/audio.h>
 #include <hardware/audio_effect.h>
 #include <media/IEffect.h>
 #include <media/IEffectClient.h>
@@ -53,13 +54,14 @@
                                 int frameCount,
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
-                                int output,
+                                audio_io_handle_t output,
+                                bool isTimed,
                                 int *sessionId,
                                 status_t *status) = 0;
 
     virtual sp<IAudioRecord> openRecord(
                                 pid_t pid,
-                                int input,
+                                audio_io_handle_t input,
                                 uint32_t sampleRate,
                                 audio_format_t format,
                                 uint32_t channelMask,
@@ -71,11 +73,11 @@
     /* query the audio hardware state. This state never changes,
      * and therefore can be cached.
      */
-    virtual     uint32_t    sampleRate(int output) const = 0;
-    virtual     int         channelCount(int output) const = 0;
-    virtual     audio_format_t format(int output) const = 0;
-    virtual     size_t      frameCount(int output) const = 0;
-    virtual     uint32_t    latency(int output) const = 0;
+    virtual     uint32_t    sampleRate(audio_io_handle_t output) const = 0;
+    virtual     int         channelCount(audio_io_handle_t output) const = 0;
+    virtual     audio_format_t format(audio_io_handle_t output) const = 0;
+    virtual     size_t      frameCount(audio_io_handle_t output) const = 0;
+    virtual     uint32_t    latency(audio_io_handle_t output) const = 0;
 
     /* set/get the audio hardware state. This will probably be used by
      * the preference panel, mostly.
@@ -89,10 +91,12 @@
     /* set/get stream type state. This will probably be used by
      * the preference panel, mostly.
      */
-    virtual     status_t    setStreamVolume(audio_stream_type_t stream, float value, int output) = 0;
+    virtual     status_t    setStreamVolume(audio_stream_type_t stream, float value,
+                                    audio_io_handle_t output) = 0;
     virtual     status_t    setStreamMute(audio_stream_type_t stream, bool muted) = 0;
 
-    virtual     float       streamVolume(audio_stream_type_t stream, int output) const = 0;
+    virtual     float       streamVolume(audio_stream_type_t stream,
+                                    audio_io_handle_t output) const = 0;
     virtual     bool        streamMute(audio_stream_type_t stream) const = 0;
 
     // set audio mode
@@ -102,63 +106,68 @@
     virtual     status_t    setMicMute(bool state) = 0;
     virtual     bool        getMicMute() const = 0;
 
-    virtual     status_t    setParameters(int ioHandle, const String8& keyValuePairs) = 0;
-    virtual     String8     getParameters(int ioHandle, const String8& keys) = 0;
+    virtual     status_t    setParameters(audio_io_handle_t ioHandle,
+                                    const String8& keyValuePairs) = 0;
+    virtual     String8     getParameters(audio_io_handle_t ioHandle, const String8& keys) const = 0;
 
     // register a current process for audio output change notifications
     virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
 
     // retrieve the audio recording buffer size
-    virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) = 0;
+    virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const = 0;
 
-    virtual int openOutput(uint32_t *pDevices,
+    virtual audio_io_handle_t openOutput(uint32_t *pDevices,
                                     uint32_t *pSamplingRate,
                                     audio_format_t *pFormat,
                                     uint32_t *pChannels,
                                     uint32_t *pLatencyMs,
                                     uint32_t flags) = 0;
-    virtual int openDuplicateOutput(int output1, int output2) = 0;
-    virtual status_t closeOutput(int output) = 0;
-    virtual status_t suspendOutput(int output) = 0;
-    virtual status_t restoreOutput(int output) = 0;
+    virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
+                                    audio_io_handle_t output2) = 0;
+    virtual status_t closeOutput(audio_io_handle_t output) = 0;
+    virtual status_t suspendOutput(audio_io_handle_t output) = 0;
+    virtual status_t restoreOutput(audio_io_handle_t output) = 0;
 
-    virtual int openInput(uint32_t *pDevices,
+    virtual audio_io_handle_t openInput(uint32_t *pDevices,
                                     uint32_t *pSamplingRate,
                                     audio_format_t *pFormat,
                                     uint32_t *pChannels,
                                     audio_in_acoustics_t acoustics) = 0;
-    virtual status_t closeInput(int input) = 0;
+    virtual status_t closeInput(audio_io_handle_t input) = 0;
 
-    virtual status_t setStreamOutput(audio_stream_type_t stream, int output) = 0;
+    virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output) = 0;
 
     virtual status_t setVoiceVolume(float volume) = 0;
 
-    virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output) = 0;
+    virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
+                                    audio_io_handle_t output) const = 0;
 
-    virtual unsigned int  getInputFramesLost(int ioHandle) = 0;
+    virtual unsigned int getInputFramesLost(audio_io_handle_t ioHandle) const = 0;
 
     virtual int newAudioSessionId() = 0;
 
     virtual void acquireAudioSessionId(int audioSession) = 0;
     virtual void releaseAudioSessionId(int audioSession) = 0;
 
-    virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
+    virtual status_t queryNumberEffects(uint32_t *numEffects) const = 0;
 
-    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) = 0;
+    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) const = 0;
 
-    virtual status_t getEffectDescriptor(effect_uuid_t *pEffectUUID, effect_descriptor_t *pDescriptor) = 0;
+    virtual status_t getEffectDescriptor(const effect_uuid_t *pEffectUUID,
+                                        effect_descriptor_t *pDescriptor) const = 0;
 
     virtual sp<IEffect> createEffect(pid_t pid,
                                     effect_descriptor_t *pDesc,
                                     const sp<IEffectClient>& client,
                                     int32_t priority,
-                                    int output,
+                                    audio_io_handle_t output,
                                     int sessionId,
                                     status_t *status,
                                     int *id,
                                     int *enabled) = 0;
 
-    virtual status_t moveEffects(int session, int srcOutput, int dstOutput) = 0;
+    virtual status_t moveEffects(int session, audio_io_handle_t srcOutput,
+                                    audio_io_handle_t dstOutput) = 0;
 };
 
 
diff --git a/include/media/IAudioFlingerClient.h b/include/media/IAudioFlingerClient.h
index aa0cdcf..f3b4df1 100644
--- a/include/media/IAudioFlingerClient.h
+++ b/include/media/IAudioFlingerClient.h
@@ -21,6 +21,7 @@
 #include <utils/RefBase.h>
 #include <binder/IInterface.h>
 #include <utils/KeyedVector.h>
+#include <system/audio.h>
 
 namespace android {
 
@@ -32,7 +33,7 @@
     DECLARE_META_INTERFACE(AudioFlingerClient);
 
     // Notifies a change of audio input/output configuration.
-    virtual void ioConfigChanged(int event, int ioHandle, void *param2) = 0;
+    virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, void *param2) = 0;
 
 };
 
diff --git a/include/media/IAudioRecord.h b/include/media/IAudioRecord.h
index 46735de..7869020 100644
--- a/include/media/IAudioRecord.h
+++ b/include/media/IAudioRecord.h
@@ -37,8 +37,9 @@
 
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
+     * tid identifies the client callback thread, or 0 if not needed.
      */
-    virtual status_t    start() = 0;
+    virtual status_t    start(pid_t tid) = 0;
 
     /* Stop a track. If set, the callback will cease being called and
      * obtainBuffer will return an error. Buffers that are already released 
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index b83e552..77f3e21 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -24,7 +24,7 @@
 #include <utils/Errors.h>
 #include <binder/IInterface.h>
 #include <binder/IMemory.h>
-
+#include <utils/LinearTransform.h>
 
 namespace android {
 
@@ -40,17 +40,18 @@
 
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
+     * tid identifies the client callback thread, or 0 if not needed.
      */
-    virtual status_t    start() = 0;
+    virtual status_t    start(pid_t tid) = 0;
 
     /* Stop a track. If set, the callback will cease being called and
      * obtainBuffer will return an error. Buffers that are already released 
-     * will be processed, unless flush() is called.
+     * will continue to be processed, unless/until flush() is called.
      */
     virtual void        stop() = 0;
 
-    /* Flush a stopped track. All pending buffers are discarded.
-     * This function has no effect if the track is not stopped.
+    /* Flush a stopped or paused track. All pending/released buffers are discarded.
+     * This function has no effect if the track is not stopped or paused.
      */
     virtual void        flush() = 0;
 
@@ -61,7 +62,7 @@
     
     /* Pause a track. If set, the callback will cease being called and
      * obtainBuffer will return an error. Buffers that are already released 
-     * will be processed, unless flush() is called.
+     * will continue to be processed, unless/until flush() is called.
      */
     virtual void        pause() = 0;
 
@@ -70,6 +71,23 @@
      */
     virtual status_t    attachAuxEffect(int effectId) = 0;
 
+
+    /* Allocate a shared memory buffer suitable for holding timed audio
+       samples */
+    virtual status_t    allocateTimedBuffer(size_t size,
+                                            sp<IMemory>* buffer) = 0;
+
+    /* Queue a buffer obtained via allocateTimedBuffer for playback at the given
+       timestamp */
+    virtual status_t    queueTimedBuffer(const sp<IMemory>& buffer,
+                                         int64_t pts) = 0;
+
+    /* Define the linear transform that will be applied to the timestamps
+       given to queueTimedBuffer (which are expressed in media time).
+       Target specifies whether this transform converts media time to local time
+       or Tungsten time. The values for target are defined in AudioTrack.h */
+    virtual status_t    setMediaTimeTransform(const LinearTransform& xform,
+                                              int target) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 7beb176..23a3e49 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -46,6 +46,9 @@
     // The shared library with the test player is passed passed as an
     // argument to the 'test:' url in the setDataSource call.
     TEST_PLAYER = 5,
+
+    AAH_RX_PLAYER = 100,
+    AAH_TX_PLAYER = 101,
 };
 
 
@@ -96,6 +99,8 @@
         virtual void        flush() = 0;
         virtual void        pause() = 0;
         virtual void        close() = 0;
+
+        virtual status_t    setPlaybackRatePermille(int32_t rate) { return INVALID_OPERATION; }
     };
 
                         MediaPlayerBase() : mCookie(0), mNotify(0) {}
diff --git a/include/media/MemoryLeakTrackUtil.h b/include/media/MemoryLeakTrackUtil.h
index 290b748..ac0f6b2 100644
--- a/include/media/MemoryLeakTrackUtil.h
+++ b/include/media/MemoryLeakTrackUtil.h
@@ -19,7 +19,7 @@
 
 namespace android {
 /*
- * Dump the memory adddress of the calling process to the given fd.
+ * Dump the memory address of the calling process to the given fd.
  */
 extern void dumpMemoryAddresses(int fd);
 
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 00b7dd5..d0b87c8 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -145,6 +145,9 @@
     // audio track, or zero for error (e.g. no audio track) or unknown.
     KEY_PARAMETER_AUDIO_CHANNEL_COUNT = 1200,                   // get only
 
+    // Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
+    // values used for rewinding or reverse playback.
+    KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300,                // set only
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 3963d9c..70799a6 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -22,6 +22,7 @@
 #include <android/native_window.h>
 #include <media/IOMX.h>
 #include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+#include <OMX_Audio.h>
 
 namespace android {
 
@@ -37,6 +38,9 @@
         kWhatFlushCompleted      = 'fcom',
         kWhatOutputFormatChanged = 'outC',
         kWhatError               = 'erro',
+        kWhatComponentAllocated  = 'cAll',
+        kWhatComponentConfigured = 'cCon',
+        kWhatBuffersAllocated    = 'allc',
     };
 
     ACodec();
@@ -47,6 +51,10 @@
     void signalResume();
     void initiateShutdown();
 
+    void initiateAllocateComponent(const sp<AMessage> &msg);
+    void initiateConfigureComponent(const sp<AMessage> &msg);
+    void initiateStart();
+
 protected:
     virtual ~ACodec();
 
@@ -70,6 +78,9 @@
         kWhatFlush                   = 'flus',
         kWhatResume                  = 'resm',
         kWhatDrainDeferredMessages   = 'drai',
+        kWhatAllocateComponent       = 'allo',
+        kWhatConfigureComponent      = 'conf',
+        kWhatStart                   = 'star',
     };
 
     enum {
@@ -118,6 +129,7 @@
     List<sp<AMessage> > mDeferredQueue;
 
     bool mSentFormat;
+    bool mIsEncoder;
 
     status_t allocateBuffersOnPort(OMX_U32 portIndex);
     status_t freeBuffersOnPort(OMX_U32 portIndex);
@@ -132,8 +144,8 @@
             uint32_t portIndex, IOMX::buffer_id bufferID,
             ssize_t *index = NULL);
 
-    void setComponentRole(bool isEncoder, const char *mime);
-    void configureCodec(const char *mime, const sp<AMessage> &msg);
+    status_t setComponentRole(bool isEncoder, const char *mime);
+    status_t configureCodec(const char *mime, const sp<AMessage> &msg);
 
     status_t setVideoPortFormatType(
             OMX_U32 portIndex,
@@ -145,20 +157,37 @@
     status_t setupVideoDecoder(
             const char *mime, int32_t width, int32_t height);
 
+    status_t setupVideoEncoder(
+            const char *mime, const sp<AMessage> &msg);
+
     status_t setVideoFormatOnPort(
             OMX_U32 portIndex,
             int32_t width, int32_t height,
             OMX_VIDEO_CODINGTYPE compressionFormat);
 
-    status_t setupAACDecoder(int32_t numChannels, int32_t sampleRate);
-    status_t setupAMRDecoder(bool isWAMR);
-    status_t setupG711Decoder(int32_t numChannels);
+    status_t setupAACCodec(
+            bool encoder,
+            int32_t numChannels, int32_t sampleRate, int32_t bitRate);
+
+    status_t selectAudioPortFormat(
+            OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat);
+
+    status_t setupAMRCodec(bool encoder, bool isWAMR, int32_t bitRate);
+    status_t setupG711Codec(bool encoder, int32_t numChannels);
 
     status_t setupRawAudioFormat(
             OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels);
 
     status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
 
+    status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
+    status_t setupH263EncoderParameters(const sp<AMessage> &msg);
+    status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
+
+    status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
+    status_t configureBitrate(int32_t bitrate);
+    status_t setupErrorCorrectionParameters();
+
     status_t initNativeWindow();
 
     // Returns true iff all buffers on the given port have status OWNED_BY_US.
@@ -173,7 +202,9 @@
 
     void sendFormatChange();
 
-    void signalError(OMX_ERRORTYPE error = OMX_ErrorUndefined);
+    void signalError(
+            OMX_ERRORTYPE error = OMX_ErrorUndefined,
+            status_t internalError = UNKNOWN_ERROR);
 
     DISALLOW_EVIL_CONSTRUCTORS(ACodec);
 };
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 0b79324..70c47ae 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -64,6 +64,8 @@
     bool isSeeking();
     bool reachedEOS(status_t *finalStatus);
 
+    status_t setPlaybackRatePermille(int32_t ratePermille);
+
 private:
     friend class VideoEditorAudioPlayer;
     sp<MediaSource> mSource;
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 79437bf..f5466e8 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -95,6 +95,7 @@
         int32_t startFrame, int32_t rampDurationFrames,
         uint8_t *data,   size_t bytes);
 
+    void queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs);
     void releaseQueuedFrames_l();
     void waitOutstandingEncodingFrames_l();
     status_t reset();
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
new file mode 100644
index 0000000..8c11c9c
--- /dev/null
+++ b/include/media/stagefright/MediaCodec.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+#ifndef MEDIA_CODEC_H_
+
+#define MEDIA_CODEC_H_
+
+#include <gui/ISurfaceTexture.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ABuffer;
+struct ACodec;
+struct AMessage;
+struct SoftwareRenderer;
+struct SurfaceTextureClient;
+
+struct MediaCodec : public AHandler {
+    enum ConfigureFlags {
+        CONFIGURE_FLAG_ENCODE   = 1,
+    };
+
+    enum BufferFlags {
+        BUFFER_FLAG_SYNCFRAME   = 1,
+        BUFFER_FLAG_CODECCONFIG = 2,
+        BUFFER_FLAG_EOS         = 4,
+    };
+
+    static sp<MediaCodec> CreateByType(
+            const sp<ALooper> &looper, const char *mime, bool encoder);
+
+    static sp<MediaCodec> CreateByComponentName(
+            const sp<ALooper> &looper, const char *name);
+
+    status_t configure(
+            const sp<AMessage> &format,
+            const sp<SurfaceTextureClient> &nativeWindow,
+            uint32_t flags);
+
+    status_t start();
+    status_t stop();
+
+    status_t flush();
+
+    status_t queueInputBuffer(
+            size_t index,
+            size_t offset,
+            size_t size,
+            int64_t presentationTimeUs,
+            uint32_t flags);
+
+    status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs = 0ll);
+
+    status_t dequeueOutputBuffer(
+            size_t *index,
+            size_t *offset,
+            size_t *size,
+            int64_t *presentationTimeUs,
+            uint32_t *flags,
+            int64_t timeoutUs = 0ll);
+
+    status_t renderOutputBufferAndRelease(size_t index);
+    status_t releaseOutputBuffer(size_t index);
+
+    status_t getOutputFormat(sp<AMessage> *format) const;
+
+    status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
+    status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const;
+
+protected:
+    virtual ~MediaCodec();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum State {
+        UNINITIALIZED,
+        INITIALIZING,
+        INITIALIZED,
+        CONFIGURING,
+        CONFIGURED,
+        STARTING,
+        STARTED,
+        FLUSHING,
+        STOPPING,
+    };
+
+    enum {
+        kPortIndexInput         = 0,
+        kPortIndexOutput        = 1,
+    };
+
+    enum {
+        kWhatInit                       = 'init',
+        kWhatConfigure                  = 'conf',
+        kWhatStart                      = 'strt',
+        kWhatStop                       = 'stop',
+        kWhatDequeueInputBuffer         = 'deqI',
+        kWhatQueueInputBuffer           = 'queI',
+        kWhatDequeueOutputBuffer        = 'deqO',
+        kWhatReleaseOutputBuffer        = 'relO',
+        kWhatGetBuffers                 = 'getB',
+        kWhatFlush                      = 'flus',
+        kWhatGetOutputFormat            = 'getO',
+        kWhatDequeueInputTimedOut       = 'dITO',
+        kWhatDequeueOutputTimedOut      = 'dOTO',
+        kWhatCodecNotify                = 'codc',
+    };
+
+    enum {
+        kFlagIsSoftwareCodec            = 1,
+        kFlagOutputFormatChanged        = 2,
+        kFlagOutputBuffersChanged       = 4,
+        kFlagStickyError                = 8,
+        kFlagDequeueInputPending        = 16,
+        kFlagDequeueOutputPending       = 32,
+    };
+
+    struct BufferInfo {
+        void *mBufferID;
+        sp<ABuffer> mData;
+        sp<AMessage> mNotify;
+        bool mOwnedByClient;
+    };
+
+    State mState;
+    sp<ALooper> mLooper;
+    sp<ALooper> mCodecLooper;
+    sp<ACodec> mCodec;
+    uint32_t mReplyID;
+    uint32_t mFlags;
+    sp<SurfaceTextureClient> mNativeWindow;
+    SoftwareRenderer *mSoftRenderer;
+    sp<AMessage> mOutputFormat;
+
+    List<size_t> mAvailPortBuffers[2];
+    Vector<BufferInfo> mPortBuffers[2];
+
+    int32_t mDequeueInputTimeoutGeneration;
+    uint32_t mDequeueInputReplyID;
+
+    int32_t mDequeueOutputTimeoutGeneration;
+    uint32_t mDequeueOutputReplyID;
+
+    MediaCodec(const sp<ALooper> &looper);
+
+    static status_t PostAndAwaitResponse(
+            const sp<AMessage> &msg, sp<AMessage> *response);
+
+    status_t init(const char *name, bool nameIsType, bool encoder);
+
+    void setState(State newState);
+    void returnBuffersToCodec();
+    void returnBuffersToCodecOnPort(int32_t portIndex);
+    size_t updateBuffers(int32_t portIndex, const sp<AMessage> &msg);
+    status_t onQueueInputBuffer(const sp<AMessage> &msg);
+    status_t onReleaseOutputBuffer(const sp<AMessage> &msg);
+    ssize_t dequeuePortBuffer(int32_t portIndex);
+
+    bool handleDequeueInputBuffer(uint32_t replyID, bool newRequest = false);
+    bool handleDequeueOutputBuffer(uint32_t replyID, bool newRequest = false);
+    void cancelPendingDequeueOperations();
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_CODEC_H_
diff --git a/include/media/stagefright/MediaDebug.h b/include/media/stagefright/MediaDebug.h
deleted file mode 100644
index 2ca9667..0000000
--- a/include/media/stagefright/MediaDebug.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MEDIA_DEBUG_H_
-
-#define MEDIA_DEBUG_H_
-
-#include <cutils/log.h>
-
-#define LITERAL_TO_STRING_INTERNAL(x)    #x
-#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
-
-#define CHECK_EQ(x,y)                                                   \
-    LOG_ALWAYS_FATAL_IF(                                                \
-            (x) != (y),                                                 \
-            __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x " != " #y)
-
-#define CHECK(x)                                                        \
-    LOG_ALWAYS_FATAL_IF(                                                \
-            !(x),                                                       \
-            __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x)
-
-#endif  // MEDIA_DEBUG_H_
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
index 21d00b8..dd3bf28 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -40,6 +40,7 @@
     // Not technically an error.
     INFO_FORMAT_CHANGED    = MEDIA_ERROR_BASE - 12,
     INFO_DISCONTINUITY     = MEDIA_ERROR_BASE - 13,
+    INFO_OUTPUT_BUFFERS_CHANGED = MEDIA_ERROR_BASE - 14,
 
     // The following constant values should be in sync with
     // drm/drm_framework_common.h
diff --git a/include/media/stagefright/NativeWindowWrapper.h b/include/media/stagefright/NativeWindowWrapper.h
index f323cbc..97cc0ce 100644
--- a/include/media/stagefright/NativeWindowWrapper.h
+++ b/include/media/stagefright/NativeWindowWrapper.h
@@ -18,40 +18,28 @@
 
 #define NATIVE_WINDOW_WRAPPER_H_
 
-#include <surfaceflinger/Surface.h>
 #include <gui/SurfaceTextureClient.h>
 
 namespace android {
 
-// Both Surface and SurfaceTextureClient are RefBase that implement the
-// ANativeWindow interface, but at different addresses. ANativeWindow is not
-// a RefBase but acts like one for use with sp<>.  This wrapper converts a
-// Surface or SurfaceTextureClient into a single reference-counted object
-// that holds an sp reference to the underlying Surface or SurfaceTextureClient,
-// It provides a method to get the ANativeWindow.
+// SurfaceTextureClient derives from ANativeWindow which derives from multiple
+// base classes, in order to carry it in AMessages, we'll temporarily wrap it
+// into a NativeWindowWrapper.
 
 struct NativeWindowWrapper : RefBase {
     NativeWindowWrapper(
-            const sp<Surface> &surface) :
-        mSurface(surface) { }
-
-    NativeWindowWrapper(
             const sp<SurfaceTextureClient> &surfaceTextureClient) :
         mSurfaceTextureClient(surfaceTextureClient) { }
 
     sp<ANativeWindow> getNativeWindow() const {
-        if (mSurface != NULL) {
-            return mSurface;
-        } else {
-            return mSurfaceTextureClient;
-        }
+        return mSurfaceTextureClient;
     }
 
-    // If needed later we can provide a method to ask what kind of native window
+    sp<SurfaceTextureClient> getSurfaceTextureClient() const {
+        return mSurfaceTextureClient;
+    }
 
 private:
-    // At most one of mSurface and mSurfaceTextureClient will be non-NULL
-    const sp<Surface> mSurface;
     const sp<SurfaceTextureClient> mSurfaceTextureClient;
 
     DISALLOW_EVIL_CONSTRUCTORS(NativeWindowWrapper);
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
new file mode 100644
index 0000000..96efdff
--- /dev/null
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+#ifndef NU_MEDIA_EXTRACTOR_H_
+#define NU_MEDIA_EXTRACTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+struct MediaBuffer;
+struct MediaExtractor;
+struct MediaSource;
+
+struct NuMediaExtractor : public RefBase {
+    NuMediaExtractor();
+
+    status_t setDataSource(const char *path);
+
+    size_t countTracks() const;
+    status_t getTrackFormat(size_t index, sp<AMessage> *format) const;
+
+    status_t selectTrack(size_t index);
+
+    status_t seekTo(int64_t timeUs);
+
+    status_t advance();
+    status_t readSampleData(const sp<ABuffer> &buffer);
+    status_t getSampleTrackIndex(size_t *trackIndex);
+    status_t getSampleTime(int64_t *sampleTimeUs);
+
+protected:
+    virtual ~NuMediaExtractor();
+
+private:
+    enum TrackFlags {
+        kIsVorbis       = 1,
+    };
+
+    struct TrackInfo {
+        sp<MediaSource> mSource;
+        size_t mTrackIndex;
+        status_t mFinalResult;
+        MediaBuffer *mSample;
+        int64_t mSampleTimeUs;
+        uint32_t mFlags;  // bitmask of "TrackFlags"
+    };
+
+    sp<MediaExtractor> mImpl;
+
+    Vector<TrackInfo> mSelectedTracks;
+
+    ssize_t fetchTrackSamples(int64_t seekTimeUs = -1ll);
+    void releaseTrackSamples();
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuMediaExtractor);
+};
+
+}  // namespace android
+
+#endif  // NU_MEDIA_EXTRACTOR_H_
+
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 4c30e04..e541c18 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -335,7 +335,7 @@
     status_t applyRotation();
     status_t waitForBufferFilled_l();
 
-    int64_t retrieveDecodingTimeUs(bool isCodecSpecific);
+    int64_t getDecodingTimeUs();
 
     status_t parseAVCCodecSpecificData(
             const void *data, size_t size,
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 7ec54aa..e5416e4 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -25,6 +25,7 @@
 
 namespace android {
 
+struct ABuffer;
 struct AString;
 struct Parcel;
 
@@ -50,6 +51,7 @@
     void setPointer(const char *name, void *value);
     void setString(const char *name, const char *s, ssize_t len = -1);
     void setObject(const char *name, const sp<RefBase> &obj);
+    void setBuffer(const char *name, const sp<ABuffer> &buffer);
     void setMessage(const char *name, const sp<AMessage> &obj);
 
     void setRect(
@@ -64,6 +66,7 @@
     bool findPointer(const char *name, void **value) const;
     bool findString(const char *name, AString *value) const;
     bool findObject(const char *name, sp<RefBase> *obj) const;
+    bool findBuffer(const char *name, sp<ABuffer> *buffer) const;
     bool findMessage(const char *name, sp<AMessage> *obj) const;
 
     bool findRect(
@@ -90,10 +93,6 @@
 
     AString debugString(int32_t indent = 0) const;
 
-protected:
-    virtual ~AMessage();
-
-private:
     enum Type {
         kTypeInt32,
         kTypeInt64,
@@ -105,8 +104,16 @@
         kTypeObject,
         kTypeMessage,
         kTypeRect,
+        kTypeBuffer,
     };
 
+    size_t countEntries() const;
+    const char *getEntryNameAt(size_t index, Type *type) const;
+
+protected:
+    virtual ~AMessage();
+
+private:
     uint32_t mWhat;
     ALooper::handler_id mTarget;
 
@@ -131,7 +138,7 @@
     };
 
     enum {
-        kMaxNumItems = 16
+        kMaxNumItems = 32
     };
     Item mItems[kMaxNumItems];
     size_t mNumItems;
@@ -140,6 +147,9 @@
     void freeItem(Item *item);
     const Item *findItem(const char *name, Type type) const;
 
+    void setObjectInternal(
+            const char *name, const sp<RefBase> &obj, Type type);
+
     DISALLOW_EVIL_CONSTRUCTORS(AMessage);
 };
 
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index dd97ce4..af2db93 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -54,6 +54,7 @@
 #define CBLK_RESTORED_ON        0x0040  // track has been restored after invalidation
 #define CBLK_RESTORED_OFF       0x0040  // by AudioFlinger
 
+// Important: do not add any virtual methods, including ~
 struct audio_track_cblk_t
 {
 
@@ -61,16 +62,23 @@
     // are in the same line of data cache.
                 Mutex       lock;           // sizeof(int)
                 Condition   cv;             // sizeof(int)
+
+                // next 4 are offsets within "buffers"
     volatile    uint32_t    user;
     volatile    uint32_t    server;
                 uint32_t    userBase;
                 uint32_t    serverBase;
+
+                // if there is a shared buffer, "buffers" is the value of pointer() for the shared
+                // buffer, otherwise "buffers" points immediately after the control block
                 void*       buffers;
                 uint32_t    frameCount;
+
                 // Cache line boundary
+
                 uint32_t    loopStart;
-                uint32_t    loopEnd;
-                int         loopCount;
+                uint32_t    loopEnd;        // read-only for server, read/write for client
+                int         loopCount;      // read/write for client
 
                 // Channel volumes are fixed point U4.12, so 0x1000 means 1.0.
                 // Left channel is in [0:15], right channel is in [16:31].
@@ -81,29 +89,39 @@
 public:
 
                 uint32_t    sampleRate;
+
                 // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for
                 // 8 bit PCM data: in this case,  mCblk->frameSize is based on a sample size of
                 // 16 bit because data is converted to 16 bit before being stored in buffer
 
+                // read-only for client, server writes once at initialization and is then read-only
                 uint8_t     frameSize;       // would normally be size_t, but 8 bits is plenty
+
+                // never used
                 uint8_t     pad1;
+
+                // used by client only
                 uint16_t    bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
 
-                uint16_t    waitTimeMs;      // Cumulated wait time
+                uint16_t    waitTimeMs;      // Cumulated wait time, used by client only
 private:
+                // client write-only, server read-only
                 uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0
 public:
     volatile    int32_t     flags;
 
                 // Cache line boundary (32 bytes)
 
+                // Since the control block is always located in shared memory, this constructor
+                // is only used for placement new().  It is never used for regular new() or stack.
                             audio_track_cblk_t();
-                uint32_t    stepUser(uint32_t frameCount);
-                bool        stepServer(uint32_t frameCount);
+                uint32_t    stepUser(uint32_t frameCount);      // called by client only, where
+                // client includes regular AudioTrack and AudioFlinger::PlaybackThread::OutputTrack
+                bool        stepServer(uint32_t frameCount);    // called by server only
                 void*       buffer(uint32_t offset) const;
                 uint32_t    framesAvailable();
                 uint32_t    framesAvailable_l();
-                uint32_t    framesReady();
+                uint32_t    framesReady();                      // called by server only
                 bool        tryLock();
 
                 // No barriers on the following operations, so the ordering of loads/stores
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
deleted file mode 100644
index 95e4447..0000000
--- a/include/ui/InputTransport.h
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _UI_INPUT_TRANSPORT_H
-#define _UI_INPUT_TRANSPORT_H
-
-/**
- * Native input transport.
- *
- * Uses anonymous shared memory as a whiteboard for sending input events from an
- * InputPublisher to an InputConsumer and ensuring appropriate synchronization.
- * One interesting feature is that published events can be updated in place as long as they
- * have not yet been consumed.
- *
- * The InputPublisher and InputConsumer only take care of transferring event data
- * over an InputChannel and sending synchronization signals.  The InputDispatcher and InputQueue
- * build on these abstractions to add multiplexing and queueing.
- */
-
-#include <semaphore.h>
-#include <ui/Input.h>
-#include <utils/Errors.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-
-namespace android {
-
-/*
- * An input channel consists of a shared memory buffer and a pair of pipes
- * used to send input messages from an InputPublisher to an InputConsumer
- * across processes.  Each channel has a descriptive name for debugging purposes.
- *
- * Each endpoint has its own InputChannel object that specifies its own file descriptors.
- *
- * The input channel is closed when all references to it are released.
- */
-class InputChannel : public RefBase {
-protected:
-    virtual ~InputChannel();
-
-public:
-    InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
-            int32_t sendPipeFd);
-
-    /* Creates a pair of input channels and their underlying shared memory buffers
-     * and pipes.
-     *
-     * Returns OK on success.
-     */
-    static status_t openInputChannelPair(const String8& name,
-            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
-
-    inline String8 getName() const { return mName; }
-    inline int32_t getAshmemFd() const { return mAshmemFd; }
-    inline int32_t getReceivePipeFd() const { return mReceivePipeFd; }
-    inline int32_t getSendPipeFd() const { return mSendPipeFd; }
-
-    /* Sends a signal to the other endpoint.
-     *
-     * Returns OK on success.
-     * Returns DEAD_OBJECT if the channel's peer has been closed.
-     * Other errors probably indicate that the channel is broken.
-     */
-    status_t sendSignal(char signal);
-
-    /* Receives a signal send by the other endpoint.
-     * (Should only call this after poll() indicates that the receivePipeFd has available input.)
-     *
-     * Returns OK on success.
-     * Returns WOULD_BLOCK if there is no signal present.
-     * Returns DEAD_OBJECT if the channel's peer has been closed.
-     * Other errors probably indicate that the channel is broken.
-     */
-    status_t receiveSignal(char* outSignal);
-
-private:
-    String8 mName;
-    int32_t mAshmemFd;
-    int32_t mReceivePipeFd;
-    int32_t mSendPipeFd;
-};
-
-/*
- * Private intermediate representation of input events as messages written into an
- * ashmem buffer.
- */
-struct InputMessage {
-    /* Semaphore count is set to 1 when the message is published.
-     * It becomes 0 transiently while the publisher updates the message.
-     * It becomes 0 permanently when the consumer consumes the message.
-     */
-    sem_t semaphore;
-
-    /* Initialized to false by the publisher.
-     * Set to true by the consumer when it consumes the message.
-     */
-    bool consumed;
-
-    int32_t type;
-
-    struct SampleData {
-        nsecs_t eventTime;
-        PointerCoords coords[0]; // variable length
-    };
-
-    int32_t deviceId;
-    int32_t source;
-
-    union {
-        struct {
-            int32_t action;
-            int32_t flags;
-            int32_t keyCode;
-            int32_t scanCode;
-            int32_t metaState;
-            int32_t repeatCount;
-            nsecs_t downTime;
-            nsecs_t eventTime;
-        } key;
-
-        struct {
-            int32_t action;
-            int32_t flags;
-            int32_t metaState;
-            int32_t buttonState;
-            int32_t edgeFlags;
-            nsecs_t downTime;
-            float xOffset;
-            float yOffset;
-            float xPrecision;
-            float yPrecision;
-            size_t pointerCount;
-            PointerProperties pointerProperties[MAX_POINTERS];
-            size_t sampleCount;
-            SampleData sampleData[0]; // variable length
-        } motion;
-    };
-
-    /* Gets the number of bytes to add to step to the next SampleData object in a motion
-     * event message for a given number of pointers.
-     */
-    static inline size_t sampleDataStride(size_t pointerCount) {
-        return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords);
-    }
-
-    /* Adds the SampleData stride to the given pointer. */
-    static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) {
-        return reinterpret_cast<InputMessage::SampleData*>(reinterpret_cast<char*>(ptr) + stride);
-    }
-};
-
-/*
- * Publishes input events to an anonymous shared memory buffer.
- * Uses atomic operations to coordinate shared access with a single concurrent consumer.
- */
-class InputPublisher {
-public:
-    /* Creates a publisher associated with an input channel. */
-    explicit InputPublisher(const sp<InputChannel>& channel);
-
-    /* Destroys the publisher and releases its input channel. */
-    ~InputPublisher();
-
-    /* Gets the underlying input channel. */
-    inline sp<InputChannel> getChannel() { return mChannel; }
-
-    /* Prepares the publisher for use.  Must be called before it is used.
-     * Returns OK on success.
-     *
-     * This method implicitly calls reset(). */
-    status_t initialize();
-
-    /* Resets the publisher to its initial state and unpins its ashmem buffer.
-     * Returns OK on success.
-     *
-     * Should be called after an event has been consumed to release resources used by the
-     * publisher until the next event is ready to be published.
-     */
-    status_t reset();
-
-    /* Publishes a key event to the ashmem buffer.
-     *
-     * Returns OK on success.
-     * Returns INVALID_OPERATION if the publisher has not been reset.
-     */
-    status_t publishKeyEvent(
-            int32_t deviceId,
-            int32_t source,
-            int32_t action,
-            int32_t flags,
-            int32_t keyCode,
-            int32_t scanCode,
-            int32_t metaState,
-            int32_t repeatCount,
-            nsecs_t downTime,
-            nsecs_t eventTime);
-
-    /* Publishes a motion event to the ashmem buffer.
-     *
-     * Returns OK on success.
-     * Returns INVALID_OPERATION if the publisher has not been reset.
-     * Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS.
-     */
-    status_t publishMotionEvent(
-            int32_t deviceId,
-            int32_t source,
-            int32_t action,
-            int32_t flags,
-            int32_t edgeFlags,
-            int32_t metaState,
-            int32_t buttonState,
-            float xOffset,
-            float yOffset,
-            float xPrecision,
-            float yPrecision,
-            nsecs_t downTime,
-            nsecs_t eventTime,
-            size_t pointerCount,
-            const PointerProperties* pointerProperties,
-            const PointerCoords* pointerCoords);
-
-    /* Appends a motion sample to a motion event unless already consumed.
-     *
-     * Returns OK on success.
-     * Returns INVALID_OPERATION if the current event is not a AMOTION_EVENT_ACTION_MOVE event.
-     * Returns FAILED_TRANSACTION if the current event has already been consumed.
-     * Returns NO_MEMORY if the buffer is full and no additional samples can be added.
-     */
-    status_t appendMotionSample(
-            nsecs_t eventTime,
-            const PointerCoords* pointerCoords);
-
-    /* Sends a dispatch signal to the consumer to inform it that a new message is available.
-     *
-     * Returns OK on success.
-     * Errors probably indicate that the channel is broken.
-     */
-    status_t sendDispatchSignal();
-
-    /* Receives the finished signal from the consumer in reply to the original dispatch signal.
-     * Returns whether the consumer handled the message.
-     *
-     * Returns OK on success.
-     * Returns WOULD_BLOCK if there is no signal present.
-     * Other errors probably indicate that the channel is broken.
-     */
-    status_t receiveFinishedSignal(bool* outHandled);
-
-private:
-    sp<InputChannel> mChannel;
-
-    size_t mAshmemSize;
-    InputMessage* mSharedMessage;
-    bool mPinned;
-    bool mSemaphoreInitialized;
-    bool mWasDispatched;
-
-    size_t mMotionEventPointerCount;
-    InputMessage::SampleData* mMotionEventSampleDataTail;
-    size_t mMotionEventSampleDataStride;
-
-    status_t publishInputEvent(
-            int32_t type,
-            int32_t deviceId,
-            int32_t source);
-};
-
-/*
- * Consumes input events from an anonymous shared memory buffer.
- * Uses atomic operations to coordinate shared access with a single concurrent publisher.
- */
-class InputConsumer {
-public:
-    /* Creates a consumer associated with an input channel. */
-    explicit InputConsumer(const sp<InputChannel>& channel);
-
-    /* Destroys the consumer and releases its input channel. */
-    ~InputConsumer();
-
-    /* Gets the underlying input channel. */
-    inline sp<InputChannel> getChannel() { return mChannel; }
-
-    /* Prepares the consumer for use.  Must be called before it is used. */
-    status_t initialize();
-
-    /* Consumes the input event in the buffer and copies its contents into
-     * an InputEvent object created using the specified factory.
-     * This operation will block if the publisher is updating the event.
-     *
-     * Returns OK on success.
-     * Returns INVALID_OPERATION if there is no currently published event.
-     * Returns NO_MEMORY if the event could not be created.
-     */
-    status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent);
-
-    /* Sends a finished signal to the publisher to inform it that the current message is
-     * finished processing and specifies whether the message was handled by the consumer.
-     *
-     * Returns OK on success.
-     * Errors probably indicate that the channel is broken.
-     */
-    status_t sendFinishedSignal(bool handled);
-
-    /* Receives the dispatched signal from the publisher.
-     *
-     * Returns OK on success.
-     * Returns WOULD_BLOCK if there is no signal present.
-     * Other errors probably indicate that the channel is broken.
-     */
-    status_t receiveDispatchSignal();
-
-private:
-    sp<InputChannel> mChannel;
-
-    size_t mAshmemSize;
-    InputMessage* mSharedMessage;
-
-    void populateKeyEvent(KeyEvent* keyEvent) const;
-    void populateMotionEvent(MotionEvent* motionEvent) const;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_TRANSPORT_H
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index 848c5a1..fc260c4 100644
--- a/include/ui/PixelFormat.h
+++ b/include/ui/PixelFormat.h
@@ -21,7 +21,6 @@
 // skia or SurfaceFlinger are not required to support all of these formats
 // (either as source or destination)
 
-// XXX: we should consolidate these formats and skia's
 
 #ifndef UI_PIXELFORMAT_H
 #define UI_PIXELFORMAT_H
@@ -29,7 +28,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <utils/Errors.h>
-#include <pixelflinger/format.h>
 #include <hardware/hardware.h>
 
 namespace android {
@@ -65,10 +63,7 @@
     PIXEL_FORMAT_BGRA_8888   = HAL_PIXEL_FORMAT_BGRA_8888,  // 4x8-bit BGRA
     PIXEL_FORMAT_RGBA_5551   = HAL_PIXEL_FORMAT_RGBA_5551,  // 16-bit ARGB
     PIXEL_FORMAT_RGBA_4444   = HAL_PIXEL_FORMAT_RGBA_4444,  // 16-bit ARGB
-    PIXEL_FORMAT_A_8         = GGL_PIXEL_FORMAT_A_8,        // 8-bit A
-    PIXEL_FORMAT_L_8         = GGL_PIXEL_FORMAT_L_8,        // 8-bit L (R=G=B=L)
-    PIXEL_FORMAT_LA_88       = GGL_PIXEL_FORMAT_LA_88,      // 16-bit LA
-    PIXEL_FORMAT_RGB_332     = GGL_PIXEL_FORMAT_RGB_332,    // 8-bit RGB
+    PIXEL_FORMAT_A_8         = 8,                           // 8-bit A
 
     // New formats can be added if they're also defined in
     // pixelflinger/format.h
@@ -76,8 +71,7 @@
 
 typedef int32_t PixelFormat;
 
-struct PixelFormatInfo
-{
+struct PixelFormatInfo {
     enum {
         INDEX_ALPHA   = 0,
         INDEX_RED     = 1,
@@ -89,8 +83,6 @@
         ALPHA               = 1,
         RGB                 = 2,
         RGBA                = 3,
-        LUMINANCE           = 4,
-        LUMINANCE_ALPHA     = 5,
         OTHER               = 0xFF
     };
 
diff --git a/include/ui/Region.h b/include/ui/Region.h
index 6c9a620..f242f18 100644
--- a/include/ui/Region.h
+++ b/include/ui/Region.h
@@ -55,43 +55,51 @@
             void        set(uint32_t w, uint32_t h);
         
             Region&     orSelf(const Rect& rhs);
+            Region&     xorSelf(const Rect& rhs);
             Region&     andSelf(const Rect& rhs);
             Region&     subtractSelf(const Rect& rhs);
 
             // boolean operators, applied on this
             Region&     orSelf(const Region& rhs);
+            Region&     xorSelf(const Region& rhs);
             Region&     andSelf(const Region& rhs);
             Region&     subtractSelf(const Region& rhs);
 
             // boolean operators
     const   Region      merge(const Rect& rhs) const;
+    const   Region      mergeExclusive(const Rect& rhs) const;
     const   Region      intersect(const Rect& rhs) const;
     const   Region      subtract(const Rect& rhs) const;
 
             // boolean operators
     const   Region      merge(const Region& rhs) const;
+    const   Region      mergeExclusive(const Region& rhs) const;
     const   Region      intersect(const Region& rhs) const;
     const   Region      subtract(const Region& rhs) const;
 
             // these translate rhs first
             Region&     translateSelf(int dx, int dy);
             Region&     orSelf(const Region& rhs, int dx, int dy);
+            Region&     xorSelf(const Region& rhs, int dx, int dy);
             Region&     andSelf(const Region& rhs, int dx, int dy);
             Region&     subtractSelf(const Region& rhs, int dx, int dy);
 
             // these translate rhs first
     const   Region      translate(int dx, int dy) const;
     const   Region      merge(const Region& rhs, int dx, int dy) const;
+    const   Region      mergeExclusive(const Region& rhs, int dx, int dy) const;
     const   Region      intersect(const Region& rhs, int dx, int dy) const;
     const   Region      subtract(const Region& rhs, int dx, int dy) const;
 
     // convenience operators overloads
     inline  const Region      operator | (const Region& rhs) const;
+    inline  const Region      operator ^ (const Region& rhs) const;
     inline  const Region      operator & (const Region& rhs) const;
     inline  const Region      operator - (const Region& rhs) const;
     inline  const Region      operator + (const Point& pt) const;
 
     inline  Region&     operator |= (const Region& rhs);
+    inline  Region&     operator ^= (const Region& rhs);
     inline  Region&     operator &= (const Region& rhs);
     inline  Region&     operator -= (const Region& rhs);
     inline  Region&     operator += (const Point& pt);
@@ -158,6 +166,9 @@
 const Region Region::operator | (const Region& rhs) const {
     return merge(rhs);
 }
+const Region Region::operator ^ (const Region& rhs) const {
+    return mergeExclusive(rhs);
+}
 const Region Region::operator & (const Region& rhs) const {
     return intersect(rhs);
 }
@@ -172,6 +183,9 @@
 Region& Region::operator |= (const Region& rhs) {
     return orSelf(rhs);
 }
+Region& Region::operator ^= (const Region& rhs) {
+    return xorSelf(rhs);
+}
 Region& Region::operator &= (const Region& rhs) {
     return andSelf(rhs);
 }
diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h
index 6bcdea4f..85535bd 100644
--- a/include/utils/KeyedVector.h
+++ b/include/utils/KeyedVector.h
@@ -66,7 +66,7 @@
             ssize_t         indexOfKey(const KEY& key) const;
 
     /*!
-     * modifing the array
+     * modifying the array
      */
 
             VALUE&          editValueFor(const KEY& key);
@@ -122,7 +122,7 @@
 
 template<typename KEY, typename VALUE> inline
 const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
-    ssize_t i = indexOfKey(key);
+    ssize_t i = this->indexOfKey(key);
     assert(i>=0);
     return mVector.itemAt(i).value;
 }
@@ -139,7 +139,7 @@
 
 template<typename KEY, typename VALUE> inline
 VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
-    ssize_t i = indexOfKey(key);
+    ssize_t i = this->indexOfKey(key);
     assert(i>=0);
     return mVector.editItemAt(i).value;
 }
@@ -190,7 +190,7 @@
 
 template<typename KEY, typename VALUE> inline
 const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
-    ssize_t i = indexOfKey(key);
+    ssize_t i = this->indexOfKey(key);
     return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
 }
 
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index db6388a..0fe7bd8 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -124,7 +124,7 @@
     public static final String EXTRA_SENDER = "sender";
 
     /**
-     * Action to bring up the CertInstaller
+     * Action to bring up the CertInstaller.
      */
     private static final String ACTION_INSTALL = "android.credentials.INSTALL";
 
@@ -167,6 +167,22 @@
     // Compatible with old android.security.Credentials.PKCS12
     public static final String EXTRA_PKCS12 = "PKCS12";
 
+
+    /**
+     * @hide TODO This is temporary and will be removed
+     * Broadcast Action: Indicates the trusted storage has changed. Sent when
+     * one of this happens:
+     *
+     * <ul>
+     * <li>a new CA is added,
+     * <li>an existing CA is removed or disabled,
+     * <li>a disabled CA is enabled,
+     * <li>trusted storage is reset (all user certs are cleared),
+     * <li>when permission to access a private key is changed.
+     * </ul>
+     */
+    public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
+
     /**
      * Returns an {@code Intent} that can be used for credential
      * installation. The intent may be used without any extras, in
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
new file mode 100644
index 0000000..c5f8a87
--- /dev/null
+++ b/libs/androidfw/Android.mk
@@ -0,0 +1,113 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+# libandroidfw is partially built for the host (used by build time keymap validation tool)
+# These files are common to host and target builds.
+
+# formerly in libutils
+commonUtilsSources:= \
+    Asset.cpp \
+    AssetDir.cpp \
+    AssetManager.cpp \
+    ObbFile.cpp \
+    ResourceTypes.cpp \
+    StreamingZipInflater.cpp \
+    ZipFileCRO.cpp \
+    ZipFileRO.cpp \
+    ZipUtils.cpp
+
+# formerly in libui
+commonUiSources:= \
+    Input.cpp \
+    Keyboard.cpp \
+    KeyCharacterMap.cpp \
+    KeyLayoutMap.cpp \
+    VirtualKeyMap.cpp
+
+commonSources:= \
+	$(commonUtilsSources) \
+	$(commonUiSources)
+
+# For the host
+# =====================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= $(commonSources)
+
+LOCAL_MODULE:= libandroidfw
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+	external/zlib
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# For the device
+# =====================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	$(commonSources) \
+	BackupData.cpp \
+	BackupHelpers.cpp \
+	InputTransport.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	liblog \
+	libcutils \
+	libutils \
+	libbinder \
+	libskia \
+	libz
+
+LOCAL_C_INCLUDES := \
+    external/skia/include/core \
+    external/icu4c/common \
+	external/zlib
+
+LOCAL_MODULE:= libandroidfw
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+ifeq ($(TARGET_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES += \
+	external/skia/include/core \
+	external/zlib \
+	external/icu4c/common \
+	bionic/libc/private
+LOCAL_LDLIBS := -lrt -ldl -lpthread
+LOCAL_MODULE := libandroidfw
+LOCAL_SRC_FILES := $(commonUtilsSources) BackupData.cpp BackupHelpers.cpp
+include $(BUILD_STATIC_LIBRARY)
+endif
+
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/utils/Asset.cpp b/libs/androidfw/Asset.cpp
similarity index 98%
rename from libs/utils/Asset.cpp
rename to libs/androidfw/Asset.cpp
index 50e701a..cb7628d 100644
--- a/libs/utils/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -21,23 +21,23 @@
 #define LOG_TAG "asset"
 //#define NDEBUG 0
 
-#include <utils/Asset.h>
+#include <androidfw/Asset.h>
+#include <androidfw/StreamingZipInflater.h>
+#include <androidfw/ZipFileRO.h>
+#include <androidfw/ZipUtils.h>
 #include <utils/Atomic.h>
 #include <utils/FileMap.h>
-#include <utils/StreamingZipInflater.h>
-#include <utils/ZipUtils.h>
-#include <utils/ZipFileRO.h>
 #include <utils/Log.h>
 #include <utils/threads.h>
 
-#include <string.h>
-#include <memory.h>
-#include <fcntl.h>
-#include <errno.h>
 #include <assert.h>
-#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 using namespace android;
 
diff --git a/libs/utils/AssetDir.cpp b/libs/androidfw/AssetDir.cpp
similarity index 97%
rename from libs/utils/AssetDir.cpp
rename to libs/androidfw/AssetDir.cpp
index c5f664e..475f521 100644
--- a/libs/utils/AssetDir.cpp
+++ b/libs/androidfw/AssetDir.cpp
@@ -19,7 +19,7 @@
 // implementation is in the header file or in friend functions in
 // AssetManager.
 //
-#include <utils/AssetDir.h>
+#include <androidfw/AssetDir.h>
 
 using namespace android;
 
diff --git a/libs/utils/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
similarity index 99%
rename from libs/utils/AssetManager.cpp
rename to libs/androidfw/AssetManager.cpp
index 47a2b99..4829add 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -21,23 +21,23 @@
 #define LOG_TAG "asset"
 //#define LOG_NDEBUG 0
 
-#include <utils/AssetManager.h>
-#include <utils/AssetDir.h>
-#include <utils/Asset.h>
+#include <androidfw/Asset.h>
+#include <androidfw/AssetDir.h>
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/ZipFileRO.h>
 #include <utils/Atomic.h>
-#include <utils/String8.h>
-#include <utils/ResourceTypes.h>
-#include <utils/String8.h>
-#include <utils/ZipFileRO.h>
 #include <utils/Log.h>
-#include <utils/Timers.h>
+#include <utils/String8.h>
+#include <utils/String8.h>
 #include <utils/threads.h>
+#include <utils/Timers.h>
 
+#include <assert.h>
 #include <dirent.h>
 #include <errno.h>
-#include <assert.h>
-#include <strings.h>
 #include <fcntl.h>
+#include <strings.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
diff --git a/libs/utils/BackupData.cpp b/libs/androidfw/BackupData.cpp
similarity index 99%
rename from libs/utils/BackupData.cpp
rename to libs/androidfw/BackupData.cpp
index f956306..7b1bcba 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "backup_data"
 
-#include <utils/BackupHelpers.h>
+#include <androidfw/BackupHelpers.h>
 #include <utils/ByteOrder.h>
 
 #include <stdio.h>
diff --git a/libs/utils/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
similarity index 99%
rename from libs/utils/BackupHelpers.cpp
rename to libs/androidfw/BackupHelpers.cpp
index f77a891..7a817a7 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "file_backup_helper"
 
-#include <utils/BackupHelpers.h>
+#include <androidfw/BackupHelpers.h>
 
 #include <utils/KeyedVector.h>
 #include <utils/ByteOrder.h>
@@ -546,7 +546,7 @@
 
     // read/write up to this much at a time.
     const size_t BUFSIZE = 32 * 1024;
-    char* buf = new char[BUFSIZE];
+    char* buf = (char *)calloc(1,BUFSIZE);
     char* paxHeader = buf + 512;    // use a different chunk of it as separate scratch
     char* paxData = buf + 1024;
 
@@ -556,9 +556,6 @@
         goto cleanup;
     }
 
-    // Good to go -- first construct the standard tar header at the start of the buffer
-    memset(buf, 0, BUFSIZE);
-
     // Magic fields for the ustar file format
     strcat(buf + 257, "ustar");
     strcat(buf + 263, "00");
diff --git a/libs/ui/Input.cpp b/libs/androidfw/Input.cpp
similarity index 99%
rename from libs/ui/Input.cpp
rename to libs/androidfw/Input.cpp
index 263c8d9..ca09caf 100644
--- a/libs/ui/Input.cpp
+++ b/libs/androidfw/Input.cpp
@@ -24,7 +24,7 @@
 #include <unistd.h>
 #include <ctype.h>
 
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 
 #include <math.h>
 #include <limits.h>
diff --git a/libs/androidfw/InputTransport.cpp b/libs/androidfw/InputTransport.cpp
new file mode 100644
index 0000000..1ebd75c
--- /dev/null
+++ b/libs/androidfw/InputTransport.cpp
@@ -0,0 +1,611 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// Provides a shared memory transport for input events.
+//
+#define LOG_TAG "InputTransport"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about channel messages (send message, receive message)
+#define DEBUG_CHANNEL_MESSAGES 0
+
+// Log debug messages whenever InputChannel objects are created/destroyed
+#define DEBUG_CHANNEL_LIFECYCLE 0
+
+// Log debug messages about transport actions
+#define DEBUG_TRANSPORT_ACTIONS 0
+
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <androidfw/InputTransport.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
+namespace android {
+
+// Socket buffer size.  The default is typically about 128KB, which is much larger than
+// we really need.  So we make it smaller.  It just needs to be big enough to hold
+// a few dozen large multi-finger motion events in the case where an application gets
+// behind processing touches.
+static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
+
+
+// --- InputMessage ---
+
+bool InputMessage::isValid(size_t actualSize) const {
+    if (size() == actualSize) {
+        switch (header.type) {
+        case TYPE_KEY:
+            return true;
+        case TYPE_MOTION:
+            return body.motion.pointerCount > 0
+                    && body.motion.pointerCount <= MAX_POINTERS;
+        case TYPE_FINISHED:
+            return true;
+        }
+    }
+    return false;
+}
+
+size_t InputMessage::size() const {
+    switch (header.type) {
+    case TYPE_KEY:
+        return sizeof(Header) + body.key.size();
+    case TYPE_MOTION:
+        return sizeof(Header) + body.motion.size();
+    case TYPE_FINISHED:
+        return sizeof(Header) + body.finished.size();
+    }
+    return sizeof(Header);
+}
+
+
+// --- InputChannel ---
+
+InputChannel::InputChannel(const String8& name, int fd) :
+        mName(name), mFd(fd) {
+#if DEBUG_CHANNEL_LIFECYCLE
+    ALOGD("Input channel constructed: name='%s', fd=%d",
+            mName.string(), fd);
+#endif
+
+    int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
+    LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
+            "non-blocking.  errno=%d", mName.string(), errno);
+}
+
+InputChannel::~InputChannel() {
+#if DEBUG_CHANNEL_LIFECYCLE
+    ALOGD("Input channel destroyed: name='%s', fd=%d",
+            mName.string(), mFd);
+#endif
+
+    ::close(mFd);
+}
+
+status_t InputChannel::openInputChannelPair(const String8& name,
+        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
+    int sockets[2];
+    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
+        status_t result = -errno;
+        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
+                name.string(), errno);
+        outServerChannel.clear();
+        outClientChannel.clear();
+        return result;
+    }
+
+    int bufferSize = SOCKET_BUFFER_SIZE;
+    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
+    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
+    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
+    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
+
+    String8 serverChannelName = name;
+    serverChannelName.append(" (server)");
+    outServerChannel = new InputChannel(serverChannelName, sockets[0]);
+
+    String8 clientChannelName = name;
+    clientChannelName.append(" (client)");
+    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
+    return OK;
+}
+
+status_t InputChannel::sendMessage(const InputMessage* msg) {
+    size_t msgLength = msg->size();
+    ssize_t nWrite;
+    do {
+        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+    } while (nWrite == -1 && errno == EINTR);
+
+    if (nWrite < 0) {
+        int error = errno;
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
+                msg->header.type, error);
+#endif
+        if (error == EAGAIN || error == EWOULDBLOCK) {
+            return WOULD_BLOCK;
+        }
+        if (error == EPIPE || error == ENOTCONN) {
+            return DEAD_OBJECT;
+        }
+        return -error;
+    }
+
+    if (size_t(nWrite) != msgLength) {
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
+                mName.string(), msg->header.type);
+#endif
+        return DEAD_OBJECT;
+    }
+
+#if DEBUG_CHANNEL_MESSAGES
+    ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
+#endif
+    return OK;
+}
+
+status_t InputChannel::receiveMessage(InputMessage* msg) {
+    ssize_t nRead;
+    do {
+        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
+    } while (nRead == -1 && errno == EINTR);
+
+    if (nRead < 0) {
+        int error = errno;
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno);
+#endif
+        if (error == EAGAIN || error == EWOULDBLOCK) {
+            return WOULD_BLOCK;
+        }
+        if (error == EPIPE || error == ENOTCONN) {
+            return DEAD_OBJECT;
+        }
+        return -error;
+    }
+
+    if (nRead == 0) { // check for EOF
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string());
+#endif
+        return DEAD_OBJECT;
+    }
+
+    if (!msg->isValid(nRead)) {
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ received invalid message", mName.string());
+#endif
+        return BAD_VALUE;
+    }
+
+#if DEBUG_CHANNEL_MESSAGES
+    ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type);
+#endif
+    return OK;
+}
+
+
+// --- InputPublisher ---
+
+InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
+        mChannel(channel) {
+}
+
+InputPublisher::~InputPublisher() {
+}
+
+status_t InputPublisher::publishKeyEvent(
+        uint32_t seq,
+        int32_t deviceId,
+        int32_t source,
+        int32_t action,
+        int32_t flags,
+        int32_t keyCode,
+        int32_t scanCode,
+        int32_t metaState,
+        int32_t repeatCount,
+        nsecs_t downTime,
+        nsecs_t eventTime) {
+#if DEBUG_TRANSPORT_ACTIONS
+    ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
+            "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+            "downTime=%lld, eventTime=%lld",
+            mChannel->getName().string(), seq,
+            deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
+            downTime, eventTime);
+#endif
+
+    if (!seq) {
+        ALOGE("Attempted to publish a key event with sequence number 0.");
+        return BAD_VALUE;
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::TYPE_KEY;
+    msg.body.key.seq = seq;
+    msg.body.key.deviceId = deviceId;
+    msg.body.key.source = source;
+    msg.body.key.action = action;
+    msg.body.key.flags = flags;
+    msg.body.key.keyCode = keyCode;
+    msg.body.key.scanCode = scanCode;
+    msg.body.key.metaState = metaState;
+    msg.body.key.repeatCount = repeatCount;
+    msg.body.key.downTime = downTime;
+    msg.body.key.eventTime = eventTime;
+    return mChannel->sendMessage(&msg);
+}
+
+status_t InputPublisher::publishMotionEvent(
+        uint32_t seq,
+        int32_t deviceId,
+        int32_t source,
+        int32_t action,
+        int32_t flags,
+        int32_t edgeFlags,
+        int32_t metaState,
+        int32_t buttonState,
+        float xOffset,
+        float yOffset,
+        float xPrecision,
+        float yPrecision,
+        nsecs_t downTime,
+        nsecs_t eventTime,
+        size_t pointerCount,
+        const PointerProperties* pointerProperties,
+        const PointerCoords* pointerCoords) {
+#if DEBUG_TRANSPORT_ACTIONS
+    ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
+            "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, "
+            "xOffset=%f, yOffset=%f, "
+            "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
+            "pointerCount=%d",
+            mChannel->getName().string(), seq,
+            deviceId, source, action, flags, edgeFlags, metaState, buttonState,
+            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
+#endif
+
+    if (!seq) {
+        ALOGE("Attempted to publish a motion event with sequence number 0.");
+        return BAD_VALUE;
+    }
+
+    if (pointerCount > MAX_POINTERS || pointerCount < 1) {
+        ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
+                mChannel->getName().string(), pointerCount);
+        return BAD_VALUE;
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::TYPE_MOTION;
+    msg.body.motion.seq = seq;
+    msg.body.motion.deviceId = deviceId;
+    msg.body.motion.source = source;
+    msg.body.motion.action = action;
+    msg.body.motion.flags = flags;
+    msg.body.motion.edgeFlags = edgeFlags;
+    msg.body.motion.metaState = metaState;
+    msg.body.motion.buttonState = buttonState;
+    msg.body.motion.xOffset = xOffset;
+    msg.body.motion.yOffset = yOffset;
+    msg.body.motion.xPrecision = xPrecision;
+    msg.body.motion.yPrecision = yPrecision;
+    msg.body.motion.downTime = downTime;
+    msg.body.motion.eventTime = eventTime;
+    msg.body.motion.pointerCount = pointerCount;
+    for (size_t i = 0; i < pointerCount; i++) {
+        msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
+        msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
+    }
+    return mChannel->sendMessage(&msg);
+}
+
+status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
+#if DEBUG_TRANSPORT_ACTIONS
+    ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
+            mChannel->getName().string());
+#endif
+
+    InputMessage msg;
+    status_t result = mChannel->receiveMessage(&msg);
+    if (result) {
+        *outSeq = 0;
+        *outHandled = false;
+        return result;
+    }
+    if (msg.header.type != InputMessage::TYPE_FINISHED) {
+        ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
+                mChannel->getName().string(), msg.header.type);
+        return UNKNOWN_ERROR;
+    }
+    *outSeq = msg.body.finished.seq;
+    *outHandled = msg.body.finished.handled;
+    return OK;
+}
+
+// --- InputConsumer ---
+
+InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
+        mChannel(channel), mMsgDeferred(false) {
+}
+
+InputConsumer::~InputConsumer() {
+}
+
+status_t InputConsumer::consume(InputEventFactoryInterface* factory,
+        bool consumeBatches, uint32_t* outSeq, InputEvent** outEvent) {
+#if DEBUG_TRANSPORT_ACTIONS
+    ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s",
+            mChannel->getName().string(), consumeBatches ? "true" : "false");
+#endif
+
+    *outSeq = 0;
+    *outEvent = NULL;
+
+    // Fetch the next input message.
+    // Loop until an event can be returned or no additional events are received.
+    while (!*outEvent) {
+        if (mMsgDeferred) {
+            // mMsg contains a valid input message from the previous call to consume
+            // that has not yet been processed.
+            mMsgDeferred = false;
+        } else {
+            // Receive a fresh message.
+            status_t result = mChannel->receiveMessage(&mMsg);
+            if (result) {
+                // Consume the next batched event unless batches are being held for later.
+                if (!mBatches.isEmpty() && (consumeBatches || result != WOULD_BLOCK)) {
+                    MotionEvent* motionEvent = factory->createMotionEvent();
+                    if (! motionEvent) return NO_MEMORY;
+
+                    const Batch& batch = mBatches.top();
+                    motionEvent->copyFrom(&batch.event, true /*keepHistory*/);
+                    *outSeq = batch.seq;
+                    *outEvent = motionEvent;
+                    mBatches.pop();
+#if DEBUG_TRANSPORT_ACTIONS
+                    ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
+                            mChannel->getName().string(), *outSeq);
+#endif
+                    break;
+                }
+                return result;
+            }
+        }
+
+        switch (mMsg.header.type) {
+        case InputMessage::TYPE_KEY: {
+            KeyEvent* keyEvent = factory->createKeyEvent();
+            if (!keyEvent) return NO_MEMORY;
+
+            initializeKeyEvent(keyEvent, &mMsg);
+            *outSeq = mMsg.body.key.seq;
+            *outEvent = keyEvent;
+#if DEBUG_TRANSPORT_ACTIONS
+            ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
+                    mChannel->getName().string(), *outSeq);
+#endif
+            break;
+        }
+
+        case AINPUT_EVENT_TYPE_MOTION: {
+            ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
+            if (batchIndex >= 0) {
+                Batch& batch = mBatches.editItemAt(batchIndex);
+                if (canAppendSamples(&batch.event, &mMsg)) {
+                    // Append to the batch and save the new sequence number for the tail end.
+                    uint32_t chain = batch.seq;
+                    appendSamples(&batch.event, &mMsg);
+                    batch.seq = mMsg.body.motion.seq;
+
+                    // Update the sequence number chain.
+                    SeqChain seqChain;
+                    seqChain.seq = batch.seq;
+                    seqChain.chain = chain;
+                    mSeqChains.push(seqChain);
+#if DEBUG_TRANSPORT_ACTIONS
+                    ALOGD("channel '%s' consumer ~ appended to batch event",
+                            mChannel->getName().string());
+#endif
+                    break;
+                } else {
+                    MotionEvent* motionEvent = factory->createMotionEvent();
+                    if (! motionEvent) return NO_MEMORY;
+
+                    // We cannot append to the batch in progress, so we need to consume
+                    // the previous batch right now and defer the new message until later.
+                    mMsgDeferred = true;
+
+                    // Return the end of the previous batch.
+                    motionEvent->copyFrom(&batch.event, true /*keepHistory*/);
+                    *outSeq = batch.seq;
+                    *outEvent = motionEvent;
+                    mBatches.removeAt(batchIndex);
+#if DEBUG_TRANSPORT_ACTIONS
+                    ALOGD("channel '%s' consumer ~ consumed batch event and "
+                            "deferred current event, seq=%u",
+                            mChannel->getName().string(), *outSeq);
+#endif
+                    break;
+                }
+            }
+
+            // Start a new batch if needed.
+            if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
+                    || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                mBatches.push();
+                Batch& batch = mBatches.editTop();
+                batch.seq = mMsg.body.motion.seq;
+                initializeMotionEvent(&batch.event, &mMsg);
+#if DEBUG_TRANSPORT_ACTIONS
+                ALOGD("channel '%s' consumer ~ started batch event",
+                        mChannel->getName().string());
+#endif
+                break;
+            }
+
+            MotionEvent* motionEvent = factory->createMotionEvent();
+            if (! motionEvent) return NO_MEMORY;
+
+            initializeMotionEvent(motionEvent, &mMsg);
+            *outSeq = mMsg.body.motion.seq;
+            *outEvent = motionEvent;
+#if DEBUG_TRANSPORT_ACTIONS
+            ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
+                    mChannel->getName().string(), *outSeq);
+#endif
+            break;
+        }
+
+        default:
+            ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
+                    mChannel->getName().string(), mMsg.header.type);
+            return UNKNOWN_ERROR;
+        }
+    }
+    return OK;
+}
+
+status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
+#if DEBUG_TRANSPORT_ACTIONS
+    ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+            mChannel->getName().string(), seq, handled ? "true" : "false");
+#endif
+
+    if (!seq) {
+        ALOGE("Attempted to send a finished signal with sequence number 0.");
+        return BAD_VALUE;
+    }
+
+    // Send finished signals for the batch sequence chain first.
+    size_t seqChainCount = mSeqChains.size();
+    if (seqChainCount) {
+        uint32_t currentSeq = seq;
+        uint32_t chainSeqs[seqChainCount];
+        size_t chainIndex = 0;
+        for (size_t i = seqChainCount; i-- > 0; ) {
+             const SeqChain& seqChain = mSeqChains.itemAt(i);
+             if (seqChain.seq == currentSeq) {
+                 currentSeq = seqChain.chain;
+                 chainSeqs[chainIndex++] = currentSeq;
+                 mSeqChains.removeAt(i);
+             }
+        }
+        status_t status = OK;
+        while (!status && chainIndex-- > 0) {
+            status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
+        }
+        if (status) {
+            // An error occurred so at least one signal was not sent, reconstruct the chain.
+            do {
+                SeqChain seqChain;
+                seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
+                seqChain.chain = chainSeqs[chainIndex];
+                mSeqChains.push(seqChain);
+            } while (chainIndex-- > 0);
+            return status;
+        }
+    }
+
+    // Send finished signal for the last message in the batch.
+    return sendUnchainedFinishedSignal(seq, handled);
+}
+
+status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
+    InputMessage msg;
+    msg.header.type = InputMessage::TYPE_FINISHED;
+    msg.body.finished.seq = seq;
+    msg.body.finished.handled = handled;
+    return mChannel->sendMessage(&msg);
+}
+
+bool InputConsumer::hasPendingBatch() const {
+    return !mBatches.isEmpty();
+}
+
+ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
+    for (size_t i = 0; i < mBatches.size(); i++) {
+        const Batch& batch = mBatches.itemAt(i);
+        if (batch.event.getDeviceId() == deviceId && batch.event.getSource() == source) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
+    event->initialize(
+            msg->body.key.deviceId,
+            msg->body.key.source,
+            msg->body.key.action,
+            msg->body.key.flags,
+            msg->body.key.keyCode,
+            msg->body.key.scanCode,
+            msg->body.key.metaState,
+            msg->body.key.repeatCount,
+            msg->body.key.downTime,
+            msg->body.key.eventTime);
+}
+
+void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
+    size_t pointerCount = msg->body.motion.pointerCount;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
+        pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
+    }
+
+    event->initialize(
+            msg->body.motion.deviceId,
+            msg->body.motion.source,
+            msg->body.motion.action,
+            msg->body.motion.flags,
+            msg->body.motion.edgeFlags,
+            msg->body.motion.metaState,
+            msg->body.motion.buttonState,
+            msg->body.motion.xOffset,
+            msg->body.motion.yOffset,
+            msg->body.motion.xPrecision,
+            msg->body.motion.yPrecision,
+            msg->body.motion.downTime,
+            msg->body.motion.eventTime,
+            pointerCount,
+            pointerProperties,
+            pointerCoords);
+}
+
+bool InputConsumer::canAppendSamples(const MotionEvent* event, const InputMessage *msg) {
+    size_t pointerCount = msg->body.motion.pointerCount;
+    if (event->getPointerCount() != pointerCount
+            || event->getAction() != msg->body.motion.action) {
+        return false;
+    }
+    for (size_t i = 0; i < pointerCount; i++) {
+        if (*event->getPointerProperties(i) != msg->body.motion.pointers[i].properties) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void InputConsumer::appendSamples(MotionEvent* event, const InputMessage* msg) {
+    size_t pointerCount = msg->body.motion.pointerCount;
+    PointerCoords pointerCoords[pointerCount];
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
+    }
+
+    event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
+    event->addSample(msg->body.motion.eventTime, pointerCoords);
+}
+
+} // namespace android
diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/androidfw/KeyCharacterMap.cpp
similarity index 99%
rename from libs/ui/KeyCharacterMap.cpp
rename to libs/androidfw/KeyCharacterMap.cpp
index 485234c..6984084 100644
--- a/libs/ui/KeyCharacterMap.cpp
+++ b/libs/androidfw/KeyCharacterMap.cpp
@@ -19,8 +19,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <android/keycodes.h>
-#include <ui/Keyboard.h>
-#include <ui/KeyCharacterMap.h>
+#include <androidfw/Keyboard.h>
+#include <androidfw/KeyCharacterMap.h>
 #include <utils/Log.h>
 #include <utils/Errors.h>
 #include <utils/Tokenizer.h>
diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/androidfw/KeyLayoutMap.cpp
similarity index 98%
rename from libs/ui/KeyLayoutMap.cpp
rename to libs/androidfw/KeyLayoutMap.cpp
index 44a9420..15d81ee 100644
--- a/libs/ui/KeyLayoutMap.cpp
+++ b/libs/androidfw/KeyLayoutMap.cpp
@@ -18,8 +18,8 @@
 
 #include <stdlib.h>
 #include <android/keycodes.h>
-#include <ui/Keyboard.h>
-#include <ui/KeyLayoutMap.h>
+#include <androidfw/Keyboard.h>
+#include <androidfw/KeyLayoutMap.h>
 #include <utils/Log.h>
 #include <utils/Errors.h>
 #include <utils/Tokenizer.h>
diff --git a/libs/ui/Keyboard.cpp b/libs/androidfw/Keyboard.cpp
similarity index 98%
rename from libs/ui/Keyboard.cpp
rename to libs/androidfw/Keyboard.cpp
index e4611f7..e97a5eb 100644
--- a/libs/ui/Keyboard.cpp
+++ b/libs/androidfw/Keyboard.cpp
@@ -20,10 +20,10 @@
 #include <unistd.h>
 #include <limits.h>
 
-#include <ui/Keyboard.h>
-#include <ui/KeycodeLabels.h>
-#include <ui/KeyLayoutMap.h>
-#include <ui/KeyCharacterMap.h>
+#include <androidfw/Keyboard.h>
+#include <androidfw/KeycodeLabels.h>
+#include <androidfw/KeyLayoutMap.h>
+#include <androidfw/KeyCharacterMap.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <cutils/properties.h>
diff --git a/libs/androidfw/MODULE_LICENSE_APACHE2 b/libs/androidfw/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/androidfw/MODULE_LICENSE_APACHE2
diff --git a/libs/androidfw/NOTICE b/libs/androidfw/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/androidfw/NOTICE
@@ -0,0 +1,190 @@
+
+   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/libs/utils/ObbFile.cpp b/libs/androidfw/ObbFile.cpp
similarity index 99%
rename from libs/utils/ObbFile.cpp
rename to libs/androidfw/ObbFile.cpp
index ddf5991..21e06c8 100644
--- a/libs/utils/ObbFile.cpp
+++ b/libs/androidfw/ObbFile.cpp
@@ -23,9 +23,9 @@
 
 #define LOG_TAG "ObbFile"
 
+#include <androidfw/ObbFile.h>
 #include <utils/Compat.h>
 #include <utils/Log.h>
-#include <utils/ObbFile.h>
 
 //#define DEBUG 1
 
diff --git a/libs/utils/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
similarity index 99%
rename from libs/utils/ResourceTypes.cpp
rename to libs/androidfw/ResourceTypes.cpp
index 3fa562e..07f3b16 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -17,14 +17,14 @@
 #define LOG_TAG "ResourceType"
 //#define LOG_NDEBUG 0
 
+#include <androidfw/ResourceTypes.h>
 #include <utils/Atomic.h>
 #include <utils/ByteOrder.h>
 #include <utils/Debug.h>
-#include <utils/ResourceTypes.h>
+#include <utils/Log.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
 #include <utils/TextOutput.h>
-#include <utils/Log.h>
 
 #include <stdlib.h>
 #include <string.h>
@@ -379,8 +379,7 @@
         size_t charSize;
         if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
             charSize = sizeof(uint8_t);
-            mCache = (char16_t**)malloc(sizeof(char16_t**)*mHeader->stringCount);
-            memset(mCache, 0, sizeof(char16_t**)*mHeader->stringCount);
+            mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**));
         } else {
             charSize = sizeof(char16_t);
         }
@@ -3252,16 +3251,14 @@
 
     // Bag not found, we need to compute it!
     if (!grp->bags) {
-        grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount);
+        grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*));
         if (!grp->bags) return NO_MEMORY;
-        memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount);
     }
 
     bag_set** typeSet = grp->bags[t];
     if (!typeSet) {
-        typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY);
+        typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
         if (!typeSet) return NO_MEMORY;
-        memset(typeSet, 0, sizeof(bag_set*)*NENTRY);
         grp->bags[t] = typeSet;
     }
 
diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp
similarity index 99%
rename from libs/utils/StreamingZipInflater.cpp
rename to libs/androidfw/StreamingZipInflater.cpp
index 8512170a..d3fb98d 100644
--- a/libs/utils/StreamingZipInflater.cpp
+++ b/libs/androidfw/StreamingZipInflater.cpp
@@ -18,8 +18,8 @@
 #define LOG_TAG "szipinf"
 #include <utils/Log.h>
 
+#include <androidfw/StreamingZipInflater.h>
 #include <utils/FileMap.h>
-#include <utils/StreamingZipInflater.h>
 #include <string.h>
 #include <stddef.h>
 #include <assert.h>
diff --git a/libs/ui/VirtualKeyMap.cpp b/libs/androidfw/VirtualKeyMap.cpp
similarity index 98%
rename from libs/ui/VirtualKeyMap.cpp
rename to libs/androidfw/VirtualKeyMap.cpp
index 62d5b59..2ba1673 100644
--- a/libs/ui/VirtualKeyMap.cpp
+++ b/libs/androidfw/VirtualKeyMap.cpp
@@ -18,7 +18,7 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <ui/VirtualKeyMap.h>
+#include <androidfw/VirtualKeyMap.h>
 #include <utils/Log.h>
 #include <utils/Errors.h>
 #include <utils/Tokenizer.h>
diff --git a/libs/utils/ZipFileCRO.cpp b/libs/androidfw/ZipFileCRO.cpp
similarity index 95%
rename from libs/utils/ZipFileCRO.cpp
rename to libs/androidfw/ZipFileCRO.cpp
index 55dfd9f..c8df845 100644
--- a/libs/utils/ZipFileCRO.cpp
+++ b/libs/androidfw/ZipFileCRO.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include <utils/ZipFileCRO.h>
-#include <utils/ZipFileRO.h>
+#include <androidfw/ZipFileCRO.h>
+#include <androidfw/ZipFileRO.h>
 
 using namespace android;
 
diff --git a/libs/utils/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
similarity index 99%
rename from libs/utils/ZipFileRO.cpp
rename to libs/androidfw/ZipFileRO.cpp
index 1498aac..4b7f1e7 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -19,7 +19,7 @@
 //
 #define LOG_TAG "zipro"
 //#define LOG_NDEBUG 0
-#include <utils/ZipFileRO.h>
+#include <androidfw/ZipFileRO.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
 #include <utils/threads.h>
diff --git a/libs/utils/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
similarity index 98%
rename from libs/utils/ZipUtils.cpp
rename to libs/androidfw/ZipUtils.cpp
index 2dbdc1d..db3479d 100644
--- a/libs/utils/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -20,8 +20,8 @@
 
 #define LOG_TAG "ziputil"
 
-#include <utils/ZipUtils.h>
-#include <utils/ZipFileRO.h>
+#include <androidfw/ZipUtils.h>
+#include <androidfw/ZipFileRO.h>
 #include <utils/Log.h>
 
 #include <stdlib.h>
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
new file mode 100644
index 0000000..d85009b
--- /dev/null
+++ b/libs/androidfw/tests/Android.mk
@@ -0,0 +1,47 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Build the unit tests.
+test_src_files := \
+    InputChannel_test.cpp \
+    InputEvent_test.cpp \
+    InputPublisherAndConsumer_test.cpp \
+    ObbFile_test.cpp \
+    ZipFileRO_test.cpp 
+
+shared_libraries := \
+	libandroidfw \
+	libcutils \
+	libutils \
+	libbinder \
+	libui \
+	libstlport \
+	libskia
+
+static_libraries := \
+	libgtest \
+	libgtest_main
+
+c_includes := \
+    bionic \
+    bionic/libstdc++/include \
+    external/gtest/include \
+    external/stlport/stlport \
+    external/skia/include/core
+
+module_tags := eng tests
+
+$(foreach file,$(test_src_files), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+    $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+    $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
+    $(eval include $(BUILD_EXECUTABLE)) \
+)
+
+# Build the manual test programs.
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/libs/androidfw/tests/InputChannel_test.cpp b/libs/androidfw/tests/InputChannel_test.cpp
new file mode 100644
index 0000000..0e5d19d
--- /dev/null
+++ b/libs/androidfw/tests/InputChannel_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/InputTransport.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <utils/StrongPointer.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include "../../utils/tests/TestHelpers.h"
+
+namespace android {
+
+class InputChannelTest : public testing::Test {
+protected:
+    virtual void SetUp() { }
+    virtual void TearDown() { }
+};
+
+
+TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) {
+    // Our purpose here is to verify that the input channel destructor closes the
+    // file descriptor provided to it.  One easy way is to provide it with one end
+    // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
+    Pipe pipe;
+
+    sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), pipe.sendFd);
+
+    EXPECT_STREQ("channel name", inputChannel->getName().string())
+            << "channel should have provided name";
+    EXPECT_EQ(pipe.sendFd, inputChannel->getFd())
+            << "channel should have provided fd";
+
+    inputChannel.clear(); // destroys input channel
+
+    EXPECT_EQ(-EPIPE, pipe.readSignal())
+            << "channel should have closed fd when destroyed";
+
+    // clean up fds of Pipe endpoints that were closed so we don't try to close them again
+    pipe.sendFd = -1;
+}
+
+TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
+    sp<InputChannel> serverChannel, clientChannel;
+
+    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+            serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    // Name
+    EXPECT_STREQ("channel name (server)", serverChannel->getName().string())
+            << "server channel should have suffixed name";
+    EXPECT_STREQ("channel name (client)", clientChannel->getName().string())
+            << "client channel should have suffixed name";
+
+    // Server->Client communication
+    InputMessage serverMsg;
+    memset(&serverMsg, 0, sizeof(InputMessage));
+    serverMsg.header.type = InputMessage::TYPE_KEY;
+    serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN;
+    EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
+            << "server channel should be able to send message to client channel";
+
+    InputMessage clientMsg;
+    EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg))
+            << "client channel should be able to receive message from server channel";
+    EXPECT_EQ(serverMsg.header.type, clientMsg.header.type)
+            << "client channel should receive the correct message from server channel";
+    EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action)
+            << "client channel should receive the correct message from server channel";
+
+    // Client->Server communication
+    InputMessage clientReply;
+    memset(&clientReply, 0, sizeof(InputMessage));
+    clientReply.header.type = InputMessage::TYPE_FINISHED;
+    clientReply.body.finished.seq = 0x11223344;
+    clientReply.body.finished.handled = true;
+    EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
+            << "client channel should be able to send message to server channel";
+
+    InputMessage serverReply;
+    EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply))
+            << "server channel should be able to receive message from client channel";
+    EXPECT_EQ(clientReply.header.type, serverReply.header.type)
+            << "server channel should receive the correct message from client channel";
+    EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq)
+            << "server channel should receive the correct message from client channel";
+    EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled)
+            << "server channel should receive the correct message from client channel";
+}
+
+TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
+    sp<InputChannel> serverChannel, clientChannel;
+
+    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+            serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    InputMessage msg;
+    EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg))
+            << "receiveMessage should have returned WOULD_BLOCK";
+}
+
+TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
+    sp<InputChannel> serverChannel, clientChannel;
+
+    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+            serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    serverChannel.clear(); // close server channel
+
+    InputMessage msg;
+    EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg))
+            << "receiveMessage should have returned DEAD_OBJECT";
+}
+
+TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
+    sp<InputChannel> serverChannel, clientChannel;
+
+    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+            serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    serverChannel.clear(); // close server channel
+
+    InputMessage msg;
+    msg.header.type = InputMessage::TYPE_KEY;
+    EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg))
+            << "sendMessage should have returned DEAD_OBJECT";
+}
+
+
+} // namespace android
diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/androidfw/tests/InputEvent_test.cpp
similarity index 99%
rename from libs/ui/tests/InputEvent_test.cpp
rename to libs/androidfw/tests/InputEvent_test.cpp
index e21c464..ac5549c 100644
--- a/libs/ui/tests/InputEvent_test.cpp
+++ b/libs/androidfw/tests/InputEvent_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include <gtest/gtest.h>
 #include <binder/Parcel.h>
 
diff --git a/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp
new file mode 100644
index 0000000..bb45247
--- /dev/null
+++ b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/InputTransport.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+
+#include "../../utils/tests/TestHelpers.h"
+
+namespace android {
+
+class InputPublisherAndConsumerTest : public testing::Test {
+protected:
+    sp<InputChannel> serverChannel, clientChannel;
+    InputPublisher* mPublisher;
+    InputConsumer* mConsumer;
+    PreallocatedInputEventFactory mEventFactory;
+
+    virtual void SetUp() {
+        status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+                serverChannel, clientChannel);
+
+        mPublisher = new InputPublisher(serverChannel);
+        mConsumer = new InputConsumer(clientChannel);
+    }
+
+    virtual void TearDown() {
+        if (mPublisher) {
+            delete mPublisher;
+            mPublisher = NULL;
+        }
+
+        if (mConsumer) {
+            delete mConsumer;
+            mConsumer = NULL;
+        }
+
+        serverChannel.clear();
+        clientChannel.clear();
+    }
+
+    void PublishAndConsumeKeyEvent();
+    void PublishAndConsumeMotionEvent();
+};
+
+TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
+    EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
+    EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
+    status_t status;
+
+    const uint32_t seq = 15;
+    const int32_t deviceId = 1;
+    const int32_t source = AINPUT_SOURCE_KEYBOARD;
+    const int32_t action = AKEY_EVENT_ACTION_DOWN;
+    const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
+    const int32_t keyCode = AKEYCODE_ENTER;
+    const int32_t scanCode = 13;
+    const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+    const int32_t repeatCount = 1;
+    const nsecs_t downTime = 3;
+    const nsecs_t eventTime = 4;
+
+    status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags,
+            keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+    ASSERT_EQ(OK, status)
+            << "publisher publishKeyEvent should return OK";
+
+    uint32_t consumeSeq;
+    InputEvent* event;
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, &consumeSeq, &event);
+    ASSERT_EQ(OK, status)
+            << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != NULL)
+            << "consumer should have returned non-NULL event";
+    ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType())
+            << "consumer should have returned a key event";
+
+    KeyEvent* keyEvent = static_cast<KeyEvent*>(event);
+    EXPECT_EQ(seq, consumeSeq);
+    EXPECT_EQ(deviceId, keyEvent->getDeviceId());
+    EXPECT_EQ(source, keyEvent->getSource());
+    EXPECT_EQ(action, keyEvent->getAction());
+    EXPECT_EQ(flags, keyEvent->getFlags());
+    EXPECT_EQ(keyCode, keyEvent->getKeyCode());
+    EXPECT_EQ(scanCode, keyEvent->getScanCode());
+    EXPECT_EQ(metaState, keyEvent->getMetaState());
+    EXPECT_EQ(repeatCount, keyEvent->getRepeatCount());
+    EXPECT_EQ(downTime, keyEvent->getDownTime());
+    EXPECT_EQ(eventTime, keyEvent->getEventTime());
+
+    status = mConsumer->sendFinishedSignal(seq, true);
+    ASSERT_EQ(OK, status)
+            << "consumer sendFinishedSignal should return OK";
+
+    uint32_t finishedSeq = 0;
+    bool handled = false;
+    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    ASSERT_EQ(OK, status)
+            << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, finishedSeq)
+            << "publisher receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(handled)
+            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
+    status_t status;
+
+    const uint32_t seq = 15;
+    const int32_t deviceId = 1;
+    const int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+    const int32_t action = AMOTION_EVENT_ACTION_MOVE;
+    const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+    const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
+    const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+    const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
+    const float xOffset = -10;
+    const float yOffset = -20;
+    const float xPrecision = 0.25;
+    const float yPrecision = 0.5;
+    const nsecs_t downTime = 3;
+    const size_t pointerCount = 3;
+    const nsecs_t eventTime = 4;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].clear();
+        pointerProperties[i].id = (i + 2) % pointerCount;
+        pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+        pointerCoords[i].clear();
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
+    }
+
+    status = mPublisher->publishMotionEvent(seq, deviceId, source, action, flags, edgeFlags,
+            metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision,
+            downTime, eventTime, pointerCount,
+            pointerProperties, pointerCoords);
+    ASSERT_EQ(OK, status)
+            << "publisher publishMotionEvent should return OK";
+
+    uint32_t consumeSeq;
+    InputEvent* event;
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, &consumeSeq, &event);
+    ASSERT_EQ(OK, status)
+            << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != NULL)
+            << "consumer should have returned non-NULL event";
+    ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+            << "consumer should have returned a motion event";
+
+    MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
+    EXPECT_EQ(seq, consumeSeq);
+    EXPECT_EQ(deviceId, motionEvent->getDeviceId());
+    EXPECT_EQ(source, motionEvent->getSource());
+    EXPECT_EQ(action, motionEvent->getAction());
+    EXPECT_EQ(flags, motionEvent->getFlags());
+    EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
+    EXPECT_EQ(metaState, motionEvent->getMetaState());
+    EXPECT_EQ(buttonState, motionEvent->getButtonState());
+    EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
+    EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
+    EXPECT_EQ(downTime, motionEvent->getDownTime());
+    EXPECT_EQ(eventTime, motionEvent->getEventTime());
+    EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
+    EXPECT_EQ(0U, motionEvent->getHistorySize());
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        SCOPED_TRACE(i);
+        EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
+        EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
+
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+                motionEvent->getRawX(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+                motionEvent->getRawY(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
+                motionEvent->getX(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
+                motionEvent->getY(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                motionEvent->getPressure(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+                motionEvent->getSize(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                motionEvent->getTouchMajor(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                motionEvent->getTouchMinor(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                motionEvent->getToolMajor(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                motionEvent->getToolMinor(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
+                motionEvent->getOrientation(i));
+    }
+
+    status = mConsumer->sendFinishedSignal(seq, false);
+    ASSERT_EQ(OK, status)
+            << "consumer sendFinishedSignal should return OK";
+
+    uint32_t finishedSeq = 0;
+    bool handled = true;
+    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    ASSERT_EQ(OK, status)
+            << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, finishedSeq)
+            << "publisher receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_FALSE(handled)
+            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
+    status_t status;
+    const size_t pointerCount = 0;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            pointerCount, pointerProperties, pointerCoords);
+    ASSERT_EQ(BAD_VALUE, status)
+            << "publisher publishMotionEvent should return BAD_VALUE";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
+    status_t status;
+    const size_t pointerCount = MAX_POINTERS + 1;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].clear();
+        pointerCoords[i].clear();
+    }
+
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            pointerCount, pointerProperties, pointerCoords);
+    ASSERT_EQ(BAD_VALUE, status)
+            << "publisher publishMotionEvent should return BAD_VALUE";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+}
+
+} // namespace android
diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp
similarity index 98%
rename from libs/utils/tests/ObbFile_test.cpp
rename to libs/androidfw/tests/ObbFile_test.cpp
index 46b30c2..09d4d7d 100644
--- a/libs/utils/tests/ObbFile_test.cpp
+++ b/libs/androidfw/tests/ObbFile_test.cpp
@@ -15,8 +15,8 @@
  */
 
 #define LOG_TAG "ObbFile_test"
+#include <androidfw/ObbFile.h>
 #include <utils/Log.h>
-#include <utils/ObbFile.h>
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 
diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/androidfw/tests/ZipFileRO_test.cpp
similarity index 97%
rename from libs/utils/tests/ZipFileRO_test.cpp
rename to libs/androidfw/tests/ZipFileRO_test.cpp
index 7a1d0bd..344f974 100644
--- a/libs/utils/tests/ZipFileRO_test.cpp
+++ b/libs/androidfw/tests/ZipFileRO_test.cpp
@@ -15,8 +15,8 @@
  */
 
 #define LOG_TAG "ZipFileRO_test"
+#include <androidfw/ZipFileRO.h>
 #include <utils/Log.h>
-#include <utils/ZipFileRO.h>
 
 #include <gtest/gtest.h>
 
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 33b305d..1750640 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -151,12 +151,14 @@
         return reply.readStrongBinder();
     }
 
-    virtual status_t addService(const String16& name, const sp<IBinder>& service)
+    virtual status_t addService(const String16& name, const sp<IBinder>& service,
+            bool allowIsolated)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
         data.writeString16(name);
         data.writeStrongBinder(service);
+        data.writeInt32(allowIsolated ? 1 : 0);
         status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
         return err == NO_ERROR ? reply.readExceptionCode() : err;
     }
diff --git a/libs/common_time/Android.mk b/libs/common_time/Android.mk
new file mode 100644
index 0000000..526f17b
--- /dev/null
+++ b/libs/common_time/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+#
+# libcommon_time_client
+# (binder marshalers for ICommonClock as well as common clock and local clock
+# helper code)
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcommon_time_client
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := cc_helper.cpp \
+                   local_clock.cpp \
+                   ICommonClock.cpp \
+                   ICommonTimeConfig.cpp \
+                   utils.cpp
+LOCAL_SHARED_LIBRARIES := libbinder \
+                          libhardware \
+                          libutils
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/common_time/ICommonClock.cpp b/libs/common_time/ICommonClock.cpp
new file mode 100644
index 0000000..28b43ac
--- /dev/null
+++ b/libs/common_time/ICommonClock.cpp
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+#include <linux/socket.h>
+
+#include <common_time/ICommonClock.h>
+#include <binder/Parcel.h>
+
+#include "utils.h"
+
+namespace android {
+
+/***** ICommonClock *****/
+
+enum {
+    IS_COMMON_TIME_VALID = IBinder::FIRST_CALL_TRANSACTION,
+    COMMON_TIME_TO_LOCAL_TIME,
+    LOCAL_TIME_TO_COMMON_TIME,
+    GET_COMMON_TIME,
+    GET_COMMON_FREQ,
+    GET_LOCAL_TIME,
+    GET_LOCAL_FREQ,
+    GET_ESTIMATED_ERROR,
+    GET_TIMELINE_ID,
+    GET_STATE,
+    GET_MASTER_ADDRESS,
+    REGISTER_LISTENER,
+    UNREGISTER_LISTENER,
+};
+
+const String16 ICommonClock::kServiceName("common_time.clock");
+const uint64_t ICommonClock::kInvalidTimelineID = 0;
+const int32_t ICommonClock::kErrorEstimateUnknown = 0x7FFFFFFF;
+
+class BpCommonClock : public BpInterface<ICommonClock>
+{
+  public:
+    BpCommonClock(const sp<IBinder>& impl)
+        : BpInterface<ICommonClock>(impl) {}
+
+    virtual status_t isCommonTimeValid(bool* valid, uint32_t* timelineID) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(IS_COMMON_TIME_VALID,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *valid = reply.readInt32();
+                *timelineID = reply.readInt32();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t commonTimeToLocalTime(int64_t commonTime,
+            int64_t* localTime) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        data.writeInt64(commonTime);
+        status_t status = remote()->transact(COMMON_TIME_TO_LOCAL_TIME,
+                data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *localTime = reply.readInt64();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t localTimeToCommonTime(int64_t localTime,
+            int64_t* commonTime) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        data.writeInt64(localTime);
+        status_t status = remote()->transact(LOCAL_TIME_TO_COMMON_TIME,
+                data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *commonTime = reply.readInt64();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t getCommonTime(int64_t* commonTime) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_COMMON_TIME, data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *commonTime = reply.readInt64();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t getCommonFreq(uint64_t* freq) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_COMMON_FREQ, data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *freq = reply.readInt64();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t getLocalTime(int64_t* localTime) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_LOCAL_TIME, data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *localTime = reply.readInt64();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t getLocalFreq(uint64_t* freq) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_LOCAL_FREQ, data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *freq = reply.readInt64();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t getEstimatedError(int32_t* estimate) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_ESTIMATED_ERROR, data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *estimate = reply.readInt32();
+            }
+        }
+        return status;
+    }
+
+    virtual status_t getTimelineID(uint64_t* id) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_TIMELINE_ID, data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *id = static_cast<uint64_t>(reply.readInt64());
+            }
+        }
+        return status;
+    }
+
+    virtual status_t getState(State* state) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_STATE, data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *state = static_cast<State>(reply.readInt32());
+            }
+        }
+        return status;
+    }
+
+    virtual status_t getMasterAddr(struct sockaddr_storage* addr) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_MASTER_ADDRESS, data, &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK)
+                deserializeSockaddr(&reply, addr);
+        }
+        return status;
+    }
+
+    virtual status_t registerListener(
+            const sp<ICommonClockListener>& listener) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+
+        status_t status = remote()->transact(REGISTER_LISTENER, data, &reply);
+
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t unregisterListener(
+            const sp<ICommonClockListener>& listener) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        status_t status = remote()->transact(UNREGISTER_LISTENER, data, &reply);
+
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+};
+
+IMPLEMENT_META_INTERFACE(CommonClock, "android.os.ICommonClock");
+
+status_t BnCommonClock::onTransact(uint32_t code,
+                                   const Parcel& data,
+                                   Parcel* reply,
+                                   uint32_t flags) {
+    switch(code) {
+        case IS_COMMON_TIME_VALID: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            bool valid;
+            uint32_t timelineID;
+            status_t status = isCommonTimeValid(&valid, &timelineID);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt32(valid);
+                reply->writeInt32(timelineID);
+            }
+            return OK;
+        } break;
+
+        case COMMON_TIME_TO_LOCAL_TIME: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            int64_t commonTime = data.readInt64();
+            int64_t localTime;
+            status_t status = commonTimeToLocalTime(commonTime, &localTime);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt64(localTime);
+            }
+            return OK;
+        } break;
+
+        case LOCAL_TIME_TO_COMMON_TIME: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            int64_t localTime = data.readInt64();
+            int64_t commonTime;
+            status_t status = localTimeToCommonTime(localTime, &commonTime);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt64(commonTime);
+            }
+            return OK;
+        } break;
+
+        case GET_COMMON_TIME: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            int64_t commonTime;
+            status_t status = getCommonTime(&commonTime);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt64(commonTime);
+            }
+            return OK;
+        } break;
+
+        case GET_COMMON_FREQ: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            uint64_t freq;
+            status_t status = getCommonFreq(&freq);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt64(freq);
+            }
+            return OK;
+        } break;
+
+        case GET_LOCAL_TIME: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            int64_t localTime;
+            status_t status = getLocalTime(&localTime);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt64(localTime);
+            }
+            return OK;
+        } break;
+
+        case GET_LOCAL_FREQ: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            uint64_t freq;
+            status_t status = getLocalFreq(&freq);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt64(freq);
+            }
+            return OK;
+        } break;
+
+        case GET_ESTIMATED_ERROR: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            int32_t error;
+            status_t status = getEstimatedError(&error);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt32(error);
+            }
+            return OK;
+        } break;
+
+        case GET_TIMELINE_ID: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            uint64_t id;
+            status_t status = getTimelineID(&id);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt64(static_cast<int64_t>(id));
+            }
+            return OK;
+        } break;
+
+        case GET_STATE: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            State state;
+            status_t status = getState(&state);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt32(static_cast<int32_t>(state));
+            }
+            return OK;
+        } break;
+
+        case GET_MASTER_ADDRESS: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            struct sockaddr_storage addr;
+            status_t status = getMasterAddr(&addr);
+
+            if ((status == OK) && !canSerializeSockaddr(&addr)) {
+                status = UNKNOWN_ERROR;
+            }
+
+            reply->writeInt32(status);
+
+            if (status == OK) {
+                serializeSockaddr(reply, &addr);
+            }
+
+            return OK;
+        } break;
+
+        case REGISTER_LISTENER: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            sp<ICommonClockListener> listener =
+                interface_cast<ICommonClockListener>(data.readStrongBinder());
+            status_t status = registerListener(listener);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case UNREGISTER_LISTENER: {
+            CHECK_INTERFACE(ICommonClock, data, reply);
+            sp<ICommonClockListener> listener =
+                interface_cast<ICommonClockListener>(data.readStrongBinder());
+            status_t status = unregisterListener(listener);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+    }
+    return BBinder::onTransact(code, data, reply, flags);
+}
+
+/***** ICommonClockListener *****/
+
+enum {
+    ON_TIMELINE_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpCommonClockListener : public BpInterface<ICommonClockListener>
+{
+  public:
+    BpCommonClockListener(const sp<IBinder>& impl)
+        : BpInterface<ICommonClockListener>(impl) {}
+
+    virtual void onTimelineChanged(uint64_t timelineID) {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                ICommonClockListener::getInterfaceDescriptor());
+        data.writeInt64(timelineID);
+        remote()->transact(ON_TIMELINE_CHANGED, data, &reply);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(CommonClockListener,
+                         "android.os.ICommonClockListener");
+
+status_t BnCommonClockListener::onTransact(
+        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+    switch(code) {
+        case ON_TIMELINE_CHANGED: {
+            CHECK_INTERFACE(ICommonClockListener, data, reply);
+            uint32_t timelineID = data.readInt64();
+            onTimelineChanged(timelineID);
+            return NO_ERROR;
+        } break;
+    }
+
+    return BBinder::onTransact(code, data, reply, flags);
+}
+
+}; // namespace android
diff --git a/libs/common_time/ICommonTimeConfig.cpp b/libs/common_time/ICommonTimeConfig.cpp
new file mode 100644
index 0000000..8eb37cb
--- /dev/null
+++ b/libs/common_time/ICommonTimeConfig.cpp
@@ -0,0 +1,508 @@
+/*
+ * 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.
+ */
+#include <linux/socket.h>
+
+#include <common_time/ICommonTimeConfig.h>
+#include <binder/Parcel.h>
+
+#include "utils.h"
+
+namespace android {
+
+/***** ICommonTimeConfig *****/
+
+enum {
+    GET_MASTER_ELECTION_PRIORITY = IBinder::FIRST_CALL_TRANSACTION,
+    SET_MASTER_ELECTION_PRIORITY,
+    GET_MASTER_ELECTION_ENDPOINT,
+    SET_MASTER_ELECTION_ENDPOINT,
+    GET_MASTER_ELECTION_GROUP_ID,
+    SET_MASTER_ELECTION_GROUP_ID,
+    GET_INTERFACE_BINDING,
+    SET_INTERFACE_BINDING,
+    GET_MASTER_ANNOUNCE_INTERVAL,
+    SET_MASTER_ANNOUNCE_INTERVAL,
+    GET_CLIENT_SYNC_INTERVAL,
+    SET_CLIENT_SYNC_INTERVAL,
+    GET_PANIC_THRESHOLD,
+    SET_PANIC_THRESHOLD,
+    GET_AUTO_DISABLE,
+    SET_AUTO_DISABLE,
+    FORCE_NETWORKLESS_MASTER_MODE,
+};
+
+const String16 ICommonTimeConfig::kServiceName("common_time.config");
+
+class BpCommonTimeConfig : public BpInterface<ICommonTimeConfig>
+{
+  public:
+    BpCommonTimeConfig(const sp<IBinder>& impl)
+        : BpInterface<ICommonTimeConfig>(impl) {}
+
+    virtual status_t getMasterElectionPriority(uint8_t *priority) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_MASTER_ELECTION_PRIORITY,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *priority = static_cast<uint8_t>(reply.readInt32());
+            }
+        }
+
+        return status;
+    }
+
+    virtual status_t setMasterElectionPriority(uint8_t priority) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        data.writeInt32(static_cast<int32_t>(priority));
+        status_t status = remote()->transact(SET_MASTER_ELECTION_PRIORITY,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_MASTER_ELECTION_ENDPOINT,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                deserializeSockaddr(&reply, addr);
+            }
+        }
+
+        return status;
+    }
+
+    virtual status_t setMasterElectionEndpoint(
+            const struct sockaddr_storage *addr) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        if (!canSerializeSockaddr(addr))
+            return BAD_VALUE;
+        if (NULL == addr) {
+            data.writeInt32(0);
+        } else {
+            data.writeInt32(1);
+            serializeSockaddr(&data, addr);
+        }
+        status_t status = remote()->transact(SET_MASTER_ELECTION_ENDPOINT,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t getMasterElectionGroupId(uint64_t *id) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_MASTER_ELECTION_GROUP_ID,
+                                             data,
+                                             &reply);
+
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *id = static_cast<uint64_t>(reply.readInt64());
+            }
+        }
+
+        return status;
+    }
+
+    virtual status_t setMasterElectionGroupId(uint64_t id) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        data.writeInt64(id);
+        status_t status = remote()->transact(SET_MASTER_ELECTION_GROUP_ID,
+                                             data,
+                                             &reply);
+
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t getInterfaceBinding(String16& ifaceName) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_INTERFACE_BINDING,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                ifaceName = reply.readString16();
+            }
+        }
+
+        return status;
+    }
+
+    virtual status_t setInterfaceBinding(const String16& ifaceName) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        data.writeString16(ifaceName);
+        status_t status = remote()->transact(SET_INTERFACE_BINDING,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t getMasterAnnounceInterval(int *interval) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_MASTER_ANNOUNCE_INTERVAL,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *interval = reply.readInt32();
+            }
+        }
+
+        return status;
+    }
+
+    virtual status_t setMasterAnnounceInterval(int interval) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        data.writeInt32(interval);
+        status_t status = remote()->transact(SET_MASTER_ANNOUNCE_INTERVAL,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t getClientSyncInterval(int *interval) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_CLIENT_SYNC_INTERVAL,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *interval = reply.readInt32();
+            }
+        }
+
+        return status;
+    }
+
+    virtual status_t setClientSyncInterval(int interval) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        data.writeInt32(interval);
+        status_t status = remote()->transact(SET_CLIENT_SYNC_INTERVAL,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t getPanicThreshold(int *threshold) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_PANIC_THRESHOLD,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *threshold = reply.readInt32();
+            }
+        }
+
+        return status;
+    }
+
+    virtual status_t setPanicThreshold(int threshold) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        data.writeInt32(threshold);
+        status_t status = remote()->transact(SET_PANIC_THRESHOLD,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t getAutoDisable(bool *autoDisable) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_AUTO_DISABLE,
+                                             data,
+                                             &reply);
+        if (status == OK) {
+            status = reply.readInt32();
+            if (status == OK) {
+                *autoDisable = (0 != reply.readInt32());
+            }
+        }
+
+        return status;
+    }
+
+    virtual status_t setAutoDisable(bool autoDisable) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        data.writeInt32(autoDisable ? 1 : 0);
+        status_t status = remote()->transact(SET_AUTO_DISABLE,
+                                             data,
+                                             &reply);
+
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+
+    virtual status_t forceNetworklessMasterMode() {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor());
+        status_t status = remote()->transact(FORCE_NETWORKLESS_MASTER_MODE,
+                                             data,
+                                             &reply);
+
+        if (status == OK) {
+            status = reply.readInt32();
+        }
+
+        return status;
+    }
+};
+
+IMPLEMENT_META_INTERFACE(CommonTimeConfig, "android.os.ICommonTimeConfig");
+
+status_t BnCommonTimeConfig::onTransact(uint32_t code,
+                                   const Parcel& data,
+                                   Parcel* reply,
+                                   uint32_t flags) {
+    switch(code) {
+        case GET_MASTER_ELECTION_PRIORITY: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            uint8_t priority;
+            status_t status = getMasterElectionPriority(&priority);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt32(static_cast<int32_t>(priority));
+            }
+            return OK;
+        } break;
+
+        case SET_MASTER_ELECTION_PRIORITY: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            uint8_t priority = static_cast<uint8_t>(data.readInt32());
+            status_t status = setMasterElectionPriority(priority);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case GET_MASTER_ELECTION_ENDPOINT: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            struct sockaddr_storage addr;
+            status_t status = getMasterElectionEndpoint(&addr);
+
+            if ((status == OK) && !canSerializeSockaddr(&addr)) {
+                status = UNKNOWN_ERROR;
+            }
+
+            reply->writeInt32(status);
+
+            if (status == OK) {
+                serializeSockaddr(reply, &addr);
+            }
+
+            return OK;
+        } break;
+
+        case SET_MASTER_ELECTION_ENDPOINT: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            struct sockaddr_storage addr;
+            int hasAddr = data.readInt32();
+
+            status_t status;
+            if (hasAddr) {
+                deserializeSockaddr(&data, &addr);
+                status = setMasterElectionEndpoint(&addr);
+            } else {
+                status = setMasterElectionEndpoint(&addr);
+            }
+
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case GET_MASTER_ELECTION_GROUP_ID: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            uint64_t id;
+            status_t status = getMasterElectionGroupId(&id);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt64(id);
+            }
+            return OK;
+        } break;
+
+        case SET_MASTER_ELECTION_GROUP_ID: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            uint64_t id = static_cast<uint64_t>(data.readInt64());
+            status_t status = setMasterElectionGroupId(id);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case GET_INTERFACE_BINDING: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            String16 ret;
+            status_t status = getInterfaceBinding(ret);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeString16(ret);
+            }
+            return OK;
+        } break;
+
+        case SET_INTERFACE_BINDING: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            String16 ifaceName;
+            ifaceName = data.readString16();
+            status_t status = setInterfaceBinding(ifaceName);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case GET_MASTER_ANNOUNCE_INTERVAL: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            int interval;
+            status_t status = getMasterAnnounceInterval(&interval);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt32(interval);
+            }
+            return OK;
+        } break;
+
+        case SET_MASTER_ANNOUNCE_INTERVAL: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            int interval = data.readInt32();
+            status_t status = setMasterAnnounceInterval(interval);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case GET_CLIENT_SYNC_INTERVAL: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            int interval;
+            status_t status = getClientSyncInterval(&interval);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt32(interval);
+            }
+            return OK;
+        } break;
+
+        case SET_CLIENT_SYNC_INTERVAL: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            int interval = data.readInt32();
+            status_t status = setClientSyncInterval(interval);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case GET_PANIC_THRESHOLD: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            int threshold;
+            status_t status = getPanicThreshold(&threshold);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt32(threshold);
+            }
+            return OK;
+        } break;
+
+        case SET_PANIC_THRESHOLD: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            int threshold = data.readInt32();
+            status_t status = setPanicThreshold(threshold);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case GET_AUTO_DISABLE: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            bool autoDisable;
+            status_t status = getAutoDisable(&autoDisable);
+            reply->writeInt32(status);
+            if (status == OK) {
+                reply->writeInt32(autoDisable ? 1 : 0);
+            }
+            return OK;
+        } break;
+
+        case SET_AUTO_DISABLE: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            bool autoDisable = (0 != data.readInt32());
+            status_t status = setAutoDisable(autoDisable);
+            reply->writeInt32(status);
+            return OK;
+        } break;
+
+        case FORCE_NETWORKLESS_MASTER_MODE: {
+            CHECK_INTERFACE(ICommonTimeConfig, data, reply);
+            status_t status = forceNetworklessMasterMode();
+            reply->writeInt32(status);
+            return OK;
+        } break;
+    }
+    return BBinder::onTransact(code, data, reply, flags);
+}
+
+}; // namespace android
+
diff --git a/libs/common_time/cc_helper.cpp b/libs/common_time/cc_helper.cpp
new file mode 100644
index 0000000..8d8556c
--- /dev/null
+++ b/libs/common_time/cc_helper.cpp
@@ -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.
+ */
+
+#include <stdint.h>
+
+#include <common_time/cc_helper.h>
+#include <common_time/ICommonClock.h>
+#include <utils/threads.h>
+
+namespace android {
+
+Mutex CCHelper::lock_;
+sp<ICommonClock> CCHelper::common_clock_;
+sp<ICommonClockListener> CCHelper::common_clock_listener_;
+uint32_t CCHelper::ref_count_ = 0;
+
+bool CCHelper::verifyClock_l() {
+    bool ret = false;
+
+    if (common_clock_ == NULL) {
+        common_clock_ = ICommonClock::getInstance();
+        if (common_clock_ == NULL)
+            goto bailout;
+    }
+
+    if (ref_count_ > 0) {
+        if (common_clock_listener_ == NULL) {
+            common_clock_listener_ = new CommonClockListener();
+            if (common_clock_listener_ == NULL)
+                goto bailout;
+
+            if (OK != common_clock_->registerListener(common_clock_listener_))
+                goto bailout;
+        }
+    }
+
+    ret = true;
+
+bailout:
+    if (!ret) {
+        common_clock_listener_ = NULL;
+        common_clock_ = NULL;
+    }
+    return ret;
+}
+
+CCHelper::CCHelper() {
+    Mutex::Autolock lock(&lock_);
+    ref_count_++;
+    verifyClock_l();
+}
+
+CCHelper::~CCHelper() {
+    Mutex::Autolock lock(&lock_);
+
+    assert(ref_count_ > 0);
+    ref_count_--;
+
+    // If we were the last CCHelper instance in the system, and we had
+    // previously register a listener, unregister it now so that the common time
+    // service has the chance to go into auto-disabled mode.
+    if (!ref_count_ &&
+       (common_clock_ != NULL) &&
+       (common_clock_listener_ != NULL)) {
+        common_clock_->unregisterListener(common_clock_listener_);
+        common_clock_listener_ = NULL;
+    }
+}
+
+void CCHelper::CommonClockListener::onTimelineChanged(uint64_t timelineID) {
+    // do nothing; listener is only really used as a token so the server can
+    // find out when clients die.
+}
+
+// Helper methods which attempts to make calls to the common time binder
+// service.  If the first attempt fails with DEAD_OBJECT, the helpers will
+// attempt to make a connection to the service again (assuming that the process
+// hosting the service had crashed and the client proxy we are holding is dead)
+// If the second attempt fails, or no connection can be made, the we let the
+// error propagate up the stack and let the caller deal with the situation as
+// best they can.
+#define CCHELPER_METHOD(decl, call)                 \
+    status_t CCHelper::decl {                       \
+        Mutex::Autolock lock(&lock_);               \
+                                                    \
+        if (!verifyClock_l())                       \
+            return DEAD_OBJECT;                     \
+                                                    \
+        status_t status = common_clock_->call;      \
+        if (DEAD_OBJECT == status) {                \
+            if (!verifyClock_l())                   \
+                return DEAD_OBJECT;                 \
+            status = common_clock_->call;           \
+        }                                           \
+                                                    \
+        return status;                              \
+    }
+
+#define VERIFY_CLOCK()
+
+CCHELPER_METHOD(isCommonTimeValid(bool* valid, uint32_t* timelineID),
+                isCommonTimeValid(valid, timelineID))
+CCHELPER_METHOD(commonTimeToLocalTime(int64_t commonTime, int64_t* localTime),
+                commonTimeToLocalTime(commonTime, localTime))
+CCHELPER_METHOD(localTimeToCommonTime(int64_t localTime, int64_t* commonTime),
+                localTimeToCommonTime(localTime, commonTime))
+CCHELPER_METHOD(getCommonTime(int64_t* commonTime),
+                getCommonTime(commonTime))
+CCHELPER_METHOD(getCommonFreq(uint64_t* freq),
+                getCommonFreq(freq))
+CCHELPER_METHOD(getLocalTime(int64_t* localTime),
+                getLocalTime(localTime))
+CCHELPER_METHOD(getLocalFreq(uint64_t* freq),
+                getLocalFreq(freq))
+
+}  // namespace android
diff --git a/libs/common_time/local_clock.cpp b/libs/common_time/local_clock.cpp
new file mode 100644
index 0000000..a7c61fc
--- /dev/null
+++ b/libs/common_time/local_clock.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "common_time"
+#include <utils/Log.h>
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <common_time/local_clock.h>
+#include <hardware/hardware.h>
+#include <hardware/local_time_hal.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+Mutex LocalClock::dev_lock_;
+local_time_hw_device_t* LocalClock::dev_ = NULL;
+
+LocalClock::LocalClock() {
+    int res;
+    const hw_module_t* mod;
+
+    AutoMutex lock(&dev_lock_);
+
+    if (dev_ != NULL)
+        return;
+
+    res = hw_get_module_by_class(LOCAL_TIME_HARDWARE_MODULE_ID, NULL, &mod);
+    if (res) {
+        ALOGE("Failed to open local time HAL module (res = %d)", res);
+    } else {
+        res = local_time_hw_device_open(mod, &dev_);
+        if (res) {
+            ALOGE("Failed to open local time HAL device (res = %d)", res);
+            dev_ = NULL;
+        }
+    }
+}
+
+bool LocalClock::initCheck() {
+    return (NULL != dev_);
+}
+
+int64_t LocalClock::getLocalTime() {
+    assert(NULL != dev_);
+    assert(NULL != dev_->get_local_time);
+
+    return dev_->get_local_time(dev_);
+}
+
+uint64_t LocalClock::getLocalFreq() {
+    assert(NULL != dev_);
+    assert(NULL != dev_->get_local_freq);
+
+    return dev_->get_local_freq(dev_);
+}
+
+status_t LocalClock::setLocalSlew(int16_t rate) {
+    assert(NULL != dev_);
+
+    if (!dev_->set_local_slew)
+        return INVALID_OPERATION;
+
+    return static_cast<status_t>(dev_->set_local_slew(dev_, rate));
+}
+
+int32_t LocalClock::getDebugLog(struct local_time_debug_event* records,
+                                int max_records) {
+    assert(NULL != dev_);
+
+    if (!dev_->get_debug_log)
+        return INVALID_OPERATION;
+
+    return dev_->get_debug_log(dev_, records, max_records);
+}
+
+}  // namespace android
diff --git a/libs/common_time/utils.cpp b/libs/common_time/utils.cpp
new file mode 100644
index 0000000..6539171
--- /dev/null
+++ b/libs/common_time/utils.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <arpa/inet.h>
+#include <linux/socket.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+bool canSerializeSockaddr(const struct sockaddr_storage* addr) {
+    switch (addr->ss_family) {
+        case AF_INET:
+        case AF_INET6:
+            return true;
+        default:
+            return false;
+    }
+}
+
+void serializeSockaddr(Parcel* p, const struct sockaddr_storage* addr) {
+    switch (addr->ss_family) {
+        case AF_INET: {
+            const struct sockaddr_in* s =
+                reinterpret_cast<const struct sockaddr_in*>(addr);
+            p->writeInt32(AF_INET);
+            p->writeInt32(ntohl(s->sin_addr.s_addr));
+            p->writeInt32(static_cast<int32_t>(ntohs(s->sin_port)));
+        } break;
+
+        case AF_INET6: {
+            const struct sockaddr_in6* s =
+                reinterpret_cast<const struct sockaddr_in6*>(addr);
+            const int32_t* a =
+                reinterpret_cast<const int32_t*>(s->sin6_addr.s6_addr);
+            p->writeInt32(AF_INET6);
+            p->writeInt32(ntohl(a[0]));
+            p->writeInt32(ntohl(a[1]));
+            p->writeInt32(ntohl(a[2]));
+            p->writeInt32(ntohl(a[3]));
+            p->writeInt32(static_cast<int32_t>(ntohs(s->sin6_port)));
+            p->writeInt32(ntohl(s->sin6_flowinfo));
+            p->writeInt32(ntohl(s->sin6_scope_id));
+        } break;
+    }
+}
+
+void deserializeSockaddr(const Parcel* p, struct sockaddr_storage* addr) {
+    memset(addr, 0, sizeof(addr));
+
+    addr->ss_family = p->readInt32();
+    switch(addr->ss_family) {
+        case AF_INET: {
+            struct sockaddr_in* s =
+                reinterpret_cast<struct sockaddr_in*>(addr);
+            s->sin_addr.s_addr = htonl(p->readInt32());
+            s->sin_port = htons(static_cast<uint16_t>(p->readInt32()));
+        } break;
+
+        case AF_INET6: {
+            struct sockaddr_in6* s =
+                reinterpret_cast<struct sockaddr_in6*>(addr);
+            int32_t* a = reinterpret_cast<int32_t*>(s->sin6_addr.s6_addr);
+
+            a[0] = htonl(p->readInt32());
+            a[1] = htonl(p->readInt32());
+            a[2] = htonl(p->readInt32());
+            a[3] = htonl(p->readInt32());
+            s->sin6_port = htons(static_cast<uint16_t>(p->readInt32()));
+            s->sin6_flowinfo = htonl(p->readInt32());
+            s->sin6_scope_id = htonl(p->readInt32());
+        } break;
+    }
+}
+
+}  // namespace android
diff --git a/libs/common_time/utils.h b/libs/common_time/utils.h
new file mode 100644
index 0000000..ce79d0d
--- /dev/null
+++ b/libs/common_time/utils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_LIBCOMMONCLOCK_UTILS_H
+#define ANDROID_LIBCOMMONCLOCK_UTILS_H
+
+#include <linux/socket.h>
+
+#include <binder/Parcel.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+extern bool canSerializeSockaddr(const struct sockaddr_storage* addr);
+extern void serializeSockaddr(Parcel* p, const struct sockaddr_storage* addr);
+extern status_t deserializeSockaddr(const Parcel* p,
+                                    struct sockaddr_storage* addr);
+
+};  // namespace android
+
+#endif  // ANDROID_LIBCOMMONCLOCK_UTILS_H
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index b6f5b9e..2f4ac62 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -3,6 +3,7 @@
 
 LOCAL_SRC_FILES:= \
 	BitTube.cpp \
+	BufferQueue.cpp \
 	DisplayEventReceiver.cpp \
 	IDisplayEventConnection.cpp \
 	ISensorEventConnection.cpp \
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
new file mode 100644
index 0000000..0791de2
--- /dev/null
+++ b/libs/gui/BufferQueue.cpp
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define LOG_TAG "BufferQueue"
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/BufferQueue.h>
+#include <private/gui/ComposerService.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <utils/Log.h>
+
+// This compile option causes SurfaceTexture to return the buffer that is currently
+// attached to the GL texture from dequeueBuffer when no other buffers are
+// available.  It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do
+// implicit cross-process synchronization to prevent the buffer from being
+// written to before the buffer has (a) been detached from the GL texture and
+// (b) all GL reads from the buffer have completed.
+#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
+#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    true
+#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
+#else
+#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    false
+#endif
+
+// Macros for including the BufferQueue name in log messages
+#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+// Get an ID that's unique within this process.
+static int32_t createProcessUniqueId() {
+    static volatile int32_t globalCounter = 0;
+    return android_atomic_inc(&globalCounter);
+}
+
+BufferQueue::BufferQueue( bool allowSynchronousMode ) :
+    mDefaultWidth(1),
+    mDefaultHeight(1),
+    mPixelFormat(PIXEL_FORMAT_RGBA_8888),
+    mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+    mClientBufferCount(0),
+    mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+    mCurrentTexture(INVALID_BUFFER_SLOT),
+    mNextTransform(0),
+    mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+    mSynchronousMode(false),
+    mAllowSynchronousMode(allowSynchronousMode),
+    mConnectedApi(NO_CONNECTED_API),
+    mAbandoned(false),
+    mFrameCounter(0)
+{
+    // Choose a name using the PID and a process-unique ID.
+    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
+
+    ST_LOGV("BufferQueue");
+    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+    mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
+    mNextCrop.makeInvalid();
+}
+
+BufferQueue::~BufferQueue() {
+    ST_LOGV("~BufferQueue");
+}
+
+status_t BufferQueue::setBufferCountServerLocked(int bufferCount) {
+    if (bufferCount > NUM_BUFFER_SLOTS)
+        return BAD_VALUE;
+
+    // special-case, nothing to do
+    if (bufferCount == mBufferCount)
+        return OK;
+
+    if (!mClientBufferCount &&
+        bufferCount >= mBufferCount) {
+        // easy, we just have more buffers
+        mBufferCount = bufferCount;
+        mServerBufferCount = bufferCount;
+        mDequeueCondition.signal();
+    } else {
+        // we're here because we're either
+        // - reducing the number of available buffers
+        // - or there is a client-buffer-count in effect
+
+        // less than 2 buffers is never allowed
+        if (bufferCount < 2)
+            return BAD_VALUE;
+
+        // when there is non client-buffer-count in effect, the client is not
+        // allowed to dequeue more than one buffer at a time,
+        // so the next time they dequeue a buffer, we know that they don't
+        // own one. the actual resizing will happen during the next
+        // dequeueBuffer.
+
+        mServerBufferCount = bufferCount;
+    }
+    return OK;
+}
+
+status_t BufferQueue::setBufferCount(int bufferCount) {
+    ST_LOGV("setBufferCount: count=%d", bufferCount);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+    if (bufferCount > NUM_BUFFER_SLOTS) {
+        ST_LOGE("setBufferCount: bufferCount larger than slots available");
+        return BAD_VALUE;
+    }
+
+    // Error out if the user has dequeued buffers
+    for (int i=0 ; i<mBufferCount ; i++) {
+        if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+            ST_LOGE("setBufferCount: client owns some buffers");
+            return -EINVAL;
+        }
+    }
+
+    const int minBufferSlots = mSynchronousMode ?
+            MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+    if (bufferCount == 0) {
+        mClientBufferCount = 0;
+        bufferCount = (mServerBufferCount >= minBufferSlots) ?
+                mServerBufferCount : minBufferSlots;
+        return setBufferCountServerLocked(bufferCount);
+    }
+
+    if (bufferCount < minBufferSlots) {
+        ST_LOGE("setBufferCount: requested buffer count (%d) is less than "
+                "minimum (%d)", bufferCount, minBufferSlots);
+        return BAD_VALUE;
+    }
+
+    // here we're guaranteed that the client doesn't have dequeued buffers
+    // and will release all of its buffer references.
+    freeAllBuffersLocked();
+    mBufferCount = bufferCount;
+    mClientBufferCount = bufferCount;
+    mCurrentTexture = INVALID_BUFFER_SLOT;
+    mQueue.clear();
+    mDequeueCondition.signal();
+    return OK;
+}
+
+int BufferQueue::query(int what, int* outValue)
+{
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("query: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
+    int value;
+    switch (what) {
+    case NATIVE_WINDOW_WIDTH:
+        value = mDefaultWidth;
+        break;
+    case NATIVE_WINDOW_HEIGHT:
+        value = mDefaultHeight;
+        break;
+    case NATIVE_WINDOW_FORMAT:
+        value = mPixelFormat;
+        break;
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+        value = mSynchronousMode ?
+                (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
+        break;
+    default:
+        return BAD_VALUE;
+    }
+    outValue[0] = value;
+    return NO_ERROR;
+}
+
+status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+    ST_LOGV("requestBuffer: slot=%d", slot);
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+    if (slot < 0 || mBufferCount <= slot) {
+        ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d",
+                mBufferCount, slot);
+        return BAD_VALUE;
+    }
+    mSlots[slot].mRequestBufferCalled = true;
+    *buf = mSlots[slot].mGraphicBuffer;
+    return NO_ERROR;
+}
+
+status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
+        uint32_t format, uint32_t usage) {
+    ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
+
+    if ((w && !h) || (!w && h)) {
+        ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
+        return BAD_VALUE;
+    }
+
+    status_t returnFlags(OK);
+    EGLDisplay dpy = EGL_NO_DISPLAY;
+    EGLSyncKHR fence = EGL_NO_SYNC_KHR;
+
+    { // Scope for the lock
+        Mutex::Autolock lock(mMutex);
+
+        int found = -1;
+        int foundSync = -1;
+        int dequeuedCount = 0;
+        bool tryAgain = true;
+        while (tryAgain) {
+            if (mAbandoned) {
+                ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+                return NO_INIT;
+            }
+
+            // We need to wait for the FIFO to drain if the number of buffer
+            // needs to change.
+            //
+            // The condition "number of buffers needs to change" is true if
+            // - the client doesn't care about how many buffers there are
+            // - AND the actual number of buffer is different from what was
+            //   set in the last setBufferCountServer()
+            //                         - OR -
+            //   setBufferCountServer() was set to a value incompatible with
+            //   the synchronization mode (for instance because the sync mode
+            //   changed since)
+            //
+            // As long as this condition is true AND the FIFO is not empty, we
+            // wait on mDequeueCondition.
+
+            const int minBufferCountNeeded = mSynchronousMode ?
+                    MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+
+            const bool numberOfBuffersNeedsToChange = !mClientBufferCount &&
+                    ((mServerBufferCount != mBufferCount) ||
+                            (mServerBufferCount < minBufferCountNeeded));
+
+            if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) {
+                // wait for the FIFO to drain
+                mDequeueCondition.wait(mMutex);
+                // NOTE: we continue here because we need to reevaluate our
+                // whole state (eg: we could be abandoned or disconnected)
+                continue;
+            }
+
+            if (numberOfBuffersNeedsToChange) {
+                // here we're guaranteed that mQueue is empty
+                freeAllBuffersLocked();
+                mBufferCount = mServerBufferCount;
+                if (mBufferCount < minBufferCountNeeded)
+                    mBufferCount = minBufferCountNeeded;
+                mCurrentTexture = INVALID_BUFFER_SLOT;
+                returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+            }
+
+            // look for a free buffer to give to the client
+            found = INVALID_BUFFER_SLOT;
+            foundSync = INVALID_BUFFER_SLOT;
+            dequeuedCount = 0;
+            for (int i = 0; i < mBufferCount; i++) {
+                const int state = mSlots[i].mBufferState;
+                if (state == BufferSlot::DEQUEUED) {
+                    dequeuedCount++;
+                }
+
+                // if buffer is FREE it CANNOT be current
+                ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
+                        "dequeueBuffer: buffer %d is both FREE and current!",
+                        i);
+
+                if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
+                    if (state == BufferSlot::FREE || i == mCurrentTexture) {
+                        foundSync = i;
+                        if (i != mCurrentTexture) {
+                            found = i;
+                            break;
+                        }
+                    }
+                } else {
+                    if (state == BufferSlot::FREE) {
+                        /* We return the oldest of the free buffers to avoid
+                         * stalling the producer if possible.  This is because
+                         * the consumer may still have pending reads of the
+                         * buffers in flight.
+                         */
+                        bool isOlder = mSlots[i].mFrameNumber <
+                                mSlots[found].mFrameNumber;
+                        if (found < 0 || isOlder) {
+                            foundSync = i;
+                            found = i;
+                        }
+                    }
+                }
+            }
+
+            // clients are not allowed to dequeue more than one buffer
+            // if they didn't set a buffer count.
+            if (!mClientBufferCount && dequeuedCount) {
+                ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
+                        "setting the buffer count");
+                return -EINVAL;
+            }
+
+            // See whether a buffer has been queued since the last
+            // setBufferCount so we know whether to perform the
+            // MIN_UNDEQUEUED_BUFFERS check below.
+            bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT;
+            if (bufferHasBeenQueued) {
+                // make sure the client is not trying to dequeue more buffers
+                // than allowed.
+                const int avail = mBufferCount - (dequeuedCount+1);
+                if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
+                    ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded "
+                            "(dequeued=%d)",
+                            MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
+                            dequeuedCount);
+                    return -EBUSY;
+                }
+            }
+
+            // we're in synchronous mode and didn't find a buffer, we need to
+            // wait for some buffers to be consumed
+            tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
+            if (tryAgain) {
+                mDequeueCondition.wait(mMutex);
+            }
+        }
+
+        if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
+            // foundSync guaranteed to be != INVALID_BUFFER_SLOT
+            found = foundSync;
+        }
+
+        if (found == INVALID_BUFFER_SLOT) {
+            // This should not happen.
+            ST_LOGE("dequeueBuffer: no available buffer slots");
+            return -EBUSY;
+        }
+
+        const int buf = found;
+        *outBuf = found;
+
+        const bool useDefaultSize = !w && !h;
+        if (useDefaultSize) {
+            // use the default size
+            w = mDefaultWidth;
+            h = mDefaultHeight;
+        }
+
+        const bool updateFormat = (format != 0);
+        if (!updateFormat) {
+            // keep the current (or default) format
+            format = mPixelFormat;
+        }
+
+        // buffer is now in DEQUEUED (but can also be current at the same time,
+        // if we're in synchronous mode)
+        mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+
+        const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
+        if ((buffer == NULL) ||
+            (uint32_t(buffer->width)  != w) ||
+            (uint32_t(buffer->height) != h) ||
+            (uint32_t(buffer->format) != format) ||
+            ((uint32_t(buffer->usage) & usage) != usage))
+        {
+            usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+            status_t error;
+            sp<GraphicBuffer> graphicBuffer(
+                    mGraphicBufferAlloc->createGraphicBuffer(
+                            w, h, format, usage, &error));
+            if (graphicBuffer == 0) {
+                ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer "
+                        "failed");
+                return error;
+            }
+            if (updateFormat) {
+                mPixelFormat = format;
+            }
+            mSlots[buf].mGraphicBuffer = graphicBuffer;
+            mSlots[buf].mRequestBufferCalled = false;
+            mSlots[buf].mFence = EGL_NO_SYNC_KHR;
+            if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
+                eglDestroyImageKHR(mSlots[buf].mEglDisplay,
+                        mSlots[buf].mEglImage);
+                mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
+                mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
+            }
+            if (mCurrentTexture == buf) {
+                // The current texture no longer references the buffer in this slot
+                // since we just allocated a new buffer.
+                mCurrentTexture = INVALID_BUFFER_SLOT;
+            }
+            returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+        }
+
+        dpy = mSlots[buf].mEglDisplay;
+        fence = mSlots[buf].mFence;
+        mSlots[buf].mFence = EGL_NO_SYNC_KHR;
+    }
+
+    if (fence != EGL_NO_SYNC_KHR) {
+        EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
+        // If something goes wrong, log the error, but return the buffer without
+        // synchronizing access to it.  It's too late at this point to abort the
+        // dequeue operation.
+        if (result == EGL_FALSE) {
+            ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError());
+        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+            ALOGE("dequeueBuffer: timeout waiting for fence");
+        }
+        eglDestroySyncKHR(dpy, fence);
+    }
+
+    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
+            mSlots[*outBuf].mGraphicBuffer->handle, returnFlags);
+
+    return returnFlags;
+}
+
+status_t BufferQueue::setSynchronousMode(bool enabled) {
+    ST_LOGV("setSynchronousMode: enabled=%d", enabled);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
+    status_t err = OK;
+    if (!mAllowSynchronousMode && enabled)
+        return err;
+
+    if (!enabled) {
+        // going to asynchronous mode, drain the queue
+        err = drainQueueLocked();
+        if (err != NO_ERROR)
+            return err;
+    }
+
+    if (mSynchronousMode != enabled) {
+        // - if we're going to asynchronous mode, the queue is guaranteed to be
+        // empty here
+        // - if the client set the number of buffers, we're guaranteed that
+        // we have at least 3 (because we don't allow less)
+        mSynchronousMode = enabled;
+        mDequeueCondition.signal();
+    }
+    return err;
+}
+
+status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
+        uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
+    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
+
+    sp<FrameAvailableListener> listener;
+
+    { // scope for the lock
+        Mutex::Autolock lock(mMutex);
+        if (mAbandoned) {
+            ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!");
+            return NO_INIT;
+        }
+        if (buf < 0 || buf >= mBufferCount) {
+            ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d",
+                    mBufferCount, buf);
+            return -EINVAL;
+        } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+            ST_LOGE("queueBuffer: slot %d is not owned by the client "
+                    "(state=%d)", buf, mSlots[buf].mBufferState);
+            return -EINVAL;
+        } else if (buf == mCurrentTexture) {
+            ST_LOGE("queueBuffer: slot %d is current!", buf);
+            return -EINVAL;
+        } else if (!mSlots[buf].mRequestBufferCalled) {
+            ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
+                    "buffer", buf);
+            return -EINVAL;
+        }
+
+        if (mSynchronousMode) {
+            // In synchronous mode we queue all buffers in a FIFO.
+            mQueue.push_back(buf);
+
+            // Synchronous mode always signals that an additional frame should
+            // be consumed.
+            listener = mFrameAvailableListener;
+        } else {
+            // In asynchronous mode we only keep the most recent buffer.
+            if (mQueue.empty()) {
+                mQueue.push_back(buf);
+
+                // Asynchronous mode only signals that a frame should be
+                // consumed if no previous frame was pending. If a frame were
+                // pending then the consumer would have already been notified.
+                listener = mFrameAvailableListener;
+            } else {
+                Fifo::iterator front(mQueue.begin());
+                // buffer currently queued is freed
+                mSlots[*front].mBufferState = BufferSlot::FREE;
+                // and we record the new buffer index in the queued list
+                *front = buf;
+            }
+        }
+
+        mSlots[buf].mBufferState = BufferSlot::QUEUED;
+        mSlots[buf].mCrop = mNextCrop;
+        mSlots[buf].mTransform = mNextTransform;
+        mSlots[buf].mScalingMode = mNextScalingMode;
+        mSlots[buf].mTimestamp = timestamp;
+        mFrameCounter++;
+        mSlots[buf].mFrameNumber = mFrameCounter;
+
+        mDequeueCondition.signal();
+
+        *outWidth = mDefaultWidth;
+        *outHeight = mDefaultHeight;
+        *outTransform = 0;
+    } // scope for the lock
+
+    // call back without lock held
+    if (listener != 0) {
+        listener->onFrameAvailable();
+    }
+    return OK;
+}
+
+void BufferQueue::cancelBuffer(int buf) {
+    ST_LOGV("cancelBuffer: slot=%d", buf);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGW("cancelBuffer: BufferQueue has been abandoned!");
+        return;
+    }
+
+    if (buf < 0 || buf >= mBufferCount) {
+        ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+                mBufferCount, buf);
+        return;
+    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+        ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
+                buf, mSlots[buf].mBufferState);
+        return;
+    }
+    mSlots[buf].mBufferState = BufferSlot::FREE;
+    mSlots[buf].mFrameNumber = 0;
+    mDequeueCondition.signal();
+}
+
+status_t BufferQueue::setCrop(const Rect& crop) {
+    ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
+            crop.bottom);
+
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        ST_LOGE("setCrop: BufferQueue has been abandoned!");
+        return NO_INIT;
+    }
+    mNextCrop = crop;
+    return OK;
+}
+
+status_t BufferQueue::setTransform(uint32_t transform) {
+    ST_LOGV("setTransform: xform=%#x", transform);
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        ST_LOGE("setTransform: BufferQueue has been abandoned!");
+        return NO_INIT;
+    }
+    mNextTransform = transform;
+    return OK;
+}
+
+status_t BufferQueue::setScalingMode(int mode) {
+    ST_LOGV("setScalingMode: mode=%d", mode);
+
+    switch (mode) {
+        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+            break;
+        default:
+            ST_LOGE("unknown scaling mode: %d", mode);
+            return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mMutex);
+    mNextScalingMode = mode;
+    return OK;
+}
+
+status_t BufferQueue::connect(int api,
+        uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
+    ST_LOGV("connect: api=%d", api);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("connect: BufferQueue has been abandoned!");
+        return NO_INIT;
+    }
+
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+        case NATIVE_WINDOW_API_CPU:
+        case NATIVE_WINDOW_API_MEDIA:
+        case NATIVE_WINDOW_API_CAMERA:
+            if (mConnectedApi != NO_CONNECTED_API) {
+                ST_LOGE("connect: already connected (cur=%d, req=%d)",
+                        mConnectedApi, api);
+                err = -EINVAL;
+            } else {
+                mConnectedApi = api;
+                *outWidth = mDefaultWidth;
+                *outHeight = mDefaultHeight;
+                *outTransform = 0;
+            }
+            break;
+        default:
+            err = -EINVAL;
+            break;
+    }
+    return err;
+}
+
+status_t BufferQueue::disconnect(int api) {
+    ST_LOGV("disconnect: api=%d", api);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        // it is not really an error to disconnect after the surface
+        // has been abandoned, it should just be a no-op.
+        return NO_ERROR;
+    }
+
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+        case NATIVE_WINDOW_API_CPU:
+        case NATIVE_WINDOW_API_MEDIA:
+        case NATIVE_WINDOW_API_CAMERA:
+            if (mConnectedApi == api) {
+                drainQueueAndFreeBuffersLocked();
+                mConnectedApi = NO_CONNECTED_API;
+                mNextCrop.makeInvalid();
+                mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+                mNextTransform = 0;
+                mDequeueCondition.signal();
+            } else {
+                ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)",
+                        mConnectedApi, api);
+                err = -EINVAL;
+            }
+            break;
+        default:
+            ST_LOGE("disconnect: unknown API %d", api);
+            err = -EINVAL;
+            break;
+    }
+    return err;
+}
+
+void BufferQueue::freeBufferLocked(int i) {
+    mSlots[i].mGraphicBuffer = 0;
+    mSlots[i].mBufferState = BufferSlot::FREE;
+    mSlots[i].mFrameNumber = 0;
+    if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
+        eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
+        mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
+        mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
+    }
+}
+
+void BufferQueue::freeAllBuffersLocked() {
+    ALOGW_IF(!mQueue.isEmpty(),
+            "freeAllBuffersLocked called but mQueue is not empty");
+    mCurrentTexture = INVALID_BUFFER_SLOT;
+    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+        freeBufferLocked(i);
+    }
+}
+
+void BufferQueue::freeAllBuffersExceptHeadLocked() {
+    ALOGW_IF(!mQueue.isEmpty(),
+            "freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
+    int head = -1;
+    if (!mQueue.empty()) {
+        Fifo::iterator front(mQueue.begin());
+        head = *front;
+    }
+    mCurrentTexture = INVALID_BUFFER_SLOT;
+    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+        if (i != head) {
+            freeBufferLocked(i);
+        }
+    }
+}
+
+status_t BufferQueue::drainQueueLocked() {
+    while (mSynchronousMode && !mQueue.isEmpty()) {
+        mDequeueCondition.wait(mMutex);
+        if (mAbandoned) {
+            ST_LOGE("drainQueueLocked: BufferQueue has been abandoned!");
+            return NO_INIT;
+        }
+        if (mConnectedApi == NO_CONNECTED_API) {
+            ST_LOGE("drainQueueLocked: BufferQueue is not connected!");
+            return NO_INIT;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t BufferQueue::drainQueueAndFreeBuffersLocked() {
+    status_t err = drainQueueLocked();
+    if (err == NO_ERROR) {
+        if (mSynchronousMode) {
+            freeAllBuffersLocked();
+        } else {
+            freeAllBuffersExceptHeadLocked();
+        }
+    }
+    return err;
+}
+
+}; // namespace android
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 3abe84a..a7bfc61 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -38,19 +38,6 @@
 #include <utils/Log.h>
 #include <utils/String8.h>
 
-// This compile option causes SurfaceTexture to return the buffer that is currently
-// attached to the GL texture from dequeueBuffer when no other buffers are
-// available.  It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do
-// implicit cross-process synchronization to prevent the buffer from being
-// written to before the buffer has (a) been detached from the GL texture and
-// (b) all GL reads from the buffer have completed.
-#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
-#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    true
-#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
-#else
-#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    false
-#endif
-
 // This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension
 // to synchronize access to the buffers.  It will cause dequeueBuffer to stall,
 // waiting for the GL reads for the buffer being dequeued to complete before
@@ -110,44 +97,22 @@
 
 static void mtxMul(float out[16], const float a[16], const float b[16]);
 
-// Get an ID that's unique within this process.
-static int32_t createProcessUniqueId() {
-    static volatile int32_t globalCounter = 0;
-    return android_atomic_inc(&globalCounter);
-}
 
 SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
         GLenum texTarget, bool useFenceSync) :
-    mDefaultWidth(1),
-    mDefaultHeight(1),
-    mPixelFormat(PIXEL_FORMAT_RGBA_8888),
-    mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
-    mClientBufferCount(0),
-    mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
-    mCurrentTexture(INVALID_BUFFER_SLOT),
+    BufferQueue(allowSynchronousMode),
     mCurrentTransform(0),
     mCurrentTimestamp(0),
-    mNextTransform(0),
-    mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
     mTexName(tex),
-    mSynchronousMode(false),
-    mAllowSynchronousMode(allowSynchronousMode),
-    mConnectedApi(NO_CONNECTED_API),
-    mAbandoned(false),
 #ifdef USE_FENCE_SYNC
     mUseFenceSync(useFenceSync),
 #else
     mUseFenceSync(false),
 #endif
-    mTexTarget(texTarget),
-    mFrameCounter(0) {
-    // Choose a name using the PID and a process-unique ID.
-    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
+    mTexTarget(texTarget)
+{
 
     ST_LOGV("SurfaceTexture");
-    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
-    mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
-    mNextCrop.makeInvalid();
     memcpy(mCurrentTransformMatrix, mtxIdentity,
             sizeof(mCurrentTransformMatrix));
 }
@@ -157,91 +122,11 @@
     freeAllBuffersLocked();
 }
 
-status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) {
-    if (bufferCount > NUM_BUFFER_SLOTS)
-        return BAD_VALUE;
-
-    // special-case, nothing to do
-    if (bufferCount == mBufferCount)
-        return OK;
-
-    if (!mClientBufferCount &&
-        bufferCount >= mBufferCount) {
-        // easy, we just have more buffers
-        mBufferCount = bufferCount;
-        mServerBufferCount = bufferCount;
-        mDequeueCondition.signal();
-    } else {
-        // we're here because we're either
-        // - reducing the number of available buffers
-        // - or there is a client-buffer-count in effect
-
-        // less than 2 buffers is never allowed
-        if (bufferCount < 2)
-            return BAD_VALUE;
-
-        // when there is non client-buffer-count in effect, the client is not
-        // allowed to dequeue more than one buffer at a time,
-        // so the next time they dequeue a buffer, we know that they don't
-        // own one. the actual resizing will happen during the next
-        // dequeueBuffer.
-
-        mServerBufferCount = bufferCount;
-    }
-    return OK;
-}
-
 status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
     Mutex::Autolock lock(mMutex);
     return setBufferCountServerLocked(bufferCount);
 }
 
-status_t SurfaceTexture::setBufferCount(int bufferCount) {
-    ST_LOGV("setBufferCount: count=%d", bufferCount);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-    if (bufferCount > NUM_BUFFER_SLOTS) {
-        ST_LOGE("setBufferCount: bufferCount larger than slots available");
-        return BAD_VALUE;
-    }
-
-    // Error out if the user has dequeued buffers
-    for (int i=0 ; i<mBufferCount ; i++) {
-        if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
-            ST_LOGE("setBufferCount: client owns some buffers");
-            return -EINVAL;
-        }
-    }
-
-    const int minBufferSlots = mSynchronousMode ?
-            MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
-    if (bufferCount == 0) {
-        mClientBufferCount = 0;
-        bufferCount = (mServerBufferCount >= minBufferSlots) ?
-                mServerBufferCount : minBufferSlots;
-        return setBufferCountServerLocked(bufferCount);
-    }
-
-    if (bufferCount < minBufferSlots) {
-        ST_LOGE("setBufferCount: requested buffer count (%d) is less than "
-                "minimum (%d)", bufferCount, minBufferSlots);
-        return BAD_VALUE;
-    }
-
-    // here we're guaranteed that the client doesn't have dequeued buffers
-    // and will release all of its buffer references.
-    freeAllBuffersLocked();
-    mBufferCount = bufferCount;
-    mClientBufferCount = bufferCount;
-    mCurrentTexture = INVALID_BUFFER_SLOT;
-    mQueue.clear();
-    mDequeueCondition.signal();
-    return OK;
-}
 
 status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
 {
@@ -258,496 +143,6 @@
     return OK;
 }
 
-status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    ST_LOGV("requestBuffer: slot=%d", slot);
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-    if (slot < 0 || mBufferCount <= slot) {
-        ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, slot);
-        return BAD_VALUE;
-    }
-    mSlots[slot].mRequestBufferCalled = true;
-    *buf = mSlots[slot].mGraphicBuffer;
-    return NO_ERROR;
-}
-
-status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
-        uint32_t format, uint32_t usage) {
-    ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
-
-    if ((w && !h) || (!w && h)) {
-        ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
-        return BAD_VALUE;
-    }
-
-    status_t returnFlags(OK);
-    EGLDisplay dpy = EGL_NO_DISPLAY;
-    EGLSyncKHR fence = EGL_NO_SYNC_KHR;
-
-    { // Scope for the lock
-        Mutex::Autolock lock(mMutex);
-
-        int found = -1;
-        int foundSync = -1;
-        int dequeuedCount = 0;
-        bool tryAgain = true;
-        while (tryAgain) {
-            if (mAbandoned) {
-                ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
-                return NO_INIT;
-            }
-
-            // We need to wait for the FIFO to drain if the number of buffer
-            // needs to change.
-            //
-            // The condition "number of buffers needs to change" is true if
-            // - the client doesn't care about how many buffers there are
-            // - AND the actual number of buffer is different from what was
-            //   set in the last setBufferCountServer()
-            //                         - OR -
-            //   setBufferCountServer() was set to a value incompatible with
-            //   the synchronization mode (for instance because the sync mode
-            //   changed since)
-            //
-            // As long as this condition is true AND the FIFO is not empty, we
-            // wait on mDequeueCondition.
-
-            const int minBufferCountNeeded = mSynchronousMode ?
-                    MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
-
-            const bool numberOfBuffersNeedsToChange = !mClientBufferCount &&
-                    ((mServerBufferCount != mBufferCount) ||
-                            (mServerBufferCount < minBufferCountNeeded));
-
-            if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) {
-                // wait for the FIFO to drain
-                mDequeueCondition.wait(mMutex);
-                // NOTE: we continue here because we need to reevaluate our
-                // whole state (eg: we could be abandoned or disconnected)
-                continue;
-            }
-
-            if (numberOfBuffersNeedsToChange) {
-                // here we're guaranteed that mQueue is empty
-                freeAllBuffersLocked();
-                mBufferCount = mServerBufferCount;
-                if (mBufferCount < minBufferCountNeeded)
-                    mBufferCount = minBufferCountNeeded;
-                mCurrentTexture = INVALID_BUFFER_SLOT;
-                returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
-            }
-
-            // look for a free buffer to give to the client
-            found = INVALID_BUFFER_SLOT;
-            foundSync = INVALID_BUFFER_SLOT;
-            dequeuedCount = 0;
-            for (int i = 0; i < mBufferCount; i++) {
-                const int state = mSlots[i].mBufferState;
-                if (state == BufferSlot::DEQUEUED) {
-                    dequeuedCount++;
-                }
-
-                // if buffer is FREE it CANNOT be current
-                ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
-                        "dequeueBuffer: buffer %d is both FREE and current!",
-                        i);
-
-                if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
-                    if (state == BufferSlot::FREE || i == mCurrentTexture) {
-                        foundSync = i;
-                        if (i != mCurrentTexture) {
-                            found = i;
-                            break;
-                        }
-                    }
-                } else {
-                    if (state == BufferSlot::FREE) {
-                        /* We return the oldest of the free buffers to avoid
-                         * stalling the producer if possible.  This is because
-                         * the consumer may still have pending reads of the
-                         * buffers in flight.
-                         */
-                        bool isOlder = mSlots[i].mFrameNumber <
-                                mSlots[found].mFrameNumber;
-                        if (found < 0 || isOlder) {
-                            foundSync = i;
-                            found = i;
-                        }
-                    }
-                }
-            }
-
-            // clients are not allowed to dequeue more than one buffer
-            // if they didn't set a buffer count.
-            if (!mClientBufferCount && dequeuedCount) {
-                ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
-                        "setting the buffer count");
-                return -EINVAL;
-            }
-
-            // See whether a buffer has been queued since the last
-            // setBufferCount so we know whether to perform the
-            // MIN_UNDEQUEUED_BUFFERS check below.
-            bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT;
-            if (bufferHasBeenQueued) {
-                // make sure the client is not trying to dequeue more buffers
-                // than allowed.
-                const int avail = mBufferCount - (dequeuedCount+1);
-                if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
-                    ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded "
-                            "(dequeued=%d)",
-                            MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
-                            dequeuedCount);
-                    return -EBUSY;
-                }
-            }
-
-            // we're in synchronous mode and didn't find a buffer, we need to
-            // wait for some buffers to be consumed
-            tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
-            if (tryAgain) {
-                mDequeueCondition.wait(mMutex);
-            }
-        }
-
-        if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
-            // foundSync guaranteed to be != INVALID_BUFFER_SLOT
-            found = foundSync;
-        }
-
-        if (found == INVALID_BUFFER_SLOT) {
-            // This should not happen.
-            ST_LOGE("dequeueBuffer: no available buffer slots");
-            return -EBUSY;
-        }
-
-        const int buf = found;
-        *outBuf = found;
-
-        const bool useDefaultSize = !w && !h;
-        if (useDefaultSize) {
-            // use the default size
-            w = mDefaultWidth;
-            h = mDefaultHeight;
-        }
-
-        const bool updateFormat = (format != 0);
-        if (!updateFormat) {
-            // keep the current (or default) format
-            format = mPixelFormat;
-        }
-
-        // buffer is now in DEQUEUED (but can also be current at the same time,
-        // if we're in synchronous mode)
-        mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
-
-        const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
-        if ((buffer == NULL) ||
-            (uint32_t(buffer->width)  != w) ||
-            (uint32_t(buffer->height) != h) ||
-            (uint32_t(buffer->format) != format) ||
-            ((uint32_t(buffer->usage) & usage) != usage))
-        {
-            usage |= GraphicBuffer::USAGE_HW_TEXTURE;
-            status_t error;
-            sp<GraphicBuffer> graphicBuffer(
-                    mGraphicBufferAlloc->createGraphicBuffer(
-                            w, h, format, usage, &error));
-            if (graphicBuffer == 0) {
-                ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer "
-                        "failed");
-                return error;
-            }
-            if (updateFormat) {
-                mPixelFormat = format;
-            }
-            mSlots[buf].mGraphicBuffer = graphicBuffer;
-            mSlots[buf].mRequestBufferCalled = false;
-            mSlots[buf].mFence = EGL_NO_SYNC_KHR;
-            if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
-                eglDestroyImageKHR(mSlots[buf].mEglDisplay,
-                        mSlots[buf].mEglImage);
-                mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
-                mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
-            }
-            if (mCurrentTexture == buf) {
-                // The current texture no longer references the buffer in this slot
-                // since we just allocated a new buffer.
-                mCurrentTexture = INVALID_BUFFER_SLOT;
-            }
-            returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
-        }
-
-        dpy = mSlots[buf].mEglDisplay;
-        fence = mSlots[buf].mFence;
-        mSlots[buf].mFence = EGL_NO_SYNC_KHR;
-    }
-
-    if (fence != EGL_NO_SYNC_KHR) {
-        EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
-        // If something goes wrong, log the error, but return the buffer without
-        // synchronizing access to it.  It's too late at this point to abort the
-        // dequeue operation.
-        if (result == EGL_FALSE) {
-            ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError());
-        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
-            ALOGE("dequeueBuffer: timeout waiting for fence");
-        }
-        eglDestroySyncKHR(dpy, fence);
-    }
-
-    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
-            mSlots[*outBuf].mGraphicBuffer->handle, returnFlags);
-
-    return returnFlags;
-}
-
-status_t SurfaceTexture::setSynchronousMode(bool enabled) {
-    ST_LOGV("setSynchronousMode: enabled=%d", enabled);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-
-    status_t err = OK;
-    if (!mAllowSynchronousMode && enabled)
-        return err;
-
-    if (!enabled) {
-        // going to asynchronous mode, drain the queue
-        err = drainQueueLocked();
-        if (err != NO_ERROR)
-            return err;
-    }
-
-    if (mSynchronousMode != enabled) {
-        // - if we're going to asynchronous mode, the queue is guaranteed to be
-        // empty here
-        // - if the client set the number of buffers, we're guaranteed that
-        // we have at least 3 (because we don't allow less)
-        mSynchronousMode = enabled;
-        mDequeueCondition.signal();
-    }
-    return err;
-}
-
-status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
-        uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
-
-    sp<FrameAvailableListener> listener;
-
-    { // scope for the lock
-        Mutex::Autolock lock(mMutex);
-        if (mAbandoned) {
-            ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!");
-            return NO_INIT;
-        }
-        if (buf < 0 || buf >= mBufferCount) {
-            ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d",
-                    mBufferCount, buf);
-            return -EINVAL;
-        } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
-            ST_LOGE("queueBuffer: slot %d is not owned by the client "
-                    "(state=%d)", buf, mSlots[buf].mBufferState);
-            return -EINVAL;
-        } else if (buf == mCurrentTexture) {
-            ST_LOGE("queueBuffer: slot %d is current!", buf);
-            return -EINVAL;
-        } else if (!mSlots[buf].mRequestBufferCalled) {
-            ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
-                    "buffer", buf);
-            return -EINVAL;
-        }
-
-        if (mSynchronousMode) {
-            // In synchronous mode we queue all buffers in a FIFO.
-            mQueue.push_back(buf);
-
-            // Synchronous mode always signals that an additional frame should
-            // be consumed.
-            listener = mFrameAvailableListener;
-        } else {
-            // In asynchronous mode we only keep the most recent buffer.
-            if (mQueue.empty()) {
-                mQueue.push_back(buf);
-
-                // Asynchronous mode only signals that a frame should be
-                // consumed if no previous frame was pending. If a frame were
-                // pending then the consumer would have already been notified.
-                listener = mFrameAvailableListener;
-            } else {
-                Fifo::iterator front(mQueue.begin());
-                // buffer currently queued is freed
-                mSlots[*front].mBufferState = BufferSlot::FREE;
-                // and we record the new buffer index in the queued list
-                *front = buf;
-            }
-        }
-
-        mSlots[buf].mBufferState = BufferSlot::QUEUED;
-        mSlots[buf].mCrop = mNextCrop;
-        mSlots[buf].mTransform = mNextTransform;
-        mSlots[buf].mScalingMode = mNextScalingMode;
-        mSlots[buf].mTimestamp = timestamp;
-        mFrameCounter++;
-        mSlots[buf].mFrameNumber = mFrameCounter;
-
-        mDequeueCondition.signal();
-
-        *outWidth = mDefaultWidth;
-        *outHeight = mDefaultHeight;
-        *outTransform = 0;
-    } // scope for the lock
-
-    // call back without lock held
-    if (listener != 0) {
-        listener->onFrameAvailable();
-    }
-    return OK;
-}
-
-void SurfaceTexture::cancelBuffer(int buf) {
-    ST_LOGV("cancelBuffer: slot=%d", buf);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ST_LOGW("cancelBuffer: SurfaceTexture has been abandoned!");
-        return;
-    }
-
-    if (buf < 0 || buf >= mBufferCount) {
-        ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, buf);
-        return;
-    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
-        ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
-                buf, mSlots[buf].mBufferState);
-        return;
-    }
-    mSlots[buf].mBufferState = BufferSlot::FREE;
-    mSlots[buf].mFrameNumber = 0;
-    mDequeueCondition.signal();
-}
-
-status_t SurfaceTexture::setCrop(const Rect& crop) {
-    ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
-            crop.bottom);
-
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        ST_LOGE("setCrop: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-    mNextCrop = crop;
-    return OK;
-}
-
-status_t SurfaceTexture::setTransform(uint32_t transform) {
-    ST_LOGV("setTransform: xform=%#x", transform);
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        ST_LOGE("setTransform: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-    mNextTransform = transform;
-    return OK;
-}
-
-status_t SurfaceTexture::connect(int api,
-        uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("connect: api=%d", api);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ST_LOGE("connect: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-
-    int err = NO_ERROR;
-    switch (api) {
-        case NATIVE_WINDOW_API_EGL:
-        case NATIVE_WINDOW_API_CPU:
-        case NATIVE_WINDOW_API_MEDIA:
-        case NATIVE_WINDOW_API_CAMERA:
-            if (mConnectedApi != NO_CONNECTED_API) {
-                ST_LOGE("connect: already connected (cur=%d, req=%d)",
-                        mConnectedApi, api);
-                err = -EINVAL;
-            } else {
-                mConnectedApi = api;
-                *outWidth = mDefaultWidth;
-                *outHeight = mDefaultHeight;
-                *outTransform = 0;
-            }
-            break;
-        default:
-            err = -EINVAL;
-            break;
-    }
-    return err;
-}
-
-status_t SurfaceTexture::disconnect(int api) {
-    ST_LOGV("disconnect: api=%d", api);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        // it is not really an error to disconnect after the surface
-        // has been abandoned, it should just be a no-op.
-        return NO_ERROR;
-    }
-
-    int err = NO_ERROR;
-    switch (api) {
-        case NATIVE_WINDOW_API_EGL:
-        case NATIVE_WINDOW_API_CPU:
-        case NATIVE_WINDOW_API_MEDIA:
-        case NATIVE_WINDOW_API_CAMERA:
-            if (mConnectedApi == api) {
-                drainQueueAndFreeBuffersLocked();
-                mConnectedApi = NO_CONNECTED_API;
-                mNextCrop.makeInvalid();
-                mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
-                mNextTransform = 0;
-                mDequeueCondition.signal();
-            } else {
-                ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)",
-                        mConnectedApi, api);
-                err = -EINVAL;
-            }
-            break;
-        default:
-            ST_LOGE("disconnect: unknown API %d", api);
-            err = -EINVAL;
-            break;
-    }
-    return err;
-}
-
-status_t SurfaceTexture::setScalingMode(int mode) {
-    ST_LOGV("setScalingMode: mode=%d", mode);
-
-    switch (mode) {
-        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
-        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
-            break;
-        default:
-            ST_LOGE("unknown scaling mode: %d", mode);
-            return BAD_VALUE;
-    }
-
-    Mutex::Autolock lock(mMutex);
-    mNextScalingMode = mode;
-    return OK;
-}
-
 status_t SurfaceTexture::updateTexImage() {
     ST_LOGV("updateTexImage");
     Mutex::Autolock lock(mMutex);
@@ -980,69 +375,6 @@
     mFrameAvailableListener = listener;
 }
 
-void SurfaceTexture::freeBufferLocked(int i) {
-    mSlots[i].mGraphicBuffer = 0;
-    mSlots[i].mBufferState = BufferSlot::FREE;
-    mSlots[i].mFrameNumber = 0;
-    if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
-        eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
-        mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
-        mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
-    }
-}
-
-void SurfaceTexture::freeAllBuffersLocked() {
-    ALOGW_IF(!mQueue.isEmpty(),
-            "freeAllBuffersLocked called but mQueue is not empty");
-    mCurrentTexture = INVALID_BUFFER_SLOT;
-    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        freeBufferLocked(i);
-    }
-}
-
-void SurfaceTexture::freeAllBuffersExceptHeadLocked() {
-    ALOGW_IF(!mQueue.isEmpty(),
-            "freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
-    int head = -1;
-    if (!mQueue.empty()) {
-        Fifo::iterator front(mQueue.begin());
-        head = *front;
-    }
-    mCurrentTexture = INVALID_BUFFER_SLOT;
-    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        if (i != head) {
-            freeBufferLocked(i);
-        }
-    }
-}
-
-status_t SurfaceTexture::drainQueueLocked() {
-    while (mSynchronousMode && !mQueue.isEmpty()) {
-        mDequeueCondition.wait(mMutex);
-        if (mAbandoned) {
-            ST_LOGE("drainQueueLocked: SurfaceTexture has been abandoned!");
-            return NO_INIT;
-        }
-        if (mConnectedApi == NO_CONNECTED_API) {
-            ST_LOGE("drainQueueLocked: SurfaceTexture is not connected!");
-            return NO_INIT;
-        }
-    }
-    return NO_ERROR;
-}
-
-status_t SurfaceTexture::drainQueueAndFreeBuffersLocked() {
-    status_t err = drainQueueLocked();
-    if (err == NO_ERROR) {
-        if (mSynchronousMode) {
-            freeAllBuffersLocked();
-        } else {
-            freeAllBuffersExceptHeadLocked();
-        }
-    }
-    return err;
-}
-
 EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
         const sp<GraphicBuffer>& graphicBuffer) {
     EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
@@ -1084,36 +416,7 @@
     return mSynchronousMode;
 }
 
-int SurfaceTexture::query(int what, int* outValue)
-{
-    Mutex::Autolock lock(mMutex);
 
-    if (mAbandoned) {
-        ST_LOGE("query: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-
-    int value;
-    switch (what) {
-    case NATIVE_WINDOW_WIDTH:
-        value = mDefaultWidth;
-        break;
-    case NATIVE_WINDOW_HEIGHT:
-        value = mDefaultHeight;
-        break;
-    case NATIVE_WINDOW_FORMAT:
-        value = mPixelFormat;
-        break;
-    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
-        value = mSynchronousMode ?
-                (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
-        break;
-    default:
-        return BAD_VALUE;
-    }
-    outValue[0] = value;
-    return NO_ERROR;
-}
 
 void SurfaceTexture::abandon() {
     Mutex::Autolock lock(mMutex);
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 16a3d73..55a860e 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -30,7 +30,7 @@
 #define DEBUG_MEMORY_USAGE 0
 
 // Turn on to enable debugging of cache flushes
-#define DEBUG_CACHE_FLUSH 1
+#define DEBUG_CACHE_FLUSH 0
 
 // Turn on to enable layers debugging when rendered as regions
 #define DEBUG_LAYERS_AS_REGIONS 0
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 1a11fbc..f9088ac 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -226,6 +226,11 @@
 
     while (!mReader.eof()) {
         int op = mReader.readInt();
+        if (op & OP_MAY_BE_SKIPPED_MASK) {
+            int skip = mReader.readInt();
+            ALOGD("%sSkip %d", (char*) indent, skip);
+            op &= ~OP_MAY_BE_SKIPPED_MASK;
+       }
 
         switch (op) {
             case DrawGLFunction: {
@@ -316,8 +321,9 @@
                 DisplayList* displayList = getDisplayList();
                 uint32_t width = getUInt();
                 uint32_t height = getUInt();
-                ALOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op],
-                    displayList, width, height, level + 1);
+                int32_t flags = getInt();
+                ALOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op],
+                    displayList, width, height, flags, level + 1);
                 renderer.outputDisplayList(displayList, level + 1);
             }
             break;
@@ -551,7 +557,7 @@
  * in the output() function, since that function processes the same list of opcodes for the
  * purposes of logging display list info for a given view.
  */
-bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) {
+bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) {
     bool needsInvalidate = false;
     TextContainer text;
     mReader.rewind();
@@ -572,6 +578,18 @@
     int saveCount = renderer.getSaveCount() - 1;
     while (!mReader.eof()) {
         int op = mReader.readInt();
+        if (op & OP_MAY_BE_SKIPPED_MASK) {
+            int32_t skip = mReader.readInt() * 4;
+            if (CC_LIKELY(flags & kReplayFlag_ClipChildren)) {
+                mReader.skip(skip);
+                DISPLAY_LIST_LOGD("%s%s skipping %d bytes", (char*) indent,
+                        OP_NAMES[op & ~OP_MAY_BE_SKIPPED_MASK], skip);
+                continue;
+            } else {
+                op &= ~OP_MAY_BE_SKIPPED_MASK;
+                ALOGD("%s", OP_NAMES[op]);
+            }
+        }
         logBuffer.writeCommand(level, op);
 
         switch (op) {
@@ -584,7 +602,7 @@
             }
             break;
             case Save: {
-                int rendererNum = getInt();
+                int32_t rendererNum = getInt();
                 DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], rendererNum);
                 renderer.save(rendererNum);
             }
@@ -595,7 +613,7 @@
             }
             break;
             case RestoreToCount: {
-                int restoreCount = saveCount + getInt();
+                int32_t restoreCount = saveCount + getInt();
                 DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], restoreCount);
                 renderer.restoreToCount(restoreCount);
             }
@@ -606,7 +624,7 @@
                 float f3 = getFloat();
                 float f4 = getFloat();
                 SkPaint* paint = getPaint(renderer);
-                int flags = getInt();
+                int32_t flags = getInt();
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent,
                     OP_NAMES[op], f1, f2, f3, f4, paint, flags);
                 renderer.saveLayer(f1, f2, f3, f4, paint, flags);
@@ -617,8 +635,8 @@
                 float f2 = getFloat();
                 float f3 = getFloat();
                 float f4 = getFloat();
-                int alpha = getInt();
-                int flags = getInt();
+                int32_t alpha = getInt();
+                int32_t flags = getInt();
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", (char*) indent,
                     OP_NAMES[op], f1, f2, f3, f4, alpha, flags);
                 renderer.saveLayerAlpha(f1, f2, f3, f4, alpha, flags);
@@ -668,7 +686,7 @@
                 float f2 = getFloat();
                 float f3 = getFloat();
                 float f4 = getFloat();
-                int regionOp = getInt();
+                int32_t regionOp = getInt();
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d", (char*) indent, OP_NAMES[op],
                     f1, f2, f3, f4, regionOp);
                 renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp);
@@ -678,10 +696,11 @@
                 DisplayList* displayList = getDisplayList();
                 uint32_t width = getUInt();
                 uint32_t height = getUInt();
-                DISPLAY_LIST_LOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op],
-                    displayList, width, height, level + 1);
+                int32_t flags = getInt();
+                DISPLAY_LIST_LOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op],
+                    displayList, width, height, flags, level + 1);
                 needsInvalidate |= renderer.drawDisplayList(displayList, width, height,
-                        dirty, level + 1);
+                        dirty, flags, level + 1);
             }
             break;
             case DrawLayer: {
@@ -730,7 +749,7 @@
             }
             break;
             case DrawBitmapMesh: {
-                int verticesCount = 0;
+                int32_t verticesCount = 0;
                 uint32_t colorsCount = 0;
 
                 SkBitmap* bitmap = getBitmap();
@@ -738,7 +757,7 @@
                 uint32_t meshHeight = getInt();
                 float* vertices = getFloats(verticesCount);
                 bool hasColors = getInt();
-                int* colors = hasColors ? getInts(colorsCount) : NULL;
+                int32_t* colors = hasColors ? getInts(colorsCount) : NULL;
                 SkPaint* paint = getPaint(renderer);
 
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
@@ -771,8 +790,8 @@
             }
             break;
             case DrawColor: {
-                int color = getInt();
-                int xferMode = getInt();
+                int32_t color = getInt();
+                int32_t xferMode = getInt();
                 DISPLAY_LIST_LOGD("%s%s 0x%x %d", (char*) indent, OP_NAMES[op], color, xferMode);
                 renderer.drawColor(color, (SkXfermode::Mode) xferMode);
             }
@@ -829,7 +848,7 @@
                 float f4 = getFloat();
                 float f5 = getFloat();
                 float f6 = getFloat();
-                int i1 = getInt();
+                int32_t i1 = getInt();
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p",
                     (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint);
@@ -844,7 +863,7 @@
             }
             break;
             case DrawLines: {
-                int count = 0;
+                int32_t count = 0;
                 float* points = getFloats(count);
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
@@ -852,7 +871,7 @@
             }
             break;
             case DrawPoints: {
-                int count = 0;
+                int32_t count = 0;
                 float* points = getFloats(count);
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
@@ -861,7 +880,7 @@
             break;
             case DrawText: {
                 getText(&text);
-                int count = getInt();
+                int32_t count = getInt();
                 float x = getFloat();
                 float y = getFloat();
                 SkPaint* paint = getPaint(renderer);
@@ -873,8 +892,8 @@
             break;
             case DrawPosText: {
                 getText(&text);
-                int count = getInt();
-                int positionsCount = 0;
+                int32_t count = getInt();
+                int32_t positionsCount = 0;
                 float* positions = getFloats(positionsCount);
                 SkPaint* paint = getPaint(renderer);
                 DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %p", (char*) indent,
@@ -913,7 +932,7 @@
                 float radius = getFloat();
                 float dx = getFloat();
                 float dy = getFloat();
-                int color = getInt();
+                int32_t color = getInt();
                 DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, 0x%x", (char*) indent, OP_NAMES[op],
                     radius, dx, dy, color);
                 renderer.setupShadow(radius, dx, dy, color);
@@ -925,8 +944,8 @@
             }
             break;
             case SetupPaintFilter: {
-                int clearBits = getInt();
-                int setBits = getInt();
+                int32_t clearBits = getInt();
+                int32_t setBits = getInt();
                 DISPLAY_LIST_LOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op],
                         clearBits, setBits);
                 renderer.setupPaintFilter(clearBits, setBits);
@@ -949,7 +968,8 @@
 // Base structure
 ///////////////////////////////////////////////////////////////////////////////
 
-DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE), mHasDrawOps(false) {
+DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE),
+        mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false), mHasDrawOps(false) {
 }
 
 DisplayListRenderer::~DisplayListRenderer() {
@@ -1019,6 +1039,7 @@
 
 void DisplayListRenderer::finish() {
     insertRestoreToCount();
+    insertTranlate();
     OpenGLRenderer::finish();
 }
 
@@ -1043,15 +1064,18 @@
 
 void DisplayListRenderer::restore() {
     if (mRestoreSaveCount < 0) {
-        addOp(DisplayList::Restore);
-    } else {
-        mRestoreSaveCount--;
+        restoreToCount(getSaveCount() - 1);
+        return;
     }
+
+    mRestoreSaveCount--;
+    insertTranlate();
     OpenGLRenderer::restore();
 }
 
 void DisplayListRenderer::restoreToCount(int saveCount) {
     mRestoreSaveCount = saveCount;
+    insertTranlate();
     OpenGLRenderer::restoreToCount(saveCount);
 }
 
@@ -1074,8 +1098,10 @@
 }
 
 void DisplayListRenderer::translate(float dx, float dy) {
-    addOp(DisplayList::Translate);
-    addPoint(dx, dy);
+    mHasTranslate = true;
+    mTranslateX += dx;
+    mTranslateY += dy;
+    insertRestoreToCount();
     OpenGLRenderer::translate(dx, dy);
 }
 
@@ -1118,12 +1144,15 @@
 }
 
 bool DisplayListRenderer::drawDisplayList(DisplayList* displayList,
-        uint32_t width, uint32_t height, Rect& dirty, uint32_t level) {
+        uint32_t width, uint32_t height, Rect& dirty, int32_t flags, uint32_t level) {
     // dirty is an out parameter and should not be recorded,
     // it matters only when replaying the display list
-    addOp(DisplayList::DrawDisplayList);
+    const bool reject = quickReject(0.0f, 0.0f, width, height);
+    uint32_t* location = addOp(DisplayList::DrawDisplayList, reject);
     addDisplayList(displayList);
     addSize(width, height);
+    addInt(flags);
+    addSkip(location);
     return false;
 }
 
@@ -1134,30 +1163,38 @@
     addPaint(paint);
 }
 
-void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
-        SkPaint* paint) {
-    addOp(DisplayList::DrawBitmap);
+void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
+    const bool reject = quickReject(left, top, left + bitmap->width(), top + bitmap->height());
+    uint32_t* location = addOp(DisplayList::DrawBitmap, reject);
     addBitmap(bitmap);
     addPoint(left, top);
     addPaint(paint);
+    addSkip(location);
 }
 
-void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix,
-        SkPaint* paint) {
-    addOp(DisplayList::DrawBitmapMatrix);
+void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
+    Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
+    const mat4 transform(*matrix);
+    transform.mapRect(r);
+
+    const bool reject = quickReject(r.left, r.top, r.right, r.bottom);
+    uint32_t* location = addOp(DisplayList::DrawBitmapMatrix, reject);
     addBitmap(bitmap);
     addMatrix(matrix);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
         float srcRight, float srcBottom, float dstLeft, float dstTop,
         float dstRight, float dstBottom, SkPaint* paint) {
-    addOp(DisplayList::DrawBitmapRect);
+    const bool reject = quickReject(dstLeft, dstTop, dstRight, dstBottom);
+    uint32_t* location = addOp(DisplayList::DrawBitmapRect, reject);
     addBitmap(bitmap);
     addBounds(srcLeft, srcTop, srcRight, srcBottom);
     addBounds(dstLeft, dstTop, dstRight, dstBottom);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
@@ -1179,13 +1216,15 @@
 void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
         const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
         float left, float top, float right, float bottom, SkPaint* paint) {
-    addOp(DisplayList::DrawPatch);
+    const bool reject = quickReject(left, top, right, bottom);
+    uint32_t* location = addOp(DisplayList::DrawPatch, reject);
     addBitmap(bitmap);
     addInts(xDivs, width);
     addInts(yDivs, height);
     addUInts(colors, numColors);
     addBounds(left, top, right, bottom);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -1196,17 +1235,23 @@
 
 void DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
         SkPaint* paint) {
-    addOp(DisplayList::DrawRect);
+    const bool reject = paint->getStyle() == SkPaint::kFill_Style &&
+            quickReject(left, top, right, bottom);
+    uint32_t* location = addOp(DisplayList::DrawRect, reject);
     addBounds(left, top, right, bottom);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom,
             float rx, float ry, SkPaint* paint) {
-    addOp(DisplayList::DrawRoundRect);
+    const bool reject = paint->getStyle() == SkPaint::kFill_Style &&
+            quickReject(left, top, right, bottom);
+    uint32_t* location = addOp(DisplayList::DrawRoundRect, reject);
     addBounds(left, top, right, bottom);
     addPoint(rx, ry);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
@@ -1233,9 +1278,15 @@
 }
 
 void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
-    addOp(DisplayList::DrawPath);
+    float left, top, offset;
+    uint32_t width, height;
+    computePathBounds(path, paint, left, top, offset, width, height);
+
+    const bool reject = quickReject(left - offset, top - offset, width, height);
+    uint32_t* location = addOp(DisplayList::DrawPath, reject);
     addPath(path);
     addPaint(paint);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) {
@@ -1252,11 +1303,8 @@
 
 void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, SkPaint* paint, float length) {
-    if (count <= 0) return;
-    addOp(DisplayList::DrawText);
-    addText(text, bytesCount);
-    addInt(count);
-    addPoint(x, y);
+    if (!text || count <= 0) return;
+
     // TODO: We should probably make a copy of the paint instead of modifying
     //       it; modifying the paint will change its generationID the first
     //       time, which might impact caches. More investigation needed to
@@ -1265,13 +1313,27 @@
     //       its own copy as it does right now.
     // Beware: this needs Glyph encoding (already done on the Paint constructor)
     paint->setAntiAlias(true);
+    if (length < 0.0f) length = paint->measureText(text, bytesCount);
+
+    bool reject = false;
+    if (CC_LIKELY(paint->getTextAlign() == SkPaint::kLeft_Align)) {
+        SkPaint::FontMetrics metrics;
+        paint->getFontMetrics(&metrics, 0.0f);
+        reject = quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom);
+    }
+
+    uint32_t* location = addOp(DisplayList::DrawText, reject);
+    addText(text, bytesCount);
+    addInt(count);
+    addPoint(x, y);
     addPaint(paint);
-    addFloat(length < 0.0f ? paint->measureText(text, bytesCount) : length);
+    addFloat(length);
+    addSkip(location);
 }
 
 void DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count,
         const float* positions, SkPaint* paint) {
-    if (count <= 0) return;
+    if (!text || count <= 0) return;
     addOp(DisplayList::DrawPosText);
     addText(text, bytesCount);
     addInt(count);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 46506e4..4a299c6 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -42,6 +42,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #define MIN_WRITER_SIZE 4096
+#define OP_MAY_BE_SKIPPED_MASK 0xff000000
 
 // Debug
 #if DEBUG_DISPLAY_LIST
@@ -110,13 +111,18 @@
         DrawGLFunction,
     };
 
+    // See flags defined in DisplayList.java
+    enum ReplayFlag {
+        kReplayFlag_ClipChildren = 0x1
+    };
+
     static const char* OP_NAMES[];
 
     void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
 
     ANDROID_API size_t getSize();
 
-    bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0);
+    bool replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0);
 
     void output(OpenGLRenderer& renderer, uint32_t level = 0);
 
@@ -167,11 +173,11 @@
         return (SkiaColorFilter*) getInt();
     }
 
-    inline int getIndex() {
+    inline int32_t getIndex() {
         return mReader.readInt();
     }
 
-    inline int getInt() {
+    inline int32_t getInt() {
         return mReader.readInt();
     }
 
@@ -209,7 +215,7 @@
         return (uint32_t*) mReader.skip(count * sizeof(uint32_t));
     }
 
-    float* getFloats(int& count) {
+    float* getFloats(int32_t& count) {
         count = getInt();
         return (float*) mReader.skip(count * sizeof(float));
     }
@@ -279,7 +285,7 @@
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
     virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
-            Rect& dirty, uint32_t level = 0);
+            Rect& dirty, int32_t flags, uint32_t level = 0);
     virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
@@ -358,13 +364,45 @@
         }
     }
 
-    inline void addOp(DisplayList::Op drawOp) {
+    void insertTranlate() {
+        if (mHasTranslate) {
+            if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
+                mWriter.writeInt(DisplayList::Translate);
+                addPoint(mTranslateX, mTranslateY);
+                mTranslateX = mTranslateY = 0.0f;
+            }
+            mHasTranslate = false;
+        }
+    }
+
+    inline void addOp(const DisplayList::Op drawOp) {
         insertRestoreToCount();
+        insertTranlate();
         mWriter.writeInt(drawOp);
         mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList;
     }
 
-    inline void addInt(int value) {
+    uint32_t* addOp(const DisplayList::Op drawOp, const bool reject) {
+        insertRestoreToCount();
+        insertTranlate();
+        mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList;
+        if (reject) {
+            mWriter.writeInt(OP_MAY_BE_SKIPPED_MASK | drawOp);
+            mWriter.writeInt(0);
+            uint32_t* location = reject ? mWriter.peek32(mWriter.size() - 4) : NULL;
+            return location;
+        }
+        mWriter.writeInt(drawOp);
+        return NULL;
+    }
+
+    inline void addSkip(uint32_t* location) {
+        if (location) {
+            *location = (int32_t) (mWriter.peek32(mWriter.size() - 4) - location);
+        }
+    }
+
+    inline void addInt(int32_t value) {
         mWriter.writeInt(value);
     }
 
@@ -391,9 +429,9 @@
         mWriter.writeScalar(value);
     }
 
-    void addFloats(const float* values, int count) {
+    void addFloats(const float* values, int32_t count) {
         mWriter.writeInt(count);
-        for (int i = 0; i < count; i++) {
+        for (int32_t i = 0; i < count; i++) {
             mWriter.writeScalar(values[i]);
         }
     }
@@ -424,7 +462,9 @@
         SkPath* pathCopy = mPathMap.valueFor(path);
         if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) {
             pathCopy = new SkPath(*path);
-            mPathMap.add(path, pathCopy);
+            pathCopy->setSourcePath(path);
+            // replaceValueFor() performs an add if the entry doesn't exist
+            mPathMap.replaceValueFor(path, pathCopy);
             mPaths.add(pathCopy);
         }
 
@@ -440,7 +480,8 @@
         SkPaint* paintCopy = mPaintMap.valueFor(paint);
         if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) {
             paintCopy = new SkPaint(*paint);
-            mPaintMap.add(paint, paintCopy);
+            // replaceValueFor() performs an add if the entry doesn't exist
+            mPaintMap.replaceValueFor(paint, paintCopy);
             mPaints.add(paintCopy);
         }
 
@@ -482,7 +523,8 @@
         // TODO: We also need to handle generation ID changes in compose shaders
         if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) {
             shaderCopy = shader->copy();
-            mShaderMap.add(shader, shaderCopy);
+            // replaceValueFor() performs an add if the entry doesn't exist
+            mShaderMap.replaceValueFor(shader, shaderCopy);
             mShaders.add(shaderCopy);
             Caches::getInstance().resourceCache.incrementRefcount(shaderCopy);
         }
@@ -513,6 +555,11 @@
     SkWriter32 mWriter;
 
     int mRestoreSaveCount;
+
+    float mTranslateX;
+    float mTranslateY;
+    bool mHasTranslate;
+
     bool mHasDrawOps;
 
     friend class DisplayList;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index afae70f..55e2ca5 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1321,7 +1321,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
-        Rect& dirty, uint32_t level) {
+        Rect& dirty, int32_t flags, uint32_t level) {
     if (quickReject(0.0f, 0.0f, width, height)) {
         return false;
     }
@@ -1329,7 +1329,7 @@
     // All the usual checks and setup operations (quickReject, setupDraw, etc.)
     // will be performed by the display list itself
     if (displayList && displayList->isRenderable()) {
-        return displayList->replay(*this, dirty, level);
+        return displayList->replay(*this, dirty, flags, level);
     }
 
     return false;
@@ -2189,8 +2189,7 @@
     SkPaint::FontMetrics metrics;
     paint->getFontMetrics(&metrics, 0.0f);
     // If no length was specified, just perform the hit test on the Y axis
-    if (quickReject(x, y + metrics.fTop,
-            x + (length >= 0.0f ? length : INT_MAX / 2), y + metrics.fBottom)) {
+    if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
         return;
     }
 
@@ -2298,6 +2297,7 @@
 
     mCaches.activeTexture(0);
 
+    // TODO: Perform early clip test before we rasterize the path
     const PathTexture* texture = mCaches.pathCache.get(path, paint);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 3c2d09e..3f63c3fe 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -98,7 +98,7 @@
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
     virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
-            Rect& dirty, uint32_t level = 0);
+            Rect& dirty, int32_t flags, uint32_t level = 0);
     virtual void outputDisplayList(DisplayList* displayList, uint32_t level = 0);
     virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index e893f7a..e363b73 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -24,6 +24,23 @@
 namespace android {
 namespace uirenderer {
 
+// Defined in ShapeCache.h
+void computePathBounds(const SkPath *path, const SkPaint* paint,
+        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+    const SkRect& bounds = path->getBounds();
+
+    const float pathWidth = fmax(bounds.width(), 1.0f);
+    const float pathHeight = fmax(bounds.height(), 1.0f);
+
+    left = bounds.fLeft;
+    top = bounds.fTop;
+
+    offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+
+    width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+    height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Path cache
 ///////////////////////////////////////////////////////////////////////////////
@@ -66,9 +83,17 @@
 }
 
 PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
+    const SkPath* sourcePath = path->getSourcePath();
+    if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) {
+        path = const_cast<SkPath*>(sourcePath);
+    }
+
     PathCacheEntry entry(path, paint);
     PathTexture* texture = mCache.get(entry);
 
+    float left, top, offset;
+    uint32_t width, height;
+
     if (!texture) {
         texture = addTexture(entry, path, paint);
     } else if (path->getGenerationID() != texture->generation) {
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
index 30ce690..f180e94 100644
--- a/libs/hwui/ShapeCache.h
+++ b/libs/hwui/ShapeCache.h
@@ -489,18 +489,16 @@
     }
 }
 
+void computePathBounds(const SkPath *path, const SkPaint* paint,
+        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
+
 template<class Entry>
 PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
         const SkPaint* paint) {
-    const SkRect& bounds = path->getBounds();
 
-    const float pathWidth = fmax(bounds.width(), 1.0f);
-    const float pathHeight = fmax(bounds.height(), 1.0f);
-
-    const float offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
-
-    const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
-    const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+    float left, top, offset;
+    uint32_t width, height;
+    computePathBounds(path, paint, left, top, offset, width, height);
 
     if (width > mMaxTextureSize || height > mMaxTextureSize) {
         ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
@@ -517,8 +515,8 @@
     }
 
     PathTexture* texture = new PathTexture;
-    texture->left = bounds.fLeft;
-    texture->top = bounds.fTop;
+    texture->left = left;
+    texture->top = top;
     texture->offset = offset;
     texture->width = width;
     texture->height = height;
@@ -542,7 +540,7 @@
     SkSafeUnref(pathPaint.setXfermode(mode));
 
     SkCanvas canvas(bitmap);
-    canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset);
+    canvas.translate(-left + offset, -top + offset);
     canvas.drawPath(*path, pathPaint);
 
     generateTexture(bitmap, texture);
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index a85362d..de2c674 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -31,6 +31,7 @@
     transform = &mTransformRoot;
     clipRect = &mClipRectRoot;
     region = NULL;
+    clipRegion = NULL;
 }
 
 /**
@@ -42,6 +43,8 @@
         invisible(s->invisible), empty(false),
         viewport(s->viewport), height(s->height) {
 
+    clipRegion = NULL;
+
     if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
         mTransformRoot.load(*s->transform);
         transform = &mTransformRoot;
@@ -52,8 +55,17 @@
     if (saveFlags & SkCanvas::kClip_SaveFlag) {
         mClipRectRoot.set(*s->clipRect);
         clipRect = &mClipRectRoot;
+#if STENCIL_BUFFER_SIZE
+        if (s->clipRegion) {
+            mClipRegionRoot.merge(*s->clipRegion);
+            clipRegion = &mClipRegionRoot;
+        }
+#endif
     } else {
         clipRect = s->clipRect;
+#if STENCIL_BUFFER_SIZE
+        clipRegion = s->clipRegion;
+#endif
     }
 
     if (s->flags & Snapshot::kFlagFboTarget) {
@@ -68,6 +80,77 @@
 // Clipping
 ///////////////////////////////////////////////////////////////////////////////
 
+void Snapshot::ensureClipRegion() {
+#if STENCIL_BUFFER_SIZE
+    if (!clipRegion) {
+        clipRegion = &mClipRegionRoot;
+        android::Rect tmp(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+        clipRegion->set(tmp);
+    }
+#endif
+}
+
+void Snapshot::copyClipRectFromRegion() {
+#if STENCIL_BUFFER_SIZE
+    if (!clipRegion->isEmpty()) {
+        android::Rect bounds(clipRegion->bounds());
+        clipRect->set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+
+        if (clipRegion->isRect()) {
+            clipRegion->clear();
+            clipRegion = NULL;
+        }
+    } else {
+        clipRect->setEmpty();
+        clipRegion = NULL;
+    }
+#endif
+}
+
+bool Snapshot::clipRegionOr(float left, float top, float right, float bottom) {
+#if STENCIL_BUFFER_SIZE
+    android::Rect tmp(left, top, right, bottom);
+    clipRegion->orSelf(tmp);
+    copyClipRectFromRegion();
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool Snapshot::clipRegionXor(float left, float top, float right, float bottom) {
+#if STENCIL_BUFFER_SIZE
+    android::Rect tmp(left, top, right, bottom);
+    clipRegion->xorSelf(tmp);
+    copyClipRectFromRegion();
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool Snapshot::clipRegionAnd(float left, float top, float right, float bottom) {
+#if STENCIL_BUFFER_SIZE
+    android::Rect tmp(left, top, right, bottom);
+    clipRegion->andSelf(tmp);
+    copyClipRectFromRegion();
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool Snapshot::clipRegionNand(float left, float top, float right, float bottom) {
+#if STENCIL_BUFFER_SIZE
+    android::Rect tmp(left, top, right, bottom);
+    clipRegion->subtractSelf(tmp);
+    copyClipRectFromRegion();
+    return true;
+#else
+    return false;
+#endif
+}
+
 bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
     Rect r(left, top, right, bottom);
     transform->mapRect(r);
@@ -77,32 +160,46 @@
 bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
     bool clipped = false;
 
-    // NOTE: The unimplemented operations require support for regions
-    // Supporting regions would require using a stencil buffer instead
-    // of the scissor. The stencil buffer itself is not too expensive
-    // (memory cost excluded) but on fillrate limited devices, managing
-    // the stencil might have a negative impact on the framerate.
     switch (op) {
-        case SkRegion::kDifference_Op:
+        case SkRegion::kDifference_Op: {
+            ensureClipRegion();
+            clipped = clipRegionNand(r.left, r.top, r.right, r.bottom);
             break;
-        case SkRegion::kIntersect_Op:
-            clipped = clipRect->intersect(r);
-            if (!clipped) {
-                clipRect->setEmpty();
-                clipped = true;
+        }
+        case SkRegion::kIntersect_Op: {
+            if (CC_UNLIKELY(clipRegion)) {
+                clipped = clipRegionOr(r.left, r.top, r.right, r.bottom);
+            } else {
+                clipped = clipRect->intersect(r);
+                if (!clipped) {
+                    clipRect->setEmpty();
+                    clipped = true;
+                }
             }
             break;
-        case SkRegion::kUnion_Op:
-            clipped = clipRect->unionWith(r);
+        }
+        case SkRegion::kUnion_Op: {
+            if (CC_UNLIKELY(clipRegion)) {
+                clipped = clipRegionAnd(r.left, r.top, r.right, r.bottom);
+            } else {
+                clipped = clipRect->unionWith(r);
+            }
             break;
-        case SkRegion::kXOR_Op:
+        }
+        case SkRegion::kXOR_Op: {
+            ensureClipRegion();
+            clipped = clipRegionXor(r.left, r.top, r.right, r.bottom);
             break;
-        case SkRegion::kReverseDifference_Op:
+        }
+        case SkRegion::kReverseDifference_Op: {
+            // TODO!!!!!!!
             break;
-        case SkRegion::kReplace_Op:
-            clipRect->set(r);
+        }
+        case SkRegion::kReplace_Op: {
+            setClip(r.left, r.top, r.right, r.bottom);
             clipped = true;
             break;
+        }
     }
 
     if (clipped) {
@@ -114,6 +211,12 @@
 
 void Snapshot::setClip(float left, float top, float right, float bottom) {
     clipRect->set(left, top, right, bottom);
+#if STENCIL_BUFFER_SIZE
+    if (clipRegion) {
+        clipRegion->clear();
+        clipRegion = NULL;
+    }
+#endif
     flags |= Snapshot::kFlagClipSet;
 }
 
@@ -129,8 +232,7 @@
 
 void Snapshot::resetClip(float left, float top, float right, float bottom) {
     clipRect = &mClipRectRoot;
-    clipRect->set(left, top, right, bottom);
-    flags |= Snapshot::kFlagClipSet;
+    setClip(left, top, right, bottom);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index c94af7e..b2bc879 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -181,7 +181,7 @@
     mat4* transform;
 
     /**
-     * Current clip region. The clip is stored in canvas-space coordinates,
+     * Current clip rect. The clip is stored in canvas-space coordinates,
      * (screen-space coordinates in the regular case.)
      *
      * This is a reference to a rect owned by this snapshot or another
@@ -190,6 +190,17 @@
     Rect* clipRect;
 
     /**
+     * Current clip region. The clip is stored in canvas-space coordinates,
+     * (screen-space coordinates in the regular case.)
+     *
+     * This is a reference to a region owned by this snapshot or another
+     * snapshot. This pointer must not be freed. See ::mClipRegionRoot.
+     *
+     * This field is used only if STENCIL_BUFFER_SIZE is > 0.
+     */
+    Region* clipRegion;
+
+    /**
      * The ancestor layer's dirty region.
      *
      * This is a reference to a region owned by a layer. This pointer must
@@ -198,10 +209,22 @@
     Region* region;
 
 private:
+    void ensureClipRegion();
+    void copyClipRectFromRegion();
+
+    bool clipRegionOr(float left, float top, float right, float bottom);
+    bool clipRegionXor(float left, float top, float right, float bottom);
+    bool clipRegionAnd(float left, float top, float right, float bottom);
+    bool clipRegionNand(float left, float top, float right, float bottom);
+
     mat4 mTransformRoot;
     Rect mClipRectRoot;
     Rect mLocalClip;
 
+#if STENCIL_BUFFER_SIZE
+    Region mClipRegionRoot;
+#endif
+
 }; // class Snapshot
 
 }; // namespace uirenderer
diff --git a/libs/rs/Allocation.cpp b/libs/rs/Allocation.cpp
new file mode 100644
index 0000000..46df171
--- /dev/null
+++ b/libs/rs/Allocation.cpp
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#include <utils/Log.h>
+#include <malloc.h>
+
+#include "RenderScript.h"
+#include "Element.h"
+#include "Type.h"
+#include "Allocation.h"
+
+
+void * Allocation::getIDSafe() const {
+    //if (mAdaptedAllocation != NULL) {
+        //return mAdaptedAllocation.getID();
+    //}
+    return getID();
+}
+
+void Allocation::updateCacheInfo(const Type *t) {
+    mCurrentDimX = t->getX();
+    mCurrentDimY = t->getY();
+    mCurrentDimZ = t->getZ();
+    mCurrentCount = mCurrentDimX;
+    if (mCurrentDimY > 1) {
+        mCurrentCount *= mCurrentDimY;
+    }
+    if (mCurrentDimZ > 1) {
+        mCurrentCount *= mCurrentDimZ;
+    }
+}
+
+Allocation::Allocation(void *id, RenderScript *rs, const Type *t, uint32_t usage) : BaseObj(id, rs) {
+    if ((usage & ~(RS_ALLOCATION_USAGE_SCRIPT |
+                   RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE |
+                   RS_ALLOCATION_USAGE_GRAPHICS_VERTEX |
+                   RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS |
+                   RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET |
+                   RS_ALLOCATION_USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE |
+                   RS_ALLOCATION_USAGE_IO_INPUT |
+                   RS_ALLOCATION_USAGE_IO_OUTPUT)) != 0) {
+        ALOGE("Unknown usage specified.");
+    }
+
+    if ((usage & (RS_ALLOCATION_USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE |
+                  RS_ALLOCATION_USAGE_IO_INPUT)) != 0) {
+        mWriteAllowed = false;
+        if ((usage & ~(RS_ALLOCATION_USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE |
+                       RS_ALLOCATION_USAGE_IO_INPUT |
+                       RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE |
+                       RS_ALLOCATION_USAGE_SCRIPT)) != 0) {
+            ALOGE("Invalid usage combination.");
+        }
+    }
+
+    mType = t;
+    mUsage = usage;
+
+    if (t != NULL) {
+        updateCacheInfo(t);
+    }
+}
+
+void Allocation::validateIsInt32() {
+    RsDataType dt = mType->getElement()->getDataType();
+    if ((dt == RS_TYPE_SIGNED_32) || (dt == RS_TYPE_UNSIGNED_32)) {
+        return;
+    }
+    ALOGE("32 bit integer source does not match allocation type %i", dt);
+}
+
+void Allocation::validateIsInt16() {
+    RsDataType dt = mType->getElement()->getDataType();
+    if ((dt == RS_TYPE_SIGNED_16) || (dt == RS_TYPE_UNSIGNED_16)) {
+        return;
+    }
+    ALOGE("16 bit integer source does not match allocation type %i", dt);
+}
+
+void Allocation::validateIsInt8() {
+    RsDataType dt = mType->getElement()->getDataType();
+    if ((dt == RS_TYPE_SIGNED_8) || (dt == RS_TYPE_UNSIGNED_8)) {
+        return;
+    }
+    ALOGE("8 bit integer source does not match allocation type %i", dt);
+}
+
+void Allocation::validateIsFloat32() {
+    RsDataType dt = mType->getElement()->getDataType();
+    if (dt == RS_TYPE_FLOAT_32) {
+        return;
+    }
+    ALOGE("32 bit float source does not match allocation type %i", dt);
+}
+
+void Allocation::validateIsObject() {
+    RsDataType dt = mType->getElement()->getDataType();
+    if ((dt == RS_TYPE_ELEMENT) ||
+        (dt == RS_TYPE_TYPE) ||
+        (dt == RS_TYPE_ALLOCATION) ||
+        (dt == RS_TYPE_SAMPLER) ||
+        (dt == RS_TYPE_SCRIPT) ||
+        (dt == RS_TYPE_MESH) ||
+        (dt == RS_TYPE_PROGRAM_FRAGMENT) ||
+        (dt == RS_TYPE_PROGRAM_VERTEX) ||
+        (dt == RS_TYPE_PROGRAM_RASTER) ||
+        (dt == RS_TYPE_PROGRAM_STORE)) {
+        return;
+    }
+    ALOGE("Object source does not match allocation type %i", dt);
+}
+
+void Allocation::updateFromNative() {
+    BaseObj::updateFromNative();
+
+    const void *typeID = rsaAllocationGetType(mRS->mContext, getID());
+    if(typeID != NULL) {
+        const Type *old = mType;
+        Type *t = new Type((void *)typeID, mRS);
+        t->updateFromNative();
+        updateCacheInfo(t);
+        mType = t;
+        delete old;
+    }
+}
+
+void Allocation::syncAll(RsAllocationUsageType srcLocation) {
+    switch (srcLocation) {
+    case RS_ALLOCATION_USAGE_SCRIPT:
+    case RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS:
+    case RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE:
+    case RS_ALLOCATION_USAGE_GRAPHICS_VERTEX:
+        break;
+    default:
+        ALOGE("Source must be exactly one usage type.");
+    }
+    rsAllocationSyncAll(mRS->mContext, getIDSafe(), srcLocation);
+}
+
+void Allocation::ioSendOutput() {
+    if ((mUsage & RS_ALLOCATION_USAGE_IO_OUTPUT) == 0) {
+        ALOGE("Can only send buffer if IO_OUTPUT usage specified.");
+    }
+    rsAllocationIoSend(mRS->mContext, getID());
+}
+
+void Allocation::ioGetInput() {
+    if ((mUsage & RS_ALLOCATION_USAGE_IO_INPUT) == 0) {
+        ALOGE("Can only send buffer if IO_OUTPUT usage specified.");
+    }
+    rsAllocationIoReceive(mRS->mContext, getID());
+}
+
+/*
+void copyFrom(BaseObj[] d) {
+    mRS.validate();
+    validateIsObject();
+    if (d.length != mCurrentCount) {
+        ALOGE("Array size mismatch, allocation sizeX = " +
+                                             mCurrentCount + ", array length = " + d.length);
+    }
+    int i[] = new int[d.length];
+    for (int ct=0; ct < d.length; ct++) {
+        i[ct] = d[ct].getID();
+    }
+    copy1DRangeFromUnchecked(0, mCurrentCount, i);
+}
+*/
+
+
+/*
+void Allocation::setFromFieldPacker(int xoff, FieldPacker fp) {
+    mRS.validate();
+    int eSize = mType.mElement.getSizeBytes();
+    final byte[] data = fp.getData();
+
+    int count = data.length / eSize;
+    if ((eSize * count) != data.length) {
+        ALOGE("Field packer length " + data.length +
+                                           " not divisible by element size " + eSize + ".");
+    }
+    copy1DRangeFromUnchecked(xoff, count, data);
+}
+
+void setFromFieldPacker(int xoff, int component_number, FieldPacker fp) {
+    mRS.validate();
+    if (component_number >= mType.mElement.mElements.length) {
+        ALOGE("Component_number " + component_number + " out of range.");
+    }
+    if(xoff < 0) {
+        ALOGE("Offset must be >= 0.");
+    }
+
+    final byte[] data = fp.getData();
+    int eSize = mType.mElement.mElements[component_number].getSizeBytes();
+    eSize *= mType.mElement.mArraySizes[component_number];
+
+    if (data.length != eSize) {
+        ALOGE("Field packer sizelength " + data.length +
+                                           " does not match component size " + eSize + ".");
+    }
+
+    mRS.nAllocationElementData1D(getIDSafe(), xoff, mSelectedLOD,
+                                 component_number, data, data.length);
+}
+*/
+
+void Allocation::generateMipmaps() {
+    rsAllocationGenerateMipmaps(mRS->mContext, getID());
+}
+
+void Allocation::copy1DRangeFromUnchecked(uint32_t off, size_t count, const void *data, size_t dataLen) {
+    if(count < 1) {
+        ALOGE("Count must be >= 1.");
+        return;
+    }
+    if((off + count) > mCurrentCount) {
+        ALOGE("Overflow, Available count %zu, got %zu at offset %zu.", mCurrentCount, count, off);
+        return;
+    }
+    if((count * mType->getElement()->getSizeBytes()) > dataLen) {
+        ALOGE("Array too small for allocation type.");
+        return;
+    }
+
+    rsAllocation1DData(mRS->mContext, getIDSafe(), off, mSelectedLOD, count, data, dataLen);
+}
+
+void Allocation::copy1DRangeFrom(uint32_t off, size_t count, const int32_t *d, size_t dataLen) {
+    validateIsInt32();
+    copy1DRangeFromUnchecked(off, count, d, dataLen);
+}
+
+void Allocation::copy1DRangeFrom(uint32_t off, size_t count, const int16_t *d, size_t dataLen) {
+    validateIsInt16();
+    copy1DRangeFromUnchecked(off, count, d, dataLen);
+}
+
+void Allocation::copy1DRangeFrom(uint32_t off, size_t count, const int8_t *d, size_t dataLen) {
+    validateIsInt8();
+    copy1DRangeFromUnchecked(off, count, d, dataLen);
+}
+
+void Allocation::copy1DRangeFrom(uint32_t off, size_t count, const float *d, size_t dataLen) {
+    validateIsFloat32();
+    copy1DRangeFromUnchecked(off, count, d, dataLen);
+}
+
+void Allocation::copy1DRangeFrom(uint32_t off, size_t count, const Allocation *data, uint32_t dataOff) {
+    rsAllocationCopy2DRange(mRS->mContext, getIDSafe(), off, 0,
+                            mSelectedLOD, mSelectedFace,
+                            count, 1, data->getIDSafe(), dataOff, 0,
+                            data->mSelectedLOD, data->mSelectedFace);
+}
+
+void Allocation::validate2DRange(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h) {
+    if (mAdaptedAllocation != NULL) {
+
+    } else {
+        if (((xoff + w) > mCurrentDimX) || ((yoff + h) > mCurrentDimY)) {
+            ALOGE("Updated region larger than allocation.");
+        }
+    }
+}
+
+void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                                 const int8_t *data, size_t dataLen) {
+    validate2DRange(xoff, yoff, w, h);
+    rsAllocation2DData(mRS->mContext, getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace,
+                       w, h, data, dataLen);
+}
+
+void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                                 const int16_t *data, size_t dataLen) {
+    validate2DRange(xoff, yoff, w, h);
+    rsAllocation2DData(mRS->mContext, getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace,
+                       w, h, data, dataLen);
+}
+
+void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                                 const int32_t *data, size_t dataLen) {
+    validate2DRange(xoff, yoff, w, h);
+    rsAllocation2DData(mRS->mContext, getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace,
+                       w, h, data, dataLen);
+}
+
+void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                                 const float *data, size_t dataLen) {
+    validate2DRange(xoff, yoff, w, h);
+    rsAllocation2DData(mRS->mContext, getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace,
+                       w, h, data, dataLen);
+}
+
+void Allocation::copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                                 const Allocation *data, size_t dataLen,
+                                 uint32_t dataXoff, uint32_t dataYoff) {
+    validate2DRange(xoff, yoff, w, h);
+    rsAllocationCopy2DRange(mRS->mContext, getIDSafe(), xoff, yoff,
+                            mSelectedLOD, mSelectedFace,
+                            w, h, data->getIDSafe(), dataXoff, dataYoff,
+                            data->mSelectedLOD, data->mSelectedFace);
+}
+
+/*
+void copyTo(byte[] d) {
+    validateIsInt8();
+    mRS.validate();
+    mRS.nAllocationRead(getID(), d);
+}
+
+void copyTo(short[] d) {
+    validateIsInt16();
+    mRS.validate();
+    mRS.nAllocationRead(getID(), d);
+}
+
+void copyTo(int[] d) {
+    validateIsInt32();
+    mRS.validate();
+    mRS.nAllocationRead(getID(), d);
+}
+
+void copyTo(float[] d) {
+    validateIsFloat32();
+    mRS.validate();
+    mRS.nAllocationRead(getID(), d);
+}
+
+void resize(int dimX) {
+    if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
+        throw new RSInvalidStateException("Resize only support for 1D allocations at this time.");
+    }
+    mRS.nAllocationResize1D(getID(), dimX);
+    mRS.finish();  // Necessary because resize is fifoed and update is async.
+
+    int typeID = mRS.nAllocationGetType(getID());
+    mType = new Type(typeID, mRS);
+    mType.updateFromNative();
+    updateCacheInfo(mType);
+}
+
+void resize(int dimX, int dimY) {
+    if ((mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
+        throw new RSInvalidStateException(
+            "Resize only support for 2D allocations at this time.");
+    }
+    if (mType.getY() == 0) {
+        throw new RSInvalidStateException(
+            "Resize only support for 2D allocations at this time.");
+    }
+    mRS.nAllocationResize2D(getID(), dimX, dimY);
+    mRS.finish();  // Necessary because resize is fifoed and update is async.
+
+    int typeID = mRS.nAllocationGetType(getID());
+    mType = new Type(typeID, mRS);
+    mType.updateFromNative();
+    updateCacheInfo(mType);
+}
+*/
+
+
+Allocation *Allocation::createTyped(RenderScript *rs, const Type *type,
+                        RsAllocationMipmapControl mips, uint32_t usage) {
+    void *id = rsAllocationCreateTyped(rs->mContext, type->getID(), mips, usage, 0);
+    if (id == 0) {
+        ALOGE("Allocation creation failed.");
+        return NULL;
+    }
+    return new Allocation(id, rs, type, usage);
+}
+
+Allocation *Allocation::createTyped(RenderScript *rs, const Type *type,
+                                    RsAllocationMipmapControl mips, uint32_t usage, void *pointer) {
+    void *id = rsAllocationCreateTyped(rs->mContext, type->getID(), mips, usage, (uint32_t)pointer);
+    if (id == 0) {
+        ALOGE("Allocation creation failed.");
+    }
+    return new Allocation(id, rs, type, usage);
+}
+
+Allocation *Allocation::createTyped(RenderScript *rs, const Type *type, uint32_t usage) {
+    return createTyped(rs, type, RS_ALLOCATION_MIPMAP_NONE, usage);
+}
+
+Allocation *Allocation::createSized(RenderScript *rs, const Element *e, size_t count, uint32_t usage) {
+    Type::Builder b(rs, e);
+    b.setX(count);
+    const Type *t = b.create();
+
+    void *id = rsAllocationCreateTyped(rs->mContext, t->getID(), RS_ALLOCATION_MIPMAP_NONE, usage, 0);
+    if (id == 0) {
+        ALOGE("Allocation creation failed.");
+    }
+    return new Allocation(id, rs, t, usage);
+}
+
+
+/*
+SurfaceTexture getSurfaceTexture() {
+    if ((mUsage & USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE) == 0) {
+        throw new RSInvalidStateException("Allocation is not a surface texture.");
+    }
+
+    int id = mRS.nAllocationGetSurfaceTextureID(getID());
+    return new SurfaceTexture(id);
+
+}
+
+void setSurfaceTexture(SurfaceTexture sur) {
+    if ((mUsage & USAGE_IO_OUTPUT) == 0) {
+        throw new RSInvalidStateException("Allocation is not USAGE_IO_OUTPUT.");
+    }
+
+    mRS.validate();
+    mRS.nAllocationSetSurfaceTexture(getID(), sur);
+}
+
+
+static Allocation createFromBitmapResource(RenderScript rs,
+                                                  Resources res,
+                                                  int id,
+                                                  MipmapControl mips,
+                                                  int usage) {
+
+    rs.validate();
+    Bitmap b = BitmapFactory.decodeResource(res, id);
+    Allocation alloc = createFromBitmap(rs, b, mips, usage);
+    b.recycle();
+    return alloc;
+}
+
+static Allocation createFromBitmapResource(RenderScript rs,
+                                                  Resources res,
+                                                  int id) {
+    return createFromBitmapResource(rs, res, id,
+                                    MipmapControl.MIPMAP_NONE,
+                                    USAGE_GRAPHICS_TEXTURE);
+}
+
+static Allocation createFromString(RenderScript rs,
+                                          String str,
+                                          int usage) {
+    rs.validate();
+    byte[] allocArray = NULL;
+    try {
+        allocArray = str.getBytes("UTF-8");
+        Allocation alloc = Allocation.createSized(rs, Element.U8(rs), allocArray.length, usage);
+        alloc.copyFrom(allocArray);
+        return alloc;
+    }
+    catch (Exception e) {
+        throw new RSRuntimeException("Could not convert string to utf-8.");
+    }
+}
+*/
+
diff --git a/libs/rs/Allocation.h b/libs/rs/Allocation.h
new file mode 100644
index 0000000..c9e00a4
--- /dev/null
+++ b/libs/rs/Allocation.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#ifndef __ANDROID_ALLOCATION_H__
+#define __ANDROID_ALLOCATION_H__
+
+#include <pthread.h>
+#include <rs.h>
+
+#include "RenderScript.h"
+#include "Type.h"
+#include "Element.h"
+
+class Allocation : public BaseObj {
+protected:
+    const Type *mType;
+    uint32_t mUsage;
+    Allocation *mAdaptedAllocation;
+
+    bool mConstrainedLOD;
+    bool mConstrainedFace;
+    bool mConstrainedY;
+    bool mConstrainedZ;
+    bool mReadAllowed;
+    bool mWriteAllowed;
+    uint32_t mSelectedY;
+    uint32_t mSelectedZ;
+    uint32_t mSelectedLOD;
+    RsAllocationCubemapFace mSelectedFace;
+
+    uint32_t mCurrentDimX;
+    uint32_t mCurrentDimY;
+    uint32_t mCurrentDimZ;
+    uint32_t mCurrentCount;
+
+
+    void * getIDSafe() const;
+    void updateCacheInfo(const Type *t);
+
+    Allocation(void *id, RenderScript *rs, const Type *t, uint32_t usage);
+
+    void validateIsInt32();
+    void validateIsInt16();
+    void validateIsInt8();
+    void validateIsFloat32();
+    void validateIsObject();
+
+    virtual void updateFromNative();
+
+    void validate2DRange(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h);
+
+public:
+    const Type * getType() {
+        return mType;
+    }
+
+    void syncAll(RsAllocationUsageType srcLocation);
+    void ioSendOutput();
+    void ioGetInput();
+
+    //void copyFrom(BaseObj[] d);
+    //void copyFromUnchecked(int[] d);
+    //void copyFromUnchecked(short[] d);
+    //void copyFromUnchecked(byte[] d);
+    //void copyFromUnchecked(float[] d);
+    //void copyFrom(int[] d);
+    //void copyFrom(short[] d);
+    //void copyFrom(byte[] d);
+    //void copyFrom(float[] d);
+    //void setFromFieldPacker(int xoff, FieldPacker fp);
+    //void setFromFieldPacker(int xoff, int component_number, FieldPacker fp);
+    void generateMipmaps();
+    void copy1DRangeFromUnchecked(uint32_t off, size_t count, const void *data, size_t dataLen);
+    void copy1DRangeFrom(uint32_t off, size_t count, const int32_t* d, size_t dataLen);
+    void copy1DRangeFrom(uint32_t off, size_t count, const int16_t* d, size_t dataLen);
+    void copy1DRangeFrom(uint32_t off, size_t count, const int8_t* d, size_t dataLen);
+    void copy1DRangeFrom(uint32_t off, size_t count, const float* d, size_t dataLen);
+    void copy1DRangeFrom(uint32_t off, size_t count, const Allocation *data, uint32_t dataOff);
+
+    void copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                         const int32_t *data, size_t dataLen);
+    void copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                         const int16_t *data, size_t dataLen);
+    void copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                         const int8_t *data, size_t dataLen);
+    void copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                         const float *data, size_t dataLen);
+    void copy2DRangeFrom(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h,
+                         const Allocation *data, size_t dataLen,
+                         uint32_t dataXoff, uint32_t dataYoff);
+
+    //void copyTo(byte[] d);
+    //void copyTo(short[] d);
+    //void copyTo(int[] d);
+    //void copyTo(float[] d);
+    void resize(int dimX);
+    void resize(int dimX, int dimY);
+
+    static Allocation *createTyped(RenderScript *rs, const Type *type,
+                                   RsAllocationMipmapControl mips, uint32_t usage);
+    static Allocation *createTyped(RenderScript *rs, const Type *type,
+                                   RsAllocationMipmapControl mips, uint32_t usage, void * pointer);
+
+    static Allocation *createTyped(RenderScript *rs, const Type *type,
+                                   uint32_t usage = RS_ALLOCATION_USAGE_SCRIPT);
+    static Allocation *createSized(RenderScript *rs, const Element *e, size_t count,
+                                   uint32_t usage = RS_ALLOCATION_USAGE_SCRIPT);
+    //SurfaceTexture *getSurfaceTexture();
+    //void setSurfaceTexture(SurfaceTexture *sur);
+
+};
+
+#endif
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index 58d3e5c..45ed453 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -89,13 +89,13 @@
 	rsFifoSocket.cpp \
 	rsFileA3D.cpp \
 	rsFont.cpp \
-	rsLocklessFifo.cpp \
 	rsObjectBase.cpp \
 	rsMatrix2x2.cpp \
 	rsMatrix3x3.cpp \
 	rsMatrix4x4.cpp \
 	rsMesh.cpp \
 	rsMutex.cpp \
+	rsPath.cpp \
 	rsProgram.cpp \
 	rsProgramFragment.cpp \
 	rsProgramStore.cpp \
@@ -118,6 +118,7 @@
 	driver/rsdGL.cpp \
 	driver/rsdMesh.cpp \
 	driver/rsdMeshObj.cpp \
+	driver/rsdPath.cpp \
 	driver/rsdProgram.cpp \
 	driver/rsdProgramRaster.cpp \
 	driver/rsdProgramStore.cpp \
@@ -126,9 +127,16 @@
 	driver/rsdSampler.cpp \
 	driver/rsdShader.cpp \
 	driver/rsdShaderCache.cpp \
-	driver/rsdVertexArray.cpp
+	driver/rsdVertexArray.cpp \
+	RenderScript.cpp \
+	BaseObj.cpp \
+	Element.cpp \
+	Type.cpp \
+	Allocation.cpp \
+	Script.cpp \
+	ScriptC.cpp
 
-LOCAL_SHARED_LIBRARIES += libz libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libbcc libbcinfo
+LOCAL_SHARED_LIBRARIES += libz libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libbcc libbcinfo libgui
 
 LOCAL_STATIC_LIBRARIES := libdex libft2
 
@@ -196,13 +204,13 @@
 	rsFifoSocket.cpp \
 	rsFileA3D.cpp \
 	rsFont.cpp \
-	rsLocklessFifo.cpp \
 	rsObjectBase.cpp \
 	rsMatrix2x2.cpp \
 	rsMatrix3x3.cpp \
 	rsMatrix4x4.cpp \
 	rsMesh.cpp \
 	rsMutex.cpp \
+	rsPath.cpp \
 	rsProgram.cpp \
 	rsProgramFragment.cpp \
 	rsProgramStore.cpp \
diff --git a/libs/rs/BaseObj.cpp b/libs/rs/BaseObj.cpp
new file mode 100644
index 0000000..66e6fac
--- /dev/null
+++ b/libs/rs/BaseObj.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+
+#include <rs.h>
+
+#include "RenderScript.h"
+#include "BaseObj.h"
+
+void * BaseObj::getID() const {
+    if (mID == NULL) {
+        ALOGE("Internal error: Object id 0.");
+    }
+    return mID;
+}
+
+void * BaseObj::getObjID(const BaseObj *o) {
+    return o == NULL ? NULL : o->getID();
+}
+
+
+BaseObj::BaseObj(void *id, RenderScript *rs) {
+    mRS = rs;
+    mID = id;
+}
+
+void BaseObj::checkValid() {
+    if (mID == 0) {
+        ALOGE("Invalid object.");
+    }
+}
+
+BaseObj::~BaseObj() {
+    rsObjDestroy(mRS->mContext, mID);
+    mRS = NULL;
+    mID = NULL;
+}
+
+void BaseObj::updateFromNative() {
+    const char *name = NULL;
+    rsaGetName(mRS, mID, &name);
+    mName = name;
+}
+
+bool BaseObj::equals(const BaseObj *obj) {
+    // Early-out check to see if both BaseObjs are actually the same
+    if (this == obj)
+        return true;
+    return mID == obj->mID;
+}
+
+
+
diff --git a/libs/rs/BaseObj.h b/libs/rs/BaseObj.h
new file mode 100644
index 0000000..79761b1
--- /dev/null
+++ b/libs/rs/BaseObj.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#ifndef __ANDROID_BASE_OBJ_H__
+#define __ANDROID_BASE_OBJ_H__
+
+
+#include <pthread.h>
+#include <rs.h>
+
+#include "RenderScript.h"
+
+class BaseObj {
+protected:
+    friend class Element;
+    friend class Type;
+    friend class Allocation;
+    friend class Script;
+    friend class ScriptC;
+
+    void *mID;
+    RenderScript *mRS;
+    android::String8 mName;
+
+    void * getID() const;
+
+    BaseObj(void *id, RenderScript *rs);
+    void checkValid();
+
+    static void * getObjID(const BaseObj *o);
+
+public:
+
+    virtual ~BaseObj();
+    virtual void updateFromNative();
+    virtual bool equals(const BaseObj *obj);
+};
+
+#endif
diff --git a/libs/rs/Element.cpp b/libs/rs/Element.cpp
new file mode 100644
index 0000000..d193892
--- /dev/null
+++ b/libs/rs/Element.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#include <utils/Log.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "RenderScript.h"
+#include "Element.h"
+
+
+const Element * Element::getSubElement(uint32_t index) {
+    if (!mVisibleElementMap.size()) {
+        mRS->throwError("Element contains no sub-elements");
+    }
+    if (index >= mVisibleElementMap.size()) {
+        mRS->throwError("Illegal sub-element index");
+    }
+    return mElements[mVisibleElementMap[index]];
+}
+
+const char * Element::getSubElementName(uint32_t index) {
+    if (!mVisibleElementMap.size()) {
+        mRS->throwError("Element contains no sub-elements");
+    }
+    if (index >= mVisibleElementMap.size()) {
+        mRS->throwError("Illegal sub-element index");
+    }
+    return mElementNames[mVisibleElementMap[index]];
+}
+
+size_t Element::getSubElementArraySize(uint32_t index) {
+    if (!mVisibleElementMap.size()) {
+        mRS->throwError("Element contains no sub-elements");
+    }
+    if (index >= mVisibleElementMap.size()) {
+        mRS->throwError("Illegal sub-element index");
+    }
+    return mArraySizes[mVisibleElementMap[index]];
+}
+
+uint32_t Element::getSubElementOffsetBytes(uint32_t index) {
+    if (mVisibleElementMap.size()) {
+        mRS->throwError("Element contains no sub-elements");
+    }
+    if (index >= mVisibleElementMap.size()) {
+        mRS->throwError("Illegal sub-element index");
+    }
+    return mOffsetInBytes[mVisibleElementMap[index]];
+}
+
+
+#define CREATE_USER(N, T) const Element * Element::N(RenderScript *rs) { \
+    return createUser(rs, RS_TYPE_##T); \
+}
+CREATE_USER(BOOLEAN, BOOLEAN);
+CREATE_USER(U8, UNSIGNED_8);
+CREATE_USER(I8, SIGNED_8);
+CREATE_USER(U16, UNSIGNED_16);
+CREATE_USER(I16, SIGNED_16);
+CREATE_USER(U32, UNSIGNED_32);
+CREATE_USER(I32, SIGNED_32);
+CREATE_USER(U64, UNSIGNED_64);
+CREATE_USER(I64, SIGNED_64);
+CREATE_USER(F32, FLOAT_32);
+CREATE_USER(F64, FLOAT_64);
+CREATE_USER(ELEMENT, ELEMENT);
+CREATE_USER(TYPE, TYPE);
+CREATE_USER(ALLOCATION, ALLOCATION);
+CREATE_USER(SAMPLER, SAMPLER);
+CREATE_USER(SCRIPT, SCRIPT);
+CREATE_USER(MESH, MESH);
+CREATE_USER(PROGRAM_FRAGMENT, PROGRAM_FRAGMENT);
+CREATE_USER(PROGRAM_VERTEX, PROGRAM_VERTEX);
+CREATE_USER(PROGRAM_RASTER, PROGRAM_RASTER);
+CREATE_USER(PROGRAM_STORE, PROGRAM_STORE);
+CREATE_USER(MATRIX_4X4, MATRIX_4X4);
+CREATE_USER(MATRIX_3X3, MATRIX_3X3);
+CREATE_USER(MATRIX_2X2, MATRIX_2X2);
+
+#define CREATE_PIXEL(N, T, K) const Element * Element::N(RenderScript *rs) { \
+    return createPixel(rs, RS_TYPE_##T, RS_KIND_##K); \
+}
+CREATE_PIXEL(A_8, UNSIGNED_8, PIXEL_A);
+CREATE_PIXEL(RGB_565, UNSIGNED_5_6_5, PIXEL_RGB);
+CREATE_PIXEL(RGB_888, UNSIGNED_8, PIXEL_RGB);
+CREATE_PIXEL(RGBA_4444, UNSIGNED_4_4_4_4, PIXEL_RGBA);
+CREATE_PIXEL(RGBA_8888, UNSIGNED_8, PIXEL_RGBA);
+
+#define CREATE_VECTOR(N, T) const Element * Element::N##_2(RenderScript *rs) { \
+    return createVector(rs, RS_TYPE_##T, 2); \
+} \
+const Element * Element::N##_3(RenderScript *rs) { \
+    return createVector(rs, RS_TYPE_##T, 3); \
+} \
+const Element * Element::N##_4(RenderScript *rs) { \
+    return createVector(rs, RS_TYPE_##T, 4); \
+}
+CREATE_VECTOR(U8, UNSIGNED_8);
+CREATE_VECTOR(I8, SIGNED_8);
+CREATE_VECTOR(U16, UNSIGNED_16);
+CREATE_VECTOR(I16, SIGNED_16);
+CREATE_VECTOR(U32, UNSIGNED_32);
+CREATE_VECTOR(I32, SIGNED_32);
+CREATE_VECTOR(U64, UNSIGNED_64);
+CREATE_VECTOR(I64, SIGNED_64);
+CREATE_VECTOR(F32, FLOAT_32);
+CREATE_VECTOR(F64, FLOAT_64);
+
+
+void Element::updateVisibleSubElements() {
+    if (!mElements.size()) {
+        return;
+    }
+    mVisibleElementMap.clear();
+
+    int noPaddingFieldCount = 0;
+    size_t fieldCount = mElementNames.size();
+    // Find out how many elements are not padding
+    for (size_t ct = 0; ct < fieldCount; ct ++) {
+        if (mElementNames[ct].string()[0] != '#') {
+            noPaddingFieldCount ++;
+        }
+    }
+
+    // Make a map that points us at non-padding elements
+    for (size_t ct = 0; ct < fieldCount; ct ++) {
+        if (mElementNames[ct].string()[0] != '#') {
+            mVisibleElementMap.push((uint32_t)ct);
+        }
+    }
+}
+
+Element::Element(void *id, RenderScript *rs,
+                 android::Vector<const Element *> &elements,
+                 android::Vector<android::String8> &elementNames,
+                 android::Vector<uint32_t> &arraySizes) : BaseObj(id, rs) {
+    mSizeBytes = 0;
+    mVectorSize = 1;
+    mElements = elements;
+    mArraySizes = arraySizes;
+    mElementNames = elementNames;
+
+    mType = RS_TYPE_NONE;
+    mKind = RS_KIND_USER;
+
+    for (size_t ct = 0; ct < mElements.size(); ct++ ) {
+        mOffsetInBytes.push(mSizeBytes);
+        mSizeBytes += mElements[ct]->mSizeBytes * mArraySizes[ct];
+    }
+    updateVisibleSubElements();
+}
+
+
+static uint32_t GetSizeInBytesForType(RsDataType dt) {
+    switch(dt) {
+    case RS_TYPE_NONE:
+        return 0;
+    case RS_TYPE_SIGNED_8:
+    case RS_TYPE_UNSIGNED_8:
+    case RS_TYPE_BOOLEAN:
+        return 1;
+
+    case RS_TYPE_FLOAT_16:
+    case RS_TYPE_SIGNED_16:
+    case RS_TYPE_UNSIGNED_16:
+    case RS_TYPE_UNSIGNED_5_6_5:
+    case RS_TYPE_UNSIGNED_5_5_5_1:
+    case RS_TYPE_UNSIGNED_4_4_4_4:
+        return 2;
+
+    case RS_TYPE_FLOAT_32:
+    case RS_TYPE_SIGNED_32:
+    case RS_TYPE_UNSIGNED_32:
+        return 4;
+
+    case RS_TYPE_FLOAT_64:
+    case RS_TYPE_SIGNED_64:
+    case RS_TYPE_UNSIGNED_64:
+        return 8;
+
+    case RS_TYPE_MATRIX_4X4:
+        return 16 * 4;
+    case RS_TYPE_MATRIX_3X3:
+        return 9 * 4;
+    case RS_TYPE_MATRIX_2X2:
+        return 4 * 4;
+
+    case RS_TYPE_TYPE:
+    case RS_TYPE_ALLOCATION:
+    case RS_TYPE_SAMPLER:
+    case RS_TYPE_SCRIPT:
+    case RS_TYPE_MESH:
+    case RS_TYPE_PROGRAM_FRAGMENT:
+    case RS_TYPE_PROGRAM_VERTEX:
+    case RS_TYPE_PROGRAM_RASTER:
+    case RS_TYPE_PROGRAM_STORE:
+        return 4;
+
+    default:
+        break;
+    }
+
+    ALOGE("Missing type %i", dt);
+    return 0;
+}
+
+Element::Element(void *id, RenderScript *rs,
+                 RsDataType dt, RsDataKind dk, bool norm, uint32_t size) :
+    BaseObj(id, rs)
+{
+    uint32_t tsize = GetSizeInBytesForType(dt);
+    if ((dt != RS_TYPE_UNSIGNED_5_6_5) &&
+        (dt != RS_TYPE_UNSIGNED_4_4_4_4) &&
+        (dt != RS_TYPE_UNSIGNED_5_5_5_1)) {
+        if (size == 3) {
+            mSizeBytes = tsize * 4;
+        } else {
+            mSizeBytes = tsize * size;
+        }
+    } else {
+        mSizeBytes = tsize;
+    }
+    mType = dt;
+    mKind = dk;
+    mNormalized = norm;
+    mVectorSize = size;
+}
+
+Element::~Element() {
+}
+
+   /*
+    Element(int id, RenderScript rs) {
+        super(id, rs);
+    }
+    */
+
+void Element::updateFromNative() {
+    BaseObj::updateFromNative();
+/*
+    // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
+    int[] dataBuffer = new int[5];
+    mRS.nElementGetNativeData(getID(), dataBuffer);
+
+    mNormalized = dataBuffer[2] == 1 ? true : false;
+    mVectorSize = dataBuffer[3];
+    mSize = 0;
+    for (DataType dt: DataType.values()) {
+        if(dt.mID == dataBuffer[0]){
+            mType = dt;
+            mSize = mType.mSize * mVectorSize;
+        }
+    }
+    for (DataKind dk: DataKind.values()) {
+        if(dk.mID == dataBuffer[1]){
+            mKind = dk;
+        }
+    }
+
+    int numSubElements = dataBuffer[4];
+    if(numSubElements > 0) {
+        mElements = new Element[numSubElements];
+        mElementNames = new String[numSubElements];
+        mArraySizes = new int[numSubElements];
+        mOffsetInBytes = new int[numSubElements];
+
+        int[] subElementIds = new int[numSubElements];
+        mRS.nElementGetSubElements(getID(), subElementIds, mElementNames, mArraySizes);
+        for(int i = 0; i < numSubElements; i ++) {
+            mElements[i] = new Element(subElementIds[i], mRS);
+            mElements[i].updateFromNative();
+            mOffsetInBytes[i] = mSize;
+            mSize += mElements[i].mSize * mArraySizes[i];
+        }
+    }
+    */
+    updateVisibleSubElements();
+}
+
+const Element * Element::createUser(RenderScript *rs, RsDataType dt) {
+    void * id = rsElementCreate(rs->mContext, dt, RS_KIND_USER, false, 1);
+    return new Element(id, rs, dt, RS_KIND_USER, false, 1);
+}
+
+const Element * Element::createVector(RenderScript *rs, RsDataType dt, uint32_t size) {
+    if (size < 2 || size > 4) {
+        rs->throwError("Vector size out of range 2-4.");
+    }
+    void *id = rsElementCreate(rs->mContext, dt, RS_KIND_USER, false, size);
+    return new Element(id, rs, dt, RS_KIND_USER, false, size);
+}
+
+const Element * Element::createPixel(RenderScript *rs, RsDataType dt, RsDataKind dk) {
+    if (!(dk == RS_KIND_PIXEL_L ||
+          dk == RS_KIND_PIXEL_A ||
+          dk == RS_KIND_PIXEL_LA ||
+          dk == RS_KIND_PIXEL_RGB ||
+          dk == RS_KIND_PIXEL_RGBA ||
+          dk == RS_KIND_PIXEL_DEPTH)) {
+        rs->throwError("Unsupported DataKind");
+    }
+    if (!(dt == RS_TYPE_UNSIGNED_8 ||
+          dt == RS_TYPE_UNSIGNED_16 ||
+          dt == RS_TYPE_UNSIGNED_5_6_5 ||
+          dt == RS_TYPE_UNSIGNED_4_4_4_4 ||
+          dt == RS_TYPE_UNSIGNED_5_5_5_1)) {
+        rs->throwError("Unsupported DataType");
+    }
+    if (dt == RS_TYPE_UNSIGNED_5_6_5 && dk != RS_KIND_PIXEL_RGB) {
+        rs->throwError("Bad kind and type combo");
+    }
+    if (dt == RS_TYPE_UNSIGNED_5_5_5_1 && dk != RS_KIND_PIXEL_RGBA) {
+        rs->throwError("Bad kind and type combo");
+    }
+    if (dt == RS_TYPE_UNSIGNED_4_4_4_4 && dk != RS_KIND_PIXEL_RGBA) {
+        rs->throwError("Bad kind and type combo");
+    }
+    if (dt == RS_TYPE_UNSIGNED_16 && dk != RS_KIND_PIXEL_DEPTH) {
+        rs->throwError("Bad kind and type combo");
+    }
+
+    int size = 1;
+    switch (dk) {
+    case RS_KIND_PIXEL_LA:
+        size = 2;
+        break;
+    case RS_KIND_PIXEL_RGB:
+        size = 3;
+        break;
+    case RS_KIND_PIXEL_RGBA:
+        size = 4;
+        break;
+    case RS_KIND_PIXEL_DEPTH:
+        size = 2;
+        break;
+    default:
+        break;
+    }
+
+    void * id = rsElementCreate(rs->mContext, dt, dk, true, size);
+    return new Element(id, rs, dt, dk, true, size);
+}
+
+bool Element::isCompatible(const Element *e) {
+    // Try strict BaseObj equality to start with.
+    if (this == e) {
+        return true;
+    }
+
+    // Ignore mKind because it is allowed to be different (user vs. pixel).
+    // We also ignore mNormalized because it can be different. The mType
+    // field must be non-null since we require name equivalence for
+    // user-created Elements.
+    return ((mSizeBytes == e->mSizeBytes) &&
+            (mType != NULL) &&
+            (mType == e->mType) &&
+            (mVectorSize == e->mVectorSize));
+}
+
+Element::Builder::Builder(RenderScript *rs) {
+    mRS = rs;
+    mSkipPadding = false;
+}
+
+void Element::Builder::add(const Element *e, android::String8 &name, uint32_t arraySize) {
+    // Skip padding fields after a vector 3 type.
+    if (mSkipPadding) {
+        const char *s1 = "#padding_";
+        const char *s2 = name;
+        size_t len = strlen(s1);
+        if (strlen(s2) >= len) {
+            if (!memcmp(s1, s2, len)) {
+                mSkipPadding = false;
+                return;
+            }
+        }
+    }
+
+    if (e->mVectorSize == 3) {
+        mSkipPadding = true;
+    } else {
+        mSkipPadding = false;
+    }
+
+    mElements.add(e);
+    mElementNames.add(name);
+    mArraySizes.add(arraySize);
+}
+
+const Element * Element::Builder::create() {
+    size_t fieldCount = mElements.size();
+    const char ** nameArray = (const char **)calloc(fieldCount, sizeof(char *));
+    size_t* sizeArray = (size_t*)calloc(fieldCount, sizeof(size_t));
+
+    for (size_t ct = 0; ct < fieldCount; ct++) {
+        nameArray[ct] = mElementNames[ct].string();
+        sizeArray[ct] = mElementNames[ct].length();
+    }
+
+    void *id = rsElementCreate2(mRS->mContext,
+                                (RsElement *)mElements.array(), fieldCount,
+                                nameArray, fieldCount * sizeof(size_t),  sizeArray,
+                                (const uint32_t *)mArraySizes.array(), fieldCount);
+
+
+    free(nameArray);
+    free(sizeArray);
+
+    Element *e = new Element(id, mRS, mElements, mElementNames, mArraySizes);
+    return e;
+}
+
diff --git a/libs/rs/Element.h b/libs/rs/Element.h
new file mode 100644
index 0000000..a579dc3
--- /dev/null
+++ b/libs/rs/Element.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#ifndef __ANDROID_ELEMENT_H__
+#define __ANDROID_ELEMENT_H__
+
+#include <rs.h>
+#include "RenderScript.h"
+#include "BaseObj.h"
+
+class Element : public BaseObj {
+public:
+    /**
+     * Return if a element is too complex for use as a data source for a Mesh or
+     * a Program.
+     *
+     * @return boolean
+     */
+    bool isComplex();
+
+    /**
+    * @hide
+    * @return number of sub-elements in this element
+    */
+    size_t getSubElementCount() {
+        return mVisibleElementMap.size();
+    }
+
+    /**
+    * @hide
+    * @param index index of the sub-element to return
+    * @return sub-element in this element at given index
+    */
+    const Element * getSubElement(uint32_t index);
+
+    /**
+    * @hide
+    * @param index index of the sub-element
+    * @return sub-element in this element at given index
+    */
+    const char * getSubElementName(uint32_t index);
+
+    /**
+    * @hide
+    * @param index index of the sub-element
+    * @return array size of sub-element in this element at given index
+    */
+    size_t getSubElementArraySize(uint32_t index);
+
+    /**
+    * @hide
+    * @param index index of the sub-element
+    * @return offset in bytes of sub-element in this element at given index
+    */
+    uint32_t getSubElementOffsetBytes(uint32_t index);
+
+    /**
+    * @hide
+    * @return element data type
+    */
+    RsDataType getDataType() const {
+        return mType;
+    }
+
+    /**
+    * @hide
+    * @return element data kind
+    */
+    RsDataKind getDataKind() const {
+        return mKind;
+    }
+
+    size_t getSizeBytes() const {
+        return mSizeBytes;
+    }
+
+
+    static const Element * BOOLEAN(RenderScript *rs);
+    static const Element * U8(RenderScript *rs);
+    static const Element * I8(RenderScript *rs);
+    static const Element * U16(RenderScript *rs);
+    static const Element * I16(RenderScript *rs);
+    static const Element * U32(RenderScript *rs);
+    static const Element * I32(RenderScript *rs);
+    static const Element * U64(RenderScript *rs);
+    static const Element * I64(RenderScript *rs);
+    static const Element * F32(RenderScript *rs);
+    static const Element * F64(RenderScript *rs);
+    static const Element * ELEMENT(RenderScript *rs);
+    static const Element * TYPE(RenderScript *rs);
+    static const Element * ALLOCATION(RenderScript *rs);
+    static const Element * SAMPLER(RenderScript *rs);
+    static const Element * SCRIPT(RenderScript *rs);
+    static const Element * MESH(RenderScript *rs);
+    static const Element * PROGRAM_FRAGMENT(RenderScript *rs);
+    static const Element * PROGRAM_VERTEX(RenderScript *rs);
+    static const Element * PROGRAM_RASTER(RenderScript *rs);
+    static const Element * PROGRAM_STORE(RenderScript *rs);
+
+    static const Element * A_8(RenderScript *rs);
+    static const Element * RGB_565(RenderScript *rs);
+    static const Element * RGB_888(RenderScript *rs);
+    static const Element * RGBA_5551(RenderScript *rs);
+    static const Element * RGBA_4444(RenderScript *rs);
+    static const Element * RGBA_8888(RenderScript *rs);
+
+    static const Element * F32_2(RenderScript *rs);
+    static const Element * F32_3(RenderScript *rs);
+    static const Element * F32_4(RenderScript *rs);
+    static const Element * F64_2(RenderScript *rs);
+    static const Element * F64_3(RenderScript *rs);
+    static const Element * F64_4(RenderScript *rs);
+    static const Element * U8_2(RenderScript *rs);
+    static const Element * U8_3(RenderScript *rs);
+    static const Element * U8_4(RenderScript *rs);
+    static const Element * I8_2(RenderScript *rs);
+    static const Element * I8_3(RenderScript *rs);
+    static const Element * I8_4(RenderScript *rs);
+    static const Element * U16_2(RenderScript *rs);
+    static const Element * U16_3(RenderScript *rs);
+    static const Element * U16_4(RenderScript *rs);
+    static const Element * I16_2(RenderScript *rs);
+    static const Element * I16_3(RenderScript *rs);
+    static const Element * I16_4(RenderScript *rs);
+    static const Element * U32_2(RenderScript *rs);
+    static const Element * U32_3(RenderScript *rs);
+    static const Element * U32_4(RenderScript *rs);
+    static const Element * I32_2(RenderScript *rs);
+    static const Element * I32_3(RenderScript *rs);
+    static const Element * I32_4(RenderScript *rs);
+    static const Element * U64_2(RenderScript *rs);
+    static const Element * U64_3(RenderScript *rs);
+    static const Element * U64_4(RenderScript *rs);
+    static const Element * I64_2(RenderScript *rs);
+    static const Element * I64_3(RenderScript *rs);
+    static const Element * I64_4(RenderScript *rs);
+    static const Element * MATRIX_4X4(RenderScript *rs);
+    static const Element * MATRIX_3X3(RenderScript *rs);
+    static const Element * MATRIX_2X2(RenderScript *rs);
+
+    Element(void *id, RenderScript *rs,
+            android::Vector<const Element *> &elements,
+            android::Vector<android::String8> &elementNames,
+            android::Vector<uint32_t> &arraySizes);
+    Element(void *id, RenderScript *rs, RsDataType dt, RsDataKind dk, bool norm, uint32_t size);
+    Element(RenderScript *rs);
+    virtual ~Element();
+
+    void updateFromNative();
+    static const Element * createUser(RenderScript *rs, RsDataType dt);
+    static const Element * createVector(RenderScript *rs, RsDataType dt, uint32_t size);
+    static const Element * createPixel(RenderScript *rs, RsDataType dt, RsDataKind dk);
+    bool isCompatible(const Element *e);
+
+    class Builder {
+    private:
+        RenderScript *mRS;
+        android::Vector<const Element *> mElements;
+        android::Vector<android::String8> mElementNames;
+        android::Vector<uint32_t> mArraySizes;
+        bool mSkipPadding;
+
+    public:
+        Builder(RenderScript *rs);
+        ~Builder();
+        void add(const Element *, android::String8 &name, uint32_t arraySize = 1);
+        const Element * create();
+    };
+
+private:
+    void updateVisibleSubElements();
+
+    android::Vector<const Element *> mElements;
+    android::Vector<android::String8> mElementNames;
+    android::Vector<uint32_t> mArraySizes;
+    android::Vector<uint32_t> mVisibleElementMap;
+    android::Vector<uint32_t> mOffsetInBytes;
+
+    RsDataType mType;
+    RsDataKind mKind;
+    bool mNormalized;
+    size_t mSizeBytes;
+    size_t mVectorSize;
+};
+
+#endif
diff --git a/libs/rs/RenderScript.cpp b/libs/rs/RenderScript.cpp
new file mode 100644
index 0000000..39f1024
--- /dev/null
+++ b/libs/rs/RenderScript.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#include <utils/Log.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "RenderScript.h"
+
+bool RenderScript::gInitialized = false;
+pthread_mutex_t RenderScript::gInitMutex = PTHREAD_MUTEX_INITIALIZER;
+
+RenderScript::RenderScript() {
+    mDev = NULL;
+    mContext = NULL;
+    mErrorFunc = NULL;
+    mMessageFunc = NULL;
+    mMessageRun = false;
+
+    memset(&mElements, 0, sizeof(mElements));
+}
+
+RenderScript::~RenderScript() {
+    mMessageRun = false;
+
+    rsContextDeinitToClient(mContext);
+
+    void *res = NULL;
+    int status = pthread_join(mMessageThreadId, &res);
+
+    rsContextDestroy(mContext);
+    mContext = NULL;
+    rsDeviceDestroy(mDev);
+    mDev = NULL;
+}
+
+bool RenderScript::init(int targetApi) {
+    mDev = rsDeviceCreate();
+    if (mDev == 0) {
+        ALOGE("Device creation failed");
+        return false;
+    }
+
+    mContext = rsContextCreate(mDev, 0, targetApi);
+    if (mContext == 0) {
+        ALOGE("Context creation failed");
+        return false;
+    }
+
+
+    pid_t mNativeMessageThreadId;
+
+    int status = pthread_create(&mMessageThreadId, NULL, threadProc, this);
+    if (status) {
+        ALOGE("Failed to start RenderScript message thread.");
+        return false;
+    }
+    // Wait for the message thread to be active.
+    while (!mMessageRun) {
+        usleep(1000);
+    }
+
+    return true;
+}
+
+void RenderScript::throwError(const char *err) const {
+    ALOGE("RS CPP error: %s", err);
+    int * v = NULL;
+    v[0] = 0;
+}
+
+
+void * RenderScript::threadProc(void *vrsc) {
+    RenderScript *rs = static_cast<RenderScript *>(vrsc);
+    size_t rbuf_size = 256;
+    void * rbuf = malloc(rbuf_size);
+
+    rsContextInitToClient(rs->mContext);
+    rs->mMessageRun = true;
+
+    while (rs->mMessageRun) {
+        size_t receiveLen = 0;
+        uint32_t usrID = 0;
+        uint32_t subID = 0;
+        RsMessageToClientType r = rsContextPeekMessage(rs->mContext,
+                                                       &receiveLen, sizeof(receiveLen),
+                                                       &usrID, sizeof(usrID));
+
+        if (receiveLen >= rbuf_size) {
+            rbuf_size = receiveLen + 32;
+            rbuf = realloc(rbuf, rbuf_size);
+        }
+        if (!rbuf) {
+            ALOGE("RenderScript::message handler realloc error %zu", rbuf_size);
+            // No clean way to recover now?
+        }
+        rsContextGetMessage(rs->mContext, rbuf, rbuf_size, &receiveLen, sizeof(receiveLen),
+                            &subID, sizeof(subID));
+
+        switch(r) {
+        case RS_MESSAGE_TO_CLIENT_ERROR:
+            ALOGE("RS Error %s", (const char *)rbuf);
+
+            if(rs->mMessageFunc != NULL) {
+                rs->mErrorFunc(usrID, (const char *)rbuf);
+            }
+            break;
+        case RS_MESSAGE_TO_CLIENT_EXCEPTION:
+            // teardown. But we want to avoid starving other threads during
+            // teardown by yielding until the next line in the destructor can
+            // execute to set mRun = false
+            usleep(1000);
+            break;
+        case RS_MESSAGE_TO_CLIENT_USER:
+            if(rs->mMessageFunc != NULL) {
+                rs->mMessageFunc(usrID, rbuf, receiveLen);
+            } else {
+                ALOGE("Received a message from the script with no message handler installed.");
+            }
+            break;
+
+        default:
+            ALOGE("RenderScript unknown message type %i", r);
+        }
+    }
+
+    if (rbuf) {
+        free(rbuf);
+    }
+    ALOGE("RenderScript Message thread exiting.");
+    return NULL;
+}
+
+void RenderScript::setErrorHandler(ErrorHandlerFunc_t func) {
+    mErrorFunc = func;
+}
+
+void RenderScript::setMessageHandler(MessageHandlerFunc_t func) {
+    mMessageFunc  = func;
+}
+
+void RenderScript::contextDump() {
+}
+
+void RenderScript::finish() {
+
+}
+
+
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 6d54268..0eb6a6d 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2008-2012 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.
@@ -14,58 +14,143 @@
  * limitations under the License.
  */
 
-#ifndef RENDER_SCRIPT_H
-#define RENDER_SCRIPT_H
+#ifndef ANDROID_RENDERSCRIPT_H
+#define ANDROID_RENDERSCRIPT_H
 
-#include <stdint.h>
-#include <sys/types.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <pthread.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
 
-#include "RenderScriptDefines.h"
+#include "rs.h"
 
-//
-// A3D loading and object update code.
-// Should only be called at object creation, not thread safe
-RsObjectBase rsaFileA3DGetEntryByIndex(RsContext, uint32_t idx, RsFile);
-RsFile rsaFileA3DCreateFromMemory(RsContext, const void *data, uint32_t len);
-RsFile rsaFileA3DCreateFromAsset(RsContext, void *asset);
-RsFile rsaFileA3DCreateFromFile(RsContext, const char *path);
-void rsaFileA3DGetNumIndexEntries(RsContext, int32_t *numEntries, RsFile);
-void rsaFileA3DGetIndexEntries(RsContext, RsFileIndexEntry *fileEntries,
-                               uint32_t numEntries, RsFile);
-void rsaGetName(RsContext, void * obj, const char **name);
-// Mesh update functions
-void rsaMeshGetVertexBufferCount(RsContext, RsMesh, int32_t *vtxCount);
-void rsaMeshGetIndexCount(RsContext, RsMesh, int32_t *idxCount);
-void rsaMeshGetVertices(RsContext, RsMesh, RsAllocation *vtxData, uint32_t vtxDataCount);
-void rsaMeshGetIndices(RsContext, RsMesh, RsAllocation *va,
-                       uint32_t *primType, uint32_t idxDataCount);
-// Allocation update
-const void* rsaAllocationGetType(RsContext con, RsAllocation va);
-// Type update
-void rsaTypeGetNativeData(RsContext, RsType, uint32_t *typeData, uint32_t typeDataSize);
-// Element update
-void rsaElementGetNativeData(RsContext, RsElement, uint32_t *elemData, uint32_t elemDataSize);
-void rsaElementGetSubElements(RsContext, RsElement, uint32_t *ids, const char **names,
-                              uint32_t *arraySizes, uint32_t dataSize);
+class Element;
+class Type;
+class Allocation;
 
-RsDevice rsDeviceCreate();
-void rsDeviceDestroy(RsDevice dev);
-void rsDeviceSetConfig(RsDevice dev, RsDeviceParam p, int32_t value);
-RsContext rsContextCreate(RsDevice dev, uint32_t version, uint32_t sdkVersion);
-RsContext rsContextCreateGL(RsDevice dev, uint32_t version, uint32_t sdkVersion,
-                            RsSurfaceConfig sc, uint32_t dpi);
+class RenderScript {
+    friend class BaseObj;
+    friend class Allocation;
+    friend class Element;
+    friend class Type;
+    friend class Script;
+    friend class ScriptC;
 
-#include "rsgApiFuncDecl.h"
+public:
+    RenderScript();
+    virtual ~RenderScript();
 
-#ifdef __cplusplus
+    typedef void (*ErrorHandlerFunc_t)(uint32_t errorNum, const char *errorText);
+    typedef void (*MessageHandlerFunc_t)(uint32_t msgNum, const void *msgData, size_t msgLen);
+
+
+    void setErrorHandler(ErrorHandlerFunc_t func);
+    ErrorHandlerFunc_t getErrorHandler() {return mErrorFunc;}
+
+    void setMessageHandler(MessageHandlerFunc_t func);
+    MessageHandlerFunc_t getMessageHandler() {return mMessageFunc;}
+
+    bool init(int targetApi);
+    void contextDump();
+    void finish();
+
+private:
+    static bool gInitialized;
+    static pthread_mutex_t gInitMutex;
+
+    pthread_t mMessageThreadId;
+    pid_t mNativeMessageThreadId;
+    bool mMessageRun;
+
+    RsDevice mDev;
+    RsContext mContext;
+
+    ErrorHandlerFunc_t mErrorFunc;
+    MessageHandlerFunc_t mMessageFunc;
+
+    struct {
+        Element *U8;
+        Element *I8;
+        Element *U16;
+        Element *I16;
+        Element *U32;
+        Element *I32;
+        Element *U64;
+        Element *I64;
+        Element *F32;
+        Element *F64;
+        Element *BOOLEAN;
+
+        Element *ELEMENT;
+        Element *TYPE;
+        Element *ALLOCATION;
+        Element *SAMPLER;
+        Element *SCRIPT;
+        Element *MESH;
+        Element *PROGRAM_FRAGMENT;
+        Element *PROGRAM_VERTEX;
+        Element *PROGRAM_RASTER;
+        Element *PROGRAM_STORE;
+
+        Element *A_8;
+        Element *RGB_565;
+        Element *RGB_888;
+        Element *RGBA_5551;
+        Element *RGBA_4444;
+        Element *RGBA_8888;
+
+        Element *FLOAT_2;
+        Element *FLOAT_3;
+        Element *FLOAT_4;
+
+        Element *DOUBLE_2;
+        Element *DOUBLE_3;
+        Element *DOUBLE_4;
+
+        Element *UCHAR_2;
+        Element *UCHAR_3;
+        Element *UCHAR_4;
+
+        Element *CHAR_2;
+        Element *CHAR_3;
+        Element *CHAR_4;
+
+        Element *USHORT_2;
+        Element *USHORT_3;
+        Element *USHORT_4;
+
+        Element *SHORT_2;
+        Element *SHORT_3;
+        Element *SHORT_4;
+
+        Element *UINT_2;
+        Element *UINT_3;
+        Element *UINT_4;
+
+        Element *INT_2;
+        Element *INT_3;
+        Element *INT_4;
+
+        Element *ULONG_2;
+        Element *ULONG_3;
+        Element *ULONG_4;
+
+        Element *LONG_2;
+        Element *LONG_3;
+        Element *LONG_4;
+
+        Element *MATRIX_4X4;
+        Element *MATRIX_3X3;
+        Element *MATRIX_2X2;
+    } mElements;
+
+
+
+    void throwError(const char *err) const;
+
+    static void * threadProc(void *);
+
 };
+
 #endif
 
-#endif // RENDER_SCRIPT_H
-
-
-
diff --git a/libs/rs/Script.cpp b/libs/rs/Script.cpp
new file mode 100644
index 0000000..b6112dd
--- /dev/null
+++ b/libs/rs/Script.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#include <utils/Log.h>
+#include <malloc.h>
+
+#include "RenderScript.h"
+#include "Element.h"
+#include "Type.h"
+#include "Allocation.h"
+#include "Script.h"
+
+void Script::invoke(uint32_t slot, const void *v, size_t len) {
+    rsScriptInvokeV(mRS->mContext, getID(), slot, v, len);
+}
+
+void Script::forEach(uint32_t slot, const Allocation *ain, const Allocation *aout,
+                       const void *usr, size_t usrLen) {
+    if ((ain == NULL) && (aout == NULL)) {
+        mRS->throwError("At least one of ain or aout is required to be non-null.");
+    }
+    void *in_id = BaseObj::getObjID(ain);
+    void *out_id = BaseObj::getObjID(aout);
+    rsScriptForEach(mRS->mContext, getID(), slot, in_id, out_id, usr, usrLen);
+}
+
+
+Script::Script(void *id, RenderScript *rs) : BaseObj(id, rs) {
+}
+
+
+void Script::bindAllocation(const Allocation *va, uint32_t slot) {
+    rsScriptBindAllocation(mRS->mContext, getID(), BaseObj::getObjID(va), slot);
+}
+
+
+void Script::setVar(uint32_t index, const BaseObj *o) {
+    rsScriptSetVarObj(mRS->mContext, getID(), index, (o == NULL) ? 0 : o->getID());
+}
+
+void Script::setVar(uint32_t index, const void *v, size_t len) {
+    rsScriptSetVarV(mRS->mContext, getID(), index, v, len);
+}
+
+
+
+void Script::FieldBase::init(RenderScript *rs, uint32_t dimx, uint32_t usages) {
+    mAllocation = Allocation::createSized(rs, mElement, dimx, RS_ALLOCATION_USAGE_SCRIPT | usages);
+}
+
+//Script::FieldBase::FieldBase() {
+//}
+
+
diff --git a/libs/rs/Script.h b/libs/rs/Script.h
new file mode 100644
index 0000000..54d1e40
--- /dev/null
+++ b/libs/rs/Script.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#ifndef __ANDROID_SCRIPT_H__
+#define __ANDROID_SCRIPT_H__
+
+#include <pthread.h>
+#include <rs.h>
+
+#include "RenderScript.h"
+#include "Allocation.h"
+
+class Type;
+class Element;
+class Allocation;
+
+class Script : public BaseObj {
+protected:
+    Script(void *id, RenderScript *rs);
+    void forEach(uint32_t slot, const Allocation *in, const Allocation *out, const void *v, size_t);
+    void bindAllocation(const Allocation *va, uint32_t slot);
+    void setVar(uint32_t index, const void *, size_t len);
+    void setVar(uint32_t index, const BaseObj *o);
+    void invoke(uint32_t slot, const void *v, size_t len);
+
+
+    void invoke(uint32_t slot) {
+        invoke(slot, NULL, 0);
+    }
+    void setVar(uint32_t index, float v) {
+        setVar(index, &v, sizeof(v));
+    }
+    void setVar(uint32_t index, double v) {
+        setVar(index, &v, sizeof(v));
+    }
+    void setVar(uint32_t index, int32_t v) {
+        setVar(index, &v, sizeof(v));
+    }
+    void setVar(uint32_t index, int64_t v) {
+        setVar(index, &v, sizeof(v));
+    }
+    void setVar(uint32_t index, bool v) {
+        setVar(index, &v, sizeof(v));
+    }
+
+public:
+    class FieldBase {
+    protected:
+        const Element *mElement;
+        Allocation *mAllocation;
+
+        void init(RenderScript *rs, uint32_t dimx, uint32_t usages = 0);
+
+    public:
+        const Element *getElement() {
+            return mElement;
+        }
+
+        const Type *getType() {
+            return mAllocation->getType();
+        }
+
+        const Allocation *getAllocation() {
+            return mAllocation;
+        }
+
+        //void updateAllocation();
+    };
+};
+
+#endif
diff --git a/libs/rs/ScriptC.cpp b/libs/rs/ScriptC.cpp
new file mode 100644
index 0000000..53d75b8
--- /dev/null
+++ b/libs/rs/ScriptC.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#include <utils/Log.h>
+#include <malloc.h>
+
+#include "ScriptC.h"
+
+ScriptC::ScriptC(RenderScript *rs,
+                 const char *codeTxt, size_t codeLength,
+                 const char *cachedName, size_t cachedNameLength,
+                 const char *cacheDir, size_t cacheDirLength)
+: Script(NULL, rs) {
+    mID = rsScriptCCreate(rs->mContext, cachedName, cachedNameLength,
+                          cacheDir, cacheDirLength, codeTxt, codeLength);
+}
+
diff --git a/libs/rs/ScriptC.h b/libs/rs/ScriptC.h
new file mode 100644
index 0000000..25f00ec
--- /dev/null
+++ b/libs/rs/ScriptC.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008-2012 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.
+ */
+
+#ifndef __ANDROID_SCRIPTC_H__
+#define __ANDROID_SCRIPTC_H__
+
+#include <pthread.h>
+#include <rs.h>
+
+#include "Script.h"
+
+class ScriptC : public Script {
+protected:
+    ScriptC(RenderScript *rs, void *txt, size_t len);
+    ScriptC(RenderScript *rs,
+            const char *codeTxt, size_t codeLength,
+            const char *cachedName, size_t cachedNameLength,
+            const char *cacheDir, size_t cacheDirLength);
+
+};
+
+#endif
diff --git a/libs/rs/Type.cpp b/libs/rs/Type.cpp
new file mode 100644
index 0000000..3249f97
--- /dev/null
+++ b/libs/rs/Type.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#include <utils/Log.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "RenderScript.h"
+#include "Element.h"
+#include "Type.h"
+
+void Type::calcElementCount() {
+    bool hasLod = hasMipmaps();
+    uint32_t x = getX();
+    uint32_t y = getY();
+    uint32_t z = getZ();
+    uint32_t faces = 1;
+    if (hasFaces()) {
+        faces = 6;
+    }
+    if (x == 0) {
+        x = 1;
+    }
+    if (y == 0) {
+        y = 1;
+    }
+    if (z == 0) {
+        z = 1;
+    }
+
+    uint32_t count = x * y * z * faces;
+    while (hasLod && ((x > 1) || (y > 1) || (z > 1))) {
+        if(x > 1) {
+            x >>= 1;
+        }
+        if(y > 1) {
+            y >>= 1;
+        }
+        if(z > 1) {
+            z >>= 1;
+        }
+
+        count += x * y * z * faces;
+    }
+    mElementCount = count;
+}
+
+
+Type::Type(void *id, RenderScript *rs) : BaseObj(id, rs) {
+    mDimX = 0;
+    mDimY = 0;
+    mDimZ = 0;
+    mDimMipmaps = false;
+    mDimFaces = false;
+    mElement = NULL;
+}
+
+void Type::updateFromNative() {
+    // We have 6 integer to obtain mDimX; mDimY; mDimZ;
+    // mDimLOD; mDimFaces; mElement;
+
+    /*
+    int[] dataBuffer = new int[6];
+    mRS.nTypeGetNativeData(getID(), dataBuffer);
+
+    mDimX = dataBuffer[0];
+    mDimY = dataBuffer[1];
+    mDimZ = dataBuffer[2];
+    mDimMipmaps = dataBuffer[3] == 1 ? true : false;
+    mDimFaces = dataBuffer[4] == 1 ? true : false;
+
+    int elementID = dataBuffer[5];
+    if(elementID != 0) {
+        mElement = new Element(elementID, mRS);
+        mElement.updateFromNative();
+    }
+    calcElementCount();
+    */
+}
+
+Type::Builder::Builder(RenderScript *rs, const Element *e) {
+    mRS = rs;
+    mElement = e;
+    mDimX = 0;
+    mDimY = 0;
+    mDimZ = 0;
+    mDimMipmaps = false;
+    mDimFaces = false;
+}
+
+void Type::Builder::setX(uint32_t value) {
+    if(value < 1) {
+        ALOGE("Values of less than 1 for Dimension X are not valid.");
+    }
+    mDimX = value;
+}
+
+void Type::Builder::setY(int value) {
+    if(value < 1) {
+        ALOGE("Values of less than 1 for Dimension Y are not valid.");
+    }
+    mDimY = value;
+}
+
+void Type::Builder::setMipmaps(bool value) {
+    mDimMipmaps = value;
+}
+
+void Type::Builder::setFaces(bool value) {
+    mDimFaces = value;
+}
+
+const Type * Type::Builder::create() {
+    ALOGE(" %i %i %i %i %i", mDimX, mDimY, mDimZ, mDimFaces, mDimMipmaps);
+    if (mDimZ > 0) {
+        if ((mDimX < 1) || (mDimY < 1)) {
+            ALOGE("Both X and Y dimension required when Z is present.");
+        }
+        if (mDimFaces) {
+            ALOGE("Cube maps not supported with 3D types.");
+        }
+    }
+    if (mDimY > 0) {
+        if (mDimX < 1) {
+            ALOGE("X dimension required when Y is present.");
+        }
+    }
+    if (mDimFaces) {
+        if (mDimY < 1) {
+            ALOGE("Cube maps require 2D Types.");
+        }
+    }
+
+    void * id = rsTypeCreate(mRS->mContext, mElement->getID(), mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces);
+    Type *t = new Type(id, mRS);
+    t->mElement = mElement;
+    t->mDimX = mDimX;
+    t->mDimY = mDimY;
+    t->mDimZ = mDimZ;
+    t->mDimMipmaps = mDimMipmaps;
+    t->mDimFaces = mDimFaces;
+
+    t->calcElementCount();
+    return t;
+}
+
diff --git a/libs/rs/Type.h b/libs/rs/Type.h
new file mode 100644
index 0000000..53481c3
--- /dev/null
+++ b/libs/rs/Type.h
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#ifndef __ANDROID_TYPE_H__
+#define __ANDROID_TYPE_H__
+
+#include <rs.h>
+#include "RenderScript.h"
+#include "Element.h"
+
+class Type : public BaseObj {
+protected:
+    friend class Allocation;
+
+    uint32_t mDimX;
+    uint32_t mDimY;
+    uint32_t mDimZ;
+    bool mDimMipmaps;
+    bool mDimFaces;
+    size_t mElementCount;
+    const Element *mElement;
+
+    void calcElementCount();
+    virtual void updateFromNative();
+
+public:
+
+    const Element* getElement() const {
+        return mElement;
+    }
+
+    uint32_t getX() const {
+        return mDimX;
+    }
+
+    uint32_t getY() const {
+        return mDimY;
+    }
+
+    uint32_t getZ() const {
+        return mDimZ;
+    }
+
+    bool hasMipmaps() const {
+        return mDimMipmaps;
+    }
+
+    bool hasFaces() const {
+        return mDimFaces;
+    }
+
+    size_t getCount() const {
+        return mElementCount;
+    }
+
+    size_t getSizeBytes() const {
+        return mElementCount * mElement->getSizeBytes();
+    }
+
+
+    Type(void *id, RenderScript *rs);
+
+
+    class Builder {
+    protected:
+        RenderScript *mRS;
+        uint32_t mDimX;
+        uint32_t mDimY;
+        uint32_t mDimZ;
+        bool mDimMipmaps;
+        bool mDimFaces;
+        const Element *mElement;
+
+    public:
+        Builder(RenderScript *rs, const Element *e);
+
+        void setX(uint32_t value);
+        void setY(int value);
+        void setMipmaps(bool value);
+        void setFaces(bool value);
+        const Type * create();
+    };
+
+};
+
+#endif
diff --git a/libs/rs/driver/rsdAllocation.cpp b/libs/rs/driver/rsdAllocation.cpp
index 1f70e66..fb93d82 100644
--- a/libs/rs/driver/rsdAllocation.cpp
+++ b/libs/rs/driver/rsdAllocation.cpp
@@ -23,6 +23,11 @@
 
 #include "rsAllocation.h"
 
+#include "system/window.h"
+#include "hardware/gralloc.h"
+#include "ui/Rect.h"
+#include "ui/GraphicBufferMapper.h"
+
 #include <GLES/gl.h>
 #include <GLES2/gl2.h>
 #include <GLES/glext.h>
@@ -134,6 +139,13 @@
 static void UploadToTexture(const Context *rsc, const Allocation *alloc) {
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
+    if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE) {
+        if (!drv->textureID) {
+            RSD_CALL_GL(glGenTextures, 1, &drv->textureID);
+        }
+        return;
+    }
+
     if (!drv->glType || !drv->glFormat) {
         return;
     }
@@ -212,10 +224,14 @@
         return false;
     }
 
-    void * ptr = malloc(alloc->mHal.state.type->getSizeBytes());
-    if (!ptr) {
-        free(drv);
-        return false;
+    void * ptr = alloc->mHal.state.usrPtr;
+    if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT) {
+    } else {
+        ptr = malloc(alloc->mHal.state.type->getSizeBytes());
+        if (!ptr) {
+            free(drv);
+            return false;
+        }
     }
 
     drv->glTarget = GL_NONE;
@@ -238,7 +254,7 @@
     alloc->mHal.drvState.mallocPtr = ptr;
     drv->mallocPtr = (uint8_t *)ptr;
     alloc->mHal.drv = drv;
-    if (forceZero) {
+    if (forceZero && ptr) {
         memset(ptr, 0, alloc->mHal.state.type->getSizeBytes());
     }
 
@@ -269,7 +285,7 @@
         drv->renderTargetID = 0;
     }
 
-    if (drv->mallocPtr) {
+    if (drv->mallocPtr && !alloc->mHal.state.usrPtr) {
         free(drv->mallocPtr);
         drv->mallocPtr = NULL;
     }
@@ -370,9 +386,102 @@
     drv->uploadDeferred = true;
 }
 
+int32_t rsdAllocationInitSurfaceTexture(const Context *rsc, const Allocation *alloc) {
+    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+    UploadToTexture(rsc, alloc);
+    return drv->textureID;
+}
+
+static bool IoGetBuffer(const Context *rsc, Allocation *alloc, ANativeWindow *nw) {
+    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+
+    int32_t r = nw->dequeueBuffer(nw, &drv->wndBuffer);
+    if (r) {
+        rsc->setError(RS_ERROR_DRIVER, "Error getting next IO output buffer.");
+        return false;
+    }
+
+    // This lock is implicitly released by the queue buffer in IoSend
+    r = nw->lockBuffer(nw, drv->wndBuffer);
+    if (r) {
+        rsc->setError(RS_ERROR_DRIVER, "Error locking next IO output buffer.");
+        return false;
+    }
+
+    // Must lock the whole surface
+    GraphicBufferMapper &mapper = GraphicBufferMapper::get();
+    Rect bounds(drv->wndBuffer->width, drv->wndBuffer->height);
+
+    void *dst = NULL;
+    mapper.lock(drv->wndBuffer->handle,
+            GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN,
+            bounds, &dst);
+    alloc->mHal.drvState.mallocPtr = dst;
+    return true;
+}
+
+void rsdAllocationSetSurfaceTexture(const Context *rsc, Allocation *alloc, ANativeWindow *nw) {
+    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+
+    //ALOGE("rsdAllocationSetSurfaceTexture %p  %p", alloc, nw);
+
+    // Cleanup old surface if there is one.
+    if (alloc->mHal.state.wndSurface) {
+        ANativeWindow *old = alloc->mHal.state.wndSurface;
+        GraphicBufferMapper &mapper = GraphicBufferMapper::get();
+        mapper.unlock(drv->wndBuffer->handle);
+        old->queueBuffer(old, drv->wndBuffer);
+    }
+
+    if (nw != NULL) {
+        int32_t r;
+        r = native_window_set_usage(nw, GRALLOC_USAGE_SW_READ_RARELY |
+                                        GRALLOC_USAGE_SW_WRITE_OFTEN);
+        if (r) {
+            rsc->setError(RS_ERROR_DRIVER, "Error setting IO output buffer usage.");
+            return;
+        }
+
+        r = native_window_set_buffers_dimensions(nw, alloc->mHal.state.dimensionX,
+                                                 alloc->mHal.state.dimensionY);
+        if (r) {
+            rsc->setError(RS_ERROR_DRIVER, "Error setting IO output buffer dimensions.");
+            return;
+        }
+
+        r = native_window_set_buffer_count(nw, 3);
+        if (r) {
+            rsc->setError(RS_ERROR_DRIVER, "Error setting IO output buffer count.");
+            return;
+        }
+
+        IoGetBuffer(rsc, alloc, nw);
+    }
+}
+
+void rsdAllocationIoSend(const Context *rsc, Allocation *alloc) {
+    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+    ANativeWindow *nw = alloc->mHal.state.wndSurface;
+
+    GraphicBufferMapper &mapper = GraphicBufferMapper::get();
+    mapper.unlock(drv->wndBuffer->handle);
+    int32_t r = nw->queueBuffer(nw, drv->wndBuffer);
+    if (r) {
+        rsc->setError(RS_ERROR_DRIVER, "Error sending IO output buffer.");
+        return;
+    }
+
+    IoGetBuffer(rsc, alloc, nw);
+}
+
+void rsdAllocationIoReceive(const Context *rsc, Allocation *alloc) {
+    ALOGE("not implemented");
+}
+
+
 void rsdAllocationData1D(const Context *rsc, const Allocation *alloc,
                          uint32_t xoff, uint32_t lod, uint32_t count,
-                         const void *data, uint32_t sizeBytes) {
+                         const void *data, size_t sizeBytes) {
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
     const uint32_t eSize = alloc->mHal.state.type->getElementSizeBytes();
@@ -391,7 +500,7 @@
 
 void rsdAllocationData2D(const Context *rsc, const Allocation *alloc,
                          uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face,
-                         uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) {
+                         uint32_t w, uint32_t h, const void *data, size_t sizeBytes) {
     DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
 
     uint32_t eSize = alloc->mHal.state.elementSizeBytes;
diff --git a/libs/rs/driver/rsdAllocation.h b/libs/rs/driver/rsdAllocation.h
index 4fc4419..e3a5126 100644
--- a/libs/rs/driver/rsdAllocation.h
+++ b/libs/rs/driver/rsdAllocation.h
@@ -24,6 +24,7 @@
 #include <GLES2/gl2.h>
 
 class RsdFrameBufferObj;
+struct ANativeWindowBuffer;
 
 struct DrvAllocation {
     // Is this a legal structure to be used as a texture source.
@@ -47,6 +48,7 @@
     bool uploadDeferred;
 
     RsdFrameBufferObj * readBackFBO;
+    ANativeWindowBuffer *wndBuffer;
 };
 
 GLenum rsdTypeToGLType(RsDataType t);
@@ -67,6 +69,14 @@
                           RsAllocationUsageType src);
 void rsdAllocationMarkDirty(const android::renderscript::Context *rsc,
                             const android::renderscript::Allocation *alloc);
+int32_t rsdAllocationInitSurfaceTexture(const android::renderscript::Context *rsc,
+                                        const android::renderscript::Allocation *alloc);
+void rsdAllocationSetSurfaceTexture(const android::renderscript::Context *rsc,
+                                    android::renderscript::Allocation *alloc, ANativeWindow *nw);
+void rsdAllocationIoSend(const android::renderscript::Context *rsc,
+                         android::renderscript::Allocation *alloc);
+void rsdAllocationIoReceive(const android::renderscript::Context *rsc,
+                            android::renderscript::Allocation *alloc);
 
 void rsdAllocationData1D(const android::renderscript::Context *rsc,
                          const android::renderscript::Allocation *alloc,
diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp
index bec6ffff..dd78684 100644
--- a/libs/rs/driver/rsdBcc.cpp
+++ b/libs/rs/driver/rsdBcc.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #include "rsdCore.h"
 #include "rsdBcc.h"
 #include "rsdRuntime.h"
@@ -30,7 +29,6 @@
 #include "libdex/ZipArchive.h"
 }
 
-
 using namespace android;
 using namespace android::renderscript;
 
@@ -45,6 +43,7 @@
     bcinfo::MetadataExtractor *ME;
 
     InvokeFunc_t *mInvokeFunctions;
+    ForEachFunc_t *mForEachFunctions;
     void ** mFieldAddress;
     bool * mFieldIsObject;
     const uint32_t *mExportForEachSignatureList;
@@ -162,8 +161,16 @@
     }
 
     exportForEachSignatureCount = drv->ME->getExportForEachSignatureCount();
-    rsAssert(exportForEachSignatureCount <= 1);
     drv->mExportForEachSignatureList = drv->ME->getExportForEachSignatureList();
+    if (exportForEachSignatureCount > 0) {
+        drv->mForEachFunctions =
+            (ForEachFunc_t*) calloc(exportForEachSignatureCount,
+                                    sizeof(ForEachFunc_t));
+        bccGetExportForEachList(drv->mBccScript, exportForEachSignatureCount,
+                                (void **) drv->mForEachFunctions);
+    } else {
+        drv->mForEachFunctions = NULL;
+    }
 
     // Copy info over to runtime
     script->mHal.info.exportedFunctionCount = drv->ME->getExportFuncCount();
@@ -196,6 +203,7 @@
 typedef struct {
     Context *rsc;
     Script *script;
+    ForEachFunc_t kernel;
     uint32_t sig;
     const Allocation * ain;
     Allocation * aout;
@@ -235,7 +243,7 @@
     RsdHal * dc = (RsdHal *)mtls->rsc->mHal.drv;
     uint32_t sig = mtls->sig;
 
-    outer_foreach_t fn = (outer_foreach_t) mtls->script->mHal.info.root;
+    outer_foreach_t fn = (outer_foreach_t) mtls->kernel;
     while (1) {
         uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum);
         uint32_t yStart = mtls->yStart + slice * mtls->mSliceSize;
@@ -265,7 +273,7 @@
     RsdHal * dc = (RsdHal *)mtls->rsc->mHal.drv;
     uint32_t sig = mtls->sig;
 
-    outer_foreach_t fn = (outer_foreach_t) mtls->script->mHal.info.root;
+    outer_foreach_t fn = (outer_foreach_t) mtls->kernel;
     while (1) {
         uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum);
         uint32_t xStart = mtls->xStart + slice * mtls->mSliceSize;
@@ -299,8 +307,8 @@
     memset(&mtls, 0, sizeof(mtls));
 
     DrvScript *drv = (DrvScript *)s->mHal.drv;
-    // We only support slot 0 (root) at this point in time.
-    rsAssert(slot == 0);
+    mtls.kernel = drv->mForEachFunctions[slot];
+    rsAssert(mtls.kernel != NULL);
     mtls.sig = 0x1f;  // temp fix for old apps, full table in slang_rs_export_foreach.cpp
     if (drv->mExportForEachSignatureList) {
         mtls.sig = drv->mExportForEachSignatureList[slot];
@@ -391,7 +399,7 @@
         uint32_t sig = mtls.sig;
 
         //ALOGE("launch 3");
-        outer_foreach_t fn = (outer_foreach_t) mtls.script->mHal.info.root;
+        outer_foreach_t fn = (outer_foreach_t) mtls.kernel;
         for (p.ar[0] = mtls.arrayStart; p.ar[0] < mtls.arrayEnd; p.ar[0]++) {
             for (p.z = mtls.zStart; p.z < mtls.zEnd; p.z++) {
                 for (p.y = mtls.yStart; p.y < mtls.yEnd; p.y++) {
@@ -517,6 +525,11 @@
         drv->mInvokeFunctions = NULL;
     }
 
+    if (drv->mForEachFunctions) {
+        free(drv->mForEachFunctions);
+        drv->mForEachFunctions = NULL;
+    }
+
     delete drv->ME;
     drv->ME = NULL;
 
diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp
index 1a535d0..bf2b62a 100644
--- a/libs/rs/driver/rsdCore.cpp
+++ b/libs/rs/driver/rsdCore.cpp
@@ -18,6 +18,7 @@
 #include "rsdAllocation.h"
 #include "rsdBcc.h"
 #include "rsdGL.h"
+#include "rsdPath.h"
 #include "rsdProgramStore.h"
 #include "rsdProgramRaster.h"
 #include "rsdProgramVertex.h"
@@ -72,6 +73,10 @@
         rsdAllocationResize,
         rsdAllocationSyncAll,
         rsdAllocationMarkDirty,
+        rsdAllocationInitSurfaceTexture,
+        rsdAllocationSetSurfaceTexture,
+        rsdAllocationIoSend,
+        rsdAllocationIoReceive,
         rsdAllocationData1D,
         rsdAllocationData2D,
         rsdAllocationData3D,
@@ -114,6 +119,13 @@
     },
 
     {
+        rsdPathInitStatic,
+        rsdPathInitDynamic,
+        rsdPathDraw,
+        rsdPathDestroy
+    },
+
+    {
         rsdSamplerInit,
         rsdSamplerDestroy
     },
@@ -259,6 +271,9 @@
     for (uint32_t ct=0; ct < dc->mWorkers.mCount; ct++) {
         setpriority(PRIO_PROCESS, dc->mWorkers.mNativeThreadId[ct], priority);
     }
+    if (dc->mHasGraphics) {
+        rsdGLSetPriority(rsc, priority);
+    }
 }
 
 void Shutdown(Context *rsc) {
diff --git a/libs/rs/driver/rsdCore.h b/libs/rs/driver/rsdCore.h
index 126c87a..05ca13b 100644
--- a/libs/rs/driver/rsdCore.h
+++ b/libs/rs/driver/rsdCore.h
@@ -25,6 +25,7 @@
 #include "rsdGL.h"
 
 typedef void (* InvokeFunc_t)(void);
+typedef void (* ForEachFunc_t)(void);
 typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
 
 typedef struct RsdSymbolTableRec {
@@ -41,6 +42,7 @@
 typedef struct RsdHalRec {
     uint32_t version_major;
     uint32_t version_minor;
+    bool mHasGraphics;
 
     struct Workers {
         volatile int mRunningCount;
diff --git a/libs/rs/driver/rsdGL.cpp b/libs/rs/driver/rsdGL.cpp
index 7acc054..b136cc7 100644
--- a/libs/rs/driver/rsdGL.cpp
+++ b/libs/rs/driver/rsdGL.cpp
@@ -215,6 +215,8 @@
     ret = eglChooseConfig(dc->gl.egl.display, configAttribs, 0, 0, &numConfigs);
     checkEglError("eglGetConfigs", ret);
 
+    eglSwapInterval(dc->gl.egl.display, 0);
+
     if (numConfigs) {
         EGLConfig* const configs = new EGLConfig[numConfigs];
 
@@ -359,6 +361,7 @@
     dc->gl.vertexArrayState = new RsdVertexArrayState();
     dc->gl.vertexArrayState->init(dc->gl.gl.maxVertexAttribs);
     dc->gl.currentFrameBuffer = NULL;
+    dc->mHasGraphics = true;
 
     ALOGV("%p initGLThread end", rsc);
     rsc->setWatchdogGL(NULL, 0, NULL);
@@ -419,6 +422,15 @@
     RSD_CALL_GL(eglSwapBuffers, dc->gl.egl.display, dc->gl.egl.surface);
 }
 
+void rsdGLSetPriority(const Context *rsc, int32_t priority) {
+    if (priority > 0) {
+        // Mark context as low priority.
+        ALOGV("low pri");
+    } else {
+        ALOGV("normal pri");
+    }
+}
+
 void rsdGLCheckError(const android::renderscript::Context *rsc,
                      const char *msg, bool isFatal) {
     GLenum err = glGetError();
diff --git a/libs/rs/driver/rsdGL.h b/libs/rs/driver/rsdGL.h
index 51893c3..e015cb1 100644
--- a/libs/rs/driver/rsdGL.h
+++ b/libs/rs/driver/rsdGL.h
@@ -82,6 +82,8 @@
 void rsdGLSwap(const android::renderscript::Context *rsc);
 void rsdGLCheckError(const android::renderscript::Context *rsc,
                      const char *msg, bool isFatal = false);
+void rsdGLSetPriority(const android::renderscript::Context *rsc,
+                      int32_t priority);
 
 #endif
 
diff --git a/libs/rs/driver/rsdMesh.cpp b/libs/rs/driver/rsdMesh.cpp
index eb62ddb..50daf3e 100644
--- a/libs/rs/driver/rsdMesh.cpp
+++ b/libs/rs/driver/rsdMesh.cpp
@@ -35,7 +35,7 @@
     }
     drv = new RsdMeshObj(rsc, m);
     m->mHal.drv = drv;
-    return drv->init();
+    return drv->init(rsc);
 }
 
 void rsdMeshDraw(const Context *rsc, const Mesh *m, uint32_t primIndex, uint32_t start, uint32_t len) {
diff --git a/libs/rs/driver/rsdMeshObj.cpp b/libs/rs/driver/rsdMeshObj.cpp
index 99d79dc..893f046 100644
--- a/libs/rs/driver/rsdMeshObj.cpp
+++ b/libs/rs/driver/rsdMeshObj.cpp
@@ -50,14 +50,9 @@
 }
 
 bool RsdMeshObj::isValidGLComponent(const Element *elem, uint32_t fieldIdx) {
-    // Do not create attribs for padding
-    if (elem->getFieldName(fieldIdx)[0] == '#') {
-        return false;
-    }
-
     // Only GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, GL_FLOAT are accepted.
     // Filter rs types accordingly
-    RsDataType dt = elem->getField(fieldIdx)->getComponent().getType();
+    RsDataType dt = elem->mHal.state.fields[fieldIdx]->mHal.state.dataType;
     if (dt != RS_TYPE_FLOAT_32 && dt != RS_TYPE_UNSIGNED_8 &&
         dt != RS_TYPE_UNSIGNED_16 && dt != RS_TYPE_SIGNED_8 &&
         dt != RS_TYPE_SIGNED_16) {
@@ -65,7 +60,7 @@
     }
 
     // Now make sure they are not arrays
-    uint32_t arraySize = elem->getFieldArraySize(fieldIdx);
+    uint32_t arraySize = elem->mHal.state.fieldArraySizes[fieldIdx];
     if (arraySize != 1) {
         return false;
     }
@@ -73,15 +68,15 @@
     return true;
 }
 
-bool RsdMeshObj::init() {
+bool RsdMeshObj::init(const Context *rsc) {
 
-    updateGLPrimitives();
+    updateGLPrimitives(rsc);
 
     // Count the number of gl attrs to initialize
     mAttribCount = 0;
     for (uint32_t ct=0; ct < mRSMesh->mHal.state.vertexBuffersCount; ct++) {
         const Element *elem = mRSMesh->mHal.state.vertexBuffers[ct]->getType()->getElement();
-        for (uint32_t ct=0; ct < elem->getFieldCount(); ct++) {
+        for (uint32_t ct=0; ct < elem->mHal.state.fieldsCount; ct++) {
             if (isValidGLComponent(elem, ct)) {
                 mAttribCount ++;
             }
@@ -104,21 +99,21 @@
     uint32_t userNum = 0;
     for (uint32_t ct=0; ct < mRSMesh->mHal.state.vertexBuffersCount; ct++) {
         const Element *elem = mRSMesh->mHal.state.vertexBuffers[ct]->getType()->getElement();
-        uint32_t stride = elem->getSizeBytes();
-        for (uint32_t fieldI=0; fieldI < elem->getFieldCount(); fieldI++) {
-            const Component &c = elem->getField(fieldI)->getComponent();
+        uint32_t stride = elem->mHal.state.elementSizeBytes;
+        for (uint32_t fieldI=0; fieldI < elem->mHal.state.fieldsCount; fieldI++) {
+            const Element *f = elem->mHal.state.fields[fieldI];
 
             if (!isValidGLComponent(elem, fieldI)) {
                 continue;
             }
 
-            mAttribs[userNum].size = c.getVectorSize();
-            mAttribs[userNum].offset = elem->getFieldOffsetBytes(fieldI);
-            mAttribs[userNum].type = rsdTypeToGLType(c.getType());
-            mAttribs[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized();
+            mAttribs[userNum].size = f->mHal.state.vectorSize;
+            mAttribs[userNum].offset = elem->mHal.state.fieldOffsetBytes[fieldI];
+            mAttribs[userNum].type = rsdTypeToGLType(f->mHal.state.dataType);
+            mAttribs[userNum].normalized = f->mHal.state.dataType != RS_TYPE_FLOAT_32;
             mAttribs[userNum].stride = stride;
             String8 tmp(RS_SHADER_ATTR);
-            tmp.append(elem->getFieldName(fieldI));
+            tmp.append(elem->mHal.state.fieldNames[fieldI]);
             mAttribs[userNum].name.setTo(tmp.string());
 
             // Remember which allocation this attribute came from
@@ -133,7 +128,7 @@
 void RsdMeshObj::renderPrimitiveRange(const Context *rsc, uint32_t primIndex,
                                       uint32_t start, uint32_t len) const {
     if (len < 1 || primIndex >= mRSMesh->mHal.state.primitivesCount || mAttribCount == 0) {
-        ALOGE("Invalid mesh or parameters");
+        rsc->setError(RS_ERROR_FATAL_DRIVER, "Invalid mesh or parameters");
         return;
     }
 
@@ -186,7 +181,7 @@
     rsdGLCheckError(rsc, "Mesh::renderPrimitiveRange");
 }
 
-void RsdMeshObj::updateGLPrimitives() {
+void RsdMeshObj::updateGLPrimitives(const Context *rsc) {
     mGLPrimitives = new uint32_t[mRSMesh->mHal.state.primitivesCount];
     for (uint32_t i = 0; i < mRSMesh->mHal.state.primitivesCount; i ++) {
         switch (mRSMesh->mHal.state.primitives[i]) {
@@ -196,6 +191,7 @@
             case RS_PRIMITIVE_TRIANGLE:       mGLPrimitives[i] = GL_TRIANGLES; break;
             case RS_PRIMITIVE_TRIANGLE_STRIP: mGLPrimitives[i] = GL_TRIANGLE_STRIP; break;
             case RS_PRIMITIVE_TRIANGLE_FAN:   mGLPrimitives[i] = GL_TRIANGLE_FAN; break;
+            default: rsc->setError(RS_ERROR_FATAL_DRIVER, "Invalid mesh primitive"); break;
         }
     }
 }
diff --git a/libs/rs/driver/rsdMeshObj.h b/libs/rs/driver/rsdMeshObj.h
index 8b1271b..1370f01 100644
--- a/libs/rs/driver/rsdMeshObj.h
+++ b/libs/rs/driver/rsdMeshObj.h
@@ -37,15 +37,16 @@
             const android::renderscript::Mesh *);
     ~RsdMeshObj();
 
-    void renderPrimitiveRange(const android::renderscript::Context *, uint32_t primIndex, uint32_t start, uint32_t len) const;
+    void renderPrimitiveRange(const android::renderscript::Context *,
+                              uint32_t primIndex, uint32_t start, uint32_t len) const;
 
-    bool init();
+    bool init(const android::renderscript::Context *rsc);
 
 protected:
     const android::renderscript::Mesh *mRSMesh;
 
     uint32_t *mGLPrimitives;
-    void updateGLPrimitives();
+    void updateGLPrimitives(const android::renderscript::Context *rsc);
 
     bool isValidGLComponent(const android::renderscript::Element *elem, uint32_t fieldIdx);
     // Attribues that allow us to map to GL
diff --git a/libs/rs/driver/rsdPath.cpp b/libs/rs/driver/rsdPath.cpp
new file mode 100644
index 0000000..e04bc02
--- /dev/null
+++ b/libs/rs/driver/rsdPath.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <GLES/glext.h>
+
+#include <rs_hal.h>
+#include <rsContext.h>
+#include <rsPath.h>
+
+#include "rsdCore.h"
+#include "rsdPath.h"
+#include "rsdAllocation.h"
+#include "rsdGL.h"
+#include "rsdVertexArray.h"
+#include "rsdShaderCache.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+class DrvPath {
+protected:
+    DrvPath();
+public:
+    virtual ~DrvPath();
+    virtual void draw(Context *) = 0;
+};
+
+class DrvPathStatic : public DrvPath {
+public:
+    typedef struct {
+        float x1, xc, x2;
+        float y1, yc, y2;
+    } segment_t;
+
+    segment_t *mSegments;
+    uint32_t mSegmentCount;
+
+    DrvPathStatic(const Allocation *vtx, const Allocation *loops);
+    virtual ~DrvPathStatic();
+
+    virtual void draw(Context *);
+};
+
+class DrvPathDynamic : public DrvPath {
+public:
+    DrvPathDynamic();
+    virtual ~DrvPathDynamic();
+};
+
+static void cleanup(const Context *rsc, const Path *m) {
+    DrvPath *dp = (DrvPath *)m->mHal.drv;
+    if (dp) {
+        delete dp;
+    }
+}
+
+bool rsdPathInitStatic(const Context *rsc, const Path *m,
+                       const Allocation *vtx, const Allocation *loops) {
+    DrvPathStatic *drv = NULL;
+    cleanup(rsc, m);
+
+    DrvPathStatic *dps = new DrvPathStatic(vtx, loops);
+    //LOGE("init path m %p,  %p", m, dps);
+    m->mHal.drv = dps;
+    return dps != NULL;
+}
+
+bool rsdPathInitDynamic(const Context *rsc, const Path *m) {
+    return false;
+}
+
+
+void rsdPathDraw(const Context *rsc, const Path *m) {
+    //LOGE("render m=%p", m);
+
+    DrvPath *drv = (DrvPath *)m->mHal.drv;
+    if(drv) {
+        //LOGE("render 2 drv=%p", drv);
+        drv->draw((Context *)rsc);
+    }
+}
+
+void rsdPathDestroy(const Context *rsc, const Path *m) {
+    cleanup(rsc, m);
+    m->mHal.drv = NULL;
+}
+
+
+
+
+DrvPath::DrvPath() {
+}
+
+DrvPath::~DrvPath() {
+}
+
+DrvPathStatic::DrvPathStatic(const Allocation *vtx, const Allocation *loops) {
+    mSegmentCount = vtx->getType()->getDimX() / 3;
+    mSegments = new segment_t[mSegmentCount];
+
+    const float *fin = (const float *)vtx->getPtr();
+    for (uint32_t ct=0; ct < mSegmentCount; ct++) {
+        segment_t *s = &mSegments[ct];
+        s->x1 = fin[0];
+        s->y1 = fin[1];
+
+        s->xc = fin[2];
+        s->yc = fin[3];
+
+        s->x2 = fin[4];
+        s->y2 = fin[5];
+        fin += 6;
+    }
+}
+
+DrvPathStatic::~DrvPathStatic() {
+}
+
+void DrvPathStatic::draw(Context *rsc) {
+    const static float color[24] = {
+        1.f, 0.f, 0.f, 1.f,  0.5f, 0.f, 0.f, 1.f,
+        1.f, 0.f, 0.f, 1.f,  0.5f, 0.f, 0.f, 1.f,
+        1.f, 1.f, 1.f, 1.f,  1.f, 1.f, 1.f, 1.f};
+    float vtx[12];
+
+    //LOGE("draw");
+    if (!rsc->setupCheck()) {
+        return;
+    }
+
+    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+    if (!dc->gl.shaderCache->setup(rsc)) {
+        return;
+    }
+
+    RsdVertexArray::Attrib attribs[2];
+    attribs[0].set(GL_FLOAT, 2, 8, false, (uint32_t)vtx, "ATTRIB_position");
+    attribs[1].set(GL_FLOAT, 4, 16, false, (uint32_t)color, "ATTRIB_color");
+    RsdVertexArray va(attribs, 2);
+    va.setup(rsc);
+
+    //LOGE("mSegmentCount %i", mSegmentCount);
+    for (uint32_t ct=0; ct < mSegmentCount; ct++) {
+        segment_t *s = &mSegments[ct];
+
+        vtx[0] = s->x1;
+        vtx[1] = s->y1;
+        vtx[2] = s->xc;
+        vtx[3] = s->yc;
+
+        vtx[4] = s->x2;
+        vtx[5] = s->y2;
+        vtx[6] = s->xc;
+        vtx[7] = s->yc;
+
+        vtx[8] = s->x1;
+        vtx[9] = s->y1;
+        vtx[10] = s->x2;
+        vtx[11] = s->y2;
+
+        RSD_CALL_GL(glDrawArrays, GL_LINES, 0, 6);
+    }
+
+}
+
+DrvPathDynamic::DrvPathDynamic() {
+}
+
+DrvPathDynamic::~DrvPathDynamic() {
+}
diff --git a/libs/rs/driver/rsdPath.h b/libs/rs/driver/rsdPath.h
new file mode 100644
index 0000000..fa00972
--- /dev/null
+++ b/libs/rs/driver/rsdPath.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef RSD_PATH_H
+#define RSD_PATH_H
+
+#include <rs_hal.h>
+
+
+bool rsdPathInitStatic(const android::renderscript::Context *rsc,
+                       const android::renderscript::Path *m,
+                       const android::renderscript::Allocation *vertex,
+                       const android::renderscript::Allocation *loops);
+bool rsdPathInitDynamic(const android::renderscript::Context *rsc,
+                        const android::renderscript::Path *m);
+void rsdPathDraw(const android::renderscript::Context *rsc,
+                 const android::renderscript::Path *m);
+void rsdPathDestroy(const android::renderscript::Context *rsc,
+                    const android::renderscript::Path *m);
+
+
+#endif
diff --git a/libs/rs/driver/rsdProgram.cpp b/libs/rs/driver/rsdProgram.cpp
index 54484df..fa4cb0f 100644
--- a/libs/rs/driver/rsdProgram.cpp
+++ b/libs/rs/driver/rsdProgram.cpp
@@ -34,8 +34,11 @@
 using namespace android::renderscript;
 
 bool rsdProgramVertexInit(const Context *rsc, const ProgramVertex *pv,
-                          const char* shader, uint32_t shaderLen) {
-    RsdShader *drv = new RsdShader(pv, GL_VERTEX_SHADER, shader, shaderLen);
+                          const char* shader, size_t shaderLen,
+                          const char** textureNames, size_t textureNamesCount,
+                          const size_t *textureNamesLength) {
+    RsdShader *drv = new RsdShader(pv, GL_VERTEX_SHADER, shader, shaderLen,
+                                   textureNames, textureNamesCount, textureNamesLength);
     pv->mHal.drv = drv;
 
     return drv->createShader();
@@ -78,8 +81,11 @@
 }
 
 bool rsdProgramFragmentInit(const Context *rsc, const ProgramFragment *pf,
-                          const char* shader, uint32_t shaderLen) {
-    RsdShader *drv = new RsdShader(pf, GL_FRAGMENT_SHADER, shader, shaderLen);
+                            const char* shader, size_t shaderLen,
+                            const char** textureNames, size_t textureNamesCount,
+                            const size_t *textureNamesLength) {
+    RsdShader *drv = new RsdShader(pf, GL_FRAGMENT_SHADER, shader, shaderLen,
+                                   textureNames, textureNamesCount, textureNamesLength);
     pf->mHal.drv = drv;
 
     return drv->createShader();
diff --git a/libs/rs/driver/rsdProgramFragment.h b/libs/rs/driver/rsdProgramFragment.h
index 366cb40..b03a9fe 100644
--- a/libs/rs/driver/rsdProgramFragment.h
+++ b/libs/rs/driver/rsdProgramFragment.h
@@ -22,7 +22,9 @@
 
 bool rsdProgramFragmentInit(const android::renderscript::Context *rsc,
                             const android::renderscript::ProgramFragment *,
-                            const char* shader, uint32_t shaderLen);
+                            const char* shader, size_t shaderLen,
+                            const char** textureNames, size_t textureNamesCount,
+                            const size_t *textureNamesLength);
 void rsdProgramFragmentSetActive(const android::renderscript::Context *rsc,
                                  const android::renderscript::ProgramFragment *);
 void rsdProgramFragmentDestroy(const android::renderscript::Context *rsc,
diff --git a/libs/rs/driver/rsdProgramRaster.cpp b/libs/rs/driver/rsdProgramRaster.cpp
index b493759..e5a0291 100644
--- a/libs/rs/driver/rsdProgramRaster.cpp
+++ b/libs/rs/driver/rsdProgramRaster.cpp
@@ -45,6 +45,9 @@
         case RS_CULL_NONE:
             RSD_CALL_GL(glDisable, GL_CULL_FACE);
             break;
+        default:
+            rsc->setError(RS_ERROR_FATAL_DRIVER, "Invalid cull type");
+            break;
     }
 
 }
diff --git a/libs/rs/driver/rsdProgramStore.cpp b/libs/rs/driver/rsdProgramStore.cpp
index fca9ba9..c1295e8 100644
--- a/libs/rs/driver/rsdProgramStore.cpp
+++ b/libs/rs/driver/rsdProgramStore.cpp
@@ -111,7 +111,7 @@
         drv->blendSrc = GL_SRC_ALPHA_SATURATE;
         break;
     default:
-        ALOGE("Unknown blend src mode.");
+        rsc->setError(RS_ERROR_FATAL_DRIVER, "Unknown blend src mode.");
         goto error;
     }
 
@@ -141,7 +141,7 @@
         drv->blendDst = GL_ONE_MINUS_DST_ALPHA;
         break;
     default:
-        ALOGE("Unknown blend dst mode.");
+        rsc->setError(RS_ERROR_FATAL_DRIVER, "Unknown blend dst mode.");
         goto error;
     }
 
diff --git a/libs/rs/driver/rsdProgramVertex.h b/libs/rs/driver/rsdProgramVertex.h
index e998572..f917a41 100644
--- a/libs/rs/driver/rsdProgramVertex.h
+++ b/libs/rs/driver/rsdProgramVertex.h
@@ -21,7 +21,9 @@
 
 bool rsdProgramVertexInit(const android::renderscript::Context *rsc,
                           const android::renderscript::ProgramVertex *,
-                          const char* shader, uint32_t shaderLen);
+                          const char* shader, size_t shaderLen,
+                          const char** textureNames, size_t textureNamesCount,
+                          const size_t *textureNamesLength);
 void rsdProgramVertexSetActive(const android::renderscript::Context *rsc,
                                const android::renderscript::ProgramVertex *);
 void rsdProgramVertexDestroy(const android::renderscript::Context *rsc,
diff --git a/libs/rs/driver/rsdRuntimeStubs.cpp b/libs/rs/driver/rsdRuntimeStubs.cpp
index 14c2970..44bfb1c 100644
--- a/libs/rs/driver/rsdRuntimeStubs.cpp
+++ b/libs/rs/driver/rsdRuntimeStubs.cpp
@@ -25,6 +25,7 @@
 #include "rsdCore.h"
 
 #include "rsdRuntime.h"
+#include "rsdPath.h"
 
 #include <time.h>
 
@@ -89,6 +90,16 @@
     rsrBindTexture(rsc, sc, pf, slot, a);
 }
 
+static void SC_BindVertexConstant(ProgramVertex *pv, uint32_t slot, Allocation *a) {
+    GET_TLS();
+    rsrBindConstant(rsc, sc, pv, slot, a);
+}
+
+static void SC_BindFragmentConstant(ProgramFragment *pf, uint32_t slot, Allocation *a) {
+    GET_TLS();
+    rsrBindConstant(rsc, sc, pf, slot, a);
+}
+
 static void SC_BindSampler(ProgramFragment *pf, uint32_t slot, Sampler *s) {
     GET_TLS();
     rsrBindSampler(rsc, sc, pf, slot, s);
@@ -204,6 +215,12 @@
     rsrDrawRect(rsc, sc, x1, y1, x2, y2, z);
 }
 
+static void SC_DrawPath(Path *p) {
+    GET_TLS();
+    //rsrDrawPath(rsc, sc, p);
+    rsdPathDraw(rsc, p);
+}
+
 static void SC_DrawMesh(Mesh *m) {
     GET_TLS();
     rsrDrawMesh(rsc, sc, m);
@@ -533,6 +550,10 @@
     { "_Z13rsClearObjectP9rs_script", (void *)&SC_ClearObject, true },
     { "_Z10rsIsObject9rs_script", (void *)&SC_IsObject, true },
 
+    { "_Z11rsSetObjectP7rs_pathS_", (void *)&SC_SetObject, true },
+    { "_Z13rsClearObjectP7rs_path", (void *)&SC_ClearObject, true },
+    { "_Z10rsIsObject7rs_path", (void *)&SC_IsObject, true },
+
     { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_SetObject, true },
     { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_ClearObject, true },
     { "_Z10rsIsObject7rs_mesh", (void *)&SC_IsObject, true },
@@ -580,6 +601,8 @@
     { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_BindProgramRaster, false },
     { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_BindSampler, false },
     { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_BindTexture, false },
+    { "_Z15rsgBindConstant19rs_program_fragmentj13rs_allocation", (void *)&SC_BindFragmentConstant, false },
+    { "_Z15rsgBindConstant17rs_program_vertexj13rs_allocation", (void *)&SC_BindVertexConstant, false },
 
     { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_VpLoadProjectionMatrix, false },
     { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_VpLoadModelMatrix, false },
@@ -603,6 +626,8 @@
     { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_DrawMeshPrimitiveRange, false },
     { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_MeshComputeBoundingBox, false },
 
+    { "_Z11rsgDrawPath7rs_path", (void *)&SC_DrawPath, false },
+
     { "_Z13rsgClearColorffff", (void *)&SC_ClearColor, false },
     { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth, false },
 
diff --git a/libs/rs/driver/rsdShader.cpp b/libs/rs/driver/rsdShader.cpp
index c70193ae..1e73b95 100644
--- a/libs/rs/driver/rsdShader.cpp
+++ b/libs/rs/driver/rsdShader.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-2012 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.
@@ -30,14 +30,16 @@
 using namespace android::renderscript;
 
 RsdShader::RsdShader(const Program *p, uint32_t type,
-                       const char * shaderText, uint32_t shaderLength) {
-
+                     const char * shaderText, size_t shaderLength,
+                     const char** textureNames, size_t textureNamesCount,
+                     const size_t *textureNamesLength) {
     mUserShader.setTo(shaderText, shaderLength);
     mRSProgram = p;
     mType = type;
     initMemberVars();
     initAttribAndUniformArray();
-    init();
+    init(textureNames, textureNamesCount, textureNamesLength);
+    createTexturesString(textureNames, textureNamesCount, textureNamesLength);
 }
 
 RsdShader::~RsdShader() {
@@ -65,37 +67,38 @@
     mIsValid = false;
 }
 
-void RsdShader::init() {
+void RsdShader::init(const char** textureNames, size_t textureNamesCount,
+                     const size_t *textureNamesLength) {
     uint32_t attribCount = 0;
     uint32_t uniformCount = 0;
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
-        initAddUserElement(mRSProgram->mHal.state.inputElements[ct], mAttribNames, NULL, &attribCount, RS_SHADER_ATTR);
+        initAddUserElement(mRSProgram->mHal.state.inputElements[ct], mAttribNames,
+                           NULL, &attribCount, RS_SHADER_ATTR);
     }
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
-        initAddUserElement(mRSProgram->mHal.state.constantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI);
+        initAddUserElement(mRSProgram->mHal.state.constantTypes[ct]->getElement(),
+                           mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI);
     }
 
     mTextureUniformIndexStart = uniformCount;
-    char buf[256];
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.texturesCount; ct++) {
-        snprintf(buf, sizeof(buf), "UNI_Tex%i", ct);
-        mUniformNames[uniformCount].setTo(buf);
+        mUniformNames[uniformCount].setTo("UNI_");
+        mUniformNames[uniformCount].append(textureNames[ct], textureNamesLength[ct]);
         mUniformArraySizes[uniformCount] = 1;
         uniformCount++;
     }
-
 }
 
 String8 RsdShader::getGLSLInputString() const {
     String8 s;
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
         const Element *e = mRSProgram->mHal.state.inputElements[ct];
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
+        for (uint32_t field=0; field < e->mHal.state.fieldsCount; field++) {
+            const Element *f = e->mHal.state.fields[field];
 
             // Cannot be complex
-            rsAssert(!f->getFieldCount());
-            switch (f->getComponent().getVectorSize()) {
+            rsAssert(!f->mHal.state.fieldsCount);
+            switch (f->mHal.state.vectorSize) {
             case 1: s.append("attribute float ATTRIB_"); break;
             case 2: s.append("attribute vec2 ATTRIB_"); break;
             case 3: s.append("attribute vec3 ATTRIB_"); break;
@@ -104,7 +107,7 @@
                 rsAssert(0);
             }
 
-            s.append(e->getFieldName(field));
+            s.append(e->mHal.state.fieldNames[field]);
             s.append(";\n");
         }
     }
@@ -114,17 +117,13 @@
 void RsdShader::appendAttributes() {
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
         const Element *e = mRSProgram->mHal.state.inputElements[ct];
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            const char *fn = e->getFieldName(field);
-
-            if (fn[0] == '#') {
-                continue;
-            }
+        for (uint32_t field=0; field < e->mHal.state.fieldsCount; field++) {
+            const Element *f = e->mHal.state.fields[field];
+            const char *fn = e->mHal.state.fieldNames[field];
 
             // Cannot be complex
-            rsAssert(!f->getFieldCount());
-            switch (f->getComponent().getVectorSize()) {
+            rsAssert(!f->mHal.state.fieldsCount);
+            switch (f->mHal.state.vectorSize) {
             case 1: mShader.append("attribute float ATTRIB_"); break;
             case 2: mShader.append("attribute vec2 ATTRIB_"); break;
             case 3: mShader.append("attribute vec3 ATTRIB_"); break;
@@ -139,17 +138,25 @@
     }
 }
 
-void RsdShader::appendTextures() {
-    char buf[256];
-    for (uint32_t ct=0; ct < mRSProgram->mHal.state.texturesCount; ct++) {
+void RsdShader::createTexturesString(const char** textureNames, size_t textureNamesCount,
+                                     const size_t *textureNamesLength) {
+    mShaderTextures.setTo("");
+    for (uint32_t ct = 0; ct < mRSProgram->mHal.state.texturesCount; ct ++) {
         if (mRSProgram->mHal.state.textureTargets[ct] == RS_TEXTURE_2D) {
-            snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct);
+            Allocation *a = mRSProgram->mHal.state.textures[ct];
+            if (a && a->mHal.state.surfaceTextureID) {
+                mShaderTextures.append("uniform samplerExternalOES UNI_");
+            } else {
+                mShaderTextures.append("uniform sampler2D UNI_");
+            }
             mTextureTargets[ct] = GL_TEXTURE_2D;
         } else {
-            snprintf(buf, sizeof(buf), "uniform samplerCube UNI_Tex%i;\n", ct);
+            mShaderTextures.append("uniform samplerCube UNI_");
             mTextureTargets[ct] = GL_TEXTURE_CUBE_MAP;
         }
-        mShader.append(buf);
+
+        mShaderTextures.append(textureNames[ct], textureNamesLength[ct]);
+        mShaderTextures.append(";\n");
     }
 }
 
@@ -160,7 +167,7 @@
     }
     appendUserConstants();
     appendAttributes();
-    appendTextures();
+    mShader.append(mShaderTextures);
 
     mShader.append(mUserShader);
 
@@ -190,12 +197,11 @@
                 char* buf = (char*) malloc(infoLen);
                 if (buf) {
                     RSD_CALL_GL(glGetShaderInfoLog, mShaderID, infoLen, NULL, buf);
-                    ALOGE("Could not compile shader \n%s\n", buf);
+                    rsc->setError(RS_ERROR_FATAL_PROGRAM_LINK, buf);
                     free(buf);
                 }
                 RSD_CALL_GL(glDeleteShader, mShaderID);
                 mShaderID = 0;
-                rsc->setError(RS_ERROR_BAD_SHADER, "Error returned from GL driver loading shader text,");
                 return false;
             }
         }
@@ -211,24 +217,20 @@
 void RsdShader::appendUserConstants() {
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
         const Element *e = mRSProgram->mHal.state.constantTypes[ct]->getElement();
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            const char *fn = e->getFieldName(field);
-
-            if (fn[0] == '#') {
-                continue;
-            }
+        for (uint32_t field=0; field < e->mHal.state.fieldsCount; field++) {
+            const Element *f = e->mHal.state.fields[field];
+            const char *fn = e->mHal.state.fieldNames[field];
 
             // Cannot be complex
-            rsAssert(!f->getFieldCount());
-            if (f->getType() == RS_TYPE_MATRIX_4X4) {
+            rsAssert(!f->mHal.state.fieldsCount);
+            if (f->mHal.state.dataType == RS_TYPE_MATRIX_4X4) {
                 mShader.append("uniform mat4 UNI_");
-            } else if (f->getType() == RS_TYPE_MATRIX_3X3) {
+            } else if (f->mHal.state.dataType == RS_TYPE_MATRIX_3X3) {
                 mShader.append("uniform mat3 UNI_");
-            } else if (f->getType() == RS_TYPE_MATRIX_2X2) {
+            } else if (f->mHal.state.dataType == RS_TYPE_MATRIX_2X2) {
                 mShader.append("uniform mat2 UNI_");
             } else {
-                switch (f->getComponent().getVectorSize()) {
+                switch (f->mHal.state.vectorSize) {
                 case 1: mShader.append("uniform float UNI_"); break;
                 case 2: mShader.append("uniform vec2 UNI_"); break;
                 case 3: mShader.append("uniform vec3 UNI_"); break;
@@ -239,8 +241,8 @@
             }
 
             mShader.append(fn);
-            if (e->getFieldArraySize(field) > 1) {
-                mShader.appendFormat("[%d]", e->getFieldArraySize(field));
+            if (e->mHal.state.fieldArraySizes[field] > 1) {
+                mShader.appendFormat("[%d]", e->mHal.state.fieldArraySizes[field]);
             }
             mShader.append(";\n");
         }
@@ -248,8 +250,8 @@
 }
 
 void RsdShader::logUniform(const Element *field, const float *fd, uint32_t arraySize ) {
-    RsDataType dataType = field->getType();
-    uint32_t elementSize = field->getSizeBytes() / sizeof(float);
+    RsDataType dataType = field->mHal.state.dataType;
+    uint32_t elementSize = field->mHal.state.elementSizeBytes / sizeof(float);
     for (uint32_t i = 0; i < arraySize; i ++) {
         if (arraySize > 1) {
             ALOGV("Array Element [%u]", i);
@@ -270,7 +272,7 @@
             ALOGV("{%f, %f",  fd[0], fd[2]);
             ALOGV(" %f, %f}", fd[1], fd[3]);
         } else {
-            switch (field->getComponent().getVectorSize()) {
+            switch (field->mHal.state.vectorSize) {
             case 1:
                 ALOGV("Uniform 1 = %f", fd[0]);
                 break;
@@ -295,7 +297,7 @@
 
 void RsdShader::setUniform(const Context *rsc, const Element *field, const float *fd,
                          int32_t slot, uint32_t arraySize ) {
-    RsDataType dataType = field->getType();
+    RsDataType dataType = field->mHal.state.dataType;
     if (dataType == RS_TYPE_MATRIX_4X4) {
         RSD_CALL_GL(glUniformMatrix4fv, slot, arraySize, GL_FALSE, fd);
     } else if (dataType == RS_TYPE_MATRIX_3X3) {
@@ -303,7 +305,7 @@
     } else if (dataType == RS_TYPE_MATRIX_2X2) {
         RSD_CALL_GL(glUniformMatrix2fv, slot, arraySize, GL_FALSE, fd);
     } else {
-        switch (field->getComponent().getVectorSize()) {
+        switch (field->mHal.state.vectorSize) {
         case 1:
             RSD_CALL_GL(glUniform1fv, slot, arraySize, fd);
             break;
@@ -422,7 +424,8 @@
 
         DrvAllocation *drvTex = (DrvAllocation *)mRSProgram->mHal.state.textures[ct]->mHal.drv;
         if (drvTex->glTarget != GL_TEXTURE_2D && drvTex->glTarget != GL_TEXTURE_CUBE_MAP) {
-            ALOGE("Attempting to bind unknown texture to shader id %u, texture unit %u", (uint)this, ct);
+            ALOGE("Attempting to bind unknown texture to shader id %u, texture unit %u",
+                  (uint)this, ct);
             rsc->setError(RS_ERROR_BAD_SHADER, "Non-texture allocation bound to a shader");
         }
         RSD_CALL_GL(glBindTexture, drvTex->glTarget, drvTex->textureID);
@@ -458,15 +461,11 @@
 
         const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
         const Element *e = mRSProgram->mHal.state.constantTypes[ct]->getElement();
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            const char *fieldName = e->getFieldName(field);
-            // If this field is padding, skip it
-            if (fieldName[0] == '#') {
-                continue;
-            }
+        for (uint32_t field=0; field < e->mHal.state.fieldsCount; field++) {
+            const Element *f = e->mHal.state.fields[field];
+            const char *fieldName = e->mHal.state.fieldNames[field];
 
-            uint32_t offset = e->getFieldOffsetBytes(field);
+            uint32_t offset = e->mHal.state.fieldOffsetBytes[field];
             const float *fd = reinterpret_cast<const float *>(&data[offset]);
 
             int32_t slot = -1;
@@ -505,22 +504,13 @@
     mAttribCount = 0;
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
         const Element *elem = mRSProgram->mHal.state.inputElements[ct];
-        for (uint32_t field=0; field < elem->getFieldCount(); field++) {
-            if (elem->getFieldName(field)[0] != '#') {
-                mAttribCount ++;
-            }
-        }
+        mAttribCount += elem->mHal.state.fieldsCount;
     }
 
     mUniformCount = 0;
     for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
         const Element *elem = mRSProgram->mHal.state.constantTypes[ct]->getElement();
-
-        for (uint32_t field=0; field < elem->getFieldCount(); field++) {
-            if (elem->getFieldName(field)[0] != '#') {
-                mUniformCount ++;
-            }
-        }
+        mUniformCount += elem->mHal.state.fieldsCount;
     }
     mUniformCount += mRSProgram->mHal.state.texturesCount;
 
@@ -540,17 +530,17 @@
 
 void RsdShader::initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths,
                                    uint32_t *count, const char *prefix) {
-    rsAssert(e->getFieldCount());
-    for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
-        const Element *ce = e->getField(ct);
-        if (ce->getFieldCount()) {
+    rsAssert(e->mHal.state.fieldsCount);
+    for (uint32_t ct=0; ct < e->mHal.state.fieldsCount; ct++) {
+        const Element *ce = e->mHal.state.fields[ct];
+        if (ce->mHal.state.fieldsCount) {
             initAddUserElement(ce, names, arrayLengths, count, prefix);
-        } else if (e->getFieldName(ct)[0] != '#') {
+        } else {
             String8 tmp(prefix);
-            tmp.append(e->getFieldName(ct));
+            tmp.append(e->mHal.state.fieldNames[ct]);
             names[*count].setTo(tmp.string());
             if (arrayLengths) {
-                arrayLengths[*count] = e->getFieldArraySize(ct);
+                arrayLengths[*count] = e->mHal.state.fieldArraySizes[ct];
             }
             (*count)++;
         }
diff --git a/libs/rs/driver/rsdShader.h b/libs/rs/driver/rsdShader.h
index 3f0d6ea..e32145f 100644
--- a/libs/rs/driver/rsdShader.h
+++ b/libs/rs/driver/rsdShader.h
@@ -39,7 +39,9 @@
 public:
 
     RsdShader(const android::renderscript::Program *p, uint32_t type,
-               const char * shaderText, uint32_t shaderLength);
+              const char * shaderText, uint32_t shaderLength,
+              const char** textureNames, size_t textureNamesCount,
+              const size_t *textureNamesLength);
     virtual ~RsdShader();
 
     bool createShader();
@@ -67,19 +69,27 @@
 
     // Applies to vertex and fragment shaders only
     void appendUserConstants();
-    void setupUserConstants(const android::renderscript::Context *rsc, RsdShaderCache *sc, bool isFragment);
-    void initAddUserElement(const android::renderscript::Element *e, android::String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix);
+    void setupUserConstants(const android::renderscript::Context *rsc,
+                            RsdShaderCache *sc, bool isFragment);
+    void initAddUserElement(const android::renderscript::Element *e,
+                            android::String8 *names, uint32_t *arrayLengths,
+                            uint32_t *count, const char *prefix);
     void setupTextures(const android::renderscript::Context *rsc, RsdShaderCache *sc);
-    void setupSampler(const android::renderscript::Context *rsc, const android::renderscript::Sampler *s, const android::renderscript::Allocation *tex);
+    void setupSampler(const android::renderscript::Context *rsc,
+                      const android::renderscript::Sampler *s,
+                      const android::renderscript::Allocation *tex);
 
     void appendAttributes();
     void appendTextures();
+    void createTexturesString(const char** textureNames, size_t textureNamesCount,
+                              const size_t *textureNamesLength);
 
     void initAttribAndUniformArray();
 
     mutable bool mDirty;
     android::String8 mShader;
     android::String8 mUserShader;
+    android::String8 mShaderTextures;
     uint32_t mShaderID;
     uint32_t mType;
 
@@ -93,10 +103,14 @@
 
     int32_t mTextureUniformIndexStart;
 
-    void logUniform(const android::renderscript::Element *field, const float *fd, uint32_t arraySize );
-    void setUniform(const android::renderscript::Context *rsc, const android::renderscript::Element *field, const float *fd, int32_t slot, uint32_t arraySize );
+    void logUniform(const android::renderscript::Element *field,
+                    const float *fd, uint32_t arraySize);
+    void setUniform(const android::renderscript::Context *rsc,
+                    const android::renderscript::Element *field,
+                    const float *fd, int32_t slot, uint32_t arraySize );
     void initMemberVars();
-    void init();
+    void init(const char** textureNames, size_t textureNamesCount,
+              const size_t *textureNamesLength);
 };
 
 #endif //ANDROID_RSD_SHADER_H
diff --git a/libs/rs/driver/rsdShaderCache.cpp b/libs/rs/driver/rsdShaderCache.cpp
index f6236e7..89d3c45 100644
--- a/libs/rs/driver/rsdShaderCache.cpp
+++ b/libs/rs/driver/rsdShaderCache.cpp
@@ -167,12 +167,11 @@
                 char* buf = (char*) malloc(bufLength);
                 if (buf) {
                     glGetProgramInfoLog(pgm, bufLength, NULL, buf);
-                    ALOGE("Could not link program:\n%s\n", buf);
+                    rsc->setError(RS_ERROR_FATAL_PROGRAM_LINK, buf);
                     free(buf);
                 }
             }
             glDeleteProgram(pgm);
-            rsc->setError(RS_ERROR_FATAL_PROGRAM_LINK, "Error linking GL Programs");
             return false;
         }
 
diff --git a/libs/rs/driver/rsdShaderCache.h b/libs/rs/driver/rsdShaderCache.h
index 17ee3e8..0beecae 100644
--- a/libs/rs/driver/rsdShaderCache.h
+++ b/libs/rs/driver/rsdShaderCache.h
@@ -98,7 +98,8 @@
     struct ProgramEntry {
         ProgramEntry(uint32_t numVtxAttr, uint32_t numVtxUnis,
                      uint32_t numFragUnis) : vtx(0), frag(0), program(0), vtxAttrCount(0),
-                                             vtxAttrs(0), vtxUniforms(0), fragUniforms(0) {
+                                             vtxAttrs(0), vtxUniforms(0), fragUniforms(0),
+                                             fragUniformIsSTO(0) {
             vtxAttrCount = numVtxAttr;
             if (numVtxAttr) {
                 vtxAttrs = new AttrData[numVtxAttr];
@@ -108,6 +109,7 @@
             }
             if (numFragUnis) {
                 fragUniforms = new UniformData[numFragUnis];
+                fragUniformIsSTO = new bool[numFragUnis];
             }
         }
         ~ProgramEntry() {
@@ -123,6 +125,10 @@
                 delete[] fragUniforms;
                 fragUniforms = NULL;
             }
+            if (fragUniformIsSTO) {
+                delete[] fragUniformIsSTO;
+                fragUniformIsSTO = NULL;
+            }
         }
         uint32_t vtx;
         uint32_t frag;
@@ -131,6 +137,7 @@
         AttrData *vtxAttrs;
         UniformData *vtxUniforms;
         UniformData *fragUniforms;
+        bool *fragUniformIsSTO;
     };
     android::Vector<ProgramEntry*> mEntries;
     ProgramEntry *mCurrent;
diff --git a/libs/rs/rs.h b/libs/rs/rs.h
new file mode 100644
index 0000000..fbcaf4a
--- /dev/null
+++ b/libs/rs/rs.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef RENDER_SCRIPT_H
+#define RENDER_SCRIPT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "rsDefines.h"
+
+//
+// A3D loading and object update code.
+// Should only be called at object creation, not thread safe
+RsObjectBase rsaFileA3DGetEntryByIndex(RsContext, uint32_t idx, RsFile);
+RsFile rsaFileA3DCreateFromMemory(RsContext, const void *data, uint32_t len);
+RsFile rsaFileA3DCreateFromAsset(RsContext, void *asset);
+RsFile rsaFileA3DCreateFromFile(RsContext, const char *path);
+void rsaFileA3DGetNumIndexEntries(RsContext, int32_t *numEntries, RsFile);
+void rsaFileA3DGetIndexEntries(RsContext, RsFileIndexEntry *fileEntries,
+                               uint32_t numEntries, RsFile);
+void rsaGetName(RsContext, void * obj, const char **name);
+// Mesh update functions
+void rsaMeshGetVertexBufferCount(RsContext, RsMesh, int32_t *vtxCount);
+void rsaMeshGetIndexCount(RsContext, RsMesh, int32_t *idxCount);
+void rsaMeshGetVertices(RsContext, RsMesh, RsAllocation *vtxData, uint32_t vtxDataCount);
+void rsaMeshGetIndices(RsContext, RsMesh, RsAllocation *va,
+                       uint32_t *primType, uint32_t idxDataCount);
+// Allocation update
+const void* rsaAllocationGetType(RsContext con, RsAllocation va);
+// Type update
+void rsaTypeGetNativeData(RsContext, RsType, uint32_t *typeData, uint32_t typeDataSize);
+// Element update
+void rsaElementGetNativeData(RsContext, RsElement, uint32_t *elemData, uint32_t elemDataSize);
+void rsaElementGetSubElements(RsContext, RsElement, uint32_t *ids, const char **names,
+                              uint32_t *arraySizes, uint32_t dataSize);
+
+RsDevice rsDeviceCreate();
+void rsDeviceDestroy(RsDevice dev);
+void rsDeviceSetConfig(RsDevice dev, RsDeviceParam p, int32_t value);
+RsContext rsContextCreate(RsDevice dev, uint32_t version, uint32_t sdkVersion);
+RsContext rsContextCreateGL(RsDevice dev, uint32_t version, uint32_t sdkVersion,
+                            RsSurfaceConfig sc, uint32_t dpi);
+
+#include "rsgApiFuncDecl.h"
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // RENDER_SCRIPT_H
+
+
+
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 6887b22..cf4a391 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -42,6 +42,7 @@
     param RsType vtype
     param RsAllocationMipmapControl mips
     param uint32_t usages
+    param uint32_t ptr
     ret RsAllocation
 }
 
@@ -63,169 +64,188 @@
     ret RsAllocation
 }
 
+AllocationGetSurfaceTextureID {
+    param RsAllocation alloc
+    ret int32_t
+}
+
+AllocationSetSurface {
+    param RsAllocation alloc
+    param RsNativeWindow sur
+    sync
+    }
+
+AllocationIoSend {
+    param RsAllocation alloc
+    }
+
+AllocationIoReceive {
+    param RsAllocation alloc
+    }
 
 
 ContextFinish {
-	sync
-	}
+    sync
+    }
 
 ContextBindRootScript {
-	param RsScript sampler
-	}
+    param RsScript sampler
+    }
 
 ContextBindProgramStore {
-	param RsProgramStore pgm
-	}
+    param RsProgramStore pgm
+    }
 
 ContextBindProgramFragment {
-	param RsProgramFragment pgm
-	}
+    param RsProgramFragment pgm
+    }
 
 ContextBindProgramVertex {
-	param RsProgramVertex pgm
-	}
+    param RsProgramVertex pgm
+    }
 
 ContextBindProgramRaster {
-	param RsProgramRaster pgm
-	}
+    param RsProgramRaster pgm
+    }
 
 ContextBindFont {
-	param RsFont pgm
-	}
+    param RsFont pgm
+    }
 
 ContextPause {
-	}
+    }
 
 ContextResume {
-	}
+    }
 
 ContextSetSurface {
-	param uint32_t width
-	param uint32_t height
-	param RsNativeWindow sur
+    param uint32_t width
+    param uint32_t height
+    param RsNativeWindow sur
         sync
-	}
+    }
 
 ContextDump {
-	param int32_t bits
+    param int32_t bits
 }
 
 ContextSetPriority {
-	param int32_t priority
-	}
+    param int32_t priority
+    }
 
 ContextDestroyWorker {
+        sync
 }
 
 AssignName {
-	param RsObjectBase obj
-	param const char *name
-	}
+    param RsObjectBase obj
+    param const char *name
+    }
 
 ObjDestroy {
-	param RsAsyncVoidPtr objPtr
-	}
+    param RsAsyncVoidPtr objPtr
+    }
 
 ElementCreate {
         direct
-	param RsDataType mType
-	param RsDataKind mKind
-	param bool mNormalized
-	param uint32_t mVectorSize
-	ret RsElement
-	}
+    param RsDataType mType
+    param RsDataKind mKind
+    param bool mNormalized
+    param uint32_t mVectorSize
+    ret RsElement
+    }
 
 ElementCreate2 {
         direct
-	param const RsElement * elements
-	param const char ** names
-	param const uint32_t * arraySize
-	ret RsElement
-	}
+    param const RsElement * elements
+    param const char ** names
+    param const uint32_t * arraySize
+    ret RsElement
+    }
 
 AllocationCopyToBitmap {
-	param RsAllocation alloc
-	param void * data
-	}
+    param RsAllocation alloc
+    param void * data
+    }
 
 
 Allocation1DData {
-	param RsAllocation va
-	param uint32_t xoff
-	param uint32_t lod
-	param uint32_t count
-	param const void *data
-	}
+    param RsAllocation va
+    param uint32_t xoff
+    param uint32_t lod
+    param uint32_t count
+    param const void *data
+    }
 
 Allocation1DElementData {
-	param RsAllocation va
-	param uint32_t x
-	param uint32_t lod
-	param const void *data
-	param size_t comp_offset
-	}
+    param RsAllocation va
+    param uint32_t x
+    param uint32_t lod
+    param const void *data
+    param size_t comp_offset
+    }
 
 Allocation2DData {
-	param RsAllocation va
-	param uint32_t xoff
-	param uint32_t yoff
-	param uint32_t lod
-	param RsAllocationCubemapFace face
-	param uint32_t w
-	param uint32_t h
-	param const void *data
-	}
+    param RsAllocation va
+    param uint32_t xoff
+    param uint32_t yoff
+    param uint32_t lod
+    param RsAllocationCubemapFace face
+    param uint32_t w
+    param uint32_t h
+    param const void *data
+    }
 
 Allocation2DElementData {
-	param RsAllocation va
-	param uint32_t x
-	param uint32_t y
-	param uint32_t lod
-	param RsAllocationCubemapFace face
-	param const void *data
-	param size_t element_offset
-	}
+    param RsAllocation va
+    param uint32_t x
+    param uint32_t y
+    param uint32_t lod
+    param RsAllocationCubemapFace face
+    param const void *data
+    param size_t element_offset
+    }
 
 AllocationGenerateMipmaps {
-	param RsAllocation va
+    param RsAllocation va
 }
 
 AllocationRead {
-	param RsAllocation va
-	param void * data
-	}
+    param RsAllocation va
+    param void * data
+    }
 
 AllocationSyncAll {
-	param RsAllocation va
-	param RsAllocationUsageType src
+    param RsAllocation va
+    param RsAllocationUsageType src
 }
 
 
 AllocationResize1D {
-	param RsAllocation va
-	param uint32_t dimX
-	}
+    param RsAllocation va
+    param uint32_t dimX
+    }
 
 AllocationResize2D {
-	param RsAllocation va
-	param uint32_t dimX
-	param uint32_t dimY
-	}
+    param RsAllocation va
+    param uint32_t dimX
+    param uint32_t dimY
+    }
 
 AllocationCopy2DRange {
-	param RsAllocation dest
-	param uint32_t destXoff
-	param uint32_t destYoff
-	param uint32_t destMip
-	param uint32_t destFace
-	param uint32_t width
-	param uint32_t height
-	param RsAllocation src
-	param uint32_t srcXoff
-	param uint32_t srcYoff
-	param uint32_t srcMip
-	param uint32_t srcFace
-	}
+    param RsAllocation dest
+    param uint32_t destXoff
+    param uint32_t destYoff
+    param uint32_t destMip
+    param uint32_t destFace
+    param uint32_t width
+    param uint32_t height
+    param RsAllocation src
+    param uint32_t srcXoff
+    param uint32_t srcYoff
+    param uint32_t srcMip
+    param uint32_t srcFace
+    }
 
 SamplerCreate {
     direct
@@ -239,26 +259,26 @@
 }
 
 ScriptBindAllocation {
-	param RsScript vtm
-	param RsAllocation va
-	param uint32_t slot
-	}
+    param RsScript vtm
+    param RsAllocation va
+    param uint32_t slot
+    }
 
 ScriptSetTimeZone {
-	param RsScript s
-	param const char * timeZone
-	}
+    param RsScript s
+    param const char * timeZone
+    }
 
 ScriptInvoke {
-	param RsScript s
-	param uint32_t slot
-	}
+    param RsScript s
+    param uint32_t slot
+    }
 
 ScriptInvokeV {
-	param RsScript s
-	param uint32_t slot
-	param const void * data
-	}
+    param RsScript s
+    param uint32_t slot
+    param const void * data
+    }
 
 ScriptForEach {
     param RsScript s
@@ -269,122 +289,133 @@
 }
 
 ScriptSetVarI {
-	param RsScript s
-	param uint32_t slot
-	param int value
-	}
+    param RsScript s
+    param uint32_t slot
+    param int value
+    }
 
 ScriptSetVarObj {
-	param RsScript s
-	param uint32_t slot
-	param RsObjectBase value
-	}
+    param RsScript s
+    param uint32_t slot
+    param RsObjectBase value
+    }
 
 ScriptSetVarJ {
-	param RsScript s
-	param uint32_t slot
-	param int64_t value
-	}
+    param RsScript s
+    param uint32_t slot
+    param int64_t value
+    }
 
 ScriptSetVarF {
-	param RsScript s
-	param uint32_t slot
-	param float value
-	}
+    param RsScript s
+    param uint32_t slot
+    param float value
+    }
 
 ScriptSetVarD {
-	param RsScript s
-	param uint32_t slot
-	param double value
-	}
+    param RsScript s
+    param uint32_t slot
+    param double value
+    }
 
 ScriptSetVarV {
-	param RsScript s
-	param uint32_t slot
-	param const void * data
-	}
+    param RsScript s
+    param uint32_t slot
+    param const void * data
+    }
 
 
 ScriptCCreate {
         param const char * resName
         param const char * cacheDir
-	param const char * text
-	ret RsScript
-	}
+    param const char * text
+    ret RsScript
+    }
 
 
 ProgramStoreCreate {
-	direct
-	param bool colorMaskR
-	param bool colorMaskG
-	param bool colorMaskB
-	param bool colorMaskA
+    direct
+    param bool colorMaskR
+    param bool colorMaskG
+    param bool colorMaskB
+    param bool colorMaskA
         param bool depthMask
         param bool ditherEnable
-	param RsBlendSrcFunc srcFunc
-	param RsBlendDstFunc destFunc
+    param RsBlendSrcFunc srcFunc
+    param RsBlendDstFunc destFunc
         param RsDepthFunc depthFunc
-	ret RsProgramStore
-	}
+    ret RsProgramStore
+    }
 
 ProgramRasterCreate {
-	direct
-	param bool pointSprite
-	param RsCullMode cull
-	ret RsProgramRaster
+    direct
+    param bool pointSprite
+    param RsCullMode cull
+    ret RsProgramRaster
 }
 
 ProgramBindConstants {
-	param RsProgram vp
-	param uint32_t slot
-	param RsAllocation constants
-	}
+    param RsProgram vp
+    param uint32_t slot
+    param RsAllocation constants
+    }
 
 
 ProgramBindTexture {
-	param RsProgramFragment pf
-	param uint32_t slot
-	param RsAllocation a
-	}
+    param RsProgramFragment pf
+    param uint32_t slot
+    param RsAllocation a
+    }
 
 ProgramBindSampler {
-	param RsProgramFragment pf
-	param uint32_t slot
-	param RsSampler s
-	}
+    param RsProgramFragment pf
+    param uint32_t slot
+    param RsSampler s
+    }
 
 ProgramFragmentCreate {
-	direct
-	param const char * shaderText
-	param const uint32_t * params
-	ret RsProgramFragment
-	}
+    direct
+    param const char * shaderText
+    param const char ** textureNames
+    param const uint32_t * params
+    ret RsProgramFragment
+    }
 
 ProgramVertexCreate {
-	direct
-	param const char * shaderText
-	param const uint32_t * params
-	ret RsProgramVertex
-	}
+    direct
+    param const char * shaderText
+    param const char ** textureNames
+    param const uint32_t * params
+    ret RsProgramVertex
+    }
 
 FontCreateFromFile {
-	param const char *name
-	param float fontSize
-	param uint32_t dpi
-	ret RsFont
-	}
+    param const char *name
+    param float fontSize
+    param uint32_t dpi
+    ret RsFont
+    }
 
 FontCreateFromMemory {
-	param const char *name
-	param float fontSize
-	param uint32_t dpi
-	param const void *data
-	ret RsFont
-	}
+    param const char *name
+    param float fontSize
+    param uint32_t dpi
+    param const void *data
+    ret RsFont
+    }
 
 MeshCreate {
-	param RsAllocation *vtx
-	param RsAllocation *idx
-	param uint32_t *primType
-	ret RsMesh
-	}
+    param RsAllocation *vtx
+    param RsAllocation *idx
+    param uint32_t *primType
+    ret RsMesh
+    }
+
+PathCreate {
+    param RsPathPrimitive pp
+    param bool isStatic
+    param RsAllocation vertex
+    param RsAllocation loops
+    param float quality
+    ret RsPath
+    }
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index fd85b07..83c88fd 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -17,32 +17,35 @@
 #include "rsContext.h"
 #include "rs_hal.h"
 
+#include "system/window.h"
 
 using namespace android;
 using namespace android::renderscript;
 
 Allocation::Allocation(Context *rsc, const Type *type, uint32_t usages,
-                       RsAllocationMipmapControl mc)
+                       RsAllocationMipmapControl mc, void * ptr)
     : ObjectBase(rsc) {
 
     memset(&mHal, 0, sizeof(mHal));
     mHal.state.mipmapControl = RS_ALLOCATION_MIPMAP_NONE;
     mHal.state.usageFlags = usages;
     mHal.state.mipmapControl = mc;
+    mHal.state.usrPtr = ptr;
 
     setType(type);
     updateCache();
 }
 
 Allocation * Allocation::createAllocation(Context *rsc, const Type *type, uint32_t usages,
-                              RsAllocationMipmapControl mc) {
-    Allocation *a = new Allocation(rsc, type, usages, mc);
+                              RsAllocationMipmapControl mc, void * ptr) {
+    Allocation *a = new Allocation(rsc, type, usages, mc, ptr);
 
     if (!rsc->mHal.funcs.allocation.init(rsc, a, type->getElement()->getHasReferences())) {
         rsc->setError(RS_ERROR_FATAL_DRIVER, "Allocation::Allocation, alloc failure");
         delete a;
         return NULL;
     }
+
     return a;
 }
 
@@ -413,6 +416,34 @@
     ALOGE("not implemented");
 }
 
+int32_t Allocation::getSurfaceTextureID(const Context *rsc) {
+    int32_t id = rsc->mHal.funcs.allocation.initSurfaceTexture(rsc, this);
+    mHal.state.surfaceTextureID = id;
+    return id;
+}
+
+void Allocation::setSurface(const Context *rsc, RsNativeWindow sur) {
+    ANativeWindow *nw = (ANativeWindow *)sur;
+    ANativeWindow *old = mHal.state.wndSurface;
+    if (nw) {
+        nw->incStrong(NULL);
+    }
+    rsc->mHal.funcs.allocation.setSurfaceTexture(rsc, this, nw);
+    mHal.state.wndSurface = nw;
+    if (old) {
+        old->decStrong(NULL);
+    }
+}
+
+void Allocation::ioSend(const Context *rsc) {
+    rsc->mHal.funcs.allocation.ioSend(rsc, this);
+}
+
+void Allocation::ioReceive(const Context *rsc) {
+    rsc->mHal.funcs.allocation.ioReceive(rsc, this);
+}
+
+
 /////////////////
 //
 
@@ -571,8 +602,8 @@
 
 RsAllocation rsi_AllocationCreateTyped(Context *rsc, RsType vtype,
                                        RsAllocationMipmapControl mips,
-                                       uint32_t usages) {
-    Allocation * alloc = Allocation::createAllocation(rsc, static_cast<Type *>(vtype), usages, mips);
+                                       uint32_t usages, uint32_t ptr) {
+    Allocation * alloc = Allocation::createAllocation(rsc, static_cast<Type *>(vtype), usages, mips, (void *)ptr);
     if (!alloc) {
         return NULL;
     }
@@ -585,7 +616,7 @@
                                             const void *data, size_t data_length, uint32_t usages) {
     Type *t = static_cast<Type *>(vtype);
 
-    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages);
+    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages, 0);
     Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
     if (texAlloc == NULL) {
         ALOGE("Memory allocation failure");
@@ -609,7 +640,7 @@
     // Cubemap allocation's faces should be Width by Width each.
     // Source data should have 6 * Width by Width pixels
     // Error checking is done in the java layer
-    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages);
+    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages, 0);
     Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
     if (texAlloc == NULL) {
         ALOGE("Memory allocation failure");
@@ -658,6 +689,26 @@
                                            (RsAllocationCubemapFace)srcFace);
 }
 
+int32_t rsi_AllocationGetSurfaceTextureID(Context *rsc, RsAllocation valloc) {
+    Allocation *alloc = static_cast<Allocation *>(valloc);
+    return alloc->getSurfaceTextureID(rsc);
+}
+
+void rsi_AllocationSetSurface(Context *rsc, RsAllocation valloc, RsNativeWindow sur) {
+    Allocation *alloc = static_cast<Allocation *>(valloc);
+    alloc->setSurface(rsc, sur);
+}
+
+void rsi_AllocationIoSend(Context *rsc, RsAllocation valloc) {
+    Allocation *alloc = static_cast<Allocation *>(valloc);
+    alloc->ioSend(rsc);
+}
+
+void rsi_AllocationIoReceive(Context *rsc, RsAllocation valloc) {
+    Allocation *alloc = static_cast<Allocation *>(valloc);
+    alloc->ioReceive(rsc);
+}
+
 }
 }
 
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 20201ca..58a6fca 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -19,6 +19,8 @@
 
 #include "rsType.h"
 
+struct ANativeWindow;
+
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
@@ -55,6 +57,9 @@
             bool hasMipmaps;
             bool hasFaces;
             bool hasReferences;
+            void * usrPtr;
+            int32_t surfaceTextureID;
+            ANativeWindow *wndSurface;
         };
         State state;
 
@@ -66,7 +71,8 @@
     Hal mHal;
 
     static Allocation * createAllocation(Context *rsc, const Type *, uint32_t usages,
-                                  RsAllocationMipmapControl mc = RS_ALLOCATION_MIPMAP_NONE);
+                                         RsAllocationMipmapControl mc = RS_ALLOCATION_MIPMAP_NONE,
+                                         void *ptr = 0);
     virtual ~Allocation();
     void updateCache();
 
@@ -123,6 +129,10 @@
         return mHal.state.mipmapControl != RS_ALLOCATION_MIPMAP_NONE;
     }
 
+    int32_t getSurfaceTextureID(const Context *rsc);
+    void setSurface(const Context *rsc, RsNativeWindow sur);
+    void ioSend(const Context *rsc);
+    void ioReceive(const Context *rsc);
 
 protected:
     Vector<const Program *> mToDirtyList;
@@ -134,7 +144,7 @@
 
 private:
     void freeChildrenUnlocked();
-    Allocation(Context *rsc, const Type *, uint32_t usages, RsAllocationMipmapControl mc);
+    Allocation(Context *rsc, const Type *, uint32_t usages, RsAllocationMipmapControl mc, void *ptr);
 
     uint32_t getPackedSize() const;
     static void writePackedData(const Type *type, uint8_t *dst, const uint8_t *src, bool dstPadded);
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
index 21b98f6..9c2c200 100644
--- a/libs/rs/rsComponent.cpp
+++ b/libs/rs/rsComponent.cpp
@@ -62,6 +62,7 @@
         rsAssert(mNormalized == true);
         break;
     default:
+        rsAssert(mKind != RS_KIND_INVALID);
         break;
     }
 
@@ -167,6 +168,9 @@
     case RS_TYPE_BOOLEAN:
         mTypeBits = 8;
         break;
+    default:
+        rsAssert(mType != RS_TYPE_INVALID);
+        break;
     }
 
     mBitsUnpadded = mTypeBits * mVectorSize;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index ad2ff0f..95ac76e 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -18,6 +18,7 @@
 #include "rsContext.h"
 #include "rsThreadIO.h"
 #include <ui/FramebufferNativeWindow.h>
+#include <gui/DisplayEventReceiver.h>
 
 #include <sys/types.h>
 #include <sys/resource.h>
@@ -245,42 +246,59 @@
     }
 
     rsc->mRunning = true;
-    bool mDraw = true;
-    bool doWait = true;
-
-    uint64_t targetTime = rsc->getTime();
-    while (!rsc->mExit) {
-        uint64_t waitTime = 0;
-        uint64_t now = rsc->getTime();
-        if (!doWait) {
-            if (now < targetTime) {
-                waitTime = targetTime - now;
-                doWait = true;
-            }
+    if (!rsc->mIsGraphicsContext) {
+        while (!rsc->mExit) {
+            rsc->mIO.playCoreCommands(rsc, -1);
         }
+    } else {
+#ifndef ANDROID_RS_SERIALIZE
+        DisplayEventReceiver displayEvent;
+        DisplayEventReceiver::Event eventBuffer[1];
+#endif
+        int vsyncRate = 0;
+        int targetRate = 0;
 
-        mDraw |= rsc->mIO.playCoreCommands(rsc, doWait, waitTime);
-        mDraw &= (rsc->mRootScript.get() != NULL);
-        mDraw &= rsc->mHasSurface;
+        bool drawOnce = false;
+        while (!rsc->mExit) {
+            rsc->timerSet(RS_TIMER_IDLE);
 
-        if (mDraw && rsc->mIsGraphicsContext) {
-            uint64_t delay = rsc->runRootScript() * 1000000;
-            targetTime = rsc->getTime() + delay;
-            doWait = (delay == 0);
-
-            if (rsc->props.mLogVisual) {
-                rsc->displayDebugStats();
+#ifndef ANDROID_RS_SERIALIZE
+            if (!rsc->mRootScript.get() || !rsc->mHasSurface || rsc->mPaused) {
+                targetRate = 0;
             }
 
-            mDraw = !rsc->mPaused;
-            rsc->timerSet(RS_TIMER_CLEAR_SWAP);
-            rsc->mHal.funcs.swap(rsc);
-            rsc->timerFrame();
-            rsc->timerSet(RS_TIMER_INTERNAL);
-            rsc->timerPrint();
-            rsc->timerReset();
-        } else {
-            doWait = true;
+            if (vsyncRate != targetRate) {
+                displayEvent.setVsyncRate(targetRate);
+                vsyncRate = targetRate;
+            }
+            if (targetRate) {
+                drawOnce |= rsc->mIO.playCoreCommands(rsc, displayEvent.getFd());
+                while (displayEvent.getEvents(eventBuffer, 1) != 0) {
+                    //ALOGE("vs2 time past %lld", (rsc->getTime() - eventBuffer[0].header.timestamp) / 1000000);
+                }
+            } else
+#endif
+            {
+                drawOnce |= rsc->mIO.playCoreCommands(rsc, -1);
+            }
+
+            if ((rsc->mRootScript.get() != NULL) && rsc->mHasSurface &&
+                (targetRate || drawOnce) && !rsc->mPaused) {
+
+                drawOnce = false;
+                targetRate = ((rsc->runRootScript() + 15) / 16);
+
+                if (rsc->props.mLogVisual) {
+                    rsc->displayDebugStats();
+                }
+
+                rsc->timerSet(RS_TIMER_CLEAR_SWAP);
+                rsc->mHal.funcs.swap(rsc);
+                rsc->timerFrame();
+                rsc->timerSet(RS_TIMER_INTERNAL);
+                rsc->timerPrint();
+                rsc->timerReset();
+            }
         }
     }
 
@@ -315,8 +333,8 @@
          mFBOCache.deinit(this);
     }
     ObjectBase::freeAllChildren(this);
-    //ALOGV("destroyWorkerThreadResources 2");
     mExit = true;
+    //ALOGV("destroyWorkerThreadResources 2");
 }
 
 void Context::printWatchdogInfo(void *ctx) {
@@ -347,6 +365,7 @@
 #else
     setpriority(PRIO_PROCESS, mNativeThreadId, p);
 #endif
+    mHal.funcs.setPriority(this, mThreadPriority);
 }
 
 Context::Context() {
@@ -382,7 +401,7 @@
     pthread_mutex_lock(&gInitMutex);
 
     mIO.init();
-    mIO.setTimoutCallback(printWatchdogInfo, this, 2e9);
+    mIO.setTimeoutCallback(printWatchdogInfo, this, 2e9);
 
     dev->addContext(this);
     mDev = dev;
@@ -434,14 +453,12 @@
     ALOGV("%p Context::~Context", this);
 
     if (!mIsContextLite) {
-        mIO.coreFlush();
-        rsAssert(mExit);
-        mExit = true;
         mPaused = false;
         void *res;
 
         mIO.shutdown();
         int status = pthread_join(mThreadId, &res);
+        rsAssert(mExit);
 
         if (mHal.funcs.shutdownDriver) {
             mHal.funcs.shutdownDriver(this);
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 61c29f9..05c799e 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -32,6 +32,7 @@
 #include "rsAdapter.h"
 #include "rsSampler.h"
 #include "rsFont.h"
+#include "rsPath.h"
 #include "rsProgramFragment.h"
 #include "rsProgramStore.h"
 #include "rsProgramRaster.h"
@@ -39,7 +40,6 @@
 #include "rsFBOCache.h"
 
 #include "rsgApiStructs.h"
-#include "rsLocklessFifo.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
diff --git a/libs/rs/RenderScriptDefines.h b/libs/rs/rsDefines.h
similarity index 91%
rename from libs/rs/RenderScriptDefines.h
rename to libs/rs/rsDefines.h
index d092520..990ef26 100644
--- a/libs/rs/RenderScriptDefines.h
+++ b/libs/rs/rsDefines.h
@@ -41,6 +41,7 @@
 typedef void * RsSampler;
 typedef void * RsScript;
 typedef void * RsMesh;
+typedef void * RsPath;
 typedef void * RsType;
 typedef void * RsObjectBase;
 
@@ -99,8 +100,11 @@
     RS_ALLOCATION_USAGE_GRAPHICS_VERTEX = 0x0004,
     RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS = 0x0008,
     RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET = 0x0010,
+    RS_ALLOCATION_USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE = 0x0020,
+    RS_ALLOCATION_USAGE_IO_INPUT = 0x0040,
+    RS_ALLOCATION_USAGE_IO_OUTPUT = 0x0080,
 
-    RS_ALLOCATION_USAGE_ALL = 0x000F
+    RS_ALLOCATION_USAGE_ALL = 0x00FF
 };
 
 enum RsAllocationMipmapControl {
@@ -152,6 +156,8 @@
     RS_TYPE_PROGRAM_VERTEX,
     RS_TYPE_PROGRAM_RASTER,
     RS_TYPE_PROGRAM_STORE,
+
+    RS_TYPE_INVALID = 10000,
 };
 
 enum RsDataKind {
@@ -163,6 +169,8 @@
     RS_KIND_PIXEL_RGB,
     RS_KIND_PIXEL_RGBA,
     RS_KIND_PIXEL_DEPTH,
+
+    RS_KIND_INVALID = 100,
 };
 
 enum RsSamplerParam {
@@ -181,6 +189,8 @@
     RS_SAMPLER_WRAP,
     RS_SAMPLER_CLAMP,
     RS_SAMPLER_LINEAR_MIP_NEAREST,
+
+    RS_SAMPLER_INVALID = 100,
 };
 
 enum RsTextureTarget {
@@ -221,7 +231,8 @@
     RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA,   // 5
     RS_BLEND_SRC_DST_ALPHA,             // 6
     RS_BLEND_SRC_ONE_MINUS_DST_ALPHA,   // 7
-    RS_BLEND_SRC_SRC_ALPHA_SATURATE     // 8
+    RS_BLEND_SRC_SRC_ALPHA_SATURATE,    // 8
+    RS_BLEND_SRC_INVALID = 100,
 };
 
 enum RsBlendDstFunc {
@@ -232,7 +243,9 @@
     RS_BLEND_DST_SRC_ALPHA,             // 4
     RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,   // 5
     RS_BLEND_DST_DST_ALPHA,             // 6
-    RS_BLEND_DST_ONE_MINUS_DST_ALPHA    // 7
+    RS_BLEND_DST_ONE_MINUS_DST_ALPHA,   // 7
+
+    RS_BLEND_DST_INVALID = 100,
 };
 
 enum RsTexEnvMode {
@@ -255,7 +268,14 @@
     RS_PRIMITIVE_LINE_STRIP,
     RS_PRIMITIVE_TRIANGLE,
     RS_PRIMITIVE_TRIANGLE_STRIP,
-    RS_PRIMITIVE_TRIANGLE_FAN
+    RS_PRIMITIVE_TRIANGLE_FAN,
+
+    RS_PRIMITIVE_INVALID = 100,
+};
+
+enum RsPathPrimitive {
+    RS_PATH_PRIMITIVE_QUADRATIC_BEZIER,
+    RS_PATH_PRIMITIVE_CUBIC_BEZIER
 };
 
 enum RsError {
@@ -309,7 +329,8 @@
 enum RsCullMode {
     RS_CULL_BACK,
     RS_CULL_FRONT,
-    RS_CULL_NONE
+    RS_CULL_NONE,
+    RS_CULL_INVALID = 100,
 };
 
 typedef struct {
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index dff9585..fb2892c 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -27,6 +27,7 @@
     mFields = NULL;
     mFieldCount = 0;
     mHasReference = false;
+    memset(&mHal, 0, sizeof(mHal));
 }
 
 Element::~Element() {
@@ -47,6 +48,12 @@
     mFields = NULL;
     mFieldCount = 0;
     mHasReference = false;
+
+    delete [] mHal.state.fields;
+    delete [] mHal.state.fieldArraySizes;
+    delete [] mHal.state.fieldNames;
+    delete [] mHal.state.fieldNameLengths;
+    delete [] mHal.state.fieldOffsetBytes;
 }
 
 size_t Element::getSizeBits() const {
@@ -157,16 +164,36 @@
 }
 
 void Element::compute() {
+    mHal.state.dataType = mComponent.getType();
+    mHal.state.dataKind = mComponent.getKind();
+    mHal.state.vectorSize = mComponent.getVectorSize();
+
     if (mFieldCount == 0) {
         mBits = mComponent.getBits();
         mBitsUnpadded = mComponent.getBitsUnpadded();
         mHasReference = mComponent.isReference();
+
+        mHal.state.elementSizeBytes = getSizeBytes();
         return;
     }
 
+    uint32_t noPaddingFieldCount = 0;
+    for (uint32_t ct = 0; ct < mFieldCount; ct ++) {
+        if (mFields[ct].name.string()[0] != '#') {
+            noPaddingFieldCount ++;
+        }
+    }
+
+    mHal.state.fields = new const Element*[noPaddingFieldCount];
+    mHal.state.fieldArraySizes = new uint32_t[noPaddingFieldCount];
+    mHal.state.fieldNames = new const char*[noPaddingFieldCount];
+    mHal.state.fieldNameLengths = new uint32_t[noPaddingFieldCount];
+    mHal.state.fieldOffsetBytes = new uint32_t[noPaddingFieldCount];
+    mHal.state.fieldsCount = noPaddingFieldCount;
+
     size_t bits = 0;
     size_t bitsUnpadded = 0;
-    for (size_t ct=0; ct < mFieldCount; ct++) {
+    for (size_t ct = 0, ctNoPadding = 0; ct < mFieldCount; ct++) {
         mFields[ct].offsetBits = bits;
         mFields[ct].offsetBitsUnpadded = bitsUnpadded;
         bits += mFields[ct].e->getSizeBits() * mFields[ct].arraySize;
@@ -175,8 +202,21 @@
         if (mFields[ct].e->mHasReference) {
             mHasReference = true;
         }
+
+        if (mFields[ct].name.string()[0] == '#') {
+            continue;
+        }
+
+        mHal.state.fields[ctNoPadding] = mFields[ct].e.get();
+        mHal.state.fieldArraySizes[ctNoPadding] = mFields[ct].arraySize;
+        mHal.state.fieldNames[ctNoPadding] = mFields[ct].name.string();
+        mHal.state.fieldNameLengths[ctNoPadding] = mFields[ct].name.length() + 1; // to include 0
+        mHal.state.fieldOffsetBytes[ctNoPadding] = mFields[ct].offsetBits >> 3;
+
+        ctNoPadding ++;
     }
 
+    mHal.state.elementSizeBytes = getSizeBytes();
 }
 
 ObjectBaseRef<const Element> Element::createRef(Context *rsc, RsDataType dt, RsDataKind dk,
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 04010fa..4b6b460 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -24,10 +24,38 @@
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-
+/*****************************************************************************
+ * CAUTION
+ *
+ * Any layout changes for this class may require a corresponding change to be
+ * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains
+ * a partial copy of the information below.
+ *
+ *****************************************************************************/
 // An element is a group of Components that occupies one cell in a structure.
 class Element : public ObjectBase {
 public:
+    struct Hal {
+        mutable void *drv;
+
+        struct State {
+            RsDataType dataType;
+            RsDataKind dataKind;
+            uint32_t vectorSize;
+            uint32_t elementSizeBytes;
+
+            // Subelements
+            const Element **fields;
+            uint32_t *fieldArraySizes;
+            const char **fieldNames;
+            uint32_t *fieldNameLengths;
+            uint32_t *fieldOffsetBytes;
+            uint32_t fieldsCount;
+        };
+        State state;
+    };
+    Hal mHal;
+
     class Builder {
     public:
         Builder();
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/rsEnv.h
similarity index 100%
rename from libs/rs/RenderScriptEnv.h
rename to libs/rs/rsEnv.h
diff --git a/libs/rs/rsFifo.h b/libs/rs/rsFifo.h
index f924b95..911f446 100644
--- a/libs/rs/rsFifo.h
+++ b/libs/rs/rsFifo.h
@@ -35,9 +35,9 @@
     virtual ~Fifo();
 
 public:
-    void virtual writeAsync(const void *data, size_t bytes) = 0;
+    bool virtual writeAsync(const void *data, size_t bytes, bool waitForSpace = true) = 0;
     void virtual writeWaitReturn(void *ret, size_t retSize) = 0;
-    size_t virtual read(void *data, size_t bytes) = 0;
+    size_t virtual read(void *data, size_t bytes, bool doWait = true, uint64_t timeToWait = 0) = 0;
     void virtual readReturn(const void *data, size_t bytes) = 0;
 
     void virtual flush() = 0;
diff --git a/libs/rs/rsFifoSocket.cpp b/libs/rs/rsFifoSocket.cpp
index 163a44b..bd511cf 100644
--- a/libs/rs/rsFifoSocket.cpp
+++ b/libs/rs/rsFifoSocket.cpp
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <unistd.h>
+#include <poll.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
@@ -29,55 +30,79 @@
 using namespace android::renderscript;
 
 FifoSocket::FifoSocket() {
-    sequence = 1;
+    mShutdown = false;
 }
 
 FifoSocket::~FifoSocket() {
 
 }
 
-bool FifoSocket::init() {
+bool FifoSocket::init(bool supportNonBlocking, bool supportReturnValues, size_t maxDataSize) {
     int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
     return false;
 }
 
 void FifoSocket::shutdown() {
+    mShutdown = true;
+    uint64_t d = 0;
+    ::send(sv[0], &d, sizeof(d), 0);
+    ::send(sv[1], &d, sizeof(d), 0);
+    close(sv[0]);
+    close(sv[1]);
 }
 
-void FifoSocket::writeAsync(const void *data, size_t bytes) {
+bool FifoSocket::writeAsync(const void *data, size_t bytes, bool waitForSpace) {
     if (bytes == 0) {
-        return;
+        return true;
     }
     //ALOGE("writeAsync %p %i", data, bytes);
     size_t ret = ::send(sv[0], data, bytes, 0);
     //ALOGE("writeAsync ret %i", ret);
     rsAssert(ret == bytes);
+    return true;
 }
 
 void FifoSocket::writeWaitReturn(void *retData, size_t retBytes) {
+    if (mShutdown) {
+        return;
+    }
+
     //ALOGE("writeWaitReturn %p %i", retData, retBytes);
-    size_t ret = ::recv(sv[0], retData, retBytes, 0);
+    size_t ret = ::recv(sv[0], retData, retBytes, MSG_WAITALL);
     //ALOGE("writeWaitReturn %i", ret);
     rsAssert(ret == retBytes);
 }
 
 size_t FifoSocket::read(void *data, size_t bytes) {
+    if (mShutdown) {
+        return 0;
+    }
+
     //ALOGE("read %p %i", data, bytes);
-    size_t ret = ::recv(sv[1], data, bytes, 0);
-    rsAssert(ret == bytes);
-    //ALOGE("read ret %i", ret);
+    size_t ret = ::recv(sv[1], data, bytes, MSG_WAITALL);
+    rsAssert(ret == bytes || mShutdown);
+    //ALOGE("read ret %i  bytes %i", ret, bytes);
+    if (mShutdown) {
+        ret = 0;
+    }
     return ret;
 }
 
-void FifoSocket::readReturn(const void *data, size_t bytes) {
-    ALOGE("readReturn %p %Zu", data, bytes);
-    size_t ret = ::send(sv[1], data, bytes, 0);
-    ALOGE("readReturn %Zu", ret);
-    rsAssert(ret == bytes);
+bool FifoSocket::isEmpty() {
+    struct pollfd p;
+    p.fd = sv[1];
+    p.events = POLLIN;
+    int r = poll(&p, 1, 0);
+    //ALOGE("poll r=%i", r);
+    return r == 0;
 }
 
 
-void FifoSocket::flush() {
+void FifoSocket::readReturn(const void *data, size_t bytes) {
+    //ALOGE("readReturn %p %Zu", data, bytes);
+    size_t ret = ::send(sv[1], data, bytes, 0);
+    //ALOGE("readReturn %Zu", ret);
+    //rsAssert(ret == bytes);
 }
 
 
diff --git a/libs/rs/rsFifoSocket.h b/libs/rs/rsFifoSocket.h
index 7df2b67..cac0a75 100644
--- a/libs/rs/rsFifoSocket.h
+++ b/libs/rs/rsFifoSocket.h
@@ -29,23 +29,23 @@
     FifoSocket();
     virtual ~FifoSocket();
 
-    bool init();
+    bool init(bool supportNonBlocking = true,
+              bool supportReturnValues = true,
+              size_t maxDataSize = 0);
     void shutdown();
 
+    bool writeAsync(const void *data, size_t bytes, bool waitForSpace = true);
+    void writeWaitReturn(void *ret, size_t retSize);
+    size_t read(void *data, size_t bytes);
+    void readReturn(const void *data, size_t bytes);
+    bool isEmpty();
 
-
-    void virtual writeAsync(const void *data, size_t bytes);
-    void virtual writeWaitReturn(void *ret, size_t retSize);
-    size_t virtual read(void *data, size_t bytes);
-    void virtual readReturn(const void *data, size_t bytes);
-
-    void virtual flush();
+    int getWriteFd() {return sv[0];}
+    int getReadFd() {return sv[1];}
 
 protected:
     int sv[2];
-    uint32_t sequence;
-
-
+    bool mShutdown;
 };
 
 }
diff --git a/libs/rs/rsFileA3D.h b/libs/rs/rsFileA3D.h
index 056b5af..baf81de5 100644
--- a/libs/rs/rsFileA3D.h
+++ b/libs/rs/rsFileA3D.h
@@ -17,11 +17,11 @@
 #ifndef ANDROID_RS_FILE_A3D_H
 #define ANDROID_RS_FILE_A3D_H
 
-#include "RenderScript.h"
+#include "rs.h"
 #include "rsMesh.h"
 
+#include <androidfw/Asset.h>
 #include <utils/String8.h>
-#include <utils/Asset.h>
 #include "rsStream.h"
 #include <stdio.h>
 
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 4f21b3b..c4276cf 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -490,8 +490,14 @@
     shaderString.append("  gl_FragColor = col;\n");
     shaderString.append("}\n");
 
-    ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
-    ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
+    const char *textureNames[] = { "Tex0" };
+    const size_t textureNamesLengths[] = { 4 };
+    size_t numTextures = sizeof(textureNamesLengths)/sizeof(*textureNamesLengths);
+
+    ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32,
+                                                                RS_KIND_USER, false, 4);
+    ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32,
+                                                                RS_KIND_USER, false, 1);
     Element::Builder builder;
     builder.add(colorElem.get(), "Color", 1);
     builder.add(gammaElem.get(), "Gamma", 1);
@@ -506,14 +512,17 @@
     tmp[3] = RS_TEXTURE_2D;
 
     mFontShaderFConstant.set(Allocation::createAllocation(mRSC, inputType.get(),
-                                            RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS));
-    ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
-                                              shaderString.length(), tmp, 4);
+                                                          RS_ALLOCATION_USAGE_SCRIPT |
+                                                          RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS));
+    ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(), shaderString.length(),
+                                              textureNames, numTextures, textureNamesLengths,
+                                              tmp, 4);
     mFontShaderF.set(pf);
     mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0);
 
     mFontSampler.set(Sampler::getSampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
-                                         RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP).get());
+                                         RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP,
+                                         RS_SAMPLER_CLAMP).get());
     mFontShaderF->bindSampler(mRSC, 0, mFontSampler.get());
 
     mFontProgramStore.set(ProgramStore::getProgramStore(mRSC, true, true, true, true,
@@ -525,10 +534,12 @@
 }
 
 void FontState::initTextTexture() {
-    ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1);
+    ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8,
+                                                                RS_KIND_PIXEL_A, true, 1);
 
     // We will allocate a texture to initially hold 32 character bitmaps
-    ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(), 1024, 256, 0, false, false);
+    ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(),
+                                                   1024, 256, 0, false, false);
 
     Allocation *cacheAlloc = Allocation::createAllocation(mRSC, texType.get(),
                                 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE);
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index 4ca794d..88c4795 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_RS_FONT_H
 #define ANDROID_RS_FONT_H
 
-#include "RenderScript.h"
+#include "rs.h"
 #include "rsStream.h"
 #include <utils/String8.h>
 #include <utils/Vector.h>
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
deleted file mode 100644
index 0466d8b..0000000
--- a/libs/rs/rsLocklessFifo.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "rsLocklessFifo.h"
-#include "utils/Timers.h"
-#include "utils/StopWatch.h"
-
-using namespace android;
-using namespace android::renderscript;
-
-LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0), mInitialized(false) {
-    mTimeoutCallback = NULL;
-    mTimeoutCallbackData = NULL;
-    mTimeoutWait = 0;
-}
-
-LocklessCommandFifo::~LocklessCommandFifo() {
-    if (!mInShutdown && mInitialized) {
-        shutdown();
-    }
-    if (mBuffer) {
-        free(mBuffer);
-    }
-}
-
-void LocklessCommandFifo::shutdown() {
-    mInShutdown = true;
-    mSignalToWorker.set();
-}
-
-bool LocklessCommandFifo::init(uint32_t sizeInBytes) {
-    // Add room for a buffer reset command
-    mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4));
-    if (!mBuffer) {
-        ALOGE("LocklessFifo allocation failure");
-        return false;
-    }
-
-    if (!mSignalToControl.init() || !mSignalToWorker.init()) {
-        ALOGE("Signal setup failed");
-        free(mBuffer);
-        return false;
-    }
-
-    mInShutdown = false;
-    mSize = sizeInBytes;
-    mPut = mBuffer;
-    mGet = mBuffer;
-    mEnd = mBuffer + (sizeInBytes) - 1;
-    //dumpState("init");
-    mInitialized = true;
-    return true;
-}
-
-uint32_t LocklessCommandFifo::getFreeSpace() const {
-    int32_t freeSpace = 0;
-    //dumpState("getFreeSpace");
-
-    if (mPut >= mGet) {
-        freeSpace = mEnd - mPut;
-    } else {
-        freeSpace = mGet - mPut;
-    }
-
-    if (freeSpace < 0) {
-        freeSpace = 0;
-    }
-    return freeSpace;
-}
-
-bool LocklessCommandFifo::isEmpty() const {
-    uint32_t p = android_atomic_acquire_load((int32_t *)&mPut);
-    return ((uint8_t *)p) == mGet;
-}
-
-
-void * LocklessCommandFifo::reserve(uint32_t sizeInBytes) {
-    // Add space for command header and loop token;
-    sizeInBytes += 8;
-
-    //dumpState("reserve");
-    if (getFreeSpace() < sizeInBytes) {
-        makeSpace(sizeInBytes);
-    }
-
-    return mPut + 4;
-}
-
-void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes) {
-    if (mInShutdown) {
-        return;
-    }
-    //dumpState("commit 1");
-    reinterpret_cast<uint16_t *>(mPut)[0] = command;
-    reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes;
-
-    int32_t s = ((sizeInBytes + 3) & ~3) + 4;
-    android_atomic_add(s, (int32_t *)&mPut);
-    //dumpState("commit 2");
-    mSignalToWorker.set();
-}
-
-void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes) {
-    if (mInShutdown) {
-        return;
-    }
-
-    //char buf[1024];
-    //sprintf(buf, "RenderScript LocklessCommandFifo::commitSync  %p %i  %i", this, command, sizeInBytes);
-    //StopWatch compileTimer(buf);
-    commit(command, sizeInBytes);
-    flush();
-}
-
-void LocklessCommandFifo::flush() {
-    //dumpState("flush 1");
-    while (mPut != mGet) {
-        while (!mSignalToControl.wait(mTimeoutWait)) {
-            if (mTimeoutCallback) {
-                mTimeoutCallback(mTimeoutCallbackData);
-            }
-        }
-    }
-    //dumpState("flush 2");
-}
-
-void LocklessCommandFifo::setTimoutCallback(void (*cbk)(void *), void *data, uint64_t timeout) {
-    mTimeoutCallback = cbk;
-    mTimeoutCallbackData = data;
-    mTimeoutWait = timeout;
-}
-
-bool LocklessCommandFifo::wait(uint64_t timeout) {
-    while (isEmpty() && !mInShutdown) {
-        mSignalToControl.set();
-        return mSignalToWorker.wait(timeout);
-    }
-    return true;
-}
-
-const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData, uint64_t timeout) {
-    while (1) {
-        //dumpState("get");
-        wait(timeout);
-
-        if (isEmpty() || mInShutdown) {
-            *command = 0;
-            *bytesData = 0;
-            return NULL;
-        }
-
-        *command = reinterpret_cast<const uint16_t *>(mGet)[0];
-        *bytesData = reinterpret_cast<const uint16_t *>(mGet)[1];
-        if (*command) {
-            // non-zero command is valid
-            return mGet+4;
-        }
-
-        // zero command means reset to beginning.
-        mGet = mBuffer;
-    }
-}
-
-void LocklessCommandFifo::next() {
-    uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1];
-
-    android_atomic_add(((bytes + 3) & ~3) + 4, (int32_t *)&mGet);
-    //mGet += ((bytes + 3) & ~3) + 4;
-    if (isEmpty()) {
-        mSignalToControl.set();
-    }
-    //dumpState("next");
-}
-
-bool LocklessCommandFifo::makeSpaceNonBlocking(uint32_t bytes) {
-    //dumpState("make space non-blocking");
-    if ((mPut+bytes) > mEnd) {
-        // Need to loop regardless of where get is.
-        if ((mGet > mPut) || (mBuffer+4 >= mGet)) {
-            return false;
-        }
-
-        // Toss in a reset then the normal wait for space will do the rest.
-        reinterpret_cast<uint16_t *>(mPut)[0] = 0;
-        reinterpret_cast<uint16_t *>(mPut)[1] = 0;
-        mPut = mBuffer;
-        mSignalToWorker.set();
-    }
-
-    // it will fit here so we just need to wait for space.
-    if (getFreeSpace() < bytes) {
-        return false;
-    }
-
-    return true;
-}
-
-void LocklessCommandFifo::makeSpace(uint32_t bytes) {
-    //dumpState("make space");
-    if ((mPut+bytes) > mEnd) {
-        // Need to loop regardless of where get is.
-        while ((mGet > mPut) || (mBuffer+4 >= mGet)) {
-            usleep(100);
-        }
-
-        // Toss in a reset then the normal wait for space will do the rest.
-        reinterpret_cast<uint16_t *>(mPut)[0] = 0;
-        reinterpret_cast<uint16_t *>(mPut)[1] = 0;
-        mPut = mBuffer;
-        mSignalToWorker.set();
-    }
-
-    // it will fit here so we just need to wait for space.
-    while (getFreeSpace() < bytes) {
-        usleep(100);
-    }
-
-}
-
-void LocklessCommandFifo::dumpState(const char *s) const {
-    ALOGV("%s %p  put %p, get %p,  buf %p,  end %p", s, this, mPut, mGet, mBuffer, mEnd);
-}
-
-void LocklessCommandFifo::printDebugData() const {
-    dumpState("printing fifo debug");
-    const uint32_t *pptr = (const uint32_t *)mGet;
-    pptr -= 8 * 4;
-    if (mGet < mBuffer) {
-        pptr = (const uint32_t *)mBuffer;
-    }
-
-
-    for (int ct=0; ct < 16; ct++) {
-        ALOGV("fifo %p = 0x%08x  0x%08x  0x%08x  0x%08x", pptr, pptr[0], pptr[1], pptr[2], pptr[3]);
-        pptr += 4;
-    }
-
-}
diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h
deleted file mode 100644
index dafc512..0000000
--- a/libs/rs/rsLocklessFifo.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_RS_LOCKLESS_FIFO_H
-#define ANDROID_RS_LOCKLESS_FIFO_H
-
-
-#include "rsUtils.h"
-#include "rsSignal.h"
-
-namespace android {
-namespace renderscript {
-
-
-// A simple FIFO to be used as a producer / consumer between two
-// threads.  One is writer and one is reader.  The common cases
-// will not require locking.  It is not threadsafe for multiple
-// readers or writers by design.
-
-class LocklessCommandFifo {
-public:
-    bool init(uint32_t size);
-    void shutdown();
-    void setTimoutCallback(void (*)(void *), void *, uint64_t timeout);
-
-    void printDebugData() const;
-
-    LocklessCommandFifo();
-    ~LocklessCommandFifo();
-
-protected:
-    uint8_t * volatile mPut;
-    uint8_t * volatile mGet;
-    uint8_t * mBuffer;
-    uint8_t * mEnd;
-    uint8_t mSize;
-    bool mInShutdown;
-    bool mInitialized;
-
-    Signal mSignalToWorker;
-    Signal mSignalToControl;
-
-public:
-    void * reserve(uint32_t bytes);
-    void commit(uint32_t command, uint32_t bytes);
-    void commitSync(uint32_t command, uint32_t bytes);
-
-    void flush();
-    bool wait(uint64_t timeout = 0);
-
-    const void * get(uint32_t *command, uint32_t *bytesData, uint64_t timeout = 0);
-    void next();
-
-    void makeSpace(uint32_t bytes);
-    bool makeSpaceNonBlocking(uint32_t bytes);
-
-    bool isEmpty() const;
-    uint32_t getFreeSpace() const;
-
-private:
-    void dumpState(const char *) const;
-
-    void (*mTimeoutCallback)(void *);
-    void * mTimeoutCallbackData;
-    uint64_t mTimeoutWait;
-};
-
-
-}
-}
-#endif
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
index 0fc73fb..8eea427 100644
--- a/libs/rs/rsMesh.h
+++ b/libs/rs/rsMesh.h
@@ -18,25 +18,23 @@
 #define ANDROID_RS_MESH_H
 
 
-#include "RenderScript.h"
+#include "rs.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-
+/*****************************************************************************
+ * CAUTION
+ *
+ * Any layout changes for this class may require a corresponding change to be
+ * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains
+ * a partial copy of the information below.
+ *
+ *****************************************************************************/
 
 // An element is a group of Components that occupies one cell in a structure.
 class Mesh : public ObjectBase {
 public:
-    Mesh(Context *);
-    Mesh(Context *, uint32_t vertexBuffersCount, uint32_t primitivesCount);
-    ~Mesh();
-
-    virtual void serialize(OStream *stream) const;
-    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; }
-    static Mesh *createFromStream(Context *rsc, IStream *stream);
-    void init();
-
     struct Hal {
         mutable void *drv;
 
@@ -57,6 +55,15 @@
     };
     Hal mHal;
 
+    Mesh(Context *);
+    Mesh(Context *, uint32_t vertexBuffersCount, uint32_t primitivesCount);
+    ~Mesh();
+
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; }
+    static Mesh *createFromStream(Context *rsc, IStream *stream);
+    void init();
+
     void setVertexBuffer(Allocation *vb, uint32_t index) {
         mVertexBuffers[index].set(vb);
         mHal.state.vertexBuffers[index] = vb;
diff --git a/libs/rs/rsPath.cpp b/libs/rs/rsPath.cpp
new file mode 100644
index 0000000..c4f4978
--- /dev/null
+++ b/libs/rs/rsPath.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Path::Path(Context *rsc) : ObjectBase(rsc) {
+}
+
+Path::Path(Context *rsc, RsPathPrimitive pp, bool isStatic,
+                      Allocation *vtx, Allocation *loops, float quality)
+: ObjectBase(rsc) {
+
+    memset(&mHal, 0, sizeof(mHal));
+    mHal.state.quality = quality;
+    mHal.state.primitive = pp;
+
+    //LOGE("i1");
+    rsc->mHal.funcs.path.initStatic(rsc, this, vtx, loops);
+
+    //LOGE("i2");
+}
+
+Path::Path(Context *rsc, uint32_t vertexBuffersCount, uint32_t primitivesCount)
+: ObjectBase(rsc) {
+
+}
+
+Path::~Path() {
+
+}
+
+
+void Path::rasterize(const BezierSegment_t *s, uint32_t num, Allocation *alloc) {
+
+    for (uint32_t i=0; i < num; i++) {
+
+    }
+
+}
+
+void Path::render(Context *rsc) {
+}
+
+void Path::serialize(OStream *stream) const {
+
+}
+
+RsA3DClassID Path::getClassId() const {
+    return RS_A3D_CLASS_ID_UNKNOWN;
+}
+
+namespace android {
+namespace renderscript {
+
+RsPath rsi_PathCreate(Context *rsc, RsPathPrimitive pp, bool isStatic,
+                      RsAllocation vtx, RsAllocation loops, float quality) {
+    return new Path(rsc, pp, isStatic, (Allocation *)vtx, (Allocation *)loops, quality);
+}
+
+}
+}
diff --git a/libs/rs/rsPath.h b/libs/rs/rsPath.h
new file mode 100644
index 0000000..7c05503
--- /dev/null
+++ b/libs/rs/rsPath.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_PATH_H
+#define ANDROID_RS_PATH_H
+
+
+#include "rs.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Path : public ObjectBase {
+public:
+    struct {
+        mutable void * drv;
+
+        struct State {
+            RsPathPrimitive primitive;
+            float quality;
+        };
+        State state;
+    } mHal;
+
+    Path(Context *);
+    Path(Context *, uint32_t vertexBuffersCount, uint32_t primitivesCount);
+    Path(Context *, RsPathPrimitive pp, bool isStatic, Allocation *vtx, Allocation *loop, float q);
+
+    ~Path();
+
+    void render(Context *);
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const;
+
+private:
+
+
+    typedef struct {
+        float x[4];
+        float y[4];
+    } BezierSegment_t;
+
+    bool subdivideCheck(const BezierSegment_t *s, float u1, float u2);
+
+    void rasterize(const BezierSegment_t *s, uint32_t num, Allocation *alloc);
+
+
+};
+
+}
+}
+#endif //ANDROID_RS_PATH_H
+
+
+
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 8061515..7114f29 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -20,8 +20,8 @@
 using namespace android;
 using namespace android::renderscript;
 
-Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength,
-                 const uint32_t * params, uint32_t paramLength)
+Program::Program(Context *rsc, const char * shaderText, size_t shaderLength,
+                 const uint32_t * params, size_t paramLength)
     : ProgramBase(rsc) {
 
     initMemberVars();
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index 06fc3ec..d032930 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -58,8 +58,8 @@
     };
     Hal mHal;
 
-    Program(Context *, const char * shaderText, uint32_t shaderLength,
-                       const uint32_t * params, uint32_t paramLength);
+    Program(Context *, const char * shaderText, size_t shaderLength,
+            const uint32_t * params, size_t paramLength);
     virtual ~Program();
     virtual bool freeChildren();
 
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 4e73ca6..bebde1e 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -20,16 +20,18 @@
 using namespace android;
 using namespace android::renderscript;
 
-ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText,
-                                 uint32_t shaderLength, const uint32_t * params,
-                                 uint32_t paramLength)
+ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText, size_t shaderLength,
+                                 const char** textureNames, size_t textureNamesCount, const size_t *textureNamesLength,
+
+                                 const uint32_t * params, size_t paramLength)
     : Program(rsc, shaderText, shaderLength, params, paramLength) {
     mConstantColor[0] = 1.f;
     mConstantColor[1] = 1.f;
     mConstantColor[2] = 1.f;
     mConstantColor[3] = 1.f;
 
-    mRSC->mHal.funcs.fragment.init(mRSC, this, mUserShader.string(), mUserShader.length());
+    mRSC->mHal.funcs.fragment.init(mRSC, this, mUserShader.string(), mUserShader.length(),
+                                   textureNames, textureNamesCount, textureNamesLength);
 }
 
 ProgramFragment::~ProgramFragment() {
@@ -110,8 +112,8 @@
 
     Allocation *constAlloc = Allocation::createAllocation(rsc, inputType.get(),
                               RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS);
-    ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(),
-                                              shaderString.length(), tmp, 2);
+    ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(), shaderString.length(),
+                                              NULL, 0, NULL, tmp, 2);
     pf->bindAllocation(rsc, constAlloc, 0);
     pf->setConstantColor(rsc, 1.0f, 1.0f, 1.0f, 1.0f);
 
@@ -127,9 +129,14 @@
 namespace renderscript {
 
 RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, const char * shaderText,
-                             size_t shaderLength, const uint32_t * params,
-                             size_t paramLength) {
-    ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength, params, paramLength);
+                                            size_t shaderLength,
+                                            const char** textureNames,
+                                            size_t textureNamesCount,
+                                            const size_t *textureNamesLength,
+                                            const uint32_t * params, size_t paramLength) {
+    ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength,
+                                              textureNames, textureNamesCount, textureNamesLength,
+                                              params, paramLength);
     pf->incUserRef();
     //ALOGE("rsi_ProgramFragmentCreate %p", pf);
     return pf;
diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h
index d6e20cd..4eb28e7 100644
--- a/libs/rs/rsProgramFragment.h
+++ b/libs/rs/rsProgramFragment.h
@@ -27,9 +27,9 @@
 
 class ProgramFragment : public Program {
 public:
-    ProgramFragment(Context *rsc, const char * shaderText,
-                             uint32_t shaderLength, const uint32_t * params,
-                             uint32_t paramLength);
+    ProgramFragment(Context *rsc, const char * shaderText, size_t shaderLength,
+                    const char** textureNames, size_t textureNamesCount, const size_t *textureNamesLength,
+                     const uint32_t * params, size_t paramLength);
     virtual ~ProgramFragment();
 
     virtual void setup(Context *, ProgramFragmentState *);
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 871caac..c8a53ea 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -21,11 +21,13 @@
 using namespace android::renderscript;
 
 
-ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText,
-                             uint32_t shaderLength, const uint32_t * params,
-                             uint32_t paramLength)
+ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText, size_t shaderLength,
+                             const char** textureNames, size_t textureNamesCount, const size_t *textureNamesLength,
+
+                             const uint32_t * params, size_t paramLength)
     : Program(rsc, shaderText, shaderLength, params, paramLength) {
-    mRSC->mHal.funcs.vertex.init(mRSC, this, mUserShader.string(), mUserShader.length());
+    mRSC->mHal.funcs.vertex.init(mRSC, this, mUserShader.string(), mUserShader.length(),
+                                 textureNames, textureNamesCount, textureNamesLength);
 }
 
 ProgramVertex::~ProgramVertex() {
@@ -191,8 +193,8 @@
     tmp[2] = RS_PROGRAM_PARAM_INPUT;
     tmp[3] = (uint32_t)attrElem.get();
 
-    ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(),
-                                          shaderString.length(), tmp, 4);
+    ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(), shaderString.length(),
+                                          NULL, 0, NULL, tmp, 4);
     Allocation *alloc = Allocation::createAllocation(rsc, inputType.get(),
                               RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS);
     pv->bindAllocation(rsc, alloc, 0);
@@ -229,10 +231,13 @@
 namespace android {
 namespace renderscript {
 
-RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText,
-                             size_t shaderLength, const uint32_t * params,
-                             size_t paramLength) {
-    ProgramVertex *pv = new ProgramVertex(rsc, shaderText, shaderLength, params, paramLength);
+RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText, size_t shaderLength,
+                                        const char** textureNames, size_t textureNamesCount,
+                                        const size_t *textureNamesLength,
+                                        const uint32_t * params, size_t paramLength) {
+    ProgramVertex *pv = new ProgramVertex(rsc, shaderText, shaderLength,
+                                          textureNames, textureNamesCount, textureNamesLength,
+                                          params, paramLength);
     pv->incUserRef();
     return pv;
 }
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 5cfdd8b..67c2a88 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -27,8 +27,9 @@
 
 class ProgramVertex : public Program {
 public:
-    ProgramVertex(Context *,const char * shaderText, uint32_t shaderLength,
-                  const uint32_t * params, uint32_t paramLength);
+    ProgramVertex(Context *,const char * shaderText, size_t shaderLength,
+                  const char** textureNames, size_t textureNamesCount, const size_t *textureNamesLength,
+                  const uint32_t * params, size_t paramLength);
     virtual ~ProgramVertex();
 
     virtual void setup(Context *rsc, ProgramVertexState *state);
diff --git a/libs/rs/rsRuntime.h b/libs/rs/rsRuntime.h
index cb962a8..3bded62 100644
--- a/libs/rs/rsRuntime.h
+++ b/libs/rs/rsRuntime.h
@@ -30,6 +30,8 @@
 //////////////////////////////////////////////////////////////////////////////
 
 void rsrBindTexture(Context *, Script *, ProgramFragment *, uint32_t slot, Allocation *);
+void rsrBindConstant(Context *, Script *, ProgramFragment *, uint32_t slot, Allocation *);
+void rsrBindConstant(Context *, Script *, ProgramVertex*, uint32_t slot, Allocation *);
 void rsrBindSampler(Context *, Script *, ProgramFragment *, uint32_t slot, Sampler *);
 void rsrBindProgramStore(Context *, Script *, ProgramStore *);
 void rsrBindProgramFragment(Context *, Script *, ProgramFragment *);
@@ -68,6 +70,7 @@
 void rsrDrawSpriteScreenspace(Context *, Script *,
                               float x, float y, float z, float w, float h);
 void rsrDrawRect(Context *, Script *, float x1, float y1, float x2, float y2, float z);
+void rsrDrawPath(Context *, Script *, Path *);
 void rsrDrawMesh(Context *, Script *, Mesh *);
 void rsrDrawMeshPrimitive(Context *, Script *, Mesh *, uint32_t primIndex);
 void rsrDrawMeshPrimitiveRange(Context *, Script *, Mesh *,
diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h
index 654cd9c..013e4ca 100644
--- a/libs/rs/rsSampler.h
+++ b/libs/rs/rsSampler.h
@@ -18,7 +18,7 @@
 #define ANDROID_RS_SAMPLER_H
 
 #include "rsAllocation.h"
-#include "RenderScript.h"
+#include "rs.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
index 357dbe3..6a3bd4b 100644
--- a/libs/rs/rsScript.cpp
+++ b/libs/rs/rsScript.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2009-2012 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.
@@ -113,7 +113,7 @@
                        RsAllocation vain, RsAllocation vaout,
                        const void *params, size_t paramLen) {
     Script *s = static_cast<Script *>(vs);
-    s->runForEach(rsc,
+    s->runForEach(rsc, slot,
                   static_cast<const Allocation *>(vain), static_cast<Allocation *>(vaout),
                   params, paramLen);
 
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index 99dceaf..7879ea6 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2009-2012 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.
@@ -74,6 +74,7 @@
     virtual bool freeChildren();
 
     virtual void runForEach(Context *rsc,
+                            uint32_t slot,
                             const Allocation * ain,
                             Allocation * aout,
                             const void * usr,
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index b4eb995..79725b97 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2009-2012 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.
@@ -128,6 +128,7 @@
 
 
 void ScriptC::runForEach(Context *rsc,
+                         uint32_t slot,
                          const Allocation * ain,
                          Allocation * aout,
                          const void * usr,
@@ -138,7 +139,7 @@
 
     setupGLState(rsc);
     setupScript(rsc);
-    rsc->mHal.funcs.script.invokeForEach(rsc, this, 0, ain, aout, usr, usrBytes, sc);
+    rsc->mHal.funcs.script.invokeForEach(rsc, this, slot, ain, aout, usr, usrBytes, sc);
 }
 
 void ScriptC::Invoke(Context *rsc, uint32_t slot, const void *data, size_t len) {
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index c65a5bf..92e1f4f 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2009-2012 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.
@@ -19,7 +19,7 @@
 
 #include "rsScript.h"
 
-#include "RenderScriptEnv.h"
+#include "rsEnv.h"
 
 #ifndef ANDROID_RS_SERIALIZE
 #include "bcinfo/BitcodeTranslator.h"
@@ -47,6 +47,7 @@
     virtual uint32_t run(Context *);
 
     virtual void runForEach(Context *rsc,
+                            uint32_t slot,
                             const Allocation * ain,
                             Allocation * aout,
                             const void * usr,
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 183e207..a5a0fae 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2009-2012 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.
@@ -157,7 +157,7 @@
                 Allocation *in, Allocation *out,
                 const void *usr, uint32_t usrBytes,
                 const RsScriptCall *call) {
-    target->runForEach(rsc, in, out, usr, usrBytes, call);
+    target->runForEach(rsc, /* root slot */ 0, in, out, usr, usrBytes, call);
 }
 
 void rsrAllocationSyncAll(Context *rsc, Script *sc, Allocation *a, RsAllocationUsageType usage) {
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index 7964792..97469d3 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -50,6 +50,18 @@
     pf->bindTexture(rsc, slot, a);
 }
 
+void rsrBindConstant(Context *rsc, Script *sc, ProgramFragment *pf, uint32_t slot, Allocation *a) {
+    CHECK_OBJ_OR_NULL(a);
+    CHECK_OBJ(pf);
+    pf->bindAllocation(rsc, a, slot);
+}
+
+void rsrBindConstant(Context *rsc, Script *sc, ProgramVertex *pv, uint32_t slot, Allocation *a) {
+    CHECK_OBJ_OR_NULL(a);
+    CHECK_OBJ(pv);
+    pv->bindAllocation(rsc, a, slot);
+}
+
 void rsrBindSampler(Context *rsc, Script *sc, ProgramFragment *pf, uint32_t slot, Sampler *s) {
     CHECK_OBJ_OR_NULL(vs);
     CHECK_OBJ(vpf);
@@ -200,6 +212,14 @@
     rsrDrawQuad(rsc, sc, x1, y2, z, x2, y2, z, x2, y1, z, x1, y1, z);
 }
 
+void rsrDrawPath(Context *rsc, Script *sc, Path *sm) {
+    CHECK_OBJ(sm);
+    if (!rsc->setupCheck()) {
+        return;
+    }
+    sm->render(rsc);
+}
+
 void rsrDrawMesh(Context *rsc, Script *sc, Mesh *sm) {
     CHECK_OBJ(sm);
     if (!rsc->setupCheck()) {
diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
index 1917774..7182f53 100644
--- a/libs/rs/rsThreadIO.cpp
+++ b/libs/rs/rsThreadIO.cpp
@@ -18,227 +18,211 @@
 
 #include "rsThreadIO.h"
 
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <fcntl.h>
+#include <poll.h>
+
+
 using namespace android;
 using namespace android::renderscript;
 
-ThreadIO::ThreadIO() : mUsingSocket(false) {
+ThreadIO::ThreadIO() {
+    mRunning = true;
+    mPureFifo = false;
+    mMaxInlineSize = 1024;
 }
 
 ThreadIO::~ThreadIO() {
 }
 
-void ThreadIO::init(bool useSocket) {
-    mUsingSocket = useSocket;
-    mToCore.init(16 * 1024);
-
-    if (mUsingSocket) {
-        mToClientSocket.init();
-        mToCoreSocket.init();
-    } else {
-        mToClient.init(1024);
-    }
+void ThreadIO::init() {
+    mToClient.init();
+    mToCore.init();
 }
 
 void ThreadIO::shutdown() {
-    //ALOGE("shutdown 1");
+    mRunning = false;
     mToCore.shutdown();
-    //ALOGE("shutdown 2");
-}
-
-void ThreadIO::coreFlush() {
-    //ALOGE("coreFlush 1");
-    if (mUsingSocket) {
-    } else {
-        mToCore.flush();
-    }
-    //ALOGE("coreFlush 2");
 }
 
 void * ThreadIO::coreHeader(uint32_t cmdID, size_t dataLen) {
     //ALOGE("coreHeader %i %i", cmdID, dataLen);
-    if (mUsingSocket) {
-        CoreCmdHeader hdr;
-        hdr.bytes = dataLen;
-        hdr.cmdID = cmdID;
-        mToCoreSocket.writeAsync(&hdr, sizeof(hdr));
-    } else {
-        mCoreCommandSize = dataLen;
-        mCoreCommandID = cmdID;
-        mCoreDataPtr = (uint8_t *)mToCore.reserve(dataLen);
-        mCoreDataBasePtr = mCoreDataPtr;
-    }
-    //ALOGE("coreHeader ret %p", mCoreDataPtr);
-    return mCoreDataPtr;
-}
-
-void ThreadIO::coreData(const void *data, size_t dataLen) {
-    //ALOGE("coreData %p %i", data, dataLen);
-    mToCoreSocket.writeAsync(data, dataLen);
-    //ALOGE("coreData ret %p", mCoreDataPtr);
+    CoreCmdHeader *hdr = (CoreCmdHeader *)&mSendBuffer[0];
+    hdr->bytes = dataLen;
+    hdr->cmdID = cmdID;
+    mSendLen = dataLen + sizeof(CoreCmdHeader);
+    //mToCoreSocket.writeAsync(&hdr, sizeof(hdr));
+    //ALOGE("coreHeader ret ");
+    return &mSendBuffer[sizeof(CoreCmdHeader)];
 }
 
 void ThreadIO::coreCommit() {
-    //ALOGE("coreCommit %p %p %i", mCoreDataPtr, mCoreDataBasePtr, mCoreCommandSize);
-    if (mUsingSocket) {
-    } else {
-        rsAssert((size_t)(mCoreDataPtr - mCoreDataBasePtr) <= mCoreCommandSize);
-        mToCore.commit(mCoreCommandID, mCoreCommandSize);
-    }
-    //ALOGE("coreCommit ret");
-}
-
-void ThreadIO::coreCommitSync() {
-    //ALOGE("coreCommitSync %p %p %i", mCoreDataPtr, mCoreDataBasePtr, mCoreCommandSize);
-    if (mUsingSocket) {
-    } else {
-        rsAssert((size_t)(mCoreDataPtr - mCoreDataBasePtr) <= mCoreCommandSize);
-        mToCore.commitSync(mCoreCommandID, mCoreCommandSize);
-    }
-    //ALOGE("coreCommitSync ret");
+    mToCore.writeAsync(&mSendBuffer, mSendLen);
 }
 
 void ThreadIO::clientShutdown() {
-    //ALOGE("coreShutdown 1");
     mToClient.shutdown();
-    //ALOGE("coreShutdown 2");
+}
+
+void ThreadIO::coreWrite(const void *data, size_t len) {
+    //ALOGV("core write %p %i", data, (int)len);
+    mToCore.writeAsync(data, len, true);
+}
+
+void ThreadIO::coreRead(void *data, size_t len) {
+    //ALOGV("core read %p %i", data, (int)len);
+    mToCore.read(data, len);
 }
 
 void ThreadIO::coreSetReturn(const void *data, size_t dataLen) {
-    rsAssert(dataLen <= sizeof(mToCoreRet));
-    memcpy(&mToCoreRet, data, dataLen);
+    uint32_t buf;
+    if (data == NULL) {
+        data = &buf;
+        dataLen = sizeof(buf);
+    }
+
+    mToCore.readReturn(data, dataLen);
 }
 
 void ThreadIO::coreGetReturn(void *data, size_t dataLen) {
-    memcpy(data, &mToCoreRet, dataLen);
+    uint32_t buf;
+    if (data == NULL) {
+        data = &buf;
+        dataLen = sizeof(buf);
+    }
+
+    mToCore.writeWaitReturn(data, dataLen);
 }
 
-void ThreadIO::setTimoutCallback(void (*cb)(void *), void *dat, uint64_t timeout) {
-    mToCore.setTimoutCallback(cb, dat, timeout);
+void ThreadIO::setTimeoutCallback(void (*cb)(void *), void *dat, uint64_t timeout) {
+    //mToCore.setTimeoutCallback(cb, dat, timeout);
 }
 
-
-bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand, uint64_t timeToWait) {
+bool ThreadIO::playCoreCommands(Context *con, int waitFd) {
     bool ret = false;
-    uint64_t startTime = con->getTime();
+    const bool isLocal = !isPureFifo();
 
-    while (!mToCore.isEmpty() || waitForCommand) {
-        uint32_t cmdID = 0;
-        uint32_t cmdSize = 0;
-        if (con->props.mLogTimes) {
-            con->timerSet(Context::RS_TIMER_IDLE);
+    uint8_t buf[2 * 1024];
+    const CoreCmdHeader *cmd = (const CoreCmdHeader *)&buf[0];
+    const void * data = (const void *)&buf[sizeof(CoreCmdHeader)];
+
+    struct pollfd p[2];
+    p[0].fd = mToCore.getReadFd();
+    p[0].events = POLLIN;
+    p[0].revents = 0;
+    p[1].fd = waitFd;
+    p[1].events = POLLIN;
+    p[1].revents = 0;
+    int pollCount = 1;
+    if (waitFd >= 0) {
+        pollCount = 2;
+    }
+
+    if (con->props.mLogTimes) {
+        con->timerSet(Context::RS_TIMER_IDLE);
+    }
+
+    int waitTime = -1;
+    while (mRunning) {
+        int pr = poll(p, pollCount, waitTime);
+        if (pr <= 0) {
+            break;
         }
 
-        uint64_t delay = 0;
-        if (waitForCommand) {
-            delay = timeToWait - (con->getTime() - startTime);
-            if (delay > timeToWait) {
-                delay = 0;
+        if (p[0].revents) {
+            size_t r = 0;
+            if (isLocal) {
+                r = mToCore.read(&buf[0], sizeof(CoreCmdHeader));
+                mToCore.read(&buf[sizeof(CoreCmdHeader)], cmd->bytes);
+                if (r != sizeof(CoreCmdHeader)) {
+                    // exception or timeout occurred.
+                    break;
+                }
+            } else {
+                r = mToCore.read((void *)&cmd->cmdID, sizeof(cmd->cmdID));
+            }
+
+
+            ret = true;
+            if (con->props.mLogTimes) {
+                con->timerSet(Context::RS_TIMER_INTERNAL);
+            }
+            //ALOGV("playCoreCommands 3 %i %i", cmd->cmdID, cmd->bytes);
+
+            if (cmd->cmdID >= (sizeof(gPlaybackFuncs) / sizeof(void *))) {
+                rsAssert(cmd->cmdID < (sizeof(gPlaybackFuncs) / sizeof(void *)));
+                ALOGE("playCoreCommands error con %p, cmd %i", con, cmd->cmdID);
+            }
+
+            if (isLocal) {
+                gPlaybackFuncs[cmd->cmdID](con, data, cmd->bytes);
+            } else {
+                gPlaybackRemoteFuncs[cmd->cmdID](con, this);
+            }
+
+            if (con->props.mLogTimes) {
+                con->timerSet(Context::RS_TIMER_IDLE);
+            }
+
+            if (waitFd < 0) {
+                // If we don't have a secondary wait object we should stop blocking now
+                // that at least one command has been processed.
+                waitTime = 0;
             }
         }
 
-        if (delay == 0 && timeToWait != 0 && mToCore.isEmpty()) {
+        if (p[1].revents && !p[0].revents) {
+            // We want to finish processing fifo events before processing the vsync.
+            // Otherwise we can end up falling behind and having tremendous lag.
             break;
         }
-
-        const void * data = mToCore.get(&cmdID, &cmdSize, delay);
-        if (!cmdSize) {
-            // exception or timeout occurred.
-            break;
-        }
-        ret = true;
-        if (con->props.mLogTimes) {
-            con->timerSet(Context::RS_TIMER_INTERNAL);
-        }
-        waitForCommand = false;
-        //ALOGV("playCoreCommands 3 %i %i", cmdID, cmdSize);
-
-        if (cmdID >= (sizeof(gPlaybackFuncs) / sizeof(void *))) {
-            rsAssert(cmdID < (sizeof(gPlaybackFuncs) / sizeof(void *)));
-            ALOGE("playCoreCommands error con %p, cmd %i", con, cmdID);
-            mToCore.printDebugData();
-        }
-        gPlaybackFuncs[cmdID](con, data, cmdSize << 2);
-        mToCore.next();
     }
     return ret;
 }
 
 RsMessageToClientType ThreadIO::getClientHeader(size_t *receiveLen, uint32_t *usrID) {
-    if (mUsingSocket) {
-        mToClientSocket.read(&mLastClientHeader, sizeof(mLastClientHeader));
-    } else {
-        size_t bytesData = 0;
-        const uint32_t *d = (const uint32_t *)mToClient.get(&mLastClientHeader.cmdID, (uint32_t*)&bytesData);
-        if (bytesData >= sizeof(uint32_t)) {
-            mLastClientHeader.userID = d[0];
-            mLastClientHeader.bytes = bytesData - sizeof(uint32_t);
-        } else {
-            mLastClientHeader.userID = 0;
-            mLastClientHeader.bytes = 0;
-        }
-    }
+    //ALOGE("getClientHeader");
+    mToClient.read(&mLastClientHeader, sizeof(mLastClientHeader));
+
     receiveLen[0] = mLastClientHeader.bytes;
     usrID[0] = mLastClientHeader.userID;
+    //ALOGE("getClientHeader %i %i %i", mLastClientHeader.cmdID, usrID[0], receiveLen[0]);
     return (RsMessageToClientType)mLastClientHeader.cmdID;
 }
 
 RsMessageToClientType ThreadIO::getClientPayload(void *data, size_t *receiveLen,
                                 uint32_t *usrID, size_t bufferLen) {
+    //ALOGE("getClientPayload");
     receiveLen[0] = mLastClientHeader.bytes;
     usrID[0] = mLastClientHeader.userID;
     if (bufferLen < mLastClientHeader.bytes) {
         return RS_MESSAGE_TO_CLIENT_RESIZE;
     }
-    if (mUsingSocket) {
-        if (receiveLen[0]) {
-            mToClientSocket.read(data, receiveLen[0]);
-        }
-        return (RsMessageToClientType)mLastClientHeader.cmdID;
-    } else {
-        uint32_t bytesData = 0;
-        uint32_t commandID = 0;
-        const uint32_t *d = (const uint32_t *)mToClient.get(&commandID, &bytesData);
-        //ALOGE("getMessageToClient 3    %i  %i", commandID, bytesData);
-        //ALOGE("getMessageToClient  %i %i", commandID, *subID);
-        if (bufferLen >= receiveLen[0]) {
-            memcpy(data, d+1, receiveLen[0]);
-            mToClient.next();
-            return (RsMessageToClientType)commandID;
-        }
+    if (receiveLen[0]) {
+        mToClient.read(data, receiveLen[0]);
     }
-    return RS_MESSAGE_TO_CLIENT_RESIZE;
+    //ALOGE("getClientPayload x");
+    return (RsMessageToClientType)mLastClientHeader.cmdID;
 }
 
 bool ThreadIO::sendToClient(RsMessageToClientType cmdID, uint32_t usrID, const void *data,
                             size_t dataLen, bool waitForSpace) {
+
+    //ALOGE("sendToClient %i %i %i", cmdID, usrID, (int)dataLen);
     ClientCmdHeader hdr;
     hdr.bytes = dataLen;
     hdr.cmdID = cmdID;
     hdr.userID = usrID;
-    if (mUsingSocket) {
-        mToClientSocket.writeAsync(&hdr, sizeof(hdr));
-        if (dataLen) {
-            mToClientSocket.writeAsync(data, dataLen);
-        }
-        return true;
-    } else {
-        if (!waitForSpace) {
-            if (!mToClient.makeSpaceNonBlocking(dataLen + sizeof(hdr))) {
-                // Not enough room, and not waiting.
-                return false;
-            }
-        }
 
-        //ALOGE("sendMessageToClient 2");
-        uint32_t *p = (uint32_t *)mToClient.reserve(dataLen + sizeof(usrID));
-        p[0] = usrID;
-        if (dataLen > 0) {
-            memcpy(p+1, data, dataLen);
-        }
-        mToClient.commit(cmdID, dataLen + sizeof(usrID));
-        //ALOGE("sendMessageToClient 3");
-        return true;
+    mToClient.writeAsync(&hdr, sizeof(hdr));
+    if (dataLen) {
+        mToClient.writeAsync(data, dataLen);
     }
-    return false;
+
+    //ALOGE("sendToClient x");
+    return true;
 }
 
diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h
index ebce0ab..cb7d4ab 100644
--- a/libs/rs/rsThreadIO.h
+++ b/libs/rs/rsThreadIO.h
@@ -18,7 +18,6 @@
 #define ANDROID_RS_THREAD_IO_H
 
 #include "rsUtils.h"
-#include "rsLocklessFifo.h"
 #include "rsFifoSocket.h"
 
 // ---------------------------------------------------------------------------
@@ -32,25 +31,34 @@
     ThreadIO();
     ~ThreadIO();
 
-    void init(bool useSocket = false);
+    void init();
     void shutdown();
 
+    size_t getMaxInlineSize() {
+        return mMaxInlineSize;
+    }
+    bool isPureFifo() {
+        return mPureFifo;
+    }
+
     // Plays back commands from the client.
     // Returns true if any commands were processed.
-    bool playCoreCommands(Context *con, bool waitForCommand, uint64_t timeToWait);
+    bool playCoreCommands(Context *con, int waitFd);
 
-    void setTimoutCallback(void (*)(void *), void *, uint64_t timeout);
-    //LocklessCommandFifo mToCore;
+    void setTimeoutCallback(void (*)(void *), void *, uint64_t timeout);
 
-
-
-    void coreFlush();
     void * coreHeader(uint32_t, size_t dataLen);
-    void coreData(const void *data, size_t dataLen);
     void coreCommit();
-    void coreCommitSync();
+
     void coreSetReturn(const void *data, size_t dataLen);
     void coreGetReturn(void *data, size_t dataLen);
+    void coreWrite(const void *data, size_t len);
+    void coreRead(void *data, size_t len);
+
+    void asyncSetReturn(const void *data, size_t dataLen);
+    void asyncGetReturn(void *data, size_t dataLen);
+    void asyncWrite(const void *data, size_t len);
+    void asyncRead(void *data, size_t len);
 
 
     RsMessageToClientType getClientHeader(size_t *receiveLen, uint32_t *usrID);
@@ -71,20 +79,18 @@
     } ClientCmdHeader;
     ClientCmdHeader mLastClientHeader;
 
-    size_t mCoreCommandSize;
-    uint32_t mCoreCommandID;
-    uint8_t * mCoreDataPtr;
-    uint8_t * mCoreDataBasePtr;
+    bool mRunning;
+    bool mPureFifo;
+    size_t mMaxInlineSize;
 
-    bool mUsingSocket;
-    LocklessCommandFifo mToClient;
-    LocklessCommandFifo mToCore;
-
-    FifoSocket mToClientSocket;
-    FifoSocket mToCoreSocket;
+    FifoSocket mToClient;
+    FifoSocket mToCore;
 
     intptr_t mToCoreRet;
 
+    size_t mSendLen;
+    uint8_t mSendBuffer[2 * 1024];
+
 };
 
 
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index 7966470..70ab7b7 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -46,12 +46,8 @@
         delete [] mLODs;
         mLODs = NULL;
     }
-    mDimX = 0;
-    mDimY = 0;
-    mDimZ = 0;
-    mDimLOD = 0;
-    mFaces = false;
     mElement.clear();
+    memset(&mHal, 0, sizeof(mHal));
 }
 
 TypeState::TypeState() {
@@ -62,16 +58,16 @@
 }
 
 size_t Type::getOffsetForFace(uint32_t face) const {
-    rsAssert(mFaces);
+    rsAssert(mHal.state.faces);
     return 0;
 }
 
 void Type::compute() {
     uint32_t oldLODCount = mLODCount;
-    if (mDimLOD) {
-        uint32_t l2x = rsFindHighBit(mDimX) + 1;
-        uint32_t l2y = rsFindHighBit(mDimY) + 1;
-        uint32_t l2z = rsFindHighBit(mDimZ) + 1;
+    if (mHal.state.dimLOD) {
+        uint32_t l2x = rsFindHighBit(mHal.state.dimX) + 1;
+        uint32_t l2y = rsFindHighBit(mHal.state.dimY) + 1;
+        uint32_t l2z = rsFindHighBit(mHal.state.dimZ) + 1;
 
         mLODCount = rsMax(l2x, l2y);
         mLODCount = rsMax(mLODCount, l2z);
@@ -85,9 +81,9 @@
         mLODs = new LOD[mLODCount];
     }
 
-    uint32_t tx = mDimX;
-    uint32_t ty = mDimY;
-    uint32_t tz = mDimZ;
+    uint32_t tx = mHal.state.dimX;
+    uint32_t ty = mHal.state.dimY;
+    uint32_t tz = mHal.state.dimZ;
     size_t offset = 0;
     for (uint32_t lod=0; lod < mLODCount; lod++) {
         mLODs[lod].mX = tx;
@@ -103,10 +99,11 @@
     // At this point the offset is the size of a mipmap chain;
     mMipChainSizeBytes = offset;
 
-    if (mFaces) {
+    if (mHal.state.faces) {
         offset *= 6;
     }
     mTotalSizeBytes = offset;
+    mHal.state.element = mElement.get();
 }
 
 uint32_t Type::getLODOffset(uint32_t lod, uint32_t x) const {
@@ -127,7 +124,8 @@
     return offset;
 }
 
-uint32_t Type::getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face, uint32_t x, uint32_t y) const {
+uint32_t Type::getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face,
+                                uint32_t x, uint32_t y) const {
     uint32_t offset = mLODs[lod].mOffset;
     offset += (x + y * mLODs[lod].mX) * mElement->getSizeBytes();
 
@@ -141,7 +139,12 @@
 void Type::dumpLOGV(const char *prefix) const {
     char buf[1024];
     ObjectBase::dumpLOGV(prefix);
-    ALOGV("%s   Type: x=%zu y=%zu z=%zu mip=%i face=%i", prefix, mDimX, mDimY, mDimZ, mDimLOD, mFaces);
+    ALOGV("%s   Type: x=%u y=%u z=%u mip=%i face=%i", prefix,
+                                                      mHal.state.dimX,
+                                                      mHal.state.dimY,
+                                                      mHal.state.dimZ,
+                                                      mHal.state.dimLOD,
+                                                      mHal.state.faces);
     snprintf(buf, sizeof(buf), "%s element: ", prefix);
     mElement->dumpLOGV(buf);
 }
@@ -155,12 +158,12 @@
 
     mElement->serialize(stream);
 
-    stream->addU32(mDimX);
-    stream->addU32(mDimY);
-    stream->addU32(mDimZ);
+    stream->addU32(mHal.state.dimX);
+    stream->addU32(mHal.state.dimY);
+    stream->addU32(mHal.state.dimZ);
 
-    stream->addU8((uint8_t)(mDimLOD ? 1 : 0));
-    stream->addU8((uint8_t)(mFaces ? 1 : 0));
+    stream->addU8((uint8_t)(mHal.state.dimLOD ? 1 : 0));
+    stream->addU8((uint8_t)(mHal.state.faces ? 1 : 0));
 }
 
 Type *Type::createFromStream(Context *rsc, IStream *stream) {
@@ -232,11 +235,11 @@
     Type *nt = new Type(rsc);
     returnRef.set(nt);
     nt->mElement.set(e);
-    nt->mDimX = dimX;
-    nt->mDimY = dimY;
-    nt->mDimZ = dimZ;
-    nt->mDimLOD = dimLOD;
-    nt->mFaces = dimFaces;
+    nt->mHal.state.dimX = dimX;
+    nt->mHal.state.dimY = dimY;
+    nt->mHal.state.dimZ = dimZ;
+    nt->mHal.state.dimLOD = dimLOD;
+    nt->mHal.state.faces = dimFaces;
     nt->compute();
 
     ObjectBase::asyncLock();
@@ -248,14 +251,14 @@
 
 ObjectBaseRef<Type> Type::cloneAndResize1D(Context *rsc, uint32_t dimX) const {
     return getTypeRef(rsc, mElement.get(), dimX,
-                      mDimY, mDimZ, mDimLOD, mFaces);
+                      mHal.state.dimY, mHal.state.dimZ, mHal.state.dimLOD, mHal.state.faces);
 }
 
 ObjectBaseRef<Type> Type::cloneAndResize2D(Context *rsc,
                               uint32_t dimX,
                               uint32_t dimY) const {
     return getTypeRef(rsc, mElement.get(), dimX, dimY,
-                      mDimZ, mDimLOD, mFaces);
+                      mHal.state.dimZ, mHal.state.dimLOD, mHal.state.faces);
 }
 
 
@@ -276,8 +279,8 @@
 
 void rsaTypeGetNativeData(RsContext con, RsType type, uint32_t *typeData, uint32_t typeDataSize) {
     rsAssert(typeDataSize == 6);
-    // Pack the data in the follofing way mDimX; mDimY; mDimZ;
-    // mDimLOD; mDimFaces; mElement; into typeData
+    // Pack the data in the follofing way mHal.state.dimX; mHal.state.dimY; mHal.state.dimZ;
+    // mHal.state.dimLOD; mHal.state.faces; mElement; into typeData
     Type *t = static_cast<Type *>(type);
 
     (*typeData++) = t->getDimX();
diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h
index bc0d9ff..3878156 100644
--- a/libs/rs/rsType.h
+++ b/libs/rs/rsType.h
@@ -22,10 +22,35 @@
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-
+/*****************************************************************************
+ * CAUTION
+ *
+ * Any layout changes for this class may require a corresponding change to be
+ * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains
+ * a partial copy of the information below.
+ *
+ *****************************************************************************/
 
 class Type : public ObjectBase {
 public:
+    struct Hal {
+        mutable void *drv;
+
+        struct State {
+            const Element * element;
+
+            // Size of the structure in the various dimensions.  A missing Dimension is
+            // specified as a 0 and not a 1.
+            uint32_t dimX;
+            uint32_t dimY;
+            uint32_t dimZ;
+            bool dimLOD;
+            bool faces;
+        };
+        State state;
+    };
+    Hal mHal;
+
     Type * createTex2D(const Element *, size_t w, size_t h, bool mip);
 
     size_t getOffsetForFace(uint32_t face) const;
@@ -34,22 +59,25 @@
     size_t getElementSizeBytes() const {return mElement->getSizeBytes();}
     const Element * getElement() const {return mElement.get();}
 
-    uint32_t getDimX() const {return mDimX;}
-    uint32_t getDimY() const {return mDimY;}
-    uint32_t getDimZ() const {return mDimZ;}
-    uint32_t getDimLOD() const {return mDimLOD;}
-    bool getDimFaces() const {return mFaces;}
+    uint32_t getDimX() const {return mHal.state.dimX;}
+    uint32_t getDimY() const {return mHal.state.dimY;}
+    uint32_t getDimZ() const {return mHal.state.dimZ;}
+    uint32_t getDimLOD() const {return mHal.state.dimLOD;}
+    bool getDimFaces() const {return mHal.state.faces;}
 
     uint32_t getLODDimX(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mX;}
     uint32_t getLODDimY(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mY;}
     uint32_t getLODDimZ(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mZ;}
 
-    uint32_t getLODOffset(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mOffset;}
+    uint32_t getLODOffset(uint32_t lod) const {
+        rsAssert(lod < mLODCount); return mLODs[lod].mOffset;
+    }
     uint32_t getLODOffset(uint32_t lod, uint32_t x) const;
     uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const;
     uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const;
 
-    uint32_t getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face, uint32_t x, uint32_t y) const;
+    uint32_t getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face,
+                              uint32_t x, uint32_t y) const;
 
     uint32_t getLODCount() const {return mLODCount;}
     bool getIsNp2() const;
@@ -95,14 +123,6 @@
 
     ObjectBaseRef<const Element> mElement;
 
-    // Size of the structure in the various dimensions.  A missing Dimension is
-    // specified as a 0 and not a 1.
-    size_t mDimX;
-    size_t mDimY;
-    size_t mDimZ;
-    bool mDimLOD;
-    bool mFaces;
-
     // count of mipmap levels, 0 indicates no mipmapping
 
     size_t mMipChainSizeBytes;
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
index db6f5920..a9a992a 100644
--- a/libs/rs/rsUtils.h
+++ b/libs/rs/rsUtils.h
@@ -34,7 +34,7 @@
 
 #include <math.h>
 
-#include "RenderScript.h"
+#include "rs.h"
 
 namespace android {
 namespace renderscript {
diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h
index b8d7351..e4bf17f 100644
--- a/libs/rs/rs_hal.h
+++ b/libs/rs/rs_hal.h
@@ -17,7 +17,9 @@
 #ifndef RS_HAL_H
 #define RS_HAL_H
 
-#include <RenderScriptDefines.h>
+#include <rsDefines.h>
+
+struct ANativeWindow;
 
 namespace android {
 namespace renderscript {
@@ -29,6 +31,7 @@
 class Allocation;
 class Script;
 class ScriptC;
+class Path;
 class Program;
 class ProgramStore;
 class ProgramRaster;
@@ -115,17 +118,22 @@
         void (*syncAll)(const Context *rsc, const Allocation *alloc, RsAllocationUsageType src);
         void (*markDirty)(const Context *rsc, const Allocation *alloc);
 
+        int32_t (*initSurfaceTexture)(const Context *rsc, const Allocation *alloc);
+        void (*setSurfaceTexture)(const Context *rsc, Allocation *alloc, ANativeWindow *sur);
+        void (*ioSend)(const Context *rsc, Allocation *alloc);
+        void (*ioReceive)(const Context *rsc, Allocation *alloc);
+
         void (*data1D)(const Context *rsc, const Allocation *alloc,
                        uint32_t xoff, uint32_t lod, uint32_t count,
-                       const void *data, uint32_t sizeBytes);
+                       const void *data, size_t sizeBytes);
         void (*data2D)(const Context *rsc, const Allocation *alloc,
                        uint32_t xoff, uint32_t yoff, uint32_t lod,
                        RsAllocationCubemapFace face, uint32_t w, uint32_t h,
-                       const void *data, uint32_t sizeBytes);
+                       const void *data, size_t sizeBytes);
         void (*data3D)(const Context *rsc, const Allocation *alloc,
                        uint32_t xoff, uint32_t yoff, uint32_t zoff,
                        uint32_t lod, RsAllocationCubemapFace face,
-                       uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes);
+                       uint32_t w, uint32_t h, uint32_t d, const void *data, size_t sizeBytes);
 
         // Allocation to allocation copies
         void (*allocData1D)(const Context *rsc,
@@ -149,9 +157,9 @@
                             uint32_t srcLod, RsAllocationCubemapFace srcFace);
 
         void (*elementData1D)(const Context *rsc, const Allocation *alloc, uint32_t x,
-                              const void *data, uint32_t elementOff, uint32_t sizeBytes);
+                              const void *data, uint32_t elementOff, size_t sizeBytes);
         void (*elementData2D)(const Context *rsc, const Allocation *alloc, uint32_t x, uint32_t y,
-                              const void *data, uint32_t elementOff, uint32_t sizeBytes);
+                              const void *data, uint32_t elementOff, size_t sizeBytes);
 
 
     } allocation;
@@ -170,14 +178,18 @@
 
     struct {
         bool (*init)(const Context *rsc, const ProgramVertex *pv,
-                     const char* shader, uint32_t shaderLen);
+                     const char* shader, size_t shaderLen,
+                     const char** textureNames, size_t textureNamesCount,
+                     const size_t *textureNamesLength);
         void (*setActive)(const Context *rsc, const ProgramVertex *pv);
         void (*destroy)(const Context *rsc, const ProgramVertex *pv);
     } vertex;
 
     struct {
         bool (*init)(const Context *rsc, const ProgramFragment *pf,
-                     const char* shader, uint32_t shaderLen);
+                     const char* shader, size_t shaderLen,
+                     const char** textureNames, size_t textureNamesCount,
+                     const size_t *textureNamesLength);
         void (*setActive)(const Context *rsc, const ProgramFragment *pf);
         void (*destroy)(const Context *rsc, const ProgramFragment *pf);
     } fragment;
@@ -189,6 +201,13 @@
     } mesh;
 
     struct {
+        bool (*initStatic)(const Context *rsc, const Path *m, const Allocation *vtx, const Allocation *loops);
+        bool (*initDynamic)(const Context *rsc, const Path *m);
+        void (*draw)(const Context *rsc, const Path *m);
+        void (*destroy)(const Context *rsc, const Path *m);
+    } path;
+
+    struct {
         bool (*init)(const Context *rsc, const Sampler *m);
         void (*destroy)(const Context *rsc, const Sampler *m);
     } sampler;
diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c
index 6b84e56..99c305e 100644
--- a/libs/rs/rsg_generator.c
+++ b/libs/rs/rsg_generator.c
@@ -238,7 +238,7 @@
             //fprintf(f, "    ALOGE(\"add command %s\\n\");\n", api->name);
             if (hasInlineDataPointers(api)) {
                 fprintf(f, "    RS_CMD_%s *cmd = NULL;\n", api->name);
-                fprintf(f, "    if (dataSize < 1024) {;\n");
+                fprintf(f, "    if (dataSize < io->getMaxInlineSize()) {;\n");
                 fprintf(f, "        cmd = static_cast<RS_CMD_%s *>(io->coreHeader(RS_CMD_ID_%s, dataSize + size));\n", api->name, api->name);
                 fprintf(f, "    } else {\n");
                 fprintf(f, "        cmd = static_cast<RS_CMD_%s *>(io->coreHeader(RS_CMD_ID_%s, size));\n", api->name, api->name);
@@ -252,11 +252,11 @@
                 const VarType *vt = &api->params[ct2];
                 needFlush += vt->ptrLevel;
                 if (vt->ptrLevel && hasInlineDataPointers(api)) {
-                    fprintf(f, "    if (dataSize < 1024) {\n");
+                    fprintf(f, "    if (dataSize < io->getMaxInlineSize()) {\n");
                     fprintf(f, "        memcpy(payload, %s, %s_length);\n", vt->name, vt->name);
                     fprintf(f, "        cmd->%s = (", vt->name);
                     printVarType(f, vt);
-                    fprintf(f, ")payload;\n");
+                    fprintf(f, ")(payload - ((uint8_t *)&cmd[1]));\n");
                     fprintf(f, "        payload += %s_length;\n", vt->name);
                     fprintf(f, "    } else {\n");
                     fprintf(f, "        cmd->%s = %s;\n", vt->name, vt->name);
@@ -270,106 +270,89 @@
                 needFlush = 1;
             }
 
+            fprintf(f, "    io->coreCommit();\n");
             if (hasInlineDataPointers(api)) {
-                fprintf(f, "    if (dataSize < 1024) {\n");
-                fprintf(f, "        io->coreCommit();\n");
-                fprintf(f, "    } else {\n");
-                fprintf(f, "        io->coreCommitSync();\n");
+                fprintf(f, "    if (dataSize >= io->getMaxInlineSize()) {\n");
+                fprintf(f, "        io->coreGetReturn(NULL, 0);\n");
                 fprintf(f, "    }\n");
-            } else {
-                fprintf(f, "    io->coreCommit");
-                if (needFlush) {
-                    fprintf(f, "Sync");
-                }
-                fprintf(f, "();\n");
-            }
-
-            if (api->ret.typeName[0]) {
+            } else if (api->ret.typeName[0]) {
                 fprintf(f, "\n    ");
                 printVarType(f, &api->ret);
                 fprintf(f, " ret;\n");
                 fprintf(f, "    io->coreGetReturn(&ret, sizeof(ret));\n");
                 fprintf(f, "    return ret;\n");
+            } else if (needFlush) {
+                fprintf(f, "    io->coreGetReturn(NULL, 0);\n");
             }
         }
         fprintf(f, "};\n\n");
 
 
+        // Generate a remote sender function
+        const char * str = "core";
+        if (api->direct) {
+            str = "async";
+        }
+
         fprintf(f, "static ");
         printFuncDecl(f, api, "RF_", 0, 0);
         fprintf(f, "\n{\n");
-        fprintf(f, "    Fifo *f = NULL;\n");
-        fprintf(f, "    RS_CMD_%s cmd;\n", api->name);
-        fprintf(f, "    const uint32_t cmdSize = sizeof(cmd);\n");
+        fprintf(f, "    ThreadIO *io = &((Context *)rsc)->mIO;\n");
         fprintf(f, "    const uint32_t cmdID = RS_CMD_ID_%s;\n", api->name);
-        fprintf(f, "    f->writeAsync(&cmdID, sizeof(cmdID));\n");
-        fprintf(f, "    intptr_t offset = cmdSize;\n");
-        fprintf(f, "    uint32_t dataSize = 0;\n");
+        fprintf(f, "    io->%sWrite(&cmdID, sizeof(cmdID));\n\n", str);
+
         for (ct2=0; ct2 < api->paramCount; ct2++) {
             const VarType *vt = &api->params[ct2];
-            if (vt->isConst && vt->ptrLevel) {
-                switch(vt->ptrLevel) {
-                case 1:
-                    fprintf(f, "    dataSize += %s_length;\n", vt->name);
-                    break;
-                case 2:
-                    fprintf(f, "    for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name);
-                    fprintf(f, "        dataSize += %s_length[ct];\n", vt->name);
-                    fprintf(f, "    }\n");
-                    break;
-                default:
-                    printf("pointer level not handled!!");
-                }
+            if (vt->ptrLevel == 0) {
+                fprintf(f, "    io->%sWrite(& %s, sizeof(%s));\n", str, vt->name, vt->name);
             }
         }
         fprintf(f, "\n");
 
         for (ct2=0; ct2 < api->paramCount; ct2++) {
             const VarType *vt = &api->params[ct2];
-            switch(vt->ptrLevel) {
-            case 0:
-                fprintf(f, "    cmd.%s = %s;\n", vt->name, vt->name);
-                break;
-            case 1:
-                fprintf(f, "    cmd.%s = (", vt->name);
-                printVarType(f, vt);
-                fprintf(f, ")offset;\n");
-                fprintf(f, "    offset += %s_length;\n", vt->name);
-                break;
-            case 2:
-                fprintf(f, "    cmd.%s = (", vt->name);
-                printVarType(f, vt);
-                fprintf(f, ")offset;\n");
-                fprintf(f, "    for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name);
-                fprintf(f, "        offset += %s_length[ct];\n", vt->name);
-                fprintf(f, "    }\n");
-                break;
-            default:
-                fprintf(stderr, "pointer level not handled!!");
+            if ((vt->ptrLevel == 1) && (vt->isConst)) {
+                fprintf(f, "    io->%sWrite(%s, %s_length);\n", str, vt->name, vt->name);
             }
         }
         fprintf(f, "\n");
 
-        fprintf(f, "    f->writeAsync(&cmd, cmdSize);\n");
         for (ct2=0; ct2 < api->paramCount; ct2++) {
             const VarType *vt = &api->params[ct2];
-            if (vt->ptrLevel == 1) {
-                fprintf(f, "    f->writeAsync(%s, %s_length);\n", vt->name, vt->name);
-            }
-            if (vt->ptrLevel == 2) {
+            if ((vt->ptrLevel == 2) && (vt->isConst)) {
                 fprintf(f, "    for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name);
-                fprintf(f, "        f->writeAsync(%s, %s_length[ct]);\n", vt->name, vt->name);
-                fprintf(f, "        offset += %s_length[ct];\n", vt->name);
+                fprintf(f, "        io->%sWrite(%s[ct], %s_length[ct]);\n", str, vt->name, vt->name);
                 fprintf(f, "    }\n");
             }
         }
+        fprintf(f, "\n");
+
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if ((vt->ptrLevel == 1) && (!vt->isConst)) {
+                fprintf(f, "    io->%sGetReturn(%s, %s_length);\n", str, vt->name, vt->name);
+            }
+        }
+        fprintf(f, "\n");
+
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if ((vt->ptrLevel == 2) && (!vt->isConst)) {
+                fprintf(f, "    for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name);
+                fprintf(f, "        io->%sGetReturn(%s[ct], %s_length[ct]);\n", str, vt->name, vt->name);
+                fprintf(f, "    }\n");
+            }
+        }
+        fprintf(f, "\n");
 
         if (api->ret.typeName[0]) {
             fprintf(f, "    ");
             printVarType(f, &api->ret);
             fprintf(f, " retValue;\n");
-            fprintf(f, "    f->writeWaitReturn(&retValue, sizeof(retValue));\n");
+            fprintf(f, "    io->%sGetReturn(&retValue, sizeof(retValue));\n", str);
             fprintf(f, "    return retValue;\n");
+        } else /*if (api->sync)*/ {
+            fprintf(f, "    io->%sGetReturn(NULL, 0);\n", str);
         }
         fprintf(f, "}\n\n");
     }
@@ -425,7 +408,6 @@
     fprintf(f, "#include \"rsDevice.h\"\n");
     fprintf(f, "#include \"rsContext.h\"\n");
     fprintf(f, "#include \"rsThreadIO.h\"\n");
-    //fprintf(f, "#include \"rsgApiStructs.h\"\n");
     fprintf(f, "#include \"rsgApiFuncDecl.h\"\n");
     fprintf(f, "\n");
     fprintf(f, "namespace android {\n");
@@ -434,16 +416,22 @@
 
     for (ct=0; ct < apiCount; ct++) {
         const ApiEntry * api = &apis[ct];
+        int needFlush = 0;
 
         if (api->direct) {
             continue;
         }
 
         fprintf(f, "void rsp_%s(Context *con, const void *vp, size_t cmdSizeBytes) {\n", api->name);
-
-        //fprintf(f, "    ALOGE(\"play command %s\\n\");\n", api->name);
         fprintf(f, "    const RS_CMD_%s *cmd = static_cast<const RS_CMD_%s *>(vp);\n", api->name, api->name);
 
+        if (hasInlineDataPointers(api)) {
+            fprintf(f, "    const uint8_t *baseData = 0;\n");
+            fprintf(f, "    if (cmdSizeBytes != sizeof(RS_CMD_%s)) {\n", api->name);
+            fprintf(f, "        baseData = &((const uint8_t *)vp)[sizeof(*cmd)];\n");
+            fprintf(f, "    }\n");
+        }
+
         fprintf(f, "    ");
         if (api->ret.typeName[0]) {
             fprintf(f, "\n    ");
@@ -453,12 +441,31 @@
         fprintf(f, "rsi_%s(con", api->name);
         for (ct2=0; ct2 < api->paramCount; ct2++) {
             const VarType *vt = &api->params[ct2];
-            fprintf(f, ",\n           cmd->%s", vt->name);
+            needFlush += vt->ptrLevel;
+
+            if (hasInlineDataPointers(api) && vt->ptrLevel) {
+                fprintf(f, ",\n           (const %s *)&baseData[(intptr_t)cmd->%s]", vt->typeName, vt->name);
+            } else {
+                fprintf(f, ",\n           cmd->%s", vt->name);
+            }
         }
         fprintf(f, ");\n");
 
-        if (api->ret.typeName[0]) {
+        if (hasInlineDataPointers(api)) {
+            fprintf(f, "    size_t totalSize = 0;\n");
+            for (ct2=0; ct2 < api->paramCount; ct2++) {
+                if (api->params[ct2].ptrLevel) {
+                    fprintf(f, "    totalSize += cmd->%s_length;\n", api->params[ct2].name);
+                }
+            }
+
+            fprintf(f, "    if ((totalSize != 0) && (cmdSizeBytes == sizeof(RS_CMD_%s))) {\n", api->name);
+            fprintf(f, "        con->mIO.coreSetReturn(NULL, 0);\n");
+            fprintf(f, "    }\n");
+        } else if (api->ret.typeName[0]) {
             fprintf(f, "    con->mIO.coreSetReturn(&ret, sizeof(ret));\n");
+        } else if (api->sync || needFlush) {
+            fprintf(f, "    con->mIO.coreSetReturn(NULL, 0);\n");
         }
 
         fprintf(f, "};\n\n");
@@ -466,30 +473,42 @@
 
     for (ct=0; ct < apiCount; ct++) {
         const ApiEntry * api = &apis[ct];
+        int needFlush = 0;
 
-        fprintf(f, "void rspr_%s(Context *con, Fifo *f, uint8_t *scratch, size_t scratchSize) {\n", api->name);
-
-        //fprintf(f, "    ALOGE(\"play command %s\\n\");\n", api->name);
+        fprintf(f, "void rspr_%s(Context *con, ThreadIO *io) {\n", api->name);
         fprintf(f, "    RS_CMD_%s cmd;\n", api->name);
-        fprintf(f, "    f->read(&cmd, sizeof(cmd));\n");
+
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if (vt->ptrLevel == 0) {
+                fprintf(f, "    io->coreRead(&cmd.%s, sizeof(cmd.%s));\n", vt->name, vt->name);
+            }
+        }
+        fprintf(f, "\n");
 
         for (ct2=0; ct2 < api->paramCount; ct2++) {
             const VarType *vt = &api->params[ct2];
             if (vt->ptrLevel == 1) {
                 fprintf(f, "    cmd.%s = (", vt->name);
                 printVarType(f, vt);
-                fprintf(f, ")scratch;\n");
-                fprintf(f, "    f->read(scratch, cmd.%s_length);\n", vt->name);
-                fprintf(f, "    scratch += cmd.%s_length;\n", vt->name);
+                fprintf(f, ")malloc(cmd.%s_length);\n", vt->name);
+
+                if (vt->isConst) {
+                    fprintf(f, "    if (cmd.%s_length) io->coreRead((void *)cmd.%s, cmd.%s_length);\n", vt->name, vt->name, vt->name);
+                }
             }
+        }
+        fprintf(f, "\n");
+
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
             if (vt->ptrLevel == 2) {
-                fprintf(f, "    size_t sum_%s = 0;\n", vt->name);
                 fprintf(f, "    for (size_t ct = 0; ct < (cmd.%s_length_length / sizeof(cmd.%s_length)); ct++) {\n", vt->name, vt->name);
-                fprintf(f, "        ((size_t *)scratch)[ct] = cmd.%s_length[ct];\n", vt->name);
-                fprintf(f, "        sum_%s += cmd.%s_length[ct];\n", vt->name, vt->name);
+                fprintf(f, "        cmd.%s = (", vt->name);
+                printVarType(f, vt);
+                fprintf(f, ")malloc(cmd.%s_length[ct]);\n", vt->name);
+                fprintf(f, "        io->coreRead(& cmd.%s, cmd.%s_length[ct]);\n", vt->name, vt->name);
                 fprintf(f, "    }\n");
-                fprintf(f, "    f->read(scratch, sum_%s);\n", vt->name);
-                fprintf(f, "    scratch += sum_%s;\n", vt->name);
             }
         }
         fprintf(f, "\n");
@@ -513,8 +532,42 @@
         }
         fprintf(f, ");\n");
 
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if ((vt->ptrLevel == 1) && (!vt->isConst)) {
+                fprintf(f, "    io->coreSetReturn((void *)cmd.%s, cmd.%s_length);\n", vt->name, vt->name);
+            }
+        }
+
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if ((vt->ptrLevel == 2) && (!vt->isConst)) {
+                fprintf(f, "    for (size_t ct = 0; ct < (cmd.%s_length_length / sizeof(cmd.%s_length)); ct++) {\n", vt->name, vt->name);
+                fprintf(f, "        io->coreSetReturn((void *)cmd.%s[ct], cmd.%s_length[ct]);\n", vt->name, vt->name);
+                fprintf(f, "    }\n");
+            }
+        }
+        fprintf(f, "\n");
+
         if (api->ret.typeName[0]) {
-            fprintf(f, "    f->readReturn(&ret, sizeof(ret));\n");
+            fprintf(f, "    io->coreSetReturn(&ret, sizeof(ret));\n");
+        } else /*if (needFlush)*/ {
+            fprintf(f, "    io->coreSetReturn(NULL, 0);\n");
+        }
+
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if (vt->ptrLevel == 1) {
+                fprintf(f, "    free((void *)cmd.%s);\n", vt->name);
+            }
+        }
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if (vt->ptrLevel == 2) {
+                fprintf(f, "    for (size_t ct = 0; ct < (cmd.%s_length_length / sizeof(cmd.%s_length)); ct++) {\n", vt->name, vt->name);
+                fprintf(f, "        free((void *)cmd.%s);\n", vt->name);
+                fprintf(f, "    }\n");
+            }
         }
 
         fprintf(f, "};\n\n");
@@ -584,7 +637,7 @@
             fprintf(f, "    uint32_t size;\n");
             fprintf(f, "} RsPlaybackRemoteHeader;\n\n");
             fprintf(f, "typedef void (*RsPlaybackLocalFunc)(Context *, const void *, size_t sizeBytes);\n");
-            fprintf(f, "typedef void (*RsPlaybackRemoteFunc)(Context *, Fifo *, uint8_t *scratch, size_t scratchSize);\n");
+            fprintf(f, "typedef void (*RsPlaybackRemoteFunc)(Context *, ThreadIO *);\n");
             fprintf(f, "extern RsPlaybackLocalFunc gPlaybackFuncs[%i];\n", apiCount + 1);
             fprintf(f, "extern RsPlaybackRemoteFunc gPlaybackRemoteFuncs[%i];\n", apiCount + 1);
 
diff --git a/libs/rs/scriptc/rs_allocation.rsh b/libs/rs/scriptc/rs_allocation.rsh
index 9ec03bf..a2f69d9 100644
--- a/libs/rs/scriptc/rs_allocation.rsh
+++ b/libs/rs/scriptc/rs_allocation.rsh
@@ -168,5 +168,135 @@
 extern const void * __attribute__((overloadable))
     rsGetElementAt(rs_allocation, uint32_t x, uint32_t y, uint32_t z);
 
+/**
+ * @param a allocation to get data from
+ * @return element describing allocation layout
+ */
+extern rs_element __attribute__((overloadable))
+    rsAllocationGetElement(rs_allocation a);
+
+/**
+ * @param m mesh to get data from
+ * @return number of allocations in the mesh that contain vertex
+ *         data
+ */
+extern uint32_t __attribute__((overloadable))
+    rsMeshGetVertexAllocationCount(rs_mesh m);
+
+/**
+ * @param m mesh to get data from
+ * @return number of primitive groups in the mesh. This would
+ *         include simple primitives as well as allocations
+ *         containing index data
+ */
+extern uint32_t __attribute__((overloadable))
+    rsMeshGetPrimitiveCount(rs_mesh m);
+
+/**
+ * @param m mesh to get data from
+ * @param index index of the vertex allocation
+ * @return allocation containing vertex data
+ */
+extern rs_allocation __attribute__((overloadable))
+    rsMeshGetVertexAllocation(rs_mesh m, uint32_t index);
+
+/**
+ * @param m mesh to get data from
+ * @param index index of the index allocation
+ * @return allocation containing index data
+ */
+extern rs_allocation __attribute__((overloadable))
+    rsMeshGetIndexAllocation(rs_mesh m, uint32_t index);
+
+/**
+ * @param m mesh to get data from
+ * @param index index of the primitive
+ * @return primitive describing how the mesh is rendered
+ */
+extern rs_primitive __attribute__((overloadable))
+    rsMeshGetPrimitive(rs_mesh m, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @return number of sub-elements in this element
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementCount(rs_element e);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element to return
+ * @return sub-element in this element at given index
+ */
+extern rs_element __attribute__((overloadable))
+    rsElementGetSubElement(rs_element, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element to return
+ * @return length of the sub-element name including the null
+ *         terminator (size of buffer needed to write the name)
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementNameLength(rs_element e, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element
+ * @param name array to store the name into
+ * @param nameLength length of the provided name array
+ * @return number of characters actually written, excluding the
+ *         null terminator
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementName(rs_element e, uint32_t index, char *name, uint32_t nameLength);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element
+ * @return array size of sub-element in this element at given
+ *         index
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementArraySize(rs_element e, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @param index index of the sub-element
+ * @return offset in bytes of sub-element in this element at
+ *         given index
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSubElementOffsetBytes(rs_element e, uint32_t index);
+
+/**
+ * @param e element to get data from
+ * @return total size of the element in bytes
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetSizeBytes(rs_element e);
+
+/**
+ * @param e element to get data from
+ * @return element's data type
+ */
+extern rs_data_type __attribute__((overloadable))
+    rsElementGetDataType(rs_element e);
+
+/**
+ * @param e element to get data from
+ * @return element's data size
+ */
+extern rs_data_kind __attribute__((overloadable))
+    rsElementGetDataKind(rs_element e);
+
+/**
+ * @param e element to get data from
+ * @return length of the element vector (for float2, float3,
+ *         etc.)
+ */
+extern uint32_t __attribute__((overloadable))
+    rsElementGetVectorSize(rs_element e);
+
 #endif
 
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
index 2581953..7fdebdc 100644
--- a/libs/rs/scriptc/rs_graphics.rsh
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -22,6 +22,66 @@
  */
 #ifndef __RS_GRAPHICS_RSH__
 #define __RS_GRAPHICS_RSH__
+
+// These are API 15 once it get official
+typedef enum {
+    RS_DEPTH_FUNC_ALWAYS,
+    RS_DEPTH_FUNC_LESS,
+    RS_DEPTH_FUNC_LEQUAL,
+    RS_DEPTH_FUNC_GREATER,
+    RS_DEPTH_FUNC_GEQUAL,
+    RS_DEPTH_FUNC_EQUAL,
+    RS_DEPTH_FUNC_NOTEQUAL,
+
+    RS_DEPTH_FUNC_INVALID = 100,
+} rs_depth_func;
+
+typedef enum {
+    RS_BLEND_SRC_ZERO,                  // 0
+    RS_BLEND_SRC_ONE,                   // 1
+    RS_BLEND_SRC_DST_COLOR,             // 2
+    RS_BLEND_SRC_ONE_MINUS_DST_COLOR,   // 3
+    RS_BLEND_SRC_SRC_ALPHA,             // 4
+    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA,   // 5
+    RS_BLEND_SRC_DST_ALPHA,             // 6
+    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA,   // 7
+    RS_BLEND_SRC_SRC_ALPHA_SATURATE,    // 8
+
+    RS_BLEND_SRC_INVALID = 100,
+} rs_blend_src_func;
+
+typedef enum {
+    RS_BLEND_DST_ZERO,                  // 0
+    RS_BLEND_DST_ONE,                   // 1
+    RS_BLEND_DST_SRC_COLOR,             // 2
+    RS_BLEND_DST_ONE_MINUS_SRC_COLOR,   // 3
+    RS_BLEND_DST_SRC_ALPHA,             // 4
+    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,   // 5
+    RS_BLEND_DST_DST_ALPHA,             // 6
+    RS_BLEND_DST_ONE_MINUS_DST_ALPHA,   // 7
+
+    RS_BLEND_DST_INVALID = 100,
+} rs_blend_dst_func;
+
+typedef enum {
+    RS_CULL_BACK,
+    RS_CULL_FRONT,
+    RS_CULL_NONE,
+
+    RS_CULL_INVALID = 100,
+} rs_cull_mode;
+
+typedef enum {
+    RS_SAMPLER_NEAREST,
+    RS_SAMPLER_LINEAR,
+    RS_SAMPLER_LINEAR_MIP_LINEAR,
+    RS_SAMPLER_WRAP,
+    RS_SAMPLER_CLAMP,
+    RS_SAMPLER_LINEAR_MIP_NEAREST,
+
+    RS_SAMPLER_INVALID = 100,
+} rs_sampler_value;
+
 #if (defined(RS_VERSION) && (RS_VERSION >= 14))
 /**
  * Set the color target used for all subsequent rendering calls
@@ -82,6 +142,88 @@
 extern void __attribute__((overloadable))
     rsgBindProgramStore(rs_program_store ps);
 
+
+/**
+ * @hide
+ * Get program store depth function
+ *
+ * @param ps
+ */
+extern rs_depth_func __attribute__((overloadable))
+    rsgProgramStoreGetDepthFunc(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store depth mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetDepthMask(rs_program_store ps);
+/**
+ * @hide
+ * Get program store red component color mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetColorMaskR(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store green component color mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetColorMaskG(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store blur component color mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetColorMaskB(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store alpha component color mask
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetColorMaskA(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store blend source function
+ *
+ * @param ps
+ */
+extern rs_blend_src_func __attribute__((overloadable))
+        rsgProgramStoreGetBlendSrcFunc(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store blend destination function
+ *
+ * @param ps
+ */
+extern rs_blend_dst_func __attribute__((overloadable))
+    rsgProgramStoreGetBlendDstFunc(rs_program_store ps);
+
+/**
+ * @hide
+ * Get program store dither state
+ *
+ * @param ps
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramStoreGetDitherEnabled(rs_program_store ps);
+
+
 /**
  * Bind a new ProgramVertex to the rendering context.
  *
@@ -99,6 +241,24 @@
     rsgBindProgramRaster(rs_program_raster pr);
 
 /**
+ * @hide
+ * Get program raster point sprite state
+ *
+ * @param pr
+ */
+extern bool __attribute__((overloadable))
+    rsgProgramRasterGetPointSpriteEnabled(rs_program_raster pr);
+
+/**
+ * @hide
+ * Get program raster cull mode
+ *
+ * @param pr
+ */
+extern rs_cull_mode __attribute__((overloadable))
+    rsgProgramRasterGetCullMode(rs_program_raster pr);
+
+/**
  * Bind a new Sampler object to a ProgramFragment.  The sampler will
  * operate on the texture bound at the matching slot.
  *
@@ -108,6 +268,51 @@
     rsgBindSampler(rs_program_fragment, uint slot, rs_sampler);
 
 /**
+ * @hide
+ * Get sampler minification value
+ *
+ * @param pr
+ */
+extern rs_sampler_value __attribute__((overloadable))
+    rsgSamplerGetMinification(rs_sampler s);
+
+/**
+ * @hide
+ * Get sampler magnification value
+ *
+ * @param pr
+ */
+extern rs_sampler_value __attribute__((overloadable))
+    rsgSamplerGetMagnification(rs_sampler s);
+
+/**
+ * @hide
+ * Get sampler wrap S value
+ *
+ * @param pr
+ */
+extern rs_sampler_value __attribute__((overloadable))
+    rsgSamplerGetWrapS(rs_sampler s);
+
+/**
+ * @hide
+ * Get sampler wrap T value
+ *
+ * @param pr
+ */
+extern rs_sampler_value __attribute__((overloadable))
+    rsgSamplerGetWrapT(rs_sampler s);
+
+/**
+ * @hide
+ * Get sampler anisotropy
+ *
+ * @param pr
+ */
+extern float __attribute__((overloadable))
+    rsgSamplerGetAnisotropy(rs_sampler s);
+
+/**
  * Bind a new Allocation object to a ProgramFragment.  The
  * Allocation must be a valid texture for the Program.  The sampling
  * of the texture will be controled by the Sampler bound at the
@@ -164,6 +369,28 @@
     rsgProgramFragmentConstantColor(rs_program_fragment pf, float r, float g, float b, float a);
 
 /**
+ * Bind a new Allocation object to a ProgramFragment.  The
+ * Allocation must be a valid constant input for the Program.
+ *
+ * @param ps program object
+ * @param slot index of the constant buffer on the program
+ * @param c constants to bind
+ */
+extern void __attribute__((overloadable))
+    rsgBindConstant(rs_program_fragment ps, uint slot, rs_allocation c);
+
+/**
+ * Bind a new Allocation object to a ProgramVertex.  The
+ * Allocation must be a valid constant input for the Program.
+ *
+ * @param pv program object
+ * @param slot index of the constant buffer on the program
+ * @param c constants to bind
+ */
+extern void __attribute__((overloadable))
+    rsgBindConstant(rs_program_vertex pv, uint slot, rs_allocation c);
+
+/**
  * Get the width of the current rendering surface.
  *
  * @return uint
@@ -288,6 +515,9 @@
 extern void __attribute__((overloadable))
     rsgDrawSpriteScreenspace(float x, float y, float z, float w, float h);
 
+extern void __attribute__((overloadable))
+    rsgDrawPath(rs_path p);
+
 /**
  * Draw a mesh using the current context state.  The whole mesh is
  * rendered.
diff --git a/libs/rs/scriptc/rs_object.rsh b/libs/rs/scriptc/rs_object.rsh
index a431219..1fc3f83 100644
--- a/libs/rs/scriptc/rs_object.rsh
+++ b/libs/rs/scriptc/rs_object.rsh
@@ -56,6 +56,11 @@
  * \overload
  */
 extern void __attribute__((overloadable))
+    rsSetObject(rs_path *dst, rs_path src);
+/**
+ * \overload
+ */
+extern void __attribute__((overloadable))
     rsSetObject(rs_mesh *dst, rs_mesh src);
 /**
  * \overload
@@ -114,6 +119,11 @@
  * \overload
  */
 extern void __attribute__((overloadable))
+    rsClearObject(rs_path *dst);
+/**
+ * \overload
+ */
+extern void __attribute__((overloadable))
     rsClearObject(rs_mesh *dst);
 /**
  * \overload
@@ -175,6 +185,11 @@
  * \overload
  */
 extern bool __attribute__((overloadable))
+    rsIsObject(rs_path);
+/**
+ * \overload
+ */
+extern bool __attribute__((overloadable))
     rsIsObject(rs_mesh);
 /**
  * \overload
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index 84bca9c..5345a48 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -138,6 +138,12 @@
  */
 typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_mesh;
 /**
+ * \brief Opaque handle to a Renderscript Path object.
+ *
+ * See: android.renderscript.Path
+ */
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_path;
+/**
  * \brief Opaque handle to a Renderscript ProgramFragment object.
  *
  * See: android.renderscript.ProgramFragment
@@ -396,4 +402,96 @@
 
 #endif //defined(RS_VERSION) && (RS_VERSION >= 14)
 
+/**
+ * Describes the way mesh vertex data is interpreted when rendering
+ *
+ **/
+typedef enum {
+    RS_PRIMITIVE_POINT,
+    RS_PRIMITIVE_LINE,
+    RS_PRIMITIVE_LINE_STRIP,
+    RS_PRIMITIVE_TRIANGLE,
+    RS_PRIMITIVE_TRIANGLE_STRIP,
+    RS_PRIMITIVE_TRIANGLE_FAN,
+
+    RS_PRIMITIVE_INVALID = 100,
+} rs_primitive;
+
+/**
+ * \brief Enumeration for possible element data types
+ *
+ * DataType represents the basic type information for a basic element.  The
+ * naming convention follows.  For numeric types it is FLOAT,
+ * SIGNED, or UNSIGNED followed by the _BITS where BITS is the
+ * size of the data.  BOOLEAN is a true / false (1,0)
+ * represented in an 8 bit container.  The UNSIGNED variants
+ * with multiple bit definitions are for packed graphical data
+ * formats and represent vectors with per vector member sizes
+ * which are treated as a single unit for packing and alignment
+ * purposes.
+ *
+ * MATRIX the three matrix types contain FLOAT_32 elements and are treated
+ * as 32 bits for alignment purposes.
+ *
+ * RS_* objects.  32 bit opaque handles.
+ */
+typedef enum {
+    RS_TYPE_NONE,
+    //RS_TYPE_FLOAT_16,
+    RS_TYPE_FLOAT_32 = 2,
+    RS_TYPE_FLOAT_64,
+    RS_TYPE_SIGNED_8,
+    RS_TYPE_SIGNED_16,
+    RS_TYPE_SIGNED_32,
+    RS_TYPE_SIGNED_64,
+    RS_TYPE_UNSIGNED_8,
+    RS_TYPE_UNSIGNED_16,
+    RS_TYPE_UNSIGNED_32,
+    RS_TYPE_UNSIGNED_64,
+
+    RS_TYPE_BOOLEAN,
+
+    RS_TYPE_UNSIGNED_5_6_5,
+    RS_TYPE_UNSIGNED_5_5_5_1,
+    RS_TYPE_UNSIGNED_4_4_4_4,
+
+    RS_TYPE_MATRIX_4X4,
+    RS_TYPE_MATRIX_3X3,
+    RS_TYPE_MATRIX_2X2,
+
+    RS_TYPE_ELEMENT = 1000,
+    RS_TYPE_TYPE,
+    RS_TYPE_ALLOCATION,
+    RS_TYPE_SAMPLER,
+    RS_TYPE_SCRIPT,
+    RS_TYPE_MESH,
+    RS_TYPE_PROGRAM_FRAGMENT,
+    RS_TYPE_PROGRAM_VERTEX,
+    RS_TYPE_PROGRAM_RASTER,
+    RS_TYPE_PROGRAM_STORE,
+
+    RS_TYPE_INVALID = 10000,
+} rs_data_type;
+
+/**
+ * \brief Enumeration for possible element data kind
+ *
+ * The special interpretation of the data if required.  This is primarly
+ * useful for graphical data.  USER indicates no special interpretation is
+ * expected.  PIXEL is used in conjunction with the standard data types for
+ * representing texture formats.
+ */
+typedef enum {
+    RS_KIND_USER,
+
+    RS_KIND_PIXEL_L = 7,
+    RS_KIND_PIXEL_A,
+    RS_KIND_PIXEL_LA,
+    RS_KIND_PIXEL_RGB,
+    RS_KIND_PIXEL_RGBA,
+    RS_KIND_PIXEL_DEPTH,
+
+    RS_KIND_INVALID = 100,
+} rs_data_kind;
+
 #endif
diff --git a/libs/rs/tests/Android.mk b/libs/rs/tests/Android.mk
new file mode 100644
index 0000000..a773e84
--- /dev/null
+++ b/libs/rs/tests/Android.mk
@@ -0,0 +1,29 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	compute.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libRS \
+	libz \
+	libcutils \
+	libutils \
+	libEGL \
+	libGLESv1_CM \
+	libGLESv2 \
+	libui \
+	libbcc \
+	libbcinfo \
+	libgui
+
+LOCAL_MODULE:= rstest-compute
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_C_INCLUDES +=  .. \
+	frameworks/base/libs/rs \
+	out/target/product/stingray/obj/SHARED_LIBRARIES/libRS_intermediates	
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/libs/rs/tests/compute.cpp b/libs/rs/tests/compute.cpp
new file mode 100644
index 0000000..28b135f
--- /dev/null
+++ b/libs/rs/tests/compute.cpp
@@ -0,0 +1,37 @@
+
+#include "RenderScript.h"
+#include "Element.h"
+#include "Type.h"
+#include "Allocation.h"
+
+int main(int argc, char** argv)
+{
+
+    RenderScript *rs = new RenderScript();
+    printf("New RS %p\n", rs);
+
+    bool r = rs->init(16);
+    printf("Init returned %i\n", r);
+
+    const Element *e = Element::RGBA_8888(rs);
+    printf("Element %p\n", e);
+
+    Type::Builder tb(rs, e);
+    tb.setX(128);
+    tb.setY(128);
+    const Type *t = tb.create();
+    printf("Type %p\n", t);
+
+
+    const Allocation *a1 = Allocation::createSized(rs, e, 1000);
+    printf("Allocation %p\n", a1);
+
+
+
+    printf("Deleting stuff\n");
+    delete t;
+    delete a1;
+    delete e;
+    delete rs;
+    printf("Delete OK\n");
+}
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index f8b4452..c029291 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -13,41 +13,14 @@
 # limitations under the License.
 
 LOCAL_PATH:= $(call my-dir)
-
-# libui is partially built for the host (used by build time keymap validation tool)
-# These files are common to host and target builds.
-commonSources:= \
-	Input.cpp \
-	Keyboard.cpp \
-	KeyLayoutMap.cpp \
-	KeyCharacterMap.cpp \
-	VirtualKeyMap.cpp
-
-# For the host
-# =====================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= $(commonSources)
-
-LOCAL_MODULE:= libui
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-# For the device
-# =====================================================
-
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-	$(commonSources) \
 	EGLUtils.cpp \
 	FramebufferNativeWindow.cpp \
 	GraphicBuffer.cpp \
 	GraphicBufferAllocator.cpp \
 	GraphicBufferMapper.cpp \
-	InputTransport.cpp \
 	PixelFormat.cpp \
 	Rect.cpp \
 	Region.cpp
@@ -56,14 +29,11 @@
 	libcutils \
 	libutils \
 	libEGL \
-	libpixelflinger \
-	libhardware \
-	libhardware_legacy \
-	libskia \
-	libbinder
+	libhardware
 
-LOCAL_C_INCLUDES := \
-    external/skia/include/core
+ifneq ($(BOARD_FRAMEBUFFER_FORCE_FORMAT),)
+LOCAL_CFLAGS += -DFRAMEBUFFER_FORCE_FORMAT=$(BOARD_FRAMEBUFFER_FORCE_FORMAT)
+endif
 
 LOCAL_MODULE:= libui
 
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index d1dca0c..26d4823 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -100,6 +100,18 @@
         mNumFreeBuffers = NUM_FRAME_BUFFERS;
         mBufferHead = mNumBuffers-1;
 
+        /*
+         * This does not actually change the framebuffer format. It merely
+         * fakes this format to surfaceflinger so that when it creates
+         * framebuffer surfaces it will use this format. It's really a giant
+         * HACK to allow interworking with buggy gralloc+GPU driver
+         * implementations. You should *NEVER* need to set this for shipping
+         * devices.
+         */
+#ifdef FRAMEBUFFER_FORCE_FORMAT
+        *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT;
+#endif
+
         for (i = 0; i < mNumBuffers; i++)
         {
                 buffers[i] = new NativeBuffer(
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
deleted file mode 100644
index 09cbb31..0000000
--- a/libs/ui/InputTransport.cpp
+++ /dev/null
@@ -1,727 +0,0 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-// Provides a shared memory transport for input events.
-//
-#define LOG_TAG "InputTransport"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages about channel signalling (send signal, receive signal)
-#define DEBUG_CHANNEL_SIGNALS 0
-
-// Log debug messages whenever InputChannel objects are created/destroyed
-#define DEBUG_CHANNEL_LIFECYCLE 0
-
-// Log debug messages about transport actions (initialize, reset, publish, ...)
-#define DEBUG_TRANSPORT_ACTIONS 0
-
-
-#include <cutils/ashmem.h>
-#include <cutils/log.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <ui/InputTransport.h>
-#include <unistd.h>
-
-namespace android {
-
-#define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1))
-#define MIN_HISTORY_DEPTH 20
-
-// Must be at least sizeof(InputMessage) + sufficient space for pointer data
-static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP(
-        sizeof(InputMessage) + MIN_HISTORY_DEPTH
-                * (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)),
-        4096);
-
-// Signal sent by the producer to the consumer to inform it that a new message is
-// available to be consumed in the shared memory buffer.
-static const char INPUT_SIGNAL_DISPATCH = 'D';
-
-// Signal sent by the consumer to the producer to inform it that it has finished
-// consuming the most recent message and it handled it.
-static const char INPUT_SIGNAL_FINISHED_HANDLED = 'f';
-
-// Signal sent by the consumer to the producer to inform it that it has finished
-// consuming the most recent message but it did not handle it.
-static const char INPUT_SIGNAL_FINISHED_UNHANDLED = 'u';
-
-
-// --- InputChannel ---
-
-InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
-        int32_t sendPipeFd) :
-        mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {
-#if DEBUG_CHANNEL_LIFECYCLE
-    ALOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
-            mName.string(), ashmemFd, receivePipeFd, sendPipeFd);
-#endif
-
-    int result = fcntl(mReceivePipeFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make receive pipe "
-            "non-blocking.  errno=%d", mName.string(), errno);
-
-    result = fcntl(mSendPipeFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make send pipe "
-            "non-blocking.  errno=%d", mName.string(), errno);
-}
-
-InputChannel::~InputChannel() {
-#if DEBUG_CHANNEL_LIFECYCLE
-    ALOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
-            mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd);
-#endif
-
-    ::close(mAshmemFd);
-    ::close(mReceivePipeFd);
-    ::close(mSendPipeFd);
-}
-
-status_t InputChannel::openInputChannelPair(const String8& name,
-        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
-    status_t result;
-
-    String8 ashmemName("InputChannel ");
-    ashmemName.append(name);
-    int serverAshmemFd = ashmem_create_region(ashmemName.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
-    if (serverAshmemFd < 0) {
-        result = -errno;
-        ALOGE("channel '%s' ~ Could not create shared memory region. errno=%d",
-                name.string(), errno);
-    } else {
-        result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);
-        if (result < 0) {
-            ALOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.",
-                    name.string(), result, serverAshmemFd);
-        } else {
-            // Dup the file descriptor because the server and client input channel objects that
-            // are returned may have different lifetimes but they share the same shared memory region.
-            int clientAshmemFd;
-            clientAshmemFd = dup(serverAshmemFd);
-            if (clientAshmemFd < 0) {
-                result = -errno;
-                ALOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d",
-                        name.string(), errno);
-            } else {
-                int forward[2];
-                if (pipe(forward)) {
-                    result = -errno;
-                    ALOGE("channel '%s' ~ Could not create forward pipe.  errno=%d",
-                            name.string(), errno);
-                } else {
-                    int reverse[2];
-                    if (pipe(reverse)) {
-                        result = -errno;
-                        ALOGE("channel '%s' ~ Could not create reverse pipe.  errno=%d",
-                                name.string(), errno);
-                    } else {
-                        String8 serverChannelName = name;
-                        serverChannelName.append(" (server)");
-                        outServerChannel = new InputChannel(serverChannelName,
-                                serverAshmemFd, reverse[0], forward[1]);
-
-                        String8 clientChannelName = name;
-                        clientChannelName.append(" (client)");
-                        outClientChannel = new InputChannel(clientChannelName,
-                                clientAshmemFd, forward[0], reverse[1]);
-                        return OK;
-                    }
-                    ::close(forward[0]);
-                    ::close(forward[1]);
-                }
-                ::close(clientAshmemFd);
-            }
-        }
-        ::close(serverAshmemFd);
-    }
-
-    outServerChannel.clear();
-    outClientChannel.clear();
-    return result;
-}
-
-status_t InputChannel::sendSignal(char signal) {
-    ssize_t nWrite;
-    do {
-        nWrite = ::write(mSendPipeFd, & signal, 1);
-    } while (nWrite == -1 && errno == EINTR);
-
-    if (nWrite == 1) {
-#if DEBUG_CHANNEL_SIGNALS
-        ALOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal);
-#endif
-        return OK;
-    }
-
-#if DEBUG_CHANNEL_SIGNALS
-    ALOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno);
-#endif
-    return -errno;
-}
-
-status_t InputChannel::receiveSignal(char* outSignal) {
-    ssize_t nRead;
-    do {
-        nRead = ::read(mReceivePipeFd, outSignal, 1);
-    } while (nRead == -1 && errno == EINTR);
-
-    if (nRead == 1) {
-#if DEBUG_CHANNEL_SIGNALS
-        ALOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
-#endif
-        return OK;
-    }
-
-    if (nRead == 0) { // check for EOF
-#if DEBUG_CHANNEL_SIGNALS
-        ALOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
-#endif
-        return DEAD_OBJECT;
-    }
-
-    if (errno == EAGAIN) {
-#if DEBUG_CHANNEL_SIGNALS
-        ALOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
-#endif
-        return WOULD_BLOCK;
-    }
-
-#if DEBUG_CHANNEL_SIGNALS
-    ALOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno);
-#endif
-    return -errno;
-}
-
-
-// --- InputPublisher ---
-
-InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
-        mChannel(channel), mSharedMessage(NULL),
-        mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false),
-        mMotionEventSampleDataTail(NULL) {
-}
-
-InputPublisher::~InputPublisher() {
-    reset();
-
-    if (mSharedMessage) {
-        munmap(mSharedMessage, mAshmemSize);
-    }
-}
-
-status_t InputPublisher::initialize() {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ initialize",
-            mChannel->getName().string());
-#endif
-
-    int ashmemFd = mChannel->getAshmemFd();
-    int result = ashmem_get_size_region(ashmemFd);
-    if (result < 0) {
-        ALOGE("channel '%s' publisher ~ Error %d getting size of ashmem fd %d.",
-                mChannel->getName().string(), result, ashmemFd);
-        return UNKNOWN_ERROR;
-    }
-    mAshmemSize = (size_t) result;
-
-    mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
-            PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
-    if (! mSharedMessage) {
-        ALOGE("channel '%s' publisher ~ mmap failed on ashmem fd %d.",
-                mChannel->getName().string(), ashmemFd);
-        return NO_MEMORY;
-    }
-
-    mPinned = true;
-    mSharedMessage->consumed = false;
-
-    return reset();
-}
-
-status_t InputPublisher::reset() {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ reset",
-        mChannel->getName().string());
-#endif
-
-    if (mPinned) {
-        // Destroy the semaphore since we are about to unpin the memory region that contains it.
-        int result;
-        if (mSemaphoreInitialized) {
-            if (mSharedMessage->consumed) {
-                result = sem_post(& mSharedMessage->semaphore);
-                if (result < 0) {
-                    ALOGE("channel '%s' publisher ~ Error %d in sem_post.",
-                            mChannel->getName().string(), errno);
-                    return UNKNOWN_ERROR;
-                }
-            }
-
-            result = sem_destroy(& mSharedMessage->semaphore);
-            if (result < 0) {
-                ALOGE("channel '%s' publisher ~ Error %d in sem_destroy.",
-                        mChannel->getName().string(), errno);
-                return UNKNOWN_ERROR;
-            }
-
-            mSemaphoreInitialized = false;
-        }
-
-        // Unpin the region since we no longer care about its contents.
-        int ashmemFd = mChannel->getAshmemFd();
-        result = ashmem_unpin_region(ashmemFd, 0, 0);
-        if (result < 0) {
-            ALOGE("channel '%s' publisher ~ Error %d unpinning ashmem fd %d.",
-                    mChannel->getName().string(), result, ashmemFd);
-            return UNKNOWN_ERROR;
-        }
-
-        mPinned = false;
-    }
-
-    mMotionEventSampleDataTail = NULL;
-    mWasDispatched = false;
-    return OK;
-}
-
-status_t InputPublisher::publishInputEvent(
-        int32_t type,
-        int32_t deviceId,
-        int32_t source) {
-    if (mPinned) {
-        ALOGE("channel '%s' publisher ~ Attempted to publish a new event but publisher has "
-                "not yet been reset.", mChannel->getName().string());
-        return INVALID_OPERATION;
-    }
-
-    // Pin the region.
-    // We do not check for ASHMEM_NOT_PURGED because we don't care about the previous
-    // contents of the buffer so it does not matter whether it was purged in the meantime.
-    int ashmemFd = mChannel->getAshmemFd();
-    int result = ashmem_pin_region(ashmemFd, 0, 0);
-    if (result < 0) {
-        ALOGE("channel '%s' publisher ~ Error %d pinning ashmem fd %d.",
-                mChannel->getName().string(), result, ashmemFd);
-        return UNKNOWN_ERROR;
-    }
-
-    mPinned = true;
-
-    result = sem_init(& mSharedMessage->semaphore, 1, 1);
-    if (result < 0) {
-        ALOGE("channel '%s' publisher ~ Error %d in sem_init.",
-                mChannel->getName().string(), errno);
-        return UNKNOWN_ERROR;
-    }
-
-    mSemaphoreInitialized = true;
-
-    mSharedMessage->consumed = false;
-    mSharedMessage->type = type;
-    mSharedMessage->deviceId = deviceId;
-    mSharedMessage->source = source;
-    return OK;
-}
-
-status_t InputPublisher::publishKeyEvent(
-        int32_t deviceId,
-        int32_t source,
-        int32_t action,
-        int32_t flags,
-        int32_t keyCode,
-        int32_t scanCode,
-        int32_t metaState,
-        int32_t repeatCount,
-        nsecs_t downTime,
-        nsecs_t eventTime) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=0x%x, "
-            "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
-            "downTime=%lld, eventTime=%lld",
-            mChannel->getName().string(),
-            deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
-            downTime, eventTime);
-#endif
-
-    status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source);
-    if (result < 0) {
-        return result;
-    }
-
-    mSharedMessage->key.action = action;
-    mSharedMessage->key.flags = flags;
-    mSharedMessage->key.keyCode = keyCode;
-    mSharedMessage->key.scanCode = scanCode;
-    mSharedMessage->key.metaState = metaState;
-    mSharedMessage->key.repeatCount = repeatCount;
-    mSharedMessage->key.downTime = downTime;
-    mSharedMessage->key.eventTime = eventTime;
-    return OK;
-}
-
-status_t InputPublisher::publishMotionEvent(
-        int32_t deviceId,
-        int32_t source,
-        int32_t action,
-        int32_t flags,
-        int32_t edgeFlags,
-        int32_t metaState,
-        int32_t buttonState,
-        float xOffset,
-        float yOffset,
-        float xPrecision,
-        float yPrecision,
-        nsecs_t downTime,
-        nsecs_t eventTime,
-        size_t pointerCount,
-        const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, "
-            "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, "
-            "xOffset=%f, yOffset=%f, "
-            "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
-            "pointerCount=%d",
-            mChannel->getName().string(),
-            deviceId, source, action, flags, edgeFlags, metaState, buttonState,
-            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
-#endif
-
-    if (pointerCount > MAX_POINTERS || pointerCount < 1) {
-        ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
-                mChannel->getName().string(), pointerCount);
-        return BAD_VALUE;
-    }
-
-    status_t result = publishInputEvent(AINPUT_EVENT_TYPE_MOTION, deviceId, source);
-    if (result < 0) {
-        return result;
-    }
-
-    mSharedMessage->motion.action = action;
-    mSharedMessage->motion.flags = flags;
-    mSharedMessage->motion.edgeFlags = edgeFlags;
-    mSharedMessage->motion.metaState = metaState;
-    mSharedMessage->motion.buttonState = buttonState;
-    mSharedMessage->motion.xOffset = xOffset;
-    mSharedMessage->motion.yOffset = yOffset;
-    mSharedMessage->motion.xPrecision = xPrecision;
-    mSharedMessage->motion.yPrecision = yPrecision;
-    mSharedMessage->motion.downTime = downTime;
-    mSharedMessage->motion.pointerCount = pointerCount;
-
-    mSharedMessage->motion.sampleCount = 1;
-    mSharedMessage->motion.sampleData[0].eventTime = eventTime;
-
-    for (size_t i = 0; i < pointerCount; i++) {
-        mSharedMessage->motion.pointerProperties[i].copyFrom(pointerProperties[i]);
-        mSharedMessage->motion.sampleData[0].coords[i].copyFrom(pointerCoords[i]);
-    }
-
-    // Cache essential information about the motion event to ensure that a malicious consumer
-    // cannot confuse the publisher by modifying the contents of the shared memory buffer while
-    // it is being updated.
-    if (action == AMOTION_EVENT_ACTION_MOVE
-            || action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-        mMotionEventPointerCount = pointerCount;
-        mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount);
-        mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement(
-                mSharedMessage->motion.sampleData, mMotionEventSampleDataStride);
-    } else {
-        mMotionEventSampleDataTail = NULL;
-    }
-    return OK;
-}
-
-status_t InputPublisher::appendMotionSample(
-        nsecs_t eventTime,
-        const PointerCoords* pointerCoords) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld",
-            mChannel->getName().string(), eventTime);
-#endif
-
-    if (! mPinned || ! mMotionEventSampleDataTail) {
-        ALOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current "
-                "AMOTION_EVENT_ACTION_MOVE or AMOTION_EVENT_ACTION_HOVER_MOVE event.",
-                mChannel->getName().string());
-        return INVALID_OPERATION;
-    }
-
-    InputMessage::SampleData* newTail = InputMessage::sampleDataPtrIncrement(
-            mMotionEventSampleDataTail, mMotionEventSampleDataStride);
-    size_t newBytesUsed = reinterpret_cast<char*>(newTail) -
-            reinterpret_cast<char*>(mSharedMessage);
-
-    if (newBytesUsed > mAshmemSize) {
-#if DEBUG_TRANSPORT_ACTIONS
-        ALOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
-                "buffer is full.  Buffer size: %d bytes, pointers: %d, samples: %d",
-                mChannel->getName().string(),
-                mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
-#endif
-        return NO_MEMORY;
-    }
-
-    int result;
-    if (mWasDispatched) {
-        result = sem_trywait(& mSharedMessage->semaphore);
-        if (result < 0) {
-            if (errno == EAGAIN) {
-                // Only possible source of contention is the consumer having consumed (or being in the
-                // process of consuming) the message and left the semaphore count at 0.
-#if DEBUG_TRANSPORT_ACTIONS
-                ALOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
-                        "already been consumed.", mChannel->getName().string());
-#endif
-                return FAILED_TRANSACTION;
-            } else {
-                ALOGE("channel '%s' publisher ~ Error %d in sem_trywait.",
-                        mChannel->getName().string(), errno);
-                return UNKNOWN_ERROR;
-            }
-        }
-    }
-
-    mMotionEventSampleDataTail->eventTime = eventTime;
-    for (size_t i = 0; i < mMotionEventPointerCount; i++) {
-        mMotionEventSampleDataTail->coords[i].copyFrom(pointerCoords[i]);
-    }
-    mMotionEventSampleDataTail = newTail;
-
-    mSharedMessage->motion.sampleCount += 1;
-
-    if (mWasDispatched) {
-        result = sem_post(& mSharedMessage->semaphore);
-        if (result < 0) {
-            ALOGE("channel '%s' publisher ~ Error %d in sem_post.",
-                    mChannel->getName().string(), errno);
-            return UNKNOWN_ERROR;
-        }
-    }
-    return OK;
-}
-
-status_t InputPublisher::sendDispatchSignal() {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ sendDispatchSignal",
-            mChannel->getName().string());
-#endif
-
-    mWasDispatched = true;
-    return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
-}
-
-status_t InputPublisher::receiveFinishedSignal(bool* outHandled) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
-            mChannel->getName().string());
-#endif
-
-    char signal;
-    status_t result = mChannel->receiveSignal(& signal);
-    if (result) {
-        *outHandled = false;
-        return result;
-    }
-    if (signal == INPUT_SIGNAL_FINISHED_HANDLED) {
-        *outHandled = true;
-    } else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) {
-        *outHandled = false;
-    } else {
-        ALOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
-                mChannel->getName().string(), signal);
-        return UNKNOWN_ERROR;
-    }
-    return OK;
-}
-
-// --- InputConsumer ---
-
-InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
-        mChannel(channel), mSharedMessage(NULL) {
-}
-
-InputConsumer::~InputConsumer() {
-    if (mSharedMessage) {
-        munmap(mSharedMessage, mAshmemSize);
-    }
-}
-
-status_t InputConsumer::initialize() {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ initialize",
-            mChannel->getName().string());
-#endif
-
-    int ashmemFd = mChannel->getAshmemFd();
-    int result = ashmem_get_size_region(ashmemFd);
-    if (result < 0) {
-        ALOGE("channel '%s' consumer ~ Error %d getting size of ashmem fd %d.",
-                mChannel->getName().string(), result, ashmemFd);
-        return UNKNOWN_ERROR;
-    }
-
-    mAshmemSize = (size_t) result;
-
-    mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
-            PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
-    if (! mSharedMessage) {
-        ALOGE("channel '%s' consumer ~ mmap failed on ashmem fd %d.",
-                mChannel->getName().string(), ashmemFd);
-        return NO_MEMORY;
-    }
-
-    return OK;
-}
-
-status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ consume",
-            mChannel->getName().string());
-#endif
-
-    *outEvent = NULL;
-
-    int ashmemFd = mChannel->getAshmemFd();
-    int result = ashmem_pin_region(ashmemFd, 0, 0);
-    if (result != ASHMEM_NOT_PURGED) {
-        if (result == ASHMEM_WAS_PURGED) {
-            ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged "
-                    "which probably indicates that the publisher and consumer are out of sync.",
-                    mChannel->getName().string(), result, ashmemFd);
-            return INVALID_OPERATION;
-        }
-
-        ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",
-                mChannel->getName().string(), result, ashmemFd);
-        return UNKNOWN_ERROR;
-    }
-
-    if (mSharedMessage->consumed) {
-        ALOGE("channel '%s' consumer ~ The current message has already been consumed.",
-                mChannel->getName().string());
-        return INVALID_OPERATION;
-    }
-
-    // Acquire but *never release* the semaphore.  Contention on the semaphore is used to signal
-    // to the publisher that the message has been consumed (or is in the process of being
-    // consumed).  Eventually the publisher will reinitialize the semaphore for the next message.
-    result = sem_wait(& mSharedMessage->semaphore);
-    if (result < 0) {
-        ALOGE("channel '%s' consumer ~ Error %d in sem_wait.",
-                mChannel->getName().string(), errno);
-        return UNKNOWN_ERROR;
-    }
-
-    mSharedMessage->consumed = true;
-
-    switch (mSharedMessage->type) {
-    case AINPUT_EVENT_TYPE_KEY: {
-        KeyEvent* keyEvent = factory->createKeyEvent();
-        if (! keyEvent) return NO_MEMORY;
-
-        populateKeyEvent(keyEvent);
-
-        *outEvent = keyEvent;
-        break;
-    }
-
-    case AINPUT_EVENT_TYPE_MOTION: {
-        MotionEvent* motionEvent = factory->createMotionEvent();
-        if (! motionEvent) return NO_MEMORY;
-
-        populateMotionEvent(motionEvent);
-
-        *outEvent = motionEvent;
-        break;
-    }
-
-    default:
-        ALOGE("channel '%s' consumer ~ Received message of unknown type %d",
-                mChannel->getName().string(), mSharedMessage->type);
-        return UNKNOWN_ERROR;
-    }
-
-    return OK;
-}
-
-status_t InputConsumer::sendFinishedSignal(bool handled) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d",
-            mChannel->getName().string(), handled);
-#endif
-
-    return mChannel->sendSignal(handled
-            ? INPUT_SIGNAL_FINISHED_HANDLED
-            : INPUT_SIGNAL_FINISHED_UNHANDLED);
-}
-
-status_t InputConsumer::receiveDispatchSignal() {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ receiveDispatchSignal",
-            mChannel->getName().string());
-#endif
-
-    char signal;
-    status_t result = mChannel->receiveSignal(& signal);
-    if (result) {
-        return result;
-    }
-    if (signal != INPUT_SIGNAL_DISPATCH) {
-        ALOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher",
-                mChannel->getName().string(), signal);
-        return UNKNOWN_ERROR;
-    }
-    return OK;
-}
-
-void InputConsumer::populateKeyEvent(KeyEvent* keyEvent) const {
-    keyEvent->initialize(
-            mSharedMessage->deviceId,
-            mSharedMessage->source,
-            mSharedMessage->key.action,
-            mSharedMessage->key.flags,
-            mSharedMessage->key.keyCode,
-            mSharedMessage->key.scanCode,
-            mSharedMessage->key.metaState,
-            mSharedMessage->key.repeatCount,
-            mSharedMessage->key.downTime,
-            mSharedMessage->key.eventTime);
-}
-
-void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const {
-    motionEvent->initialize(
-            mSharedMessage->deviceId,
-            mSharedMessage->source,
-            mSharedMessage->motion.action,
-            mSharedMessage->motion.flags,
-            mSharedMessage->motion.edgeFlags,
-            mSharedMessage->motion.metaState,
-            mSharedMessage->motion.buttonState,
-            mSharedMessage->motion.xOffset,
-            mSharedMessage->motion.yOffset,
-            mSharedMessage->motion.xPrecision,
-            mSharedMessage->motion.yPrecision,
-            mSharedMessage->motion.downTime,
-            mSharedMessage->motion.sampleData[0].eventTime,
-            mSharedMessage->motion.pointerCount,
-            mSharedMessage->motion.pointerProperties,
-            mSharedMessage->motion.sampleData[0].coords);
-
-    size_t sampleCount = mSharedMessage->motion.sampleCount;
-    if (sampleCount > 1) {
-        InputMessage::SampleData* sampleData = mSharedMessage->motion.sampleData;
-        size_t sampleDataStride = InputMessage::sampleDataStride(
-                mSharedMessage->motion.pointerCount);
-
-        while (--sampleCount > 0) {
-            sampleData = InputMessage::sampleDataPtrIncrement(sampleData, sampleDataStride);
-            motionEvent->addSample(sampleData->eventTime, sampleData->coords);
-        }
-    }
-}
-
-} // namespace android
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index ee186c8..6993dac 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -15,13 +15,51 @@
  */
 
 #include <ui/PixelFormat.h>
-#include <pixelflinger/format.h>
 #include <hardware/hardware.h>
 
+// ----------------------------------------------------------------------------
 namespace android {
+// ----------------------------------------------------------------------------
 
 static const int COMPONENT_YUV = 0xFF;
 
+struct Info {
+    size_t      size;
+    size_t      bitsPerPixel;
+    struct {
+        uint8_t     ah;
+        uint8_t     al;
+        uint8_t     rh;
+        uint8_t     rl;
+        uint8_t     gh;
+        uint8_t     gl;
+        uint8_t     bh;
+        uint8_t     bl;
+    };
+    uint8_t     components;
+};
+
+static Info const sPixelFormatInfos[] = {
+        { 0,  0, { 0, 0,   0, 0,   0, 0,   0, 0 }, 0 },
+        { 4, 32, {32,24,   8, 0,  16, 8,  24,16 }, PixelFormatInfo::RGBA },
+        { 4, 24, { 0, 0,   8, 0,  16, 8,  24,16 }, PixelFormatInfo::RGB  },
+        { 3, 24, { 0, 0,   8, 0,  16, 8,  24,16 }, PixelFormatInfo::RGB  },
+        { 2, 16, { 0, 0,  16,11,  11, 5,   5, 0 }, PixelFormatInfo::RGB  },
+        { 4, 32, {32,24,  24,16,  16, 8,   8, 0 }, PixelFormatInfo::RGBA },
+        { 2, 16, { 1, 0,  16,11,  11, 6,   6, 1 }, PixelFormatInfo::RGBA },
+        { 2, 16, { 4, 0,  16,12,  12, 8,   8, 4 }, PixelFormatInfo::RGBA },
+        { 1,  8, { 8, 0,   0, 0,   0, 0,   0, 0 }, PixelFormatInfo::ALPHA}
+};
+
+static const Info* gGetPixelFormatTable(size_t* numEntries) {
+    if (numEntries) {
+        *numEntries = sizeof(sPixelFormatInfos)/sizeof(Info);
+    }
+    return sPixelFormatInfos;
+}
+
+// ----------------------------------------------------------------------------
+
 size_t PixelFormatInfo::getScanlineSize(unsigned int width) const
 {
     size_t size;
@@ -77,27 +115,12 @@
     }
 
     size_t numEntries;
-    const GGLFormat *i = gglGetPixelFormatTable(&numEntries) + format;
+    const Info *i = gGetPixelFormatTable(&numEntries) + format;
     bool valid = uint32_t(format) < numEntries;
     if (!valid) {
         return BAD_INDEX;
     }
 
-    #define COMPONENT(name) \
-        case GGL_##name: info->components = PixelFormatInfo::name; break;
-    
-    switch (i->components) {
-        COMPONENT(ALPHA)
-        COMPONENT(RGB)
-        COMPONENT(RGBA)
-        COMPONENT(LUMINANCE)
-        COMPONENT(LUMINANCE_ALPHA)
-        default:
-            return BAD_INDEX;
-    }
-    
-    #undef COMPONENT
-    
     info->format = format;
     info->bytesPerPixel = i->size;
     info->bitsPerPixel  = i->bitsPerPixel;
@@ -109,9 +132,12 @@
     info->l_green       = i->gl;
     info->h_blue        = i->bh;
     info->l_blue        = i->bl;
+    info->components    = i->components;
 
     return NO_ERROR;
 }
 
+// ----------------------------------------------------------------------------
 }; // namespace android
+// ----------------------------------------------------------------------------
 
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 8cd047a..6e2e731 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -126,6 +126,9 @@
 Region& Region::orSelf(const Rect& r) {
     return operationSelf(r, op_or);
 }
+Region& Region::xorSelf(const Rect& r) {
+    return operationSelf(r, op_xor);
+}
 Region& Region::andSelf(const Rect& r) {
     return operationSelf(r, op_and);
 }
@@ -143,6 +146,9 @@
 Region& Region::orSelf(const Region& rhs) {
     return operationSelf(rhs, op_or);
 }
+Region& Region::xorSelf(const Region& rhs) {
+    return operationSelf(rhs, op_xor);
+}
 Region& Region::andSelf(const Region& rhs) {
     return operationSelf(rhs, op_and);
 }
@@ -165,6 +171,9 @@
 const Region Region::merge(const Rect& rhs) const {
     return operation(rhs, op_or);
 }
+const Region Region::mergeExclusive(const Rect& rhs) const {
+    return operation(rhs, op_xor);
+}
 const Region Region::intersect(const Rect& rhs) const {
     return operation(rhs, op_and);
 }
@@ -182,6 +191,9 @@
 const Region Region::merge(const Region& rhs) const {
     return operation(rhs, op_or);
 }
+const Region Region::mergeExclusive(const Region& rhs) const {
+    return operation(rhs, op_xor);
+}
 const Region Region::intersect(const Region& rhs) const {
     return operation(rhs, op_and);
 }
@@ -205,6 +217,9 @@
 Region& Region::orSelf(const Region& rhs, int dx, int dy) {
     return operationSelf(rhs, dx, dy, op_or);
 }
+Region& Region::xorSelf(const Region& rhs, int dx, int dy) {
+    return operationSelf(rhs, dx, dy, op_xor);
+}
 Region& Region::andSelf(const Region& rhs, int dx, int dy) {
     return operationSelf(rhs, dx, dy, op_and);
 }
@@ -222,6 +237,9 @@
 const Region Region::merge(const Region& rhs, int dx, int dy) const {
     return operation(rhs, dx, dy, op_or);
 }
+const Region Region::mergeExclusive(const Region& rhs, int dx, int dy) const {
+    return operation(rhs, dx, dy, op_xor);
+}
 const Region Region::intersect(const Region& rhs, int dx, int dy) const {
     return operation(rhs, dx, dy, op_and);
 }
@@ -421,6 +439,7 @@
     SkRegion::Op sk_op;
     switch (op) {
         case op_or: sk_op = SkRegion::kUnion_Op; name="OR"; break;
+        case op_xor: sk_op = SkRegion::kUnion_XOR; name="XOR"; break;
         case op_and: sk_op = SkRegion::kIntersect_Op; name="AND"; break;
         case op_nand: sk_op = SkRegion::kDifference_Op; name="NAND"; break;
     }
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index 700b604..50cad36 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -2,47 +2,5 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-# Build the unit tests.
-test_src_files := \
-    InputChannel_test.cpp \
-    InputEvent_test.cpp \
-    InputPublisherAndConsumer_test.cpp
-
-shared_libraries := \
-	libcutils \
-	libutils \
-	libEGL \
-	libbinder \
-	libpixelflinger \
-	libhardware \
-	libhardware_legacy \
-	libui \
-	libstlport \
-	libskia
-
-static_libraries := \
-	libgtest \
-	libgtest_main
-
-c_includes := \
-    bionic \
-    bionic/libstdc++/include \
-    external/gtest/include \
-    external/stlport/stlport \
-    external/skia/include/core
-
-module_tags := eng tests
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
-    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
-    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
-    $(eval include $(BUILD_EXECUTABLE)) \
-)
-
 # Build the manual test programs.
 include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp
deleted file mode 100644
index eff22ee..0000000
--- a/libs/ui/tests/InputChannel_test.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * 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.
- */
-
-#include <ui/InputTransport.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <time.h>
-#include <sys/mman.h>
-#include <cutils/ashmem.h>
-
-#include "../../utils/tests/TestHelpers.h"
-
-namespace android {
-
-class InputChannelTest : public testing::Test {
-protected:
-    virtual void SetUp() { }
-    virtual void TearDown() { }
-};
-
-
-TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) {
-    // Our purpose here is to verify that the input channel destructor closes the
-    // file descriptors provided to it.  One easy way is to provide it with one end
-    // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
-    Pipe fakeAshmem, sendPipe, receivePipe;
-
-    sp<InputChannel> inputChannel = new InputChannel(String8("channel name"),
-            fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.sendFd);
-
-    EXPECT_STREQ("channel name", inputChannel->getName().string())
-            << "channel should have provided name";
-    EXPECT_EQ(fakeAshmem.sendFd, inputChannel->getAshmemFd())
-            << "channel should have provided ashmem fd";
-    EXPECT_EQ(receivePipe.receiveFd, inputChannel->getReceivePipeFd())
-            << "channel should have provided receive pipe fd";
-    EXPECT_EQ(sendPipe.sendFd, inputChannel->getSendPipeFd())
-            << "channel should have provided send pipe fd";
-
-    inputChannel.clear(); // destroys input channel
-
-    EXPECT_EQ(-EPIPE, fakeAshmem.readSignal())
-            << "channel should have closed ashmem fd when destroyed";
-    EXPECT_EQ(-EPIPE, receivePipe.writeSignal())
-            << "channel should have closed receive pipe fd when destroyed";
-    EXPECT_EQ(-EPIPE, sendPipe.readSignal())
-            << "channel should have closed send pipe fd when destroyed";
-
-    // clean up fds of Pipe endpoints that were closed so we don't try to close them again
-    fakeAshmem.sendFd = -1;
-    receivePipe.receiveFd = -1;
-    sendPipe.sendFd = -1;
-}
-
-TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
-    sp<InputChannel> serverChannel, clientChannel;
-
-    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
-            serverChannel, clientChannel);
-
-    ASSERT_EQ(OK, result)
-            << "should have successfully opened a channel pair";
-
-    // Name
-    EXPECT_STREQ("channel name (server)", serverChannel->getName().string())
-            << "server channel should have suffixed name";
-    EXPECT_STREQ("channel name (client)", clientChannel->getName().string())
-            << "client channel should have suffixed name";
-
-    // Ashmem uniqueness
-    EXPECT_NE(serverChannel->getAshmemFd(), clientChannel->getAshmemFd())
-            << "server and client channel should have different ashmem fds because it was dup'd";
-
-    // Ashmem usability
-    ssize_t serverAshmemSize = ashmem_get_size_region(serverChannel->getAshmemFd());
-    ssize_t clientAshmemSize = ashmem_get_size_region(clientChannel->getAshmemFd());
-    uint32_t* serverAshmem = static_cast<uint32_t*>(mmap(NULL, serverAshmemSize,
-            PROT_READ | PROT_WRITE, MAP_SHARED, serverChannel->getAshmemFd(), 0));
-    uint32_t* clientAshmem = static_cast<uint32_t*>(mmap(NULL, clientAshmemSize,
-            PROT_READ | PROT_WRITE, MAP_SHARED, clientChannel->getAshmemFd(), 0));
-    ASSERT_TRUE(serverAshmem != NULL)
-            << "server channel ashmem should be mappable";
-    ASSERT_TRUE(clientAshmem != NULL)
-            << "client channel ashmem should be mappable";
-    *serverAshmem = 0xf00dd00d;
-    EXPECT_EQ(0xf00dd00d, *clientAshmem)
-            << "ashmem buffer should be shared by client and server";
-    munmap(serverAshmem, serverAshmemSize);
-    munmap(clientAshmem, clientAshmemSize);
-
-    // Server->Client communication
-    EXPECT_EQ(OK, serverChannel->sendSignal('S'))
-            << "server channel should be able to send signal to client channel";
-    char signal;
-    EXPECT_EQ(OK, clientChannel->receiveSignal(& signal))
-            << "client channel should be able to receive signal from server channel";
-    EXPECT_EQ('S', signal)
-            << "client channel should receive the correct signal from server channel";
-
-    // Client->Server communication
-    EXPECT_EQ(OK, clientChannel->sendSignal('c'))
-            << "client channel should be able to send signal to server channel";
-    EXPECT_EQ(OK, serverChannel->receiveSignal(& signal))
-            << "server channel should be able to receive signal from client channel";
-    EXPECT_EQ('c', signal)
-            << "server channel should receive the correct signal from client channel";
-}
-
-TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
-    sp<InputChannel> serverChannel, clientChannel;
-
-    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
-            serverChannel, clientChannel);
-
-    ASSERT_EQ(OK, result)
-            << "should have successfully opened a channel pair";
-
-    char signal;
-    EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveSignal(& signal))
-            << "receiveSignal should have returned WOULD_BLOCK";
-}
-
-TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
-    sp<InputChannel> serverChannel, clientChannel;
-
-    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
-            serverChannel, clientChannel);
-
-    ASSERT_EQ(OK, result)
-            << "should have successfully opened a channel pair";
-
-    serverChannel.clear(); // close server channel
-
-    char signal;
-    EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal))
-            << "receiveSignal should have returned DEAD_OBJECT";
-}
-
-TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
-    sp<InputChannel> serverChannel, clientChannel;
-
-    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
-            serverChannel, clientChannel);
-
-    ASSERT_EQ(OK, result)
-            << "should have successfully opened a channel pair";
-
-    serverChannel.clear(); // close server channel
-
-    EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S'))
-            << "sendSignal should have returned DEAD_OBJECT";
-}
-
-
-} // namespace android
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
deleted file mode 100644
index fcc4cad..0000000
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ /dev/null
@@ -1,531 +0,0 @@
-/*
- * 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.
- */
-
-#include <ui/InputTransport.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <time.h>
-#include <sys/mman.h>
-#include <cutils/ashmem.h>
-
-#include "../../utils/tests/TestHelpers.h"
-
-namespace android {
-
-class InputPublisherAndConsumerTest : public testing::Test {
-protected:
-    sp<InputChannel> serverChannel, clientChannel;
-    InputPublisher* mPublisher;
-    InputConsumer* mConsumer;
-    PreallocatedInputEventFactory mEventFactory;
-
-    virtual void SetUp() {
-        status_t result = InputChannel::openInputChannelPair(String8("channel name"),
-                serverChannel, clientChannel);
-
-        mPublisher = new InputPublisher(serverChannel);
-        mConsumer = new InputConsumer(clientChannel);
-    }
-
-    virtual void TearDown() {
-        if (mPublisher) {
-            delete mPublisher;
-            mPublisher = NULL;
-        }
-
-        if (mConsumer) {
-            delete mConsumer;
-            mConsumer = NULL;
-        }
-
-        serverChannel.clear();
-        clientChannel.clear();
-    }
-
-    void Initialize();
-    void PublishAndConsumeKeyEvent();
-    void PublishAndConsumeMotionEvent(
-            size_t samplesToAppendBeforeDispatch = 0,
-            size_t samplesToAppendAfterDispatch = 0);
-};
-
-TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
-    EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
-    EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
-}
-
-void InputPublisherAndConsumerTest::Initialize() {
-    status_t status;
-
-    status = mPublisher->initialize();
-    ASSERT_EQ(OK, status)
-            << "publisher initialize should return OK";
-
-    status = mConsumer->initialize();
-    ASSERT_EQ(OK, status)
-            << "consumer initialize should return OK";
-}
-
-void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
-    status_t status;
-
-    const int32_t deviceId = 1;
-    const int32_t source = AINPUT_SOURCE_KEYBOARD;
-    const int32_t action = AKEY_EVENT_ACTION_DOWN;
-    const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
-    const int32_t keyCode = AKEYCODE_ENTER;
-    const int32_t scanCode = 13;
-    const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
-    const int32_t repeatCount = 1;
-    const nsecs_t downTime = 3;
-    const nsecs_t eventTime = 4;
-
-    status = mPublisher->publishKeyEvent(deviceId, source, action, flags,
-            keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
-    ASSERT_EQ(OK, status)
-            << "publisher publishKeyEvent should return OK";
-
-    status = mPublisher->sendDispatchSignal();
-    ASSERT_EQ(OK, status)
-            << "publisher sendDispatchSignal should return OK";
-
-    status = mConsumer->receiveDispatchSignal();
-    ASSERT_EQ(OK, status)
-            << "consumer receiveDispatchSignal should return OK";
-
-    InputEvent* event;
-    status = mConsumer->consume(& mEventFactory, & event);
-    ASSERT_EQ(OK, status)
-            << "consumer consume should return OK";
-
-    ASSERT_TRUE(event != NULL)
-            << "consumer should have returned non-NULL event";
-    ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType())
-            << "consumer should have returned a key event";
-
-    KeyEvent* keyEvent = static_cast<KeyEvent*>(event);
-    EXPECT_EQ(deviceId, keyEvent->getDeviceId());
-    EXPECT_EQ(source, keyEvent->getSource());
-    EXPECT_EQ(action, keyEvent->getAction());
-    EXPECT_EQ(flags, keyEvent->getFlags());
-    EXPECT_EQ(keyCode, keyEvent->getKeyCode());
-    EXPECT_EQ(scanCode, keyEvent->getScanCode());
-    EXPECT_EQ(metaState, keyEvent->getMetaState());
-    EXPECT_EQ(repeatCount, keyEvent->getRepeatCount());
-    EXPECT_EQ(downTime, keyEvent->getDownTime());
-    EXPECT_EQ(eventTime, keyEvent->getEventTime());
-
-    status = mConsumer->sendFinishedSignal(true);
-    ASSERT_EQ(OK, status)
-            << "consumer sendFinishedSignal should return OK";
-
-    bool handled = false;
-    status = mPublisher->receiveFinishedSignal(&handled);
-    ASSERT_EQ(OK, status)
-            << "publisher receiveFinishedSignal should return OK";
-    ASSERT_TRUE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-
-    status = mPublisher->reset();
-    ASSERT_EQ(OK, status)
-            << "publisher reset should return OK";
-}
-
-void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
-        size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) {
-    status_t status;
-
-    const int32_t deviceId = 1;
-    const int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
-    const int32_t action = AMOTION_EVENT_ACTION_MOVE;
-    const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
-    const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
-    const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
-    const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
-    const float xOffset = -10;
-    const float yOffset = -20;
-    const float xPrecision = 0.25;
-    const float yPrecision = 0.5;
-    const nsecs_t downTime = 3;
-    const size_t pointerCount = 3;
-    PointerProperties pointerProperties[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerProperties[i].id = (i + 2) % pointerCount;
-        pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-    }
-
-    Vector<nsecs_t> sampleEventTimes;
-    Vector<PointerCoords> samplePointerCoords;
-
-    for (size_t i = 0; i <= samplesToAppendAfterDispatch + samplesToAppendBeforeDispatch; i++) {
-        sampleEventTimes.push(i + 10);
-        for (size_t j = 0; j < pointerCount; j++) {
-            samplePointerCoords.push();
-            PointerCoords& pc = samplePointerCoords.editTop();
-            pc.clear();
-            pc.setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i + j);
-        }
-    }
-
-    status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags,
-            metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision,
-            downTime, sampleEventTimes[0], pointerCount,
-            pointerProperties, samplePointerCoords.array());
-    ASSERT_EQ(OK, status)
-            << "publisher publishMotionEvent should return OK";
-
-    for (size_t i = 0; i < samplesToAppendBeforeDispatch; i++) {
-        size_t sampleIndex = i + 1;
-        status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
-                samplePointerCoords.array() + sampleIndex * pointerCount);
-        ASSERT_EQ(OK, status)
-                << "publisher appendMotionEvent should return OK";
-    }
-
-    status = mPublisher->sendDispatchSignal();
-    ASSERT_EQ(OK, status)
-            << "publisher sendDispatchSignal should return OK";
-
-    for (size_t i = 0; i < samplesToAppendAfterDispatch; i++) {
-        size_t sampleIndex = i + 1 + samplesToAppendBeforeDispatch;
-        status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
-                samplePointerCoords.array() + sampleIndex * pointerCount);
-        ASSERT_EQ(OK, status)
-                << "publisher appendMotionEvent should return OK";
-    }
-
-    status = mConsumer->receiveDispatchSignal();
-    ASSERT_EQ(OK, status)
-            << "consumer receiveDispatchSignal should return OK";
-
-    InputEvent* event;
-    status = mConsumer->consume(& mEventFactory, & event);
-    ASSERT_EQ(OK, status)
-            << "consumer consume should return OK";
-
-    ASSERT_TRUE(event != NULL)
-            << "consumer should have returned non-NULL event";
-    ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
-            << "consumer should have returned a motion event";
-
-    size_t lastSampleIndex = samplesToAppendBeforeDispatch + samplesToAppendAfterDispatch;
-
-    MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
-    EXPECT_EQ(deviceId, motionEvent->getDeviceId());
-    EXPECT_EQ(source, motionEvent->getSource());
-    EXPECT_EQ(action, motionEvent->getAction());
-    EXPECT_EQ(flags, motionEvent->getFlags());
-    EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
-    EXPECT_EQ(metaState, motionEvent->getMetaState());
-    EXPECT_EQ(buttonState, motionEvent->getButtonState());
-    EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
-    EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
-    EXPECT_EQ(downTime, motionEvent->getDownTime());
-    EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
-    EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
-    EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize());
-
-    for (size_t i = 0; i < pointerCount; i++) {
-        SCOPED_TRACE(i);
-        EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
-        EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
-    }
-
-    for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) {
-        SCOPED_TRACE(sampleIndex);
-        EXPECT_EQ(sampleEventTimes[sampleIndex],
-                motionEvent->getHistoricalEventTime(sampleIndex));
-        for (size_t i = 0; i < pointerCount; i++) {
-            SCOPED_TRACE(i);
-            size_t offset = sampleIndex * pointerCount + i;
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X),
-                    motionEvent->getHistoricalRawX(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                    motionEvent->getHistoricalRawY(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
-                    motionEvent->getHistoricalX(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
-                    motionEvent->getHistoricalY(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                    motionEvent->getHistoricalPressure(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                    motionEvent->getHistoricalSize(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                    motionEvent->getHistoricalTouchMajor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                    motionEvent->getHistoricalTouchMinor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                    motionEvent->getHistoricalToolMajor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                    motionEvent->getHistoricalToolMinor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
-                    motionEvent->getHistoricalOrientation(i, sampleIndex));
-        }
-    }
-
-    SCOPED_TRACE(lastSampleIndex);
-    EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
-    for (size_t i = 0; i < pointerCount; i++) {
-        SCOPED_TRACE(i);
-        size_t offset = lastSampleIndex * pointerCount + i;
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X),
-                motionEvent->getRawX(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                motionEvent->getRawY(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
-                motionEvent->getX(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
-                motionEvent->getY(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                motionEvent->getPressure(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                motionEvent->getSize(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                motionEvent->getTouchMajor(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                motionEvent->getTouchMinor(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                motionEvent->getToolMajor(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                motionEvent->getToolMinor(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
-                motionEvent->getOrientation(i));
-    }
-
-    status = mConsumer->sendFinishedSignal(false);
-    ASSERT_EQ(OK, status)
-            << "consumer sendFinishedSignal should return OK";
-
-    bool handled = true;
-    status = mPublisher->receiveFinishedSignal(&handled);
-    ASSERT_EQ(OK, status)
-            << "publisher receiveFinishedSignal should return OK";
-    ASSERT_FALSE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-
-    status = mPublisher->reset();
-    ASSERT_EQ(OK, status)
-            << "publisher reset should return OK";
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_WhenNotReset_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-    ASSERT_EQ(OK, status)
-            << "publisher publishKeyEvent should return OK first time";
-
-    status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-    ASSERT_EQ(INVALID_OPERATION, status)
-            << "publisher publishKeyEvent should return INVALID_OPERATION because "
-                    "the publisher was not reset";
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = 1;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(OK, status)
-            << "publisher publishMotionEvent should return OK";
-
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(INVALID_OPERATION, status)
-            << "publisher publishMotionEvent should return INVALID_OPERATION because ";
-                    "the publisher was not reset";
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = 0;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(BAD_VALUE, status)
-            << "publisher publishMotionEvent should return BAD_VALUE";
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = MAX_POINTERS + 1;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(BAD_VALUE, status)
-            << "publisher publishMotionEvent should return BAD_VALUE";
-}
-
-TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledBeforeDispatchSignal_AppendsSamples) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(3, 0));
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledAfterDispatchSignalAndNotConsumed_AppendsSamples) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(0, 4));
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenNoMotionEventPublished_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    PointerCoords pointerCoords[1];
-    status = mPublisher->appendMotionSample(0, pointerCoords);
-    ASSERT_EQ(INVALID_OPERATION, status)
-            << "publisher appendMotionSample should return INVALID_OPERATION";
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEventIsNotAMove_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = MAX_POINTERS;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN,
-            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(OK, status);
-
-    status = mPublisher->appendMotionSample(0, pointerCoords);
-    ASSERT_EQ(INVALID_OPERATION, status)
-            << "publisher appendMotionSample should return INVALID_OPERATION";
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = MAX_POINTERS;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
-            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(OK, status);
-
-    status = mPublisher->sendDispatchSignal();
-    ASSERT_EQ(OK, status);
-
-    status = mConsumer->receiveDispatchSignal();
-    ASSERT_EQ(OK, status);
-
-    InputEvent* event;
-    status = mConsumer->consume(& mEventFactory, & event);
-    ASSERT_EQ(OK, status);
-
-    status = mPublisher->appendMotionSample(0, pointerCoords);
-    ASSERT_EQ(status_t(FAILED_TRANSACTION), status)
-            << "publisher appendMotionSample should return FAILED_TRANSACTION";
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = MAX_POINTERS;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
-            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(OK, status);
-
-    for (int count = 1;; count++) {
-        ASSERT_LT(count, 100000) << "should eventually reach OOM";
-
-        status = mPublisher->appendMotionSample(0, pointerCoords);
-        if (status != OK) {
-            ASSERT_GT(count, 12) << "should be able to add at least a dozen samples";
-            ASSERT_EQ(NO_MEMORY, status)
-                    << "publisher appendMotionSample should return NO_MEMORY when buffer is full";
-            break;
-        }
-    }
-
-    status = mPublisher->appendMotionSample(0, pointerCoords);
-    ASSERT_EQ(NO_MEMORY, status)
-            << "publisher appendMotionSample should return NO_MEMORY persistently until reset";
-}
-
-} // namespace android
diff --git a/libs/usb/tests/AccessoryChat/Android.mk b/libs/usb/tests/AccessoryChat/Android.mk
index 77b8424..ecb455a 100644
--- a/libs/usb/tests/AccessoryChat/Android.mk
+++ b/libs/usb/tests/AccessoryChat/Android.mk
@@ -23,9 +23,4 @@
 
 LOCAL_PACKAGE_NAME := AccessoryChat
 
-LOCAL_JAVA_LIBRARIES := com.android.future.usb.accessory
-
-# Force an old SDK version to make sure we aren't using newer UsbManager APIs
-LOCAL_SDK_VERSION := 8
-
 include $(BUILD_PACKAGE)
diff --git a/libs/usb/tests/AccessoryChat/AndroidManifest.xml b/libs/usb/tests/AccessoryChat/AndroidManifest.xml
index 802b715..6667eba 100644
--- a/libs/usb/tests/AccessoryChat/AndroidManifest.xml
+++ b/libs/usb/tests/AccessoryChat/AndroidManifest.xml
@@ -17,8 +17,9 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.accessorychat">
 
+    <uses-feature android:name="android.hardware.usb.accessory" />
+
     <application android:label="Accessory Chat">
-        <uses-library android:name="com.android.future.usb.accessory" />
 
         <activity android:name="AccessoryChat" android:label="Accessory Chat">
             <intent-filter>
@@ -35,5 +36,5 @@
                 android:resource="@xml/accessory_filter" />
         </activity>
     </application>
-    <uses-sdk android:minSdkVersion="10" />
+    <uses-sdk android:minSdkVersion="12" />
 </manifest>
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c b/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c
index 85b52dd..06b477f 100644
--- a/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c
+++ b/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c
@@ -98,7 +98,7 @@
     vendorId = usb_device_get_vendor_id(device);
     productId = usb_device_get_product_id(device);
 
-    if (vendorId == 0x18D1 || vendorId == 0x22B8) {
+    if (vendorId == 0x18D1 || vendorId == 0x22B8 || vendorId == 0x04e8) {
         if (!sDevice && (productId == 0x2D00 || productId == 0x2D01)) {
             struct usb_descriptor_header* desc;
             struct usb_descriptor_iter iter;
diff --git a/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java b/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
index c3f4fa3..bf0cef0 100644
--- a/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
+++ b/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
@@ -33,8 +33,8 @@
 import android.widget.EditText;
 import android.widget.TextView;
 
-import com.android.future.usb.UsbAccessory;
-import com.android.future.usb.UsbManager;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbAccessory;
 
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -64,9 +64,11 @@
         public void onReceive(Context context, Intent intent) {
             if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
                 synchronized (this) {
-                    UsbAccessory accessory = UsbManager.getAccessory(intent);
+                    UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                     if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
-                        openAccessory(accessory);
+                        if (accessory != null) {
+                            openAccessory(accessory);
+                        }
                     } else {
                         Log.d(TAG, "permission denied for accessory " + accessory);
                     }
@@ -80,7 +82,7 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mUsbManager = UsbManager.getInstance(this);
+        mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
         mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
         IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
         registerReceiver(mUsbReceiver, filter);
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 24cf504..a96c8e6 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -18,9 +18,6 @@
 # and once for the device.
 
 commonSources:= \
-	Asset.cpp \
-	AssetDir.cpp \
-	AssetManager.cpp \
 	BasicHashtable.cpp \
 	BlobCache.cpp \
 	BufferedTextOutput.cpp \
@@ -29,14 +26,11 @@
 	FileMap.cpp \
 	Flattenable.cpp \
 	LinearTransform.cpp \
-	ObbFile.cpp \
 	PropertyMap.cpp \
 	RefBase.cpp \
-	ResourceTypes.cpp \
 	SharedBuffer.cpp \
 	Static.cpp \
 	StopWatch.cpp \
-	StreamingZipInflater.cpp \
 	String8.cpp \
 	String16.cpp \
 	StringArray.cpp \
@@ -47,9 +41,6 @@
 	Tokenizer.cpp \
 	Unicode.cpp \
 	VectorImpl.cpp \
-	ZipFileCRO.cpp \
-	ZipFileRO.cpp \
-	ZipUtils.cpp \
 	misc.cpp
 
 
@@ -63,7 +54,6 @@
 LOCAL_MODULE:= libutils
 
 LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS)
-LOCAL_C_INCLUDES += external/zlib
 
 ifeq ($(HOST_OS),windows)
 ifeq ($(strip $(USE_CYGWIN),),)
@@ -79,7 +69,6 @@
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
-
 # For the device
 # =====================================================
 include $(CLEAR_VARS)
@@ -88,8 +77,6 @@
 # we have the common sources, plus some device-specific stuff
 LOCAL_SRC_FILES:= \
 	$(commonSources) \
-	BackupData.cpp \
-	BackupHelpers.cpp \
 	Looper.cpp
 
 ifeq ($(TARGET_OS),linux)
@@ -97,14 +84,11 @@
 endif
 
 LOCAL_C_INCLUDES += \
-		external/zlib \
-		external/icu4c/common \
 		bionic/libc/private
 
 LOCAL_LDLIBS += -lpthread
 
 LOCAL_SHARED_LIBRARIES := \
-	libz \
 	liblog \
 	libcutils \
 	libdl \
@@ -113,19 +97,6 @@
 LOCAL_MODULE:= libutils
 include $(BUILD_SHARED_LIBRARY)
 
-ifeq ($(TARGET_OS),linux)
-include $(CLEAR_VARS)
-LOCAL_C_INCLUDES += \
-		external/zlib \
-		external/icu4c/common \
-		bionic/libc/private
-LOCAL_LDLIBS := -lrt -ldl -lpthread
-LOCAL_MODULE := libutils
-LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp
-include $(BUILD_STATIC_LIBRARY)
-endif
-
-
 # Include subdirectory makefiles
 # ============================================================
 
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index 58230f4..a6811fc 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -7,10 +7,8 @@
 	BasicHashtable_test.cpp \
 	BlobCache_test.cpp \
 	Looper_test.cpp \
-	ObbFile_test.cpp \
 	String8_test.cpp \
 	Unicode_test.cpp \
-	ZipFileRO_test.cpp \
 
 shared_libraries := \
 	libz \
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 78eb89f..9748d3b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -48,6 +48,7 @@
     private final Context mContext;
     private long mVolumeKeyUpTime;
     private int  mVolumeControlStream = -1;
+    private final boolean mUseMasterVolume;
     private static String TAG = "AudioManager";
 
     /**
@@ -90,7 +91,8 @@
      * @see #EXTRA_VIBRATE_SETTING
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String VIBRATE_SETTING_CHANGED_ACTION = "android.media.VIBRATE_SETTING_CHANGED";
+    public static final String VIBRATE_SETTING_CHANGED_ACTION =
+        "android.media.VIBRATE_SETTING_CHANGED";
 
     /**
      * @hide Broadcast intent when the volume for a particular stream type changes.
@@ -104,6 +106,27 @@
     public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
 
     /**
+     * @hide Broadcast intent when the master volume changes.
+     * Includes the new volume
+     *
+     * @see #EXTRA_MASTER_VOLUME_VALUE
+     * @see #EXTRA_PREV_MASTER_VOLUME_VALUE
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String MASTER_VOLUME_CHANGED_ACTION =
+        "android.media.MASTER_VOLUME_CHANGED_ACTION";
+
+    /**
+     * @hide Broadcast intent when the master mute state changes.
+     * Includes the the new volume
+     *
+     * @see #EXTRA_MASTER_VOLUME_MUTED
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String MASTER_MUTE_CHANGED_ACTION =
+        "android.media.MASTER_MUTE_CHANGED_ACTION";
+
+    /**
      * The new vibrate setting for a particular type.
      *
      * @see #VIBRATE_SETTING_CHANGED_ACTION
@@ -140,6 +163,27 @@
     public static final String EXTRA_PREV_VOLUME_STREAM_VALUE =
         "android.media.EXTRA_PREV_VOLUME_STREAM_VALUE";
 
+    /**
+     * @hide The new master volume value for the master volume changed intent.
+     * Value is integer between 0 and 100 inclusive.
+     */
+    public static final String EXTRA_MASTER_VOLUME_VALUE =
+        "android.media.EXTRA_MASTER_VOLUME_VALUE";
+
+    /**
+     * @hide The previous master volume value for the master volume changed intent.
+     * Value is integer between 0 and 100 inclusive.
+     */
+    public static final String EXTRA_PREV_MASTER_VOLUME_VALUE =
+        "android.media.EXTRA_PREV_MASTER_VOLUME_VALUE";
+
+    /**
+     * @hide The new master volume mute state for the master mute changed intent.
+     * Value is boolean
+     */
+    public static final String EXTRA_MASTER_VOLUME_MUTED =
+        "android.media.EXTRA_MASTER_VOLUME_MUTED";
+
     /** The audio stream for phone calls */
     public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
     /** The audio stream for system sounds */
@@ -354,6 +398,8 @@
      */
     public AudioManager(Context context) {
         mContext = context;
+        mUseMasterVolume = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
     }
 
     private static IAudioService getService()
@@ -369,11 +415,12 @@
     /**
      * @hide
      */
-    public void preDispatchKeyEvent(int keyCode, int stream) {
+    public void preDispatchKeyEvent(KeyEvent event, int stream) {
         /*
          * If the user hits another key within the play sound delay, then
          * cancel the sound
          */
+        int keyCode = event.getKeyCode();
         if (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP
                 && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE
                 && mVolumeKeyUpTime + VolumePanel.PLAY_SOUND_DELAY
@@ -382,15 +429,20 @@
              * The user has hit another key during the delay (e.g., 300ms)
              * since the last volume key up, so cancel any sounds.
              */
-            adjustSuggestedStreamVolume(AudioManager.ADJUST_SAME,
+            if (mUseMasterVolume) {
+                adjustMasterVolume(ADJUST_SAME, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
+            } else {
+                adjustSuggestedStreamVolume(ADJUST_SAME,
                         stream, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
+            }
         }
     }
 
     /**
      * @hide
      */
-    public void handleKeyDown(int keyCode, int stream) {
+    public void handleKeyDown(KeyEvent event, int stream) {
+        int keyCode = event.getKeyCode();
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -399,19 +451,33 @@
                  * responsive to the user.
                  */
                 int flags = FLAG_SHOW_UI | FLAG_VIBRATE;
-                if (mVolumeControlStream != -1) {
-                    stream = mVolumeControlStream;
-                    flags |= FLAG_FORCE_STREAM;
+                if (mUseMasterVolume) {
+                    adjustMasterVolume(
+                            keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                                    ? ADJUST_RAISE
+                                    : ADJUST_LOWER,
+                            flags);
+                } else {
+                    if (mVolumeControlStream != -1) {
+                        stream = mVolumeControlStream;
+                        flags |= FLAG_FORCE_STREAM;
+                    }
+                    adjustSuggestedStreamVolume(
+                            keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                                    ? ADJUST_RAISE
+                                    : ADJUST_LOWER,
+                            stream,
+                            flags);
                 }
-                adjustSuggestedStreamVolume(
-                        keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                                ? ADJUST_RAISE
-                                : ADJUST_LOWER,
-                        stream,
-                        flags);
                 break;
             case KeyEvent.KEYCODE_VOLUME_MUTE:
-                // TODO: Actually handle MUTE.
+                if (event.getRepeatCount() == 0) {
+                    if (mUseMasterVolume) {
+                        setMasterMute(!isMasterMute());
+                    } else {
+                        // TODO: Actually handle MUTE.
+                    }
+                }
                 break;
         }
     }
@@ -419,7 +485,8 @@
     /**
      * @hide
      */
-    public void handleKeyUp(int keyCode, int stream) {
+    public void handleKeyUp(KeyEvent event, int stream) {
+        int keyCode = event.getKeyCode();
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -427,21 +494,24 @@
                  * Play a sound. This is done on key up since we don't want the
                  * sound to play when a user holds down volume down to mute.
                  */
-                int flags = FLAG_PLAY_SOUND;
-                if (mVolumeControlStream != -1) {
-                    stream = mVolumeControlStream;
-                    flags |= FLAG_FORCE_STREAM;
+                if (mUseMasterVolume) {
+                    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+                        adjustMasterVolume(ADJUST_SAME, FLAG_PLAY_SOUND);
+                    }
+                } else {
+                    int flags = FLAG_PLAY_SOUND;
+                    if (mVolumeControlStream != -1) {
+                        stream = mVolumeControlStream;
+                        flags |= FLAG_FORCE_STREAM;
+                    }
+                    adjustSuggestedStreamVolume(
+                            ADJUST_SAME,
+                            stream,
+                            flags);
                 }
-                adjustSuggestedStreamVolume(
-                        ADJUST_SAME,
-                        stream,
-                        flags);
 
                 mVolumeKeyUpTime = SystemClock.uptimeMillis();
                 break;
-            case KeyEvent.KEYCODE_VOLUME_MUTE:
-                // TODO: Actually handle MUTE.
-                break;
         }
     }
 
@@ -464,7 +534,11 @@
     public void adjustStreamVolume(int streamType, int direction, int flags) {
         IAudioService service = getService();
         try {
-            service.adjustStreamVolume(streamType, direction, flags);
+            if (mUseMasterVolume) {
+                service.adjustMasterVolume(direction, flags);
+            } else {
+                service.adjustStreamVolume(streamType, direction, flags);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in adjustStreamVolume", e);
         }
@@ -490,7 +564,11 @@
     public void adjustVolume(int direction, int flags) {
         IAudioService service = getService();
         try {
-            service.adjustVolume(direction, flags);
+            if (mUseMasterVolume) {
+                service.adjustMasterVolume(direction, flags);
+            } else {
+                service.adjustVolume(direction, flags);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in adjustVolume", e);
         }
@@ -516,9 +594,32 @@
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
         IAudioService service = getService();
         try {
-            service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
+            if (mUseMasterVolume) {
+                service.adjustMasterVolume(direction, flags);
+            } else {
+                service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
+            }
         } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in adjustVolume", e);
+            Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e);
+        }
+    }
+
+    /**
+     * Adjusts the master volume for the device's audio amplifier.
+     * <p>
+     *
+     * @param direction The direction to adjust the volume. One of
+     *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
+     *            {@link #ADJUST_SAME}.
+     * @param flags One or more flags.
+     * @hide
+     */
+    public void adjustMasterVolume(int direction, int flags) {
+        IAudioService service = getService();
+        try {
+            service.adjustMasterVolume(direction, flags);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in adjustMasterVolume", e);
         }
     }
 
@@ -564,7 +665,11 @@
     public int getStreamMaxVolume(int streamType) {
         IAudioService service = getService();
         try {
-            return service.getStreamMaxVolume(streamType);
+            if (mUseMasterVolume) {
+                return service.getMasterMaxVolume();
+            } else {
+                return service.getStreamMaxVolume(streamType);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in getStreamMaxVolume", e);
             return 0;
@@ -582,7 +687,11 @@
     public int getStreamVolume(int streamType) {
         IAudioService service = getService();
         try {
-            return service.getStreamVolume(streamType);
+            if (mUseMasterVolume) {
+                return service.getMasterVolume();
+            } else {
+                return service.getStreamVolume(streamType);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in getStreamVolume", e);
             return 0;
@@ -597,7 +706,11 @@
     public int getLastAudibleStreamVolume(int streamType) {
         IAudioService service = getService();
         try {
-            return service.getLastAudibleStreamVolume(streamType);
+            if (mUseMasterVolume) {
+                return service.getLastAudibleMasterVolume();
+            } else {
+                return service.getLastAudibleStreamVolume(streamType);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in getLastAudibleStreamVolume", e);
             return 0;
@@ -640,13 +753,82 @@
     public void setStreamVolume(int streamType, int index, int flags) {
         IAudioService service = getService();
         try {
-            service.setStreamVolume(streamType, index, flags);
+            if (mUseMasterVolume) {
+                service.setMasterVolume(index, flags);
+            } else {
+                service.setStreamVolume(streamType, index, flags);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setStreamVolume", e);
         }
     }
 
     /**
+     * Returns the maximum volume index for master volume.
+     *
+     * @hide
+     */
+    public int getMasterMaxVolume() {
+        IAudioService service = getService();
+        try {
+            return service.getMasterMaxVolume();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in getMasterMaxVolume", e);
+            return 0;
+        }
+    }
+
+    /**
+     * Returns the current volume index for master volume.
+     *
+     * @return The current volume index for master volume.
+     * @hide
+     */
+    public int getMasterVolume() {
+        IAudioService service = getService();
+        try {
+            return service.getMasterVolume();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in getMasterVolume", e);
+            return 0;
+        }
+    }
+
+    /**
+     * Get last audible volume before master volume was muted.
+     *
+     * @hide
+     */
+    public int getLastAudibleMasterVolume() {
+        IAudioService service = getService();
+        try {
+            return service.getLastAudibleMasterVolume();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in getLastAudibleMasterVolume", e);
+            return 0;
+        }
+    }
+
+    /**
+     * Sets the volume index for master volume.
+     *
+     * @param index The volume index to set. See
+     *            {@link #getMasterMaxVolume(int)} for the largest valid value.
+     * @param flags One or more flags.
+     * @see #getMasterMaxVolume(int)
+     * @see #getMasterVolume(int)
+     * @hide
+     */
+    public void setMasterVolume(int index, int flags) {
+        IAudioService service = getService();
+        try {
+            service.setMasterVolume(index, flags);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in setMasterVolume", e);
+        }
+    }
+
+    /**
      * Solo or unsolo a particular stream. All other streams are muted.
      * <p>
      * The solo command is protected against client process death: if a process
@@ -717,6 +899,35 @@
     }
 
     /**
+     * set master mute state.
+     *
+     * @hide
+     */
+    public void setMasterMute(boolean state) {
+        IAudioService service = getService();
+        try {
+            service.setMasterMute(state, mICallBack);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in setMasterMute", e);
+        }
+    }
+
+    /**
+     * get master mute state.
+     *
+     * @hide
+     */
+    public boolean isMasterMute() {
+        IAudioService service = getService();
+        try {
+            return service.isMasterMute();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in isMasterMute", e);
+            return false;
+        }
+    }
+
+    /**
      * forces the stream controlled by hard volume keys
      * specifying streamType == -1 releases control to the
      * logic.
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 13e3982..1c7f577 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -112,6 +112,7 @@
     // AudioHandler message.whats
     private static final int MSG_SET_DEVICE_VOLUME = 0;
     private static final int MSG_PERSIST_VOLUME = 1;
+    private static final int MSG_PERSIST_MASTER_VOLUME = 2;
     private static final int MSG_PERSIST_RINGER_MODE = 3;
     private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
     private static final int MSG_MEDIA_SERVER_DIED = 5;
@@ -136,7 +137,6 @@
     // Timeout for connection to bluetooth headset service
     private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
 
-
     /** @see AudioSystemThread */
     private AudioSystemThread mAudioSystemThread;
     /** @see AudioHandler */
@@ -155,6 +155,10 @@
     private static final int NUM_SOUNDPOOL_CHANNELS = 4;
     private static final int SOUND_EFFECT_VOLUME = 1000;
 
+    // Internally master volume is a float in the 0.0 - 1.0 range,
+    // but to support integer based AudioManager API we translate it to 0 - 100
+    private static final int MAX_MASTER_VOLUME = 100;
+
     /* Sound effect file names  */
     private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
     private static final String[] SOUND_EFFECT_FILES = new String[] {
@@ -275,6 +279,11 @@
     // Forced device usage for communications
     private int mForcedUseForComm;
 
+    // True if we have master volume support
+    private final boolean mUseMasterVolume;
+
+    private final int[] mMasterVolumeRamp;
+
     // List of binder death handlers for setMode() client processes.
     // The last process to have called setMode() is at the top of the list.
     private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
@@ -401,6 +410,13 @@
         TelephonyManager tmgr = (TelephonyManager)
                 context.getSystemService(Context.TELEPHONY_SERVICE);
         tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
+        mUseMasterVolume = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
+        restoreMasterVolume();
+
+        mMasterVolumeRamp = context.getResources().getIntArray(
+                com.android.internal.R.array.config_masterVolumeRamp);
     }
 
     private void createAudioSystemThread() {
@@ -575,7 +591,7 @@
                         // Post a persist volume msg
                         sendMsg(mAudioHandler,
                                 MSG_PERSIST_VOLUME,
-                                SENDMSG_REPLACE,
+                                SENDMSG_QUEUE,
                                 PERSIST_LAST_AUDIBLE,
                                 device,
                                 s,
@@ -590,7 +606,7 @@
                 // to persist). Do not change volume if stream is muted.
                 sendMsg(mAudioHandler,
                         MSG_SET_DEVICE_VOLUME,
-                        SENDMSG_NOOP,
+                        SENDMSG_QUEUE,
                         device,
                         0,
                         streamState,
@@ -602,6 +618,32 @@
         sendVolumeUpdate(streamType, oldIndex, index, flags);
     }
 
+    /** @see AudioManager#adjustMasterVolume(int) */
+    public void adjustMasterVolume(int direction, int flags) {
+        ensureValidDirection(direction);
+        int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
+        int delta = 0;
+        for (int i = 0; i < mMasterVolumeRamp.length; i += 2) {
+            int testVolume = mMasterVolumeRamp[i];
+            int testDelta =  mMasterVolumeRamp[i + 1];
+            if (direction == AudioManager.ADJUST_RAISE) {
+                if (volume >= testVolume) {
+                    delta = testDelta;
+                } else {
+                    break;
+                }
+            } else if (direction == AudioManager.ADJUST_LOWER) {
+                if (volume - testDelta >= testVolume) {
+                    delta = -testDelta;
+                } else {
+                    break;
+                }
+            }
+        }
+//        Log.d(TAG, "adjustMasterVolume volume: " + volume + " delta: " + delta + " direction: " + direction);
+        setMasterVolume(volume + delta, flags);
+    }
+
     /** @see AudioManager#setStreamVolume(int, int, int) */
     public void setStreamVolume(int streamType, int index, int flags) {
         ensureValidStreamType(streamType);
@@ -657,6 +699,27 @@
         mContext.sendBroadcast(intent);
     }
 
+    // UI update and Broadcast Intent
+    private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
+        mVolumePanel.postMasterVolumeChanged(flags);
+
+        Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
+        intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
+        intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
+        mContext.sendBroadcast(intent);
+    }
+
+    // UI update and Broadcast Intent
+    private void sendMasterMuteUpdate(boolean muted, int flags) {
+        mVolumePanel.postMasterMuteChanged(flags);
+
+        Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
+        intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
+        long origCallerIdentityToken = Binder.clearCallingIdentity();
+        mContext.sendStickyBroadcast(intent);
+        Binder.restoreCallingIdentity(origCallerIdentityToken);
+    }
+
     /**
      * Sets the stream state's index, and posts a message to set system volume.
      * This will not call out to the UI. Assumes a valid stream type.
@@ -683,7 +746,7 @@
                 // Post a persist volume msg
                 sendMsg(mAudioHandler,
                         MSG_PERSIST_VOLUME,
-                        SENDMSG_REPLACE,
+                        SENDMSG_QUEUE,
                         PERSIST_LAST_AUDIBLE,
                         device,
                         streamState,
@@ -695,7 +758,7 @@
                 // to persist).
                 sendMsg(mAudioHandler,
                         MSG_SET_DEVICE_VOLUME,
-                        SENDMSG_NOOP,
+                        SENDMSG_QUEUE,
                         device,
                         0,
                         streamState,
@@ -725,6 +788,19 @@
         return (mStreamStates[streamType].muteCount() != 0);
     }
 
+    /** @see AudioManager#setMasterMute(boolean, IBinder) */
+    public void setMasterMute(boolean state, IBinder cb) {
+        if (state != AudioSystem.getMasterMute()) {
+            AudioSystem.setMasterMute(state);
+            sendMasterMuteUpdate(state, AudioManager.FLAG_SHOW_UI);
+        }
+    }
+
+    /** get master mute state. */
+    public boolean isMasterMute() {
+        return AudioSystem.getMasterMute();
+    }
+
     /** @see AudioManager#getStreamVolume(int) */
     public int getStreamVolume(int streamType) {
         ensureValidStreamType(streamType);
@@ -732,12 +808,45 @@
         return (mStreamStates[streamType].getIndex(device, false  /* lastAudible */) + 5) / 10;
     }
 
+    public int getMasterVolume() {
+        if (isMasterMute()) return 0;
+        return getLastAudibleMasterVolume();
+    }
+
+    public void setMasterVolume(int volume, int flags) {
+        if (volume < 0) {
+            volume = 0;
+        } else if (volume > MAX_MASTER_VOLUME) {
+            volume = MAX_MASTER_VOLUME;
+        }
+        doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
+    }
+
+    private void doSetMasterVolume(float volume, int flags) {
+        // don't allow changing master volume when muted
+        if (!AudioSystem.getMasterMute()) {
+            int oldVolume = getMasterVolume();
+            AudioSystem.setMasterVolume(volume);
+
+            int newVolume = getMasterVolume();
+            if (newVolume != oldVolume) {
+                // Post a persist master volume msg
+                sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
+                        Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
+                sendMasterVolumeUpdate(flags, oldVolume, newVolume);
+            }
+        }
+    }
+
     /** @see AudioManager#getStreamMaxVolume(int) */
     public int getStreamMaxVolume(int streamType) {
         ensureValidStreamType(streamType);
         return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
     }
 
+    public int getMasterMaxVolume() {
+        return MAX_MASTER_VOLUME;
+    }
 
     /** Get last audible volume before stream was muted. */
     public int getLastAudibleStreamVolume(int streamType) {
@@ -746,6 +855,11 @@
         return (mStreamStates[streamType].getIndex(device, true  /* lastAudible */) + 5) / 10;
     }
 
+    /** Get last audible master volume before it was muted. */
+    public int getLastAudibleMasterVolume() {
+        return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
+    }
+
     /** @see AudioManager#getRingerMode() */
     public int getRingerMode() {
         synchronized(mSettingsLock) {
@@ -816,6 +930,16 @@
         }
     }
 
+    private void restoreMasterVolume() {
+        if (mUseMasterVolume) {
+            float volume = Settings.System.getFloat(mContentResolver,
+                    Settings.System.VOLUME_MASTER, -1.0f);
+            if (volume >= 0.0f) {
+                AudioSystem.setMasterVolume(volume);
+            }
+        }
+    }
+
     /** @see AudioManager#shouldVibrate(int) */
     public boolean shouldVibrate(int vibrateType) {
 
@@ -2084,7 +2208,7 @@
                                     }
                                     sendMsg(mAudioHandler,
                                             MSG_SET_ALL_VOLUMES,
-                                            SENDMSG_NOOP,
+                                            SENDMSG_QUEUE,
                                             0,
                                             0,
                                             VolumeStreamState.this, 0);
@@ -2128,7 +2252,7 @@
                                         }
                                         sendMsg(mAudioHandler,
                                                 MSG_SET_ALL_VOLUMES,
-                                                SENDMSG_NOOP,
+                                                SENDMSG_QUEUE,
                                                 0,
                                                 0,
                                                 VolumeStreamState.this, 0);
@@ -2226,7 +2350,7 @@
             // Post a persist volume msg
             sendMsg(mAudioHandler,
                     MSG_PERSIST_VOLUME,
-                    SENDMSG_REPLACE,
+                    SENDMSG_QUEUE,
                     PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
                     device,
                     streamState,
@@ -2355,6 +2479,11 @@
                     persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
                     break;
 
+                case MSG_PERSIST_MASTER_VOLUME:
+                    Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
+                            (float)msg.arg1 / (float)1000.0);
+                    break;
+
                 case MSG_PERSIST_RINGER_MODE:
                     // note that the value persisted is the current ringer mode, not the
                     // value of ringer mode as of the time the request was made to persist
@@ -2415,6 +2544,9 @@
                     // Restore ringer mode
                     setRingerModeInt(getRingerMode(), false);
 
+                    // Restore master volume
+                    restoreMasterVolume();
+
                     // indicate the end of reconfiguration phase to audio HAL
                     AudioSystem.setParameters("restarting=false");
                     break;
@@ -2839,6 +2971,11 @@
                     adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
                                             BluetoothProfile.A2DP);
                 }
+
+                if (mUseMasterVolume) {
+                    // Send sticky broadcast for initial master mute state
+                    sendMasterMuteUpdate(false, 0);
+                }
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                     // a package is being removed, not replaced
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 3080497..d354cdb 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -318,5 +318,9 @@
     public static native int initStreamVolume(int stream, int indexMin, int indexMax);
     public static native int setStreamVolumeIndex(int stream, int index, int device);
     public static native int getStreamVolumeIndex(int stream, int device);
+    public static native int setMasterVolume(float value);
+    public static native float getMasterVolume();
+    public static native int setMasterMute(boolean mute);
+    public static native boolean getMasterMute();
     public static native int getDevicesForStream(int stream);
 }
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 7d60c55..e94bddc 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -20,12 +20,12 @@
 import android.hardware.Camera.CameraInfo;
 
 /**
- * The CamcorderProfile class is used to retrieve the
+ * Retrieves the
  * predefined camcorder profile settings for camcorder applications.
  * These settings are read-only.
  *
- * The compressed output from a recording session with a given
- * CamcorderProfile contains two tracks: one for auido and one for video.
+ * <p>The compressed output from a recording session with a given
+ * CamcorderProfile contains two tracks: one for audio and one for video.
  *
  * <p>Each profile specifies the following set of parameters:
  * <ul>
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5294d36..17d8e4d 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -30,10 +30,14 @@
     void adjustVolume(int direction, int flags);
 
     void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
-    
+
     void adjustStreamVolume(int streamType, int direction, int flags);
-    
+
+    void adjustMasterVolume(int direction, int flags);
+
     void setStreamVolume(int streamType, int index, int flags);
+
+    void setMasterVolume(int index, int flags);
     
     void setStreamSolo(int streamType, boolean state, IBinder cb);
    	
@@ -41,12 +45,22 @@
 
     boolean isStreamMute(int streamType);
 
+    void setMasterMute(boolean state, IBinder cb);
+
+    boolean isMasterMute();
+
     int getStreamVolume(int streamType);
-    
+
+    int getMasterVolume();
+
     int getStreamMaxVolume(int streamType);
+
+    int getMasterMaxVolume();
     
     int getLastAudibleStreamVolume(int streamType);
 
+    int getLastAudibleMasterVolume();
+
     void setRingerMode(int ringerMode);
     
     int getRingerMode();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
new file mode 100644
index 0000000..7f496ca
--- /dev/null
+++ b/media/java/android/media/MediaCodec.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2012 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 android.media;
+
+import android.view.Surface;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * MediaCodec class can be used to access low-level media codec, i.e.
+ * encoder/decoder components.
+ * @hide
+*/
+public class MediaCodec
+{
+    /** Per buffer metadata includes an offset and size specifying
+        the range of valid data in the associated codec buffer.
+    */
+    public final static class BufferInfo {
+        public void set(
+                int offset, int size, long timeUs, int flags) {
+            mOffset = offset;
+            mSize = size;
+            mPresentationTimeUs = timeUs;
+            mFlags = flags;
+        }
+
+        public int mOffset;
+        public int mSize;
+        public long mPresentationTimeUs;
+        public int mFlags;
+    };
+
+    public static int FLAG_SYNCFRAME   = 1;
+    public static int FLAG_CODECCONFIG = 2;
+    public static int FLAG_EOS         = 4;
+
+    /** Instantiate a codec component by mime type. For decoder components
+        this is the mime type of media that this decoder should be able to
+        decoder, for encoder components it's the type of media this encoder
+        should encode _to_.
+    */
+    public static MediaCodec CreateByType(String type, boolean encoder) {
+        return new MediaCodec(type, true /* nameIsType */, encoder);
+    }
+
+    /** If you know the exact name of the component you want to instantiate
+        use this method to instantiate it. Use with caution.
+    */
+    public static MediaCodec CreateByComponentName(String name) {
+        return new MediaCodec(
+                name, false /* nameIsType */, false /* unused */);
+    }
+
+    private MediaCodec(
+            String name, boolean nameIsType, boolean encoder) {
+        native_setup(name, nameIsType, encoder);
+    }
+
+    @Override
+    protected void finalize() {
+        native_finalize();
+    }
+
+    // Make sure you call this when you're done to free up any opened
+    // component instance instead of relying on the garbage collector
+    // to do this for you at some point in the future.
+    public native final void release();
+
+    public static int CONFIGURE_FLAG_ENCODE = 1;
+
+    /** Configures a component.
+     *  @param format A map of string/value pairs describing the input format
+     *                (decoder) or the desired output format.
+     *
+     *                Video formats have the following fields:
+     *                  "mime"          - String
+     *                  "width"         - Integer
+     *                  "height"        - Integer
+     *                  optional "max-input-size"       - Integer
+     *                  optional "csd-0", "csd-1" ...   - ByteBuffer
+     *
+     *                Audio formats have the following fields:
+     *                  "mime"          - String
+     *                  "channel-count" - Integer
+     *                  "sample-rate"   - Integer
+     *                  optional "max-input-size"       - Integer
+     *                  optional "csd-0", "csd-1" ...   - ByteBuffer
+     *
+     *                If the format is used to configure an encoder, additional
+     *                fields must be included:
+     *                  "bitrate" - Integer (in bits/sec)
+     *
+     *                for video formats:
+     *                  "color-format"          - Integer
+     *                  "frame-rate"            - Integer or Float
+     *                  "i-frame-interval"      - Integer
+     *                  optional "stride"       - Integer, defaults to "width"
+     *                  optional "slice-height" - Integer, defaults to "height"
+     *
+     *  @param surface Specify a surface on which to render the output of this
+     *                 decoder.
+     *  @param flags   Specify {@see #CONFIGURE_FLAG_ENCODE} to configure the
+     *                 component as an encoder.
+    */
+    public void configure(
+            Map<String, Object> format, Surface surface, int flags) {
+        String[] keys = null;
+        Object[] values = null;
+
+        if (format != null) {
+            keys = new String[format.size()];
+            values = new Object[format.size()];
+
+            int i = 0;
+            for (Map.Entry<String, Object> entry: format.entrySet()) {
+                keys[i] = entry.getKey();
+                values[i] = entry.getValue();
+                ++i;
+            }
+        }
+
+        native_configure(keys, values, surface, flags);
+    }
+
+    private native final void native_configure(
+            String[] keys, Object[] values, Surface surface, int flags);
+
+    /** After successfully configuring the component, call start. On return
+     *  you can query the component for its input/output buffers.
+    */
+    public native final void start();
+
+    public native final void stop();
+
+    /** Flush both input and output ports of the component, all indices
+     *  previously returned in calls to dequeueInputBuffer and
+     *  dequeueOutputBuffer become invalid.
+    */
+    public native final void flush();
+
+    /** After filling a range of the input buffer at the specified index
+     *  submit it to the component.
+    */
+    public native final void queueInputBuffer(
+            int index,
+            int offset, int size, long presentationTimeUs, int flags);
+
+    // Returns the index of an input buffer to be filled with valid data
+    // or -1 if no such buffer is currently available.
+    // This method will return immediately if timeoutUs == 0, wait indefinitely
+    // for the availability of an input buffer if timeoutUs < 0 or wait up
+    // to "timeoutUs" microseconds if timeoutUs > 0.
+    public native final int dequeueInputBuffer(long timeoutUs);
+
+    // Returns the index of an output buffer that has been successfully
+    // decoded or one of the INFO_* constants below.
+    // The provided "info" will be filled with buffer meta data.
+    public static final int INFO_TRY_AGAIN_LATER        = -1;
+    public static final int INFO_OUTPUT_FORMAT_CHANGED  = -2;
+    public static final int INFO_OUTPUT_BUFFERS_CHANGED = -3;
+
+    /** Dequeue an output buffer, block at most "timeoutUs" microseconds. */
+    public native final int dequeueOutputBuffer(
+            BufferInfo info, long timeoutUs);
+
+    // If you are done with a buffer, use this call to return the buffer to
+    // the codec. If you previously specified a surface when configuring this
+    // video decoder you can optionally render the buffer.
+    public native final void releaseOutputBuffer(int index, boolean render);
+
+    /** Call this after dequeueOutputBuffer signals a format change by returning
+     *  {@see #INFO_OUTPUT_FORMAT_CHANGED}
+     */
+    public native final Map<String, Object> getOutputFormat();
+
+    /** Call this after start() returns and whenever dequeueOutputBuffer
+     *  signals an output buffer change by returning
+     *  {@see #INFO_OUTPUT_BUFFERS_CHANGED}
+     */
+    public native final ByteBuffer[] getBuffers(boolean input);
+
+    private static native final void native_init();
+
+    private native final void native_setup(
+            String name, boolean nameIsType, boolean encoder);
+
+    private native final void native_finalize();
+
+    static {
+        System.loadLibrary("media_jni");
+        native_init();
+    }
+
+    private int mNativeContext;
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
new file mode 100644
index 0000000..6a7f2f5
--- /dev/null
+++ b/media/java/android/media/MediaExtractor.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 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 android.media;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * MediaExtractor
+ * @hide
+*/
+public class MediaExtractor
+{
+    public MediaExtractor(String path) {
+        native_setup(path);
+    }
+
+    @Override
+    protected void finalize() {
+        native_finalize();
+    }
+
+    // Make sure you call this when you're done to free up any resources
+    // instead of relying on the garbage collector to do this for you at
+    // some point in the future.
+    public native final void release();
+
+    public native int countTracks();
+    public native Map<String, Object> getTrackFormat(int index);
+
+    // Subsequent calls to "readSampleData", "getSampleTrackIndex" and
+    // "getSampleTime" only retrieve information for the subset of tracks
+    // selected by the call below.
+    // Selecting the same track multiple times has no effect, the track
+    // is only selected once.
+    public native void selectTrack(int index);
+
+    // All selected tracks seek near the requested time. The next sample
+    // returned for each selected track will be a sync sample.
+    public native void seekTo(long timeUs);
+
+    public native boolean advance();
+
+    // Retrieve the current encoded sample and store it in the byte buffer
+    // starting at the given offset.
+    public native int readSampleData(ByteBuffer byteBuf, int offset);
+
+    // Returns the track index the current sample originates from.
+    public native int getSampleTrackIndex();
+
+    // Returns the current sample's presentation time in microseconds.
+    public native long getSampleTime();
+
+    private static native final void native_init();
+    private native final void native_setup(String path);
+    private native final void native_finalize();
+
+    static {
+        System.loadLibrary("media_jni");
+        native_init();
+    }
+
+    private int mNativeContext;
+}
diff --git a/media/java/android/media/MediaInserter.java b/media/java/android/media/MediaInserter.java
index e92c710..7fcbffa 100644
--- a/media/java/android/media/MediaInserter.java
+++ b/media/java/android/media/MediaInserter.java
@@ -34,6 +34,8 @@
 public class MediaInserter {
     private final HashMap<Uri, List<ContentValues>> mRowMap =
             new HashMap<Uri, List<ContentValues>>();
+    private final HashMap<Uri, List<ContentValues>> mPriorityRowMap =
+            new HashMap<Uri, List<ContentValues>>();
 
     private IContentProvider mProvider;
     private int mBufferSizePerUri;
@@ -44,26 +46,45 @@
     }
 
     public void insert(Uri tableUri, ContentValues values) throws RemoteException {
-        List<ContentValues> list = mRowMap.get(tableUri);
+        insert(tableUri, values, false);
+    }
+
+    public void insertwithPriority(Uri tableUri, ContentValues values) throws RemoteException {
+        insert(tableUri, values, true);
+    }
+
+    private void insert(Uri tableUri, ContentValues values, boolean priority) throws RemoteException {
+        HashMap<Uri, List<ContentValues>> rowmap = priority ? mPriorityRowMap : mRowMap;
+        List<ContentValues> list = rowmap.get(tableUri);
         if (list == null) {
             list = new ArrayList<ContentValues>();
-            mRowMap.put(tableUri, list);
+            rowmap.put(tableUri, list);
         }
         list.add(new ContentValues(values));
         if (list.size() >= mBufferSizePerUri) {
-            flush(tableUri);
+            flushAllPriority();
+            flush(tableUri, list);
         }
     }
 
     public void flushAll() throws RemoteException {
+        flushAllPriority();
         for (Uri tableUri : mRowMap.keySet()){
-            flush(tableUri);
+            List<ContentValues> list = mRowMap.get(tableUri);
+            flush(tableUri, list);
         }
         mRowMap.clear();
     }
 
-    private void flush(Uri tableUri) throws RemoteException {
-        List<ContentValues> list = mRowMap.get(tableUri);
+    private void flushAllPriority() throws RemoteException {
+        for (Uri tableUri : mPriorityRowMap.keySet()){
+            List<ContentValues> list = mPriorityRowMap.get(tableUri);
+            flush(tableUri, list);
+        }
+        mPriorityRowMap.clear();
+    }
+
+    private void flush(Uri tableUri, List<ContentValues> list) throws RemoteException {
         if (!list.isEmpty()) {
             ContentValues[] valuesArray = new ContentValues[list.size()];
             valuesArray = list.toArray(valuesArray);
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 85d99c1..6319630 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -303,6 +303,8 @@
     /**
      * Uses the settings from a CamcorderProfile object for recording. This method should
      * be called after the video AND audio sources are set, and before setOutputFile().
+     * If a time lapse CamcorderProfile is used, audio related source or recording
+     * parameters are ignored.
      *
      * @param profile the CamcorderProfile to use
      * @see android.media.CamcorderProfile
@@ -315,8 +317,8 @@
         setVideoEncoder(profile.videoCodec);
         if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
              profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) {
-            // Enable time lapse. Also don't set audio for time lapse.
-            setParameter(String.format("time-lapse-enable=1"));
+            // Nothing needs to be done. Call to setCaptureRate() enables
+            // time lapse video recording.
         } else {
             setAudioEncodingBitRate(profile.audioBitRate);
             setAudioChannels(profile.audioChannels);
@@ -327,7 +329,10 @@
 
     /**
      * Set video frame capture rate. This can be used to set a different video frame capture
-     * rate than the recorded video's playback rate. Currently this works only for time lapse mode.
+     * rate than the recorded video's playback rate. This method also sets the recording mode
+     * to time lapse. In time lapse video recording, only video is recorded. Audio related
+     * parameters are ignored when a time lapse recording session starts, if an application
+     * sets them.
      *
      * @param fps Rate at which frames should be captured in frames per second.
      * The fps can go as low as desired. However the fastest fps will be limited by the hardware.
@@ -339,6 +344,9 @@
      * possible.
      */
     public void setCaptureRate(double fps) {
+        // Make sure that time lapse is enabled when this method is called.
+        setParameter(String.format("time-lapse-enable=1"));
+
         double timeBetweenFrameCapture = 1 / fps;
         int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture);
         setParameter(String.format("time-between-time-lapse-frame-capture=%d",
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9dc9cef..a08d6c3 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -59,8 +59,12 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.Locale;
 
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
 /**
  * Internal service helper that no-one should use directly.
  *
@@ -347,20 +351,18 @@
 
     private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
 
-    private static class FileCacheEntry {
+    private static class FileEntry {
         long mRowId;
         String mPath;
         long mLastModified;
         int mFormat;
-        boolean mSeenInFileSystem;
         boolean mLastModifiedChanged;
 
-        FileCacheEntry(long rowId, String path, long lastModified, int format) {
+        FileEntry(long rowId, String path, long lastModified, int format) {
             mRowId = rowId;
             mPath = path;
             mLastModified = lastModified;
             mFormat = format;
-            mSeenInFileSystem = false;
             mLastModifiedChanged = false;
         }
 
@@ -372,11 +374,7 @@
 
     private MediaInserter mMediaInserter;
 
-    // hashes file path to FileCacheEntry.
-    // path should be lower case if mCaseInsensitivePaths is true
-    private HashMap<String, FileCacheEntry> mFileCache;
-
-    private ArrayList<FileCacheEntry> mPlayLists;
+    private ArrayList<FileEntry> mPlayLists;
 
     private DrmManagerClient mDrmManagerClient = null;
 
@@ -431,7 +429,7 @@
         private int mWidth;
         private int mHeight;
 
-        public FileCacheEntry beginFile(String path, String mimeType, long lastModified,
+        public FileEntry beginFile(String path, String mimeType, long lastModified,
                 long fileSize, boolean isDirectory, boolean noMedia) {
             mMimeType = mimeType;
             mFileType = 0;
@@ -464,11 +462,7 @@
                 }
             }
 
-            String key = path;
-            if (mCaseInsensitivePaths) {
-                key = path.toLowerCase();
-            }
-            FileCacheEntry entry = mFileCache.get(key);
+            FileEntry entry = makeEntryFor(path);
             // add some slack to avoid a rounding error
             long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0;
             boolean wasModified = delta > 1 || delta < -1;
@@ -476,13 +470,11 @@
                 if (wasModified) {
                     entry.mLastModified = lastModified;
                 } else {
-                    entry = new FileCacheEntry(0, path, lastModified,
+                    entry = new FileEntry(0, path, lastModified,
                             (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0));
-                    mFileCache.put(key, entry);
                 }
                 entry.mLastModifiedChanged = true;
             }
-            entry.mSeenInFileSystem = true;
 
             if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) {
                 mPlayLists.add(entry);
@@ -524,7 +516,7 @@
             Uri result = null;
 //            long t1 = System.currentTimeMillis();
             try {
-                FileCacheEntry entry = beginFile(path, mimeType, lastModified,
+                FileEntry entry = beginFile(path, mimeType, lastModified,
                         fileSize, isDirectory, noMedia);
                 // rescan for metadata if file was modified since last scan
                 if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
@@ -777,7 +769,7 @@
             return map;
         }
 
-        private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications,
+        private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
                 boolean alarms, boolean music, boolean podcasts)
                 throws RemoteException {
             // update database
@@ -922,16 +914,15 @@
                     }
                 }
 
-                // new file, insert it
-                // We insert directories immediately to ensure they are in the database
-                // before the files they contain.
-                // Otherwise we can get duplicate directory entries in the database
-                // if one of the media FileInserters is flushed before the files table FileInserter
-                // Also, we immediately insert the file if the rowId of the inserted file is
-                // needed.
-                if (inserter == null || needToSetSettings ||
-                        entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
+                // New file, insert it.
+                // Directories need to be inserted before the files they contain, so they
+                // get priority when bulk inserting.
+                // If the rowId of the inserted file is needed, it gets inserted immediately,
+                // bypassing the bulk inserter.
+                if (inserter == null || needToSetSettings) {
                     result = mMediaProvider.insert(tableUri, values);
+                } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
+                    inserter.insertwithPriority(tableUri, values);
                 } else {
                     inserter.insert(tableUri, values);
                 }
@@ -1028,55 +1019,94 @@
         String where = null;
         String[] selectionArgs = null;
 
-        if (mFileCache == null) {
-            mFileCache = new HashMap<String, FileCacheEntry>();
-        } else {
-            mFileCache.clear();
-        }
         if (mPlayLists == null) {
-            mPlayLists = new ArrayList<FileCacheEntry>();
+            mPlayLists = new ArrayList<FileEntry>();
         } else {
             mPlayLists.clear();
         }
 
         if (filePath != null) {
             // query for only one file
-            where = Files.FileColumns.DATA + "=?";
-            selectionArgs = new String[] { filePath };
+            where = MediaStore.Files.FileColumns._ID + ">?" +
+                " AND " + Files.FileColumns.DATA + "=?";
+            selectionArgs = new String[] { "", filePath };
+        } else {
+            where = MediaStore.Files.FileColumns._ID + ">?";
+            selectionArgs = new String[] { "" };
         }
 
+        // Tell the provider to not delete the file.
+        // If the file is truly gone the delete is unnecessary, and we want to avoid
+        // accidentally deleting files that are really there (this may happen if the
+        // filesystem is mounted and unmounted while the scanner is running).
+        Uri.Builder builder = mFilesUri.buildUpon();
+        builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
+        MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
+
         // Build the list of files from the content provider
         try {
             if (prescanFiles) {
-                // First read existing files from the files table
+                // First read existing files from the files table.
+                // Because we'll be deleting entries for missing files as we go,
+                // we need to query the database in small batches, to avoid problems
+                // with CursorWindow positioning.
+                long lastId = Long.MIN_VALUE;
+                Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
+                mWasEmptyPriorToScan = true;
 
-                c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
-                        where, selectionArgs, null, null);
+                while (true) {
+                    selectionArgs[0] = "" + lastId;
+                    if (c != null) {
+                        c.close();
+                        c = null;
+                    }
+                    c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
+                            where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
+                    if (c == null) {
+                        break;
+                    }
 
-                if (c != null) {
-                    mWasEmptyPriorToScan = c.getCount() == 0;
+                    int num = c.getCount();
+
+                    if (num == 0) {
+                        break;
+                    }
+                    mWasEmptyPriorToScan = false;
                     while (c.moveToNext()) {
                         long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
                         String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
                         int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
                         long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+                        lastId = rowId;
 
                         // Only consider entries with absolute path names.
                         // This allows storing URIs in the database without the
                         // media scanner removing them.
                         if (path != null && path.startsWith("/")) {
-                            String key = path;
-                            if (mCaseInsensitivePaths) {
-                                key = path.toLowerCase();
+                            boolean exists = false;
+                            try {
+                                exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK);
+                            } catch (ErrnoException e1) {
                             }
+                            if (!exists && !MtpConstants.isAbstractObject(format)) {
+                                // do not delete missing playlists, since they may have been
+                                // modified by the user.
+                                // The user can delete them in the media player instead.
+                                // instead, clear the path and lastModified fields in the row
+                                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
+                                int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
 
-                            FileCacheEntry entry = new FileCacheEntry(rowId, path,
-                                    lastModified, format);
-                            mFileCache.put(key, entry);
+                                if (!MediaFile.isPlayListFileType(fileType)) {
+                                    deleter.delete(rowId);
+                                    if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
+                                        deleter.flush();
+                                        String parent = new File(path).getParent();
+                                        mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
+                                    }
+                                }
+                            }
                         }
                     }
-                    c.close();
-                    c = null;
                 }
             }
         }
@@ -1084,6 +1114,7 @@
             if (c != null) {
                 c.close();
             }
+            deleter.flush();
         }
 
         // compute original size of images
@@ -1150,57 +1181,42 @@
         }
     }
 
-    private void postscan(String[] directories) throws RemoteException {
-        Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();
+    static class MediaBulkDeleter {
+        StringBuilder whereClause = new StringBuilder();
+        ArrayList<String> whereArgs = new ArrayList<String>(100); 
+        IContentProvider mProvider;
+        Uri mBaseUri;
 
-        while (iterator.hasNext()) {
-            FileCacheEntry entry = iterator.next();
-            String path = entry.mPath;
+        public MediaBulkDeleter(IContentProvider provider, Uri baseUri) {
+            mProvider = provider;
+            mBaseUri = baseUri;
+        }
 
-            // remove database entries for files that no longer exist.
-            boolean fileMissing = false;
-
-            if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
-                if (inScanDirectory(path, directories)) {
-                    // we didn't see this file in the scan directory.
-                    fileMissing = true;
-                } else {
-                    // the file actually a directory or other abstract object
-                    // or is outside of our scan directory,
-                    // so we need to check for file existence here.
-                    File testFile = new File(path);
-                    if (!testFile.exists()) {
-                        fileMissing = true;
-                    }
-                }
+        public void delete(long id) throws RemoteException {
+            if (whereClause.length() != 0) {
+                whereClause.append(",");
             }
-
-            if (fileMissing) {
-                // Tell the provider to not delete the file.
-                // If the file is truly gone the delete is unnecessary, and we want to avoid
-                // accidentally deleting files that are really there (this may happen if the
-                // filesystem is mounted and unmounted while the scanner is running).
-                Uri.Builder builder = mFilesUri.buildUpon();
-                builder.appendEncodedPath(String.valueOf(entry.mRowId));
-                builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
-                Uri missingUri = builder.build();
-
-                // do not delete missing playlists, since they may have been modified by the user.
-                // the user can delete them in the media player instead.
-                // instead, clear the path and lastModified fields in the row
-                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
-                int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
-
-                if (!MediaFile.isPlayListFileType(fileType)) {
-                    mMediaProvider.delete(missingUri, null, null);
-                    iterator.remove();
-                    if (entry.mPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
-                        File f = new File(path);
-                        mMediaProvider.call(MediaStore.UNHIDE_CALL, f.getParent(), null);
-                    }
-                }
+            whereClause.append("?");
+            whereArgs.add("" + id);
+            if (whereArgs.size() > 100) {
+                flush();
             }
         }
+        public void flush() throws RemoteException {
+            int size = whereArgs.size();
+            if (size > 0) {
+                String [] foo = new String [size];
+                foo = whereArgs.toArray(foo);
+                int numrows = mProvider.delete(mBaseUri, MediaStore.MediaColumns._ID + " IN (" +
+                        whereClause.toString() + ")", foo);
+                //Log.i("@@@@@@@@@", "rows deleted: " + numrows);
+                whereClause.setLength(0);
+                whereArgs.clear();
+            }
+        }
+    }
+
+    private void postscan(String[] directories) throws RemoteException {
 
         // handle playlists last, after we know what media files are on the storage.
         if (mProcessPlaylists) {
@@ -1212,7 +1228,6 @@
 
         // allow GC to clean up
         mPlayLists = null;
-        mFileCache = null;
         mMediaProvider = null;
     }
 
@@ -1386,11 +1401,7 @@
                 // build file cache so we can look up tracks in the playlist
                 prescan(null, true);
 
-                String key = path;
-                if (mCaseInsensitivePaths) {
-                    key = path.toLowerCase();
-                }
-                FileCacheEntry entry = mFileCache.get(key);
+                FileEntry entry = makeEntryFor(path);
                 if (entry != null) {
                     processPlayList(entry);
                 }
@@ -1409,6 +1420,37 @@
         }
     }
 
+    FileEntry makeEntryFor(String path) {
+        String key = path;
+        String where;
+        String[] selectionArgs;
+        if (mCaseInsensitivePaths) {
+            where = Files.FileColumns.DATA + " LIKE ?";
+            selectionArgs = new String[] { path };
+        } else {
+            where = Files.FileColumns.DATA + "=?";
+            selectionArgs = new String[] { path };
+        }
+
+        Cursor c = null;
+        try {
+            c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+                    where, selectionArgs, null, null);
+            if (c.moveToNext()) {
+                long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
+                int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
+                long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+                return new FileEntry(rowId, path, lastModified, format);
+            }
+        } catch (RemoteException e) {
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return null;
+    }
+
     // returns the number of matching file/directory names, starting from the right
     private int matchPaths(String path1, String path2) {
         int result = 0;
@@ -1459,26 +1501,37 @@
         //FIXME - should we look for "../" within the path?
 
         // best matching MediaFile for the play list entry
-        FileCacheEntry bestMatch = null;
+        FileEntry bestMatch = null;
 
         // number of rightmost file/directory names for bestMatch
         int bestMatchLength = 0;
 
-        Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();
-        while (iterator.hasNext()) {
-            FileCacheEntry cacheEntry = iterator.next();
-            String path = cacheEntry.mPath;
+        Cursor c = null;
+        try {
+            c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+                    null, null, null, null);
+        } catch (RemoteException e1) {
+        }
 
-            if (path.equalsIgnoreCase(entry)) {
-                bestMatch = cacheEntry;
-                break;    // don't bother continuing search
-            }
+        if (c != null) {
+            while (c.moveToNext()) {
+                long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
+                String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
+                int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
+                long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
 
-            int matchLength = matchPaths(path, entry);
-            if (matchLength > bestMatchLength) {
-                bestMatch = cacheEntry;
-                bestMatchLength = matchLength;
+                if (path.equalsIgnoreCase(entry)) {
+                    bestMatch = new FileEntry(rowId, path, lastModified, format);
+                    break;    // don't bother continuing search
+                }
+
+                int matchLength = matchPaths(path, entry);
+                if (matchLength > bestMatchLength) {
+                    bestMatch = new FileEntry(rowId, path, lastModified, format);
+                    bestMatchLength = matchLength;
+                }
             }
+            c.close();
         }
 
         if (bestMatch == null) {
@@ -1488,7 +1541,7 @@
         try {
             // check rowid is set. Rowid may be missing if it is inserted by bulkInsert().
             if (bestMatch.mRowId == 0) {
-                Cursor c = mMediaProvider.query(mAudioUri, ID_PROJECTION,
+                c = mMediaProvider.query(mAudioUri, ID_PROJECTION,
                         MediaStore.Files.FileColumns.DATA + "=?",
                         new String[] { bestMatch.mPath }, null, null);
                 if (c != null) {
@@ -1641,7 +1694,7 @@
         }
     }
 
-    private void processPlayList(FileCacheEntry entry) throws RemoteException {
+    private void processPlayList(FileEntry entry) throws RemoteException {
         String path = entry.mPath;
         ContentValues values = new ContentValues();
         int lastSlash = path.lastIndexOf('/');
@@ -1692,9 +1745,9 @@
     }
 
     private void processPlayLists() throws RemoteException {
-        Iterator<FileCacheEntry> iterator = mPlayLists.iterator();
+        Iterator<FileEntry> iterator = mPlayLists.iterator();
         while (iterator.hasNext()) {
-            FileCacheEntry entry = iterator.next();
+            FileEntry entry = iterator.next();
             // only process playlist files if they are new or have been modified since the last scan
             if (entry.mLastModifiedChanged) {
                 processPlayList(entry);
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index bcf7b89..91d0addd 100755
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -84,6 +84,7 @@
     // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp
     private static final int NATIVE_EVENT_PCM_CAPTURE = 0;
     private static final int NATIVE_EVENT_FFT_CAPTURE = 1;
+    private static final int NATIVE_EVENT_SERVER_DIED = 2;
 
     // Error codes:
     /**
@@ -147,6 +148,10 @@
      *  PCM and FFT capture listener registered by client
      */
     private OnDataCaptureListener mCaptureListener = null;
+    /**
+     *  Server Died listener registered by client
+     */
+    private OnServerDiedListener mServerDiedListener = null;
 
     // accessed by native methods
     private int mNativeVisualizer;
@@ -396,6 +401,9 @@
     public interface OnDataCaptureListener  {
         /**
          * Method called when a new waveform capture is available.
+         * <p>Data in the waveform buffer is valid only within the scope of the callback.
+         * Applications which needs access to the waveform data after returning from the callback
+         * should make a copy of the data instead of holding a reference.
          * @param visualizer Visualizer object on which the listener is registered.
          * @param waveform array of bytes containing the waveform representation.
          * @param samplingRate sampling rate of the audio visualized.
@@ -404,6 +412,9 @@
 
         /**
          * Method called when a new frequency capture is available.
+         * <p>Data in the fft buffer is valid only within the scope of the callback.
+         * Applications which needs access to the fft data after returning from the callback
+         * should make a copy of the data instead of holding a reference.
          * @param visualizer Visualizer object on which the listener is registered.
          * @param fft array of bytes containing the frequency representation.
          * @param samplingRate sampling rate of the audio visualized.
@@ -452,6 +463,43 @@
     }
 
     /**
+     * @hide
+     *
+     * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that
+     * the connection to the native media server has been broken and that the Visualizer object will
+     * need to be released and re-created.
+     * The client application can implement this interface and register the listener with the
+     * {@link #setServerDiedListener(OnServerDiedListener)} method.
+     */
+    public interface OnServerDiedListener  {
+        /**
+         * @hide
+         *
+         * Method called when the native media server has died.
+         * <p>If the native media server encounters a fatal error and needs to restart, the binder
+         * connection from the {@link #Visualizer} to the media server will be broken.  Data capture
+         * callbacks will stop happening, and client initiated calls to the {@link #Visualizer}
+         * instance will fail with the error code {@link #DEAD_OBJECT}.  To restore functionality,
+         * clients should {@link #release()} their old visualizer and create a new instance.
+         */
+        void onServerDied();
+    }
+
+    /**
+     * @hide
+     *
+     * Registers an OnServerDiedListener interface.
+     * <p>Call this method with a null listener to stop receiving server death notifications.
+     * @return {@link #SUCCESS} in case of success,
+     */
+    public int setServerDiedListener(OnServerDiedListener listener) {
+        synchronized (mListenerLock) {
+            mServerDiedListener = listener;
+        }
+        return SUCCESS;
+    }
+
+    /**
      * Helper class to handle the forwarding of native events to the appropriate listeners
      */
     private class NativeEventHandler extends Handler
@@ -463,11 +511,7 @@
             mVisualizer = v;
         }
 
-        @Override
-        public void handleMessage(Message msg) {
-            if (mVisualizer == null) {
-                return;
-            }
+        private void handleCaptureMessage(Message msg) {
             OnDataCaptureListener l = null;
             synchronized (mListenerLock) {
                 l = mVisualizer.mCaptureListener;
@@ -476,6 +520,7 @@
             if (l != null) {
                 byte[] data = (byte[])msg.obj;
                 int samplingRate = msg.arg1;
+
                 switch(msg.what) {
                 case NATIVE_EVENT_PCM_CAPTURE:
                     l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
@@ -484,11 +529,41 @@
                     l.onFftDataCapture(mVisualizer, data, samplingRate);
                     break;
                 default:
-                    Log.e(TAG,"Unknown native event: "+msg.what);
+                    Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what);
                     break;
                 }
             }
         }
+
+        private void handleServerDiedMessage(Message msg) {
+            OnServerDiedListener l = null;
+            synchronized (mListenerLock) {
+                l = mVisualizer.mServerDiedListener;
+            }
+
+            if (l != null)
+                l.onServerDied();
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (mVisualizer == null) {
+                return;
+            }
+
+            switch(msg.what) {
+            case NATIVE_EVENT_PCM_CAPTURE:
+            case NATIVE_EVENT_FFT_CAPTURE:
+                handleCaptureMessage(msg);
+                break;
+            case NATIVE_EVENT_SERVER_DIED:
+                handleServerDiedMessage(msg);
+                break;
+            default:
+                Log.e(TAG,"Unknown native event: "+msg.what);
+                break;
+            }
+        }
     }
 
     //---------------------------------------------------------
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 23cc0e2..070d2d9 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
+    android_media_MediaCodec.cpp \
+    android_media_MediaExtractor.cpp \
     android_media_MediaPlayer.cpp \
     android_media_MediaRecorder.cpp \
     android_media_MediaScanner.cpp \
@@ -25,6 +27,7 @@
     libcutils \
     libgui \
     libstagefright \
+    libstagefright_foundation \
     libcamera_client \
     libmtp \
     libusbhost \
@@ -39,10 +42,12 @@
     external/tremor/Tremor \
     frameworks/base/core/jni \
     frameworks/base/media/libmedia \
+    frameworks/base/media/libstagefright \
     frameworks/base/media/libstagefright/codecs/amrnb/enc/src \
     frameworks/base/media/libstagefright/codecs/amrnb/common \
     frameworks/base/media/libstagefright/codecs/amrnb/common/include \
     frameworks/base/media/mtp \
+    frameworks/base/include/media/stagefright/openmax \
     $(PV_INCLUDES) \
     $(JNI_H_INCLUDE) \
     $(call include-path-for, corecg graphics)
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
new file mode 100644
index 0000000..43ca263
--- /dev/null
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodec-JNI"
+#include <utils/Log.h>
+
+#include "android_media_MediaCodec.h"
+
+#include "android_media_Utils.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+// Keep these in sync with their equivalents in MediaCodec.java !!!
+enum {
+    DEQUEUE_INFO_TRY_AGAIN_LATER            = -1,
+    DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED      = -2,
+    DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED     = -3,
+};
+
+struct fields_t {
+    jfieldID context;
+};
+
+static fields_t gFields;
+
+////////////////////////////////////////////////////////////////////////////////
+
+JMediaCodec::JMediaCodec(
+        JNIEnv *env, jobject thiz,
+        const char *name, bool nameIsType, bool encoder)
+    : mClass(NULL),
+      mObject(NULL) {
+    jclass clazz = env->GetObjectClass(thiz);
+    CHECK(clazz != NULL);
+
+    mClass = (jclass)env->NewGlobalRef(clazz);
+    mObject = env->NewWeakGlobalRef(thiz);
+
+    mLooper = new ALooper;
+    mLooper->setName("MediaCodec_looper");
+
+    mLooper->start(
+            false,      // runOnCallingThread
+            false,       // canCallJava
+            PRIORITY_DEFAULT);
+
+    if (nameIsType) {
+        mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
+    } else {
+        mCodec = MediaCodec::CreateByComponentName(mLooper, name);
+    }
+}
+
+status_t JMediaCodec::initCheck() const {
+    return mCodec != NULL ? OK : NO_INIT;
+}
+
+JMediaCodec::~JMediaCodec() {
+    mCodec->stop();
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteWeakGlobalRef(mObject);
+    mObject = NULL;
+    env->DeleteGlobalRef(mClass);
+    mClass = NULL;
+}
+
+status_t JMediaCodec::configure(
+        const sp<AMessage> &format,
+        const sp<ISurfaceTexture> &surfaceTexture,
+        int flags) {
+    sp<SurfaceTextureClient> client;
+    if (surfaceTexture != NULL) {
+        client = new SurfaceTextureClient(surfaceTexture);
+    }
+    return mCodec->configure(format, client, flags);
+}
+
+status_t JMediaCodec::start() {
+    return mCodec->start();
+}
+
+status_t JMediaCodec::stop() {
+    return mCodec->stop();
+}
+
+status_t JMediaCodec::flush() {
+    return mCodec->flush();
+}
+
+status_t JMediaCodec::queueInputBuffer(
+        size_t index,
+        size_t offset, size_t size, int64_t timeUs, uint32_t flags) {
+    return mCodec->queueInputBuffer(index, offset, size, timeUs, flags);
+}
+
+status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
+    return mCodec->dequeueInputBuffer(index, timeoutUs);
+}
+
+status_t JMediaCodec::dequeueOutputBuffer(
+        JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
+    size_t size, offset;
+    int64_t timeUs;
+    uint32_t flags;
+    status_t err;
+    if ((err = mCodec->dequeueOutputBuffer(
+                    index, &size, &offset, &timeUs, &flags, timeoutUs)) != OK) {
+        return err;
+    }
+
+    jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo");
+
+    jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V");
+    env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
+
+    return OK;
+}
+
+status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
+    return render
+        ? mCodec->renderOutputBufferAndRelease(index)
+        : mCodec->releaseOutputBuffer(index);
+}
+
+status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
+    sp<AMessage> msg;
+    status_t err;
+    if ((err = mCodec->getOutputFormat(&msg)) != OK) {
+        return err;
+    }
+
+    return ConvertMessageToMap(env, msg, format);
+}
+
+status_t JMediaCodec::getBuffers(
+        JNIEnv *env, bool input, jobjectArray *bufArray) const {
+    Vector<sp<ABuffer> > buffers;
+
+    status_t err =
+        input
+            ? mCodec->getInputBuffers(&buffers)
+            : mCodec->getOutputBuffers(&buffers);
+
+    if (err != OK) {
+        return err;
+    }
+
+    jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer");
+
+    *bufArray = (jobjectArray)env->NewObjectArray(
+            buffers.size(), byteBufferClass, NULL);
+
+    for (size_t i = 0; i < buffers.size(); ++i) {
+        const sp<ABuffer> &buffer = buffers.itemAt(i);
+
+        jobject byteBuffer =
+            env->NewDirectByteBuffer(
+                buffer->base(),
+                buffer->capacity());
+
+        env->SetObjectArrayElement(
+                *bufArray, i, byteBuffer);
+
+        env->DeleteLocalRef(byteBuffer);
+        byteBuffer = NULL;
+    }
+
+    return OK;
+}
+
+}  // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static sp<JMediaCodec> setMediaCodec(
+        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
+    sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
+    if (codec != NULL) {
+        codec->incStrong(thiz);
+    }
+    if (old != NULL) {
+        old->decStrong(thiz);
+    }
+    env->SetIntField(thiz, gFields.context, (int)codec.get());
+
+    return old;
+}
+
+static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
+    return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
+}
+
+static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
+    setMediaCodec(env, thiz, NULL);
+}
+
+static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) {
+    switch (err) {
+        case OK:
+            return 0;
+
+        case -EAGAIN:
+            return DEQUEUE_INFO_TRY_AGAIN_LATER;
+
+        case INFO_FORMAT_CHANGED:
+            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
+
+        case INFO_OUTPUT_BUFFERS_CHANGED:
+            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
+
+        default:
+        {
+            jniThrowException(env, "java/lang/IllegalStateException", NULL);
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static void android_media_MediaCodec_native_configure(
+        JNIEnv *env,
+        jobject thiz,
+        jobjectArray keys, jobjectArray values,
+        jobject jsurface,
+        jint flags) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    sp<AMessage> format;
+    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
+
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    sp<ISurfaceTexture> surfaceTexture;
+    if (jsurface != NULL) {
+        sp<Surface> surface(Surface_getSurface(env, jsurface));
+        if (surface != NULL) {
+            surfaceTexture = surface->getSurfaceTexture();
+        } else {
+            jniThrowException(
+                    env,
+                    "java/lang/IllegalArgumentException",
+                    "The surface has been released");
+            return;
+        }
+    }
+
+    err = codec->configure(format, surfaceTexture, flags);
+
+    throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
+    ALOGV("android_media_MediaCodec_start");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = codec->start();
+
+    throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
+    ALOGV("android_media_MediaCodec_stop");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = codec->stop();
+
+    throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
+    ALOGV("android_media_MediaCodec_flush");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = codec->flush();
+
+    throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_queueInputBuffer(
+        JNIEnv *env,
+        jobject thiz,
+        jint index,
+        jint offset,
+        jint size,
+        jlong timestampUs,
+        jint flags) {
+    ALOGV("android_media_MediaCodec_queueInputBuffer");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = codec->queueInputBuffer(
+            index, offset, size, timestampUs, flags);
+
+    throwExceptionAsNecessary(env, err);
+}
+
+static jint android_media_MediaCodec_dequeueInputBuffer(
+        JNIEnv *env, jobject thiz, jlong timeoutUs) {
+    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return -1;
+    }
+
+    size_t index;
+    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
+
+    if (err == OK) {
+        return index;
+    }
+
+    return throwExceptionAsNecessary(env, err);
+}
+
+static jint android_media_MediaCodec_dequeueOutputBuffer(
+        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
+    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    size_t index;
+    status_t err = codec->dequeueOutputBuffer(
+            env, bufferInfo, &index, timeoutUs);
+
+    if (err == OK) {
+        return index;
+    }
+
+    return throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_releaseOutputBuffer(
+        JNIEnv *env, jobject thiz, jint index, jboolean render) {
+    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = codec->releaseOutputBuffer(index, render);
+
+    throwExceptionAsNecessary(env, err);
+}
+
+static jobject android_media_MediaCodec_getOutputFormat(
+        JNIEnv *env, jobject thiz) {
+    ALOGV("android_media_MediaCodec_getOutputFormat");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    jobject format;
+    status_t err = codec->getOutputFormat(env, &format);
+
+    if (err == OK) {
+        return format;
+    }
+
+    throwExceptionAsNecessary(env, err);
+
+    return NULL;
+}
+
+static jobjectArray android_media_MediaCodec_getBuffers(
+        JNIEnv *env, jobject thiz, jboolean input) {
+    ALOGV("android_media_MediaCodec_getBuffers");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    jobjectArray buffers;
+    status_t err = codec->getBuffers(env, input, &buffers);
+
+    if (err == OK) {
+        return buffers;
+    }
+
+    throwExceptionAsNecessary(env, err);
+
+    return NULL;
+}
+
+static void android_media_MediaCodec_native_init(JNIEnv *env) {
+    jclass clazz = env->FindClass("android/media/MediaCodec");
+    CHECK(clazz != NULL);
+
+    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+    CHECK(gFields.context != NULL);
+}
+
+static void android_media_MediaCodec_native_setup(
+        JNIEnv *env, jobject thiz,
+        jstring name, jboolean nameIsType, jboolean encoder) {
+    if (name == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    const char *tmp = env->GetStringUTFChars(name, NULL);
+
+    if (tmp == NULL) {
+        return;
+    }
+
+    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
+
+    status_t err = codec->initCheck();
+
+    env->ReleaseStringUTFChars(name, tmp);
+    tmp = NULL;
+
+    if (err != OK) {
+        jniThrowException(
+                env,
+                "java/io/IOException",
+                "Failed to allocate component instance");
+        return;
+    }
+
+    setMediaCodec(env,thiz, codec);
+}
+
+static void android_media_MediaCodec_native_finalize(
+        JNIEnv *env, jobject thiz) {
+    android_media_MediaCodec_release(env, thiz);
+}
+
+static JNINativeMethod gMethods[] = {
+    { "release", "()V", (void *)android_media_MediaCodec_release },
+
+    { "native_configure",
+      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;I)V",
+      (void *)android_media_MediaCodec_native_configure },
+
+    { "start", "()V", (void *)android_media_MediaCodec_start },
+    { "stop", "()V", (void *)android_media_MediaCodec_stop },
+    { "flush", "()V", (void *)android_media_MediaCodec_flush },
+
+    { "queueInputBuffer", "(IIIJI)V",
+      (void *)android_media_MediaCodec_queueInputBuffer },
+
+    { "dequeueInputBuffer", "(J)I",
+      (void *)android_media_MediaCodec_dequeueInputBuffer },
+
+    { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
+      (void *)android_media_MediaCodec_dequeueOutputBuffer },
+
+    { "releaseOutputBuffer", "(IZ)V",
+      (void *)android_media_MediaCodec_releaseOutputBuffer },
+
+    { "getOutputFormat", "()Ljava/util/Map;",
+      (void *)android_media_MediaCodec_getOutputFormat },
+
+    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
+      (void *)android_media_MediaCodec_getBuffers },
+
+    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
+
+    { "native_setup", "(Ljava/lang/String;ZZ)V",
+      (void *)android_media_MediaCodec_native_setup },
+
+    { "native_finalize", "()V",
+      (void *)android_media_MediaCodec_native_finalize },
+};
+
+int register_android_media_MediaCodec(JNIEnv *env) {
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/media/MediaCodec", gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
new file mode 100644
index 0000000..6b1257d
--- /dev/null
+++ b/media/jni/android_media_MediaCodec.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+#ifndef _ANDROID_MEDIA_MEDIACODEC_H_
+#define _ANDROID_MEDIA_MEDIACODEC_H_
+
+#include "jni.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ALooper;
+struct AMessage;
+struct ISurfaceTexture;
+struct MediaCodec;
+
+struct JMediaCodec : public RefBase {
+    JMediaCodec(
+            JNIEnv *env, jobject thiz,
+            const char *name, bool nameIsType, bool encoder);
+
+    status_t initCheck() const;
+
+    status_t configure(
+            const sp<AMessage> &format,
+            const sp<ISurfaceTexture> &surfaceTexture,
+            int flags);
+
+    status_t start();
+    status_t stop();
+
+    status_t flush();
+
+    status_t queueInputBuffer(
+            size_t index,
+            size_t offset, size_t size, int64_t timeUs, uint32_t flags);
+
+    status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs);
+
+    status_t dequeueOutputBuffer(
+            JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs);
+
+    status_t releaseOutputBuffer(size_t index, bool render);
+
+    status_t getOutputFormat(JNIEnv *env, jobject *format) const;
+
+    status_t getBuffers(
+            JNIEnv *env, bool input, jobjectArray *bufArray) const;
+
+protected:
+    virtual ~JMediaCodec();
+
+private:
+    jclass mClass;
+    jweak mObject;
+
+    sp<ALooper> mLooper;
+    sp<MediaCodec> mCodec;
+
+    DISALLOW_EVIL_CONSTRUCTORS(JMediaCodec);
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_MEDIACODEC_H_
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
new file mode 100644
index 0000000..4757adf
--- /dev/null
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaExtractor-JNI"
+#include <utils/Log.h>
+
+#include "android_media_MediaExtractor.h"
+
+#include "android_media_Utils.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/NuMediaExtractor.h>
+
+namespace android {
+
+struct fields_t {
+    jfieldID context;
+};
+
+static fields_t gFields;
+
+////////////////////////////////////////////////////////////////////////////////
+
+JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
+    : mClass(NULL),
+      mObject(NULL) {
+    jclass clazz = env->GetObjectClass(thiz);
+    CHECK(clazz != NULL);
+
+    mClass = (jclass)env->NewGlobalRef(clazz);
+    mObject = env->NewWeakGlobalRef(thiz);
+
+    mImpl = new NuMediaExtractor;
+}
+
+JMediaExtractor::~JMediaExtractor() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteWeakGlobalRef(mObject);
+    mObject = NULL;
+    env->DeleteGlobalRef(mClass);
+    mClass = NULL;
+}
+
+status_t JMediaExtractor::setDataSource(const char *path) {
+    return mImpl->setDataSource(path);
+}
+
+size_t JMediaExtractor::countTracks() const {
+    return mImpl->countTracks();
+}
+
+status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
+    sp<AMessage> msg;
+    status_t err;
+    if ((err = mImpl->getTrackFormat(index, &msg)) != OK) {
+        return err;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    return ConvertMessageToMap(env, msg, format);
+}
+
+status_t JMediaExtractor::selectTrack(size_t index) {
+    return mImpl->selectTrack(index);
+}
+
+status_t JMediaExtractor::seekTo(int64_t timeUs) {
+    return mImpl->seekTo(timeUs);
+}
+
+status_t JMediaExtractor::advance() {
+    return mImpl->advance();
+}
+
+status_t JMediaExtractor::readSampleData(
+        jobject byteBuf, size_t offset, size_t *sampleSize) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    void *dst = env->GetDirectBufferAddress(byteBuf);
+
+    if (dst == NULL) {
+        // XXX if dst is NULL also fall back to "array()"
+        return INVALID_OPERATION;
+    }
+
+    jlong dstSize = env->GetDirectBufferCapacity(byteBuf);
+
+    if (dstSize < offset) {
+        return -ERANGE;
+    }
+
+    sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
+
+    status_t err = mImpl->readSampleData(buffer);
+
+    if (err != OK) {
+        return err;
+    }
+
+    *sampleSize = buffer->size();
+
+    return OK;
+}
+
+status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
+    return mImpl->getSampleTrackIndex(trackIndex);
+}
+
+status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
+    return mImpl->getSampleTime(sampleTimeUs);
+}
+
+}  // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static sp<JMediaExtractor> setMediaExtractor(
+        JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
+    sp<JMediaExtractor> old =
+        (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
+
+    if (extractor != NULL) {
+        extractor->incStrong(thiz);
+    }
+    if (old != NULL) {
+        old->decStrong(thiz);
+    }
+    env->SetIntField(thiz, gFields.context, (int)extractor.get());
+
+    return old;
+}
+
+static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
+    return (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
+}
+
+static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
+    setMediaExtractor(env, thiz, NULL);
+}
+
+static jint android_media_MediaExtractor_countTracks(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    return extractor->countTracks();
+}
+
+static jobject android_media_MediaExtractor_getTrackFormat(
+        JNIEnv *env, jobject thiz, jint index) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    jobject format;
+    status_t err = extractor->getTrackFormat(index, &format);
+
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return NULL;
+    }
+
+    return format;
+}
+
+static void android_media_MediaExtractor_selectTrack(
+        JNIEnv *env, jobject thiz, jint index) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = extractor->selectTrack(index);
+
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+}
+
+static void android_media_MediaExtractor_seekTo(
+        JNIEnv *env, jobject thiz, jlong timeUs) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = extractor->seekTo(timeUs);
+
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+}
+
+static jboolean android_media_MediaExtractor_advance(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return false;
+    }
+
+    status_t err = extractor->advance();
+
+    if (err == ERROR_END_OF_STREAM) {
+        return false;
+    } else if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    return true;
+}
+
+static jint android_media_MediaExtractor_readSampleData(
+        JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return -1;
+    }
+
+    size_t sampleSize;
+    status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
+
+    if (err == ERROR_END_OF_STREAM) {
+        return -1;
+    } else if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    return sampleSize;
+}
+
+static jint android_media_MediaExtractor_getSampleTrackIndex(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return -1;
+    }
+
+    size_t trackIndex;
+    status_t err = extractor->getSampleTrackIndex(&trackIndex);
+
+    if (err == ERROR_END_OF_STREAM) {
+        return -1;
+    } else if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    return trackIndex;
+}
+
+static jlong android_media_MediaExtractor_getSampleTime(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return -1ll;
+    }
+
+    int64_t sampleTimeUs;
+    status_t err = extractor->getSampleTime(&sampleTimeUs);
+
+    if (err == ERROR_END_OF_STREAM) {
+        return -1ll;
+    } else if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    return sampleTimeUs;
+}
+
+static void android_media_MediaExtractor_native_init(JNIEnv *env) {
+    jclass clazz = env->FindClass("android/media/MediaExtractor");
+    CHECK(clazz != NULL);
+
+    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+    CHECK(gFields.context != NULL);
+
+    DataSource::RegisterDefaultSniffers();
+}
+
+static void android_media_MediaExtractor_native_setup(
+        JNIEnv *env, jobject thiz, jstring path) {
+    sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
+
+    if (path == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    const char *tmp = env->GetStringUTFChars(path, NULL);
+
+    if (tmp == NULL) {
+        return;
+    }
+
+    status_t err = extractor->setDataSource(tmp);
+
+    env->ReleaseStringUTFChars(path, tmp);
+    tmp = NULL;
+
+    if (err != OK) {
+        jniThrowException(
+                env,
+                "java/io/IOException",
+                "Failed to instantiate extractor.");
+        return;
+    }
+
+    setMediaExtractor(env,thiz, extractor);
+}
+
+static void android_media_MediaExtractor_native_finalize(
+        JNIEnv *env, jobject thiz) {
+    android_media_MediaExtractor_release(env, thiz);
+}
+
+static JNINativeMethod gMethods[] = {
+    { "release", "()V", (void *)android_media_MediaExtractor_release },
+
+    { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks },
+
+    { "getTrackFormat", "(I)Ljava/util/Map;",
+        (void *)android_media_MediaExtractor_getTrackFormat },
+
+    { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
+
+    { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo },
+
+    { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
+
+    { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
+        (void *)android_media_MediaExtractor_readSampleData },
+
+    { "getSampleTrackIndex", "()I",
+        (void *)android_media_MediaExtractor_getSampleTrackIndex },
+
+    { "getSampleTime", "()J",
+        (void *)android_media_MediaExtractor_getSampleTime },
+
+    { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
+
+    { "native_setup", "(Ljava/lang/String;)V",
+      (void *)android_media_MediaExtractor_native_setup },
+
+    { "native_finalize", "()V",
+      (void *)android_media_MediaExtractor_native_finalize },
+};
+
+int register_android_media_MediaExtractor(JNIEnv *env) {
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/media/MediaExtractor", gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
new file mode 100644
index 0000000..70e58c6
--- /dev/null
+++ b/media/jni/android_media_MediaExtractor.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+#ifndef _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
+#define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include "jni.h"
+
+namespace android {
+
+struct NuMediaExtractor;
+
+struct JMediaExtractor : public RefBase {
+    JMediaExtractor(JNIEnv *env, jobject thiz);
+
+    status_t setDataSource(const char *path);
+
+    size_t countTracks() const;
+    status_t getTrackFormat(size_t index, jobject *format) const;
+
+    status_t selectTrack(size_t index);
+
+    status_t seekTo(int64_t timeUs);
+
+    status_t advance();
+    status_t readSampleData(jobject byteBuf, size_t offset, size_t *sampleSize);
+    status_t getSampleTrackIndex(size_t *trackIndex);
+    status_t getSampleTime(int64_t *sampleTimeUs);
+
+protected:
+    virtual ~JMediaExtractor();
+
+private:
+    jclass mClass;
+    jweak mObject;
+    sp<NuMediaExtractor> mImpl;
+
+    DISALLOW_EVIL_CONSTRUCTORS(JMediaExtractor);
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 8ff9dd3..199d56e4 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -810,6 +810,8 @@
                 "android/media/MediaPlayer", gMethods, NELEM(gMethods));
 }
 
+extern int register_android_media_MediaCodec(JNIEnv *env);
+extern int register_android_media_MediaExtractor(JNIEnv *env);
 extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
 extern int register_android_media_MediaRecorder(JNIEnv *env);
 extern int register_android_media_MediaScanner(JNIEnv *env);
@@ -881,6 +883,16 @@
         goto bail;
     }
 
+    if (register_android_media_MediaCodec(env) < 0) {
+        ALOGE("ERROR: MediaCodec native registration failed");
+        goto bail;
+    }
+
+    if (register_android_media_MediaExtractor(env) < 0) {
+        ALOGE("ERROR: MediaCodec native registration failed");
+        goto bail;
+    }
+
     /* success -- return valid version number */
     result = JNI_VERSION_1_4;
 
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 47963b1..8b2321c 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -20,6 +20,10 @@
 #include <utils/Log.h>
 #include "android_media_Utils.h"
 
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
 namespace android {
 
 bool ConvertKeyValueArraysToKeyedVector(
@@ -71,5 +75,263 @@
     return true;
 }
 
+static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
+    jclass clazz = env->FindClass("java/lang/Integer");
+    CHECK(clazz != NULL);
+
+    jmethodID integerConstructID = env->GetMethodID(clazz, "<init>", "(I)V");
+    CHECK(integerConstructID != NULL);
+
+    return env->NewObject(clazz, integerConstructID, value);
+}
+
+static jobject makeFloatObject(JNIEnv *env, float value) {
+    jclass clazz = env->FindClass("java/lang/Float");
+    CHECK(clazz != NULL);
+
+    jmethodID floatConstructID = env->GetMethodID(clazz, "<init>", "(F)V");
+    CHECK(floatConstructID != NULL);
+
+    return env->NewObject(clazz, floatConstructID, value);
+}
+
+static jobject makeByteBufferObject(
+        JNIEnv *env, const void *data, size_t size) {
+    jbyteArray byteArrayObj = env->NewByteArray(size);
+    env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
+
+    jclass clazz = env->FindClass("java/nio/ByteBuffer");
+    CHECK(clazz != NULL);
+
+    jmethodID byteBufWrapID =
+        env->GetStaticMethodID(clazz, "wrap", "([B)Ljava/nio/ByteBuffer;");
+    CHECK(byteBufWrapID != NULL);
+
+    jobject byteBufObj = env->CallStaticObjectMethod(
+            clazz, byteBufWrapID, byteArrayObj);
+
+    env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
+
+    return byteBufObj;
+}
+
+status_t ConvertMessageToMap(
+        JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+    jclass hashMapClazz = env->FindClass("java/util/HashMap");
+
+    if (hashMapClazz == NULL) {
+        return -EINVAL;
+    }
+
+    jmethodID hashMapConstructID =
+        env->GetMethodID(hashMapClazz, "<init>", "()V");
+
+    if (hashMapConstructID == NULL) {
+        return -EINVAL;
+    }
+
+    jmethodID hashMapPutID =
+        env->GetMethodID(
+                hashMapClazz,
+                "put",
+                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+    if (hashMapPutID == NULL) {
+        return -EINVAL;
+    }
+
+    jobject hashMap = env->NewObject(hashMapClazz, hashMapConstructID);
+
+    for (size_t i = 0; i < msg->countEntries(); ++i) {
+        AMessage::Type valueType;
+        const char *key = msg->getEntryNameAt(i, &valueType);
+
+        jobject valueObj = NULL;
+
+        switch (valueType) {
+            case AMessage::kTypeInt32:
+            {
+                int32_t val;
+                CHECK(msg->findInt32(key, &val));
+
+                valueObj = makeIntegerObject(env, val);
+                break;
+            }
+
+            case AMessage::kTypeFloat:
+            {
+                float val;
+                CHECK(msg->findFloat(key, &val));
+
+                valueObj = makeFloatObject(env, val);
+                break;
+            }
+
+            case AMessage::kTypeString:
+            {
+                AString val;
+                CHECK(msg->findString(key, &val));
+
+                valueObj = env->NewStringUTF(val.c_str());
+                break;
+            }
+
+            case AMessage::kTypeBuffer:
+            {
+                sp<ABuffer> buffer;
+                CHECK(msg->findBuffer(key, &buffer));
+
+                valueObj = makeByteBufferObject(
+                        env, buffer->data(), buffer->size());
+                break;
+            }
+
+            default:
+                break;
+        }
+
+        if (valueObj != NULL) {
+            jstring keyObj = env->NewStringUTF(key);
+
+            jobject res = env->CallObjectMethod(
+                    hashMap, hashMapPutID, keyObj, valueObj);
+
+            env->DeleteLocalRef(keyObj); keyObj = NULL;
+            env->DeleteLocalRef(valueObj); valueObj = NULL;
+        }
+    }
+
+    *map = hashMap;
+
+    return OK;
+}
+
+status_t ConvertKeyValueArraysToMessage(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        sp<AMessage> *out) {
+    jclass stringClass = env->FindClass("java/lang/String");
+    CHECK(stringClass != NULL);
+
+    jclass integerClass = env->FindClass("java/lang/Integer");
+    CHECK(integerClass != NULL);
+
+    jclass floatClass = env->FindClass("java/lang/Float");
+    CHECK(floatClass != NULL);
+
+    jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
+    CHECK(byteBufClass != NULL);
+
+    sp<AMessage> msg = new AMessage;
+
+    jsize numEntries = 0;
+
+    if (keys != NULL) {
+        if (values == NULL) {
+            return -EINVAL;
+        }
+
+        numEntries = env->GetArrayLength(keys);
+
+        if (numEntries != env->GetArrayLength(values)) {
+            return -EINVAL;
+        }
+    } else if (values != NULL) {
+        return -EINVAL;
+    }
+
+    for (jsize i = 0; i < numEntries; ++i) {
+        jobject keyObj = env->GetObjectArrayElement(keys, i);
+
+        if (!env->IsInstanceOf(keyObj, stringClass)) {
+            return -EINVAL;
+        }
+
+        const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
+
+        if (tmp == NULL) {
+            return -ENOMEM;
+        }
+
+        AString key = tmp;
+
+        env->ReleaseStringUTFChars((jstring)keyObj, tmp);
+        tmp = NULL;
+
+        jobject valueObj = env->GetObjectArrayElement(values, i);
+
+        if (env->IsInstanceOf(valueObj, stringClass)) {
+            const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
+
+            if (value == NULL) {
+                return -ENOMEM;
+            }
+
+            msg->setString(key.c_str(), value);
+
+            env->ReleaseStringUTFChars((jstring)valueObj, value);
+            value = NULL;
+        } else if (env->IsInstanceOf(valueObj, integerClass)) {
+            jmethodID intValueID =
+                env->GetMethodID(integerClass, "intValue", "()I");
+            CHECK(intValueID != NULL);
+
+            jint value = env->CallIntMethod(valueObj, intValueID);
+
+            msg->setInt32(key.c_str(), value);
+        } else if (env->IsInstanceOf(valueObj, floatClass)) {
+            jmethodID floatValueID =
+                env->GetMethodID(floatClass, "floatValue", "()F");
+            CHECK(floatValueID != NULL);
+
+            jfloat value = env->CallFloatMethod(valueObj, floatValueID);
+
+            msg->setFloat(key.c_str(), value);
+        } else if (env->IsInstanceOf(valueObj, byteBufClass)) {
+            jmethodID positionID =
+                env->GetMethodID(byteBufClass, "position", "()I");
+            CHECK(positionID != NULL);
+
+            jmethodID limitID =
+                env->GetMethodID(byteBufClass, "limit", "()I");
+            CHECK(limitID != NULL);
+
+            jint position = env->CallIntMethod(valueObj, positionID);
+            jint limit = env->CallIntMethod(valueObj, limitID);
+
+            sp<ABuffer> buffer = new ABuffer(limit - position);
+
+            void *data = env->GetDirectBufferAddress(valueObj);
+
+            if (data != NULL) {
+                memcpy(buffer->data(),
+                       (const uint8_t *)data + position,
+                       buffer->size());
+            } else {
+                jmethodID arrayID =
+                    env->GetMethodID(byteBufClass, "array", "()[B");
+                CHECK(arrayID != NULL);
+
+                jbyteArray byteArray =
+                    (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
+                CHECK(byteArray != NULL);
+
+                env->GetByteArrayRegion(
+                        byteArray,
+                        position,
+                        buffer->size(),
+                        (jbyte *)buffer->data());
+
+                env->DeleteLocalRef(byteArray); byteArray = NULL;
+            }
+
+            msg->setObject(key.c_str(), buffer);
+        }
+    }
+
+    *out = msg;
+
+    return OK;
+}
+
 }  // namespace android
 
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index a2c155a..635bceb 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -33,6 +33,14 @@
     JNIEnv *env, jobjectArray keys, jobjectArray values,
     KeyedVector<String8, String8>* vector);
 
+struct AMessage;
+status_t ConvertMessageToMap(
+        JNIEnv *env, const sp<AMessage> &msg, jobject *map);
+
+status_t ConvertKeyValueArraysToMessage(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        sp<AMessage> *msg);
+
 };  // namespace android
 
 #endif //  _ANDROID_MEDIA_UTILS_H_
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index ecd4d07..f015afb 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -23,6 +23,7 @@
 #include <nativehelper/jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <utils/threads.h>
 #include "media/Visualizer.h"
 
 using namespace android;
@@ -38,6 +39,7 @@
 
 #define NATIVE_EVENT_PCM_CAPTURE                0
 #define NATIVE_EVENT_FFT_CAPTURE                1
+#define NATIVE_EVENT_SERVER_DIED                2
 
 // ----------------------------------------------------------------------------
 static const char* const kClassPathName = "android/media/audiofx/Visualizer";
@@ -54,6 +56,43 @@
 struct visualizer_callback_cookie {
     jclass      visualizer_class;  // Visualizer class
     jobject     visualizer_ref;    // Visualizer object instance
+
+    // Lazily allocated arrays used to hold callback data provided to java
+    // applications.  These arrays are allocated during the first callback and
+    // reallocated when the size of the callback data changes.  Allocating on
+    // demand and saving the arrays means that applications cannot safely hold a
+    // reference to the provided data (they need to make a copy if they want to
+    // hold onto outside of the callback scope), but it avoids GC thrash caused
+    // by constantly allocating and releasing arrays to hold callback data.
+    Mutex       callback_data_lock;
+    jbyteArray  waveform_data;
+    jbyteArray  fft_data;
+
+    visualizer_callback_cookie() {
+        waveform_data = NULL;
+        fft_data = NULL;
+    }
+
+    ~visualizer_callback_cookie() {
+        cleanupBuffers();
+    }
+
+    void cleanupBuffers() {
+        AutoMutex lock(&callback_data_lock);
+        if (waveform_data || fft_data) {
+            JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+            if (waveform_data) {
+                env->DeleteGlobalRef(waveform_data);
+                waveform_data = NULL;
+            }
+
+            if (fft_data) {
+                env->DeleteGlobalRef(fft_data);
+                fft_data = NULL;
+            }
+        }
+    }
  };
 
 // ----------------------------------------------------------------------------
@@ -66,7 +105,6 @@
 
     ~visualizerJniStorage() {
     }
-
 };
 
 
@@ -93,6 +131,26 @@
 
 
 // ----------------------------------------------------------------------------
+static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
+    if (NULL != *array) {
+        uint32_t len = env->GetArrayLength(*array);
+        if (len == size)
+            return;
+
+        env->DeleteGlobalRef(*array);
+        *array = NULL;
+    }
+
+    jbyteArray localRef = env->NewByteArray(size);
+    if (NULL != localRef) {
+        // Promote to global ref.
+        *array = (jbyteArray)env->NewGlobalRef(localRef);
+
+        // Release our (now pointless) local ref.
+        env->DeleteLocalRef(localRef);
+    }
+}
+
 static void captureCallback(void* user,
         uint32_t waveformSize,
         uint8_t *waveform,
@@ -106,6 +164,7 @@
 
     visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
     JNIEnv *env = AndroidRuntime::getJNIEnv();
+    AutoMutex lock(&callbackInfo->callback_data_lock);
 
     ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
             callbackInfo,
@@ -118,7 +177,11 @@
     }
 
     if (waveformSize != 0 && waveform != NULL) {
-        jbyteArray jArray = env->NewByteArray(waveformSize);
+        jbyteArray jArray;
+
+        ensureArraySize(env, &callbackInfo->waveform_data, waveformSize);
+        jArray = callbackInfo->waveform_data;
+
         if (jArray != NULL) {
             jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
             memcpy(nArray, waveform, waveformSize);
@@ -131,12 +194,15 @@
                 samplingrate,
                 0,
                 jArray);
-            env->DeleteLocalRef(jArray);
         }
     }
 
     if (fftSize != 0 && fft != NULL) {
-        jbyteArray jArray = env->NewByteArray(fftSize);
+        jbyteArray jArray;
+
+        ensureArraySize(env, &callbackInfo->fft_data, fftSize);
+        jArray = callbackInfo->fft_data;
+
         if (jArray != NULL) {
             jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
             memcpy(nArray, fft, fftSize);
@@ -149,7 +215,6 @@
                 samplingrate,
                 0,
                 jArray);
-            env->DeleteLocalRef(jArray);
         }
     }
 
@@ -220,6 +285,23 @@
 
 }
 
+static void android_media_visualizer_effect_callback(int32_t event,
+                                                     void *user,
+                                                     void *info) {
+    if ((event == AudioEffect::EVENT_ERROR) &&
+        (*((status_t*)info) == DEAD_OBJECT)) {
+        visualizerJniStorage* lpJniStorage = (visualizerJniStorage*)user;
+        visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
+        JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+        env->CallStaticVoidMethod(
+            callbackInfo->visualizer_class,
+            fields.midPostNativeEvent,
+            callbackInfo->visualizer_ref,
+            NATIVE_EVENT_SERVER_DIED,
+            0, 0, 0);
+    }
+}
 
 static jint
 android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
@@ -255,8 +337,8 @@
 
     // create the native Visualizer object
     lpVisualizer = new Visualizer(0,
-                                  NULL,
-                                  NULL,
+                                  android_media_visualizer_effect_callback,
+                                  lpJniStorage,
                                   sessionId);
     if (lpVisualizer == NULL) {
         ALOGE("Error creating Visualizer");
@@ -345,7 +427,17 @@
         return VISUALIZER_ERROR_NO_INIT;
     }
 
-    return translateError(lpVisualizer->setEnabled(enabled));
+    jint retVal = translateError(lpVisualizer->setEnabled(enabled));
+
+    if (!enabled) {
+        visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
+            thiz, fields.fidJniData);
+
+        if (NULL != lpJniStorage)
+            lpJniStorage->mCallbackData.cleanupBuffers();
+    }
+
+    return retVal;
 }
 
 static jboolean
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index e44dc7c..b17ae45 100755
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -57,6 +57,7 @@
     libaudioflinger \
     libbinder \
     libstagefright \
+    libstagefright_foundation \
     libstagefright_omx \
     libgui \
     libvideoeditorplayer
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 0d51def..ceb87db 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -623,7 +623,7 @@
         newTrack->setVolume(leftVolume, rightVolume);
         newTrack->setLoop(0, frameCount, loop);
 
-        // From now on, AudioTrack callbacks recevieved with previous toggle value will be ignored.
+        // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
         mToggle = toggle;
         mAudioTrack = newTrack;
         mPos = 0;
@@ -840,7 +840,7 @@
 void SoundChannel::setRate(float rate)
 {
     Mutex::Autolock lock(&mLock);
-    if (mAudioTrack != 0 && mSample.get() != 0) {
+    if (mAudioTrack != NULL && mSample != 0) {
         uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5);
         mAudioTrack->setSampleRate(sampleRate);
         mRate = rate;
@@ -852,7 +852,8 @@
 {
     mLeftVolume = leftVolume;
     mRightVolume = rightVolume;
-    if (mAudioTrack != 0) mAudioTrack->setVolume(leftVolume, rightVolume);
+    if (mAudioTrack != NULL)
+        mAudioTrack->setVolume(leftVolume, rightVolume);
 }
 
 void SoundChannel::setVolume(float leftVolume, float rightVolume)
@@ -864,7 +865,7 @@
 void SoundChannel::setLoop(int loop)
 {
     Mutex::Autolock lock(&mLock);
-    if (mAudioTrack != 0 && mSample.get() != 0) {
+    if (mAudioTrack != NULL && mSample != 0) {
         uint32_t loopEnd = mSample->size()/mNumChannels/
             ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
         mAudioTrack->setLoop(0, loopEnd, loop);
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 6b11c28..002b045 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -116,7 +116,7 @@
 class SoundChannel : public SoundEvent {
 public:
     enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING };
-    SoundChannel() : mAudioTrack(0), mState(IDLE), mNumChannels(1),
+    SoundChannel() : mAudioTrack(NULL), mState(IDLE), mNumChannels(1),
             mPos(0), mToggle(0), mAutoPaused(false) {}
     ~SoundChannel();
     void init(SoundPool* soundPool);
diff --git a/media/libaah_rtp/Android.mk b/media/libaah_rtp/Android.mk
new file mode 100644
index 0000000..54fd9ec
--- /dev/null
+++ b/media/libaah_rtp/Android.mk
@@ -0,0 +1,40 @@
+LOCAL_PATH:= $(call my-dir)
+#
+# libaah_rtp
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libaah_rtp
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+    aah_decoder_pump.cpp \
+    aah_rx_player.cpp \
+    aah_rx_player_core.cpp \
+    aah_rx_player_ring_buffer.cpp \
+    aah_rx_player_substream.cpp \
+    aah_tx_packet.cpp \
+    aah_tx_player.cpp \
+    aah_tx_sender.cpp \
+    pipe_event.cpp
+
+LOCAL_C_INCLUDES := \
+    frameworks/base/include \
+    frameworks/base/include/media/stagefright/openmax \
+    frameworks/base/media \
+    frameworks/base/media/libstagefright
+
+LOCAL_SHARED_LIBRARIES := \
+    libcommon_time_client \
+    libbinder \
+    libmedia \
+    libstagefright \
+    libstagefright_foundation \
+    libutils
+
+LOCAL_LDLIBS := \
+    -lpthread
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libaah_rtp/aah_decoder_pump.cpp b/media/libaah_rtp/aah_decoder_pump.cpp
new file mode 100644
index 0000000..72fe43b
--- /dev/null
+++ b/media/libaah_rtp/aah_decoder_pump.cpp
@@ -0,0 +1,520 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <poll.h>
+#include <pthread.h>
+
+#include <common_time/cc_helper.h>
+#include <media/AudioSystem.h>
+#include <media/AudioTrack.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/Utils.h>
+#include <utils/Timers.h>
+#include <utils/threads.h>
+
+#include "aah_decoder_pump.h"
+
+namespace android {
+
+static const long long kLongDecodeErrorThreshold = 1000000ll;
+static const uint32_t kMaxLongErrorsBeforeFatal = 3;
+static const uint32_t kMaxErrorsBeforeFatal = 60;
+
+AAH_DecoderPump::AAH_DecoderPump(OMXClient& omx)
+    : omx_(omx)
+    , thread_status_(OK)
+    , renderer_(NULL)
+    , last_queued_pts_valid_(false)
+    , last_queued_pts_(0)
+    , last_ts_transform_valid_(false)
+    , last_volume_(0xFF) {
+    thread_ = new ThreadWrapper(this);
+}
+
+AAH_DecoderPump::~AAH_DecoderPump() {
+    shutdown();
+}
+
+status_t AAH_DecoderPump::initCheck() {
+    if (thread_ == NULL) {
+        ALOGE("Failed to allocate thread");
+        return NO_MEMORY;
+    }
+
+    return OK;
+}
+
+status_t AAH_DecoderPump::queueForDecode(MediaBuffer* buf) {
+    if (NULL == buf) {
+        return BAD_VALUE;
+    }
+
+    if (OK != thread_status_) {
+        return thread_status_;
+    }
+
+    {   // Explicit scope for AutoMutex pattern.
+        AutoMutex lock(&thread_lock_);
+        in_queue_.push_back(buf);
+    }
+
+    thread_cond_.signal();
+
+    return OK;
+}
+
+void AAH_DecoderPump::queueToRenderer(MediaBuffer* decoded_sample) {
+    Mutex::Autolock lock(&render_lock_);
+    sp<MetaData> meta;
+    int64_t ts;
+    status_t res;
+
+    // Fetch the metadata and make sure the sample has a timestamp.  We
+    // cannot render samples which are missing PTSs.
+    meta = decoded_sample->meta_data();
+    if ((meta == NULL) || (!meta->findInt64(kKeyTime, &ts))) {
+        ALOGV("Decoded sample missing timestamp, cannot render.");
+        CHECK(false);
+    } else {
+        // If we currently are not holding on to a renderer, go ahead and
+        // make one now.
+        if (NULL == renderer_) {
+            renderer_ = new TimedAudioTrack();
+            if (NULL != renderer_) {
+                int frameCount;
+                AudioTrack::getMinFrameCount(&frameCount,
+                        AUDIO_STREAM_DEFAULT,
+                        static_cast<int>(format_sample_rate_));
+                int ch_format = (format_channels_ == 1)
+                    ? AUDIO_CHANNEL_OUT_MONO
+                    : AUDIO_CHANNEL_OUT_STEREO;
+
+                res = renderer_->set(AUDIO_STREAM_DEFAULT,
+                        format_sample_rate_,
+                        AUDIO_FORMAT_PCM_16_BIT,
+                        ch_format,
+                        frameCount);
+                if (res != OK) {
+                    ALOGE("Failed to setup audio renderer. (res = %d)", res);
+                    delete renderer_;
+                    renderer_ = NULL;
+                } else {
+                    CHECK(last_ts_transform_valid_);
+
+                    res = renderer_->setMediaTimeTransform(
+                            last_ts_transform_, TimedAudioTrack::COMMON_TIME);
+                    if (res != NO_ERROR) {
+                        ALOGE("Failed to set media time transform on AudioTrack"
+                              " (res = %d)", res);
+                        delete renderer_;
+                        renderer_ = NULL;
+                    } else {
+                        float volume = static_cast<float>(last_volume_)
+                                     / 255.0f;
+                        if (renderer_->setVolume(volume, volume) != OK) {
+                            ALOGW("%s: setVolume failed", __FUNCTION__);
+                        }
+
+                        renderer_->start();
+                    }
+                }
+            } else {
+                ALOGE("Failed to allocate AudioTrack to use as a renderer.");
+            }
+        }
+
+        if (NULL != renderer_) {
+            uint8_t* decoded_data =
+                reinterpret_cast<uint8_t*>(decoded_sample->data());
+            uint32_t decoded_amt  = decoded_sample->range_length();
+            decoded_data += decoded_sample->range_offset();
+
+            sp<IMemory> pcm_payload;
+            res = renderer_->allocateTimedBuffer(decoded_amt, &pcm_payload);
+            if (res != OK) {
+                ALOGE("Failed to allocate %d byte audio track buffer."
+                      " (res = %d)", decoded_amt, res);
+            } else {
+                memcpy(pcm_payload->pointer(), decoded_data, decoded_amt);
+
+                res = renderer_->queueTimedBuffer(pcm_payload, ts);
+                if (res != OK) {
+                    ALOGE("Failed to queue %d byte audio track buffer with media"
+                          " PTS %lld. (res = %d)", decoded_amt, ts, res);
+                } else {
+                    last_queued_pts_valid_ = true;
+                    last_queued_pts_ = ts;
+                }
+            }
+
+        } else {
+            ALOGE("No renderer, dropping audio payload.");
+        }
+    }
+}
+
+void AAH_DecoderPump::stopAndCleanupRenderer() {
+    if (NULL == renderer_) {
+        return;
+    }
+
+    renderer_->stop();
+    delete renderer_;
+    renderer_ = NULL;
+}
+
+void AAH_DecoderPump::setRenderTSTransform(const LinearTransform& trans) {
+    Mutex::Autolock lock(&render_lock_);
+
+    if (last_ts_transform_valid_ && !memcmp(&trans,
+                                            &last_ts_transform_,
+                                            sizeof(trans))) {
+        return;
+    }
+
+    last_ts_transform_       = trans;
+    last_ts_transform_valid_ = true;
+
+    if (NULL != renderer_) {
+        status_t res = renderer_->setMediaTimeTransform(
+                last_ts_transform_, TimedAudioTrack::COMMON_TIME);
+        if (res != NO_ERROR) {
+            ALOGE("Failed to set media time transform on AudioTrack"
+                  " (res = %d)", res);
+        }
+    }
+}
+
+void AAH_DecoderPump::setRenderVolume(uint8_t volume) {
+    Mutex::Autolock lock(&render_lock_);
+
+    if (volume == last_volume_) {
+        return;
+    }
+
+    last_volume_ = volume;
+    if (renderer_ != NULL) {
+        float volume = static_cast<float>(last_volume_) / 255.0f;
+        if (renderer_->setVolume(volume, volume) != OK) {
+            ALOGW("%s: setVolume failed", __FUNCTION__);
+        }
+    }
+}
+
+// isAboutToUnderflow is something of a hack used to figure out when it might be
+// time to give up on trying to fill in a gap in the RTP sequence and simply
+// move on with a discontinuity.  If we had perfect knowledge of when we were
+// going to underflow, it would not be a hack, but unfortunately we do not.
+// Right now, we just take the PTS of the last sample queued, and check to see
+// if its presentation time is within kAboutToUnderflowThreshold from now.  If
+// it is, then we say that we are about to underflow.  This decision is based on
+// two (possibly invalid) assumptions.
+//
+// 1) The transmitter is leading the clock by more than
+//    kAboutToUnderflowThreshold.
+// 2) The delta between the PTS of the last sample queued and the next sample
+//    is less than the transmitter's clock lead amount.
+//
+// Right now, the default transmitter lead time is 1 second, which is a pretty
+// large number and greater than the 50mSec that kAboutToUnderflowThreshold is
+// currently set to.  This should satisfy assumption #1 for now, but changes to
+// the transmitter clock lead time could effect this.
+//
+// For non-sparse streams with a homogeneous sample rate (the vast majority of
+// streams in the world), the delta between any two adjacent PTSs will always be
+// the homogeneous sample period.  It is very uncommon to see a sample period
+// greater than the 1 second clock lead we are currently using, and you
+// certainly will not see it in an MP3 file which should satisfy assumption #2.
+// Sparse audio streams (where no audio is transmitted for long periods of
+// silence) and extremely low framerate video stream (like an MPEG-2 slideshow
+// or the video stream for a pay TV audio channel) are examples of streams which
+// might violate assumption #2.
+bool AAH_DecoderPump::isAboutToUnderflow(int64_t threshold) {
+    Mutex::Autolock lock(&render_lock_);
+
+    // If we have never queued anything to the decoder, we really don't know if
+    // we are going to underflow or not.
+    if (!last_queued_pts_valid_ || !last_ts_transform_valid_) {
+        return false;
+    }
+
+    // Don't have access to Common Time?  If so, then things are Very Bad
+    // elsewhere in the system; it pretty much does not matter what we do here.
+    // Since we cannot really tell if we are about to underflow or not, its
+    // probably best to assume that we are not and proceed accordingly.
+    int64_t tt_now;
+    if (OK != cc_helper_.getCommonTime(&tt_now)) {
+        return false;
+    }
+
+    // Transform from media time to common time.
+    int64_t last_queued_pts_tt;
+    if (!last_ts_transform_.doForwardTransform(last_queued_pts_,
+                &last_queued_pts_tt)) {
+        return false;
+    }
+
+    // Check to see if we are underflowing.
+    return ((tt_now + threshold - last_queued_pts_tt) > 0);
+}
+
+void* AAH_DecoderPump::workThread() {
+    // No need to lock when accessing decoder_ from the thread.  The
+    // implementation of init and shutdown ensure that other threads never touch
+    // decoder_ while the work thread is running.
+    CHECK(decoder_ != NULL);
+    CHECK(format_  != NULL);
+
+    // Start the decoder and note its result code.  If something goes horribly
+    // wrong, callers of queueForDecode and getOutput will be able to detect
+    // that the thread encountered a fatal error and shut down by examining
+    // thread_status_.
+    thread_status_ = decoder_->start(format_.get());
+    if (OK != thread_status_) {
+        ALOGE("AAH_DecoderPump's work thread failed to start decoder (res = %d)",
+                thread_status_);
+        return NULL;
+    }
+
+    DurationTimer decode_timer;
+    uint32_t consecutive_long_errors = 0;
+    uint32_t consecutive_errors = 0;
+
+    while (!thread_->exitPending()) {
+        status_t res;
+        MediaBuffer* bufOut = NULL;
+
+        decode_timer.start();
+        res = decoder_->read(&bufOut);
+        decode_timer.stop();
+
+        if (res == INFO_FORMAT_CHANGED) {
+            // Format has changed.  Destroy our current renderer so that a new
+            // one can be created during queueToRenderer with the proper format.
+            //
+            // TODO : In order to transition seamlessly, we should change this
+            // to put the old renderer in a queue to play out completely before
+            // we destroy it.  We can still create a new renderer, the timed
+            // nature of the renderer should ensure a seamless splice.
+            stopAndCleanupRenderer();
+            res = OK;
+        }
+
+        // Try to be a little nuanced in our handling of actual decode errors.
+        // Errors could happen because of minor stream corruption or because of
+        // transient resource limitations.  In these cases, we would rather drop
+        // a little bit of output and ride out the unpleasantness then throw up
+        // our hands and abort everything.
+        //
+        // OTOH - When things are really bad (like we have a non-transient
+        // resource or bookkeeping issue, or the stream being fed to us is just
+        // complete and total garbage) we really want to terminate playback and
+        // raise an error condition all the way up to the application level so
+        // they can deal with it.
+        //
+        // Unfortunately, the error codes returned by the decoder can be a
+        // little non-specific.  For example, if an OMXCodec times out
+        // attempting to obtain an output buffer, the error we get back is a
+        // generic -1.  Try to distinguish between this resource timeout error
+        // and ES corruption error by timing how long the decode operation
+        // takes.  Maintain accounting for both errors and "long errors".  If we
+        // get more than a certain number consecutive errors of either type,
+        // consider it fatal and shutdown (which will cause the error to
+        // propagate all of the way up to the application level).  The threshold
+        // for "long errors" is deliberately much lower than that of normal
+        // decode errors, both because of how long they take to happen and
+        // because they generally indicate resource limitation errors which are
+        // unlikely to go away in pathologically bad cases (in contrast to
+        // stream corruption errors which might happen 20 times in a row and
+        // then be suddenly OK again)
+        if (res != OK) {
+            consecutive_errors++;
+            if (decode_timer.durationUsecs() >= kLongDecodeErrorThreshold)
+                consecutive_long_errors++;
+
+            CHECK(NULL == bufOut);
+
+            ALOGW("%s: Failed to decode data (res = %d)",
+                    __PRETTY_FUNCTION__, res);
+
+            if ((consecutive_errors      >= kMaxErrorsBeforeFatal) ||
+                (consecutive_long_errors >= kMaxLongErrorsBeforeFatal)) {
+                ALOGE("%s: Maximum decode error threshold has been reached."
+                      " There have been %d consecutive decode errors, and %d"
+                      " consecutive decode operations which resulted in errors"
+                      " and took more than %lld uSec to process.  The last"
+                      " decode operation took %lld uSec.",
+                      __PRETTY_FUNCTION__,
+                      consecutive_errors, consecutive_long_errors,
+                      kLongDecodeErrorThreshold, decode_timer.durationUsecs());
+                thread_status_ = res;
+                break;
+            }
+
+            continue;
+        }
+
+        if (NULL == bufOut) {
+            ALOGW("%s: Successful decode, but no buffer produced",
+                    __PRETTY_FUNCTION__);
+            continue;
+        }
+
+        // Successful decode (with actual output produced).  Clear the error
+        // counters.
+        consecutive_errors = 0;
+        consecutive_long_errors = 0;
+
+        queueToRenderer(bufOut);
+        bufOut->release();
+    }
+
+    decoder_->stop();
+    stopAndCleanupRenderer();
+
+    return NULL;
+}
+
+status_t AAH_DecoderPump::init(const sp<MetaData>& params) {
+    Mutex::Autolock lock(&init_lock_);
+
+    if (decoder_ != NULL) {
+        // already inited
+        return OK;
+    }
+
+    if (params == NULL) {
+        return BAD_VALUE;
+    }
+
+    if (!params->findInt32(kKeyChannelCount, &format_channels_)) {
+        return BAD_VALUE;
+    }
+
+    if (!params->findInt32(kKeySampleRate, &format_sample_rate_)) {
+        return BAD_VALUE;
+    }
+
+    CHECK(OK == thread_status_);
+    CHECK(decoder_ == NULL);
+
+    status_t ret_val = UNKNOWN_ERROR;
+
+    // Cache the format and attempt to create the decoder.
+    format_  = params;
+    decoder_ = OMXCodec::Create(
+            omx_.interface(),       // IOMX Handle
+            format_,                // Metadata for substream (indicates codec)
+            false,                  // Make a decoder, not an encoder
+            sp<MediaSource>(this)); // We will be the source for this codec.
+
+    if (decoder_ == NULL) {
+      ALOGE("Failed to allocate decoder in %s", __PRETTY_FUNCTION__);
+      goto bailout;
+    }
+
+    // Fire up the pump thread.  It will take care of starting and stopping the
+    // decoder.
+    ret_val = thread_->run("aah_decode_pump", ANDROID_PRIORITY_AUDIO);
+    if (OK != ret_val) {
+        ALOGE("Failed to start work thread in %s (res = %d)",
+                __PRETTY_FUNCTION__, ret_val);
+        goto bailout;
+    }
+
+bailout:
+    if (OK != ret_val) {
+        decoder_ = NULL;
+        format_  = NULL;
+    }
+
+    return OK;
+}
+
+status_t AAH_DecoderPump::shutdown() {
+    Mutex::Autolock lock(&init_lock_);
+    return shutdown_l();
+}
+
+status_t AAH_DecoderPump::shutdown_l() {
+    thread_->requestExit();
+    thread_cond_.signal();
+    thread_->requestExitAndWait();
+
+    for (MBQueue::iterator iter = in_queue_.begin();
+         iter != in_queue_.end();
+         ++iter) {
+        (*iter)->release();
+    }
+    in_queue_.clear();
+
+    last_queued_pts_valid_   = false;
+    last_ts_transform_valid_ = false;
+    last_volume_             = 0xFF;
+    thread_status_           = OK;
+
+    decoder_ = NULL;
+    format_  = NULL;
+
+    return OK;
+}
+
+status_t AAH_DecoderPump::read(MediaBuffer **buffer,
+                               const ReadOptions *options) {
+    if (!buffer) {
+        return BAD_VALUE;
+    }
+
+    *buffer = NULL;
+
+    // While its not time to shut down, and we have no data to process, wait.
+    AutoMutex lock(&thread_lock_);
+    while (!thread_->exitPending() && in_queue_.empty())
+        thread_cond_.wait(thread_lock_);
+
+    // At this point, if its not time to shutdown then we must have something to
+    // process.  Go ahead and pop the front of the queue for processing.
+    if (!thread_->exitPending()) {
+        CHECK(!in_queue_.empty());
+
+        *buffer = *(in_queue_.begin());
+        in_queue_.erase(in_queue_.begin());
+    }
+
+    // If we managed to get a buffer, then everything must be OK.  If not, then
+    // we must be shutting down.
+    return (NULL == *buffer) ? INVALID_OPERATION : OK;
+}
+
+AAH_DecoderPump::ThreadWrapper::ThreadWrapper(AAH_DecoderPump* owner)
+    : Thread(false /* canCallJava*/ )
+    , owner_(owner) {
+}
+
+bool AAH_DecoderPump::ThreadWrapper::threadLoop() {
+    CHECK(NULL != owner_);
+    owner_->workThread();
+    return false;
+}
+
+}  // namespace android
diff --git a/media/libaah_rtp/aah_decoder_pump.h b/media/libaah_rtp/aah_decoder_pump.h
new file mode 100644
index 0000000..f5a6529
--- /dev/null
+++ b/media/libaah_rtp/aah_decoder_pump.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#ifndef __DECODER_PUMP_H__
+#define __DECODER_PUMP_H__
+
+#include <pthread.h>
+
+#include <common_time/cc_helper.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/LinearTransform.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MetaData;
+class OMXClient;
+class TimedAudioTrack;
+
+class AAH_DecoderPump : public MediaSource {
+  public:
+    explicit AAH_DecoderPump(OMXClient& omx);
+    status_t initCheck();
+
+    status_t queueForDecode(MediaBuffer* buf);
+
+    status_t init(const sp<MetaData>& params);
+    status_t shutdown();
+
+    void setRenderTSTransform(const LinearTransform& trans);
+    void setRenderVolume(uint8_t volume);
+    bool isAboutToUnderflow(int64_t threshold);
+    bool getStatus() const { return thread_status_; }
+
+    // MediaSource methods
+    virtual status_t     start(MetaData *params) { return OK; }
+    virtual sp<MetaData> getFormat() { return format_; }
+    virtual status_t     stop() { return OK; }
+    virtual status_t     read(MediaBuffer **buffer,
+                              const ReadOptions *options);
+
+  protected:
+    virtual ~AAH_DecoderPump();
+
+  private:
+    class ThreadWrapper : public Thread {
+      public:
+        friend class AAH_DecoderPump;
+        explicit ThreadWrapper(AAH_DecoderPump* owner);
+
+      private:
+        virtual bool threadLoop();
+        AAH_DecoderPump* owner_;
+
+        DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper);
+    };
+
+    void* workThread();
+    virtual status_t shutdown_l();
+    void queueToRenderer(MediaBuffer* decoded_sample);
+    void stopAndCleanupRenderer();
+
+    sp<MetaData>        format_;
+    int32_t             format_channels_;
+    int32_t             format_sample_rate_;
+
+    sp<MediaSource>     decoder_;
+    OMXClient&          omx_;
+    Mutex               init_lock_;
+
+    sp<ThreadWrapper>   thread_;
+    Condition           thread_cond_;
+    Mutex               thread_lock_;
+    status_t            thread_status_;
+
+    Mutex               render_lock_;
+    TimedAudioTrack*    renderer_;
+    bool                last_queued_pts_valid_;
+    int64_t             last_queued_pts_;
+    bool                last_ts_transform_valid_;
+    LinearTransform     last_ts_transform_;
+    uint8_t             last_volume_;
+    CCHelper            cc_helper_;
+
+    // protected by the thread_lock_
+    typedef List<MediaBuffer*> MBQueue;
+    MBQueue in_queue_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AAH_DecoderPump);
+};
+
+}  // namespace android
+#endif  // __DECODER_PUMP_H__
diff --git a/media/libaah_rtp/aah_rx_player.cpp b/media/libaah_rtp/aah_rx_player.cpp
new file mode 100644
index 0000000..9dd79fd
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+
+#include <binder/IServiceManager.h>
+#include <media/MediaPlayerInterface.h>
+#include <utils/Log.h>
+
+#include "aah_rx_player.h"
+
+namespace android {
+
+const uint32_t AAH_RXPlayer::kRTPRingBufferSize = 1 << 10;
+
+sp<MediaPlayerBase> createAAH_RXPlayer() {
+    sp<MediaPlayerBase> ret = new AAH_RXPlayer();
+    return ret;
+}
+
+AAH_RXPlayer::AAH_RXPlayer()
+        : ring_buffer_(kRTPRingBufferSize)
+        , substreams_(NULL) {
+    thread_wrapper_ = new ThreadWrapper(*this);
+
+    is_playing_          = false;
+    multicast_joined_    = false;
+    transmitter_known_   = false;
+    current_epoch_known_ = false;
+    data_source_set_     = false;
+    sock_fd_             = -1;
+
+    substreams_.setCapacity(4);
+
+    memset(&listen_addr_,      0, sizeof(listen_addr_));
+    memset(&transmitter_addr_, 0, sizeof(transmitter_addr_));
+
+    fetchAudioFlinger();
+}
+
+AAH_RXPlayer::~AAH_RXPlayer() {
+    reset_l();
+    CHECK(substreams_.size() == 0);
+    omx_.disconnect();
+}
+
+status_t AAH_RXPlayer::initCheck() {
+    if (thread_wrapper_ == NULL) {
+        ALOGE("Failed to allocate thread wrapper!");
+        return NO_MEMORY;
+    }
+
+    if (!ring_buffer_.initCheck()) {
+        ALOGE("Failed to allocate reassembly ring buffer!");
+        return NO_MEMORY;
+    }
+
+    // Check for the presense of the common time service by attempting to query
+    // for CommonTime's frequency.  If we get an error back, we cannot talk to
+    // the service at all and should abort now.
+    status_t res;
+    uint64_t freq;
+    res = cc_helper_.getCommonFreq(&freq);
+    if (OK != res) {
+        ALOGE("Failed to connect to common time service!");
+        return res;
+    }
+
+    return omx_.connect();
+}
+
+status_t AAH_RXPlayer::setDataSource(
+        const char *url,
+        const KeyedVector<String8, String8> *headers) {
+    AutoMutex api_lock(&api_lock_);
+    uint32_t a, b, c, d;
+    uint16_t port;
+
+    if (data_source_set_) {
+        return INVALID_OPERATION;
+    }
+
+    if (NULL == url) {
+        return BAD_VALUE;
+    }
+
+    if (5 != sscanf(url, "%*[^:/]://%u.%u.%u.%u:%hu", &a, &b, &c, &d, &port)) {
+        ALOGE("Failed to parse URL \"%s\"", url);
+        return BAD_VALUE;
+    }
+
+    if ((a > 255) || (b > 255) || (c > 255) || (d > 255) || (port == 0)) {
+        ALOGE("Bad multicast address \"%s\"", url);
+        return BAD_VALUE;
+    }
+
+    ALOGI("setDataSource :: %u.%u.%u.%u:%hu", a, b, c, d, port);
+
+    a = (a << 24) | (b << 16) | (c <<  8) | d;
+
+    memset(&listen_addr_, 0, sizeof(listen_addr_));
+    listen_addr_.sin_family      = AF_INET;
+    listen_addr_.sin_port        = htons(port);
+    listen_addr_.sin_addr.s_addr = htonl(a);
+    data_source_set_ = true;
+
+    return OK;
+}
+
+status_t AAH_RXPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
+    return INVALID_OPERATION;
+}
+
+status_t AAH_RXPlayer::setVideoSurface(const sp<Surface>& surface) {
+    return OK;
+}
+
+status_t AAH_RXPlayer::setVideoSurfaceTexture(
+        const sp<ISurfaceTexture>& surfaceTexture) {
+    return OK;
+}
+
+status_t AAH_RXPlayer::prepare() {
+    return OK;
+}
+
+status_t AAH_RXPlayer::prepareAsync() {
+    sendEvent(MEDIA_PREPARED);
+    return OK;
+}
+
+status_t AAH_RXPlayer::start() {
+    AutoMutex api_lock(&api_lock_);
+
+    if (is_playing_) {
+        return OK;
+    }
+
+    status_t res = startWorkThread();
+    is_playing_ = (res == OK);
+    return res;
+}
+
+status_t AAH_RXPlayer::stop() {
+    return pause();
+}
+
+status_t AAH_RXPlayer::pause() {
+    AutoMutex api_lock(&api_lock_);
+    stopWorkThread();
+    CHECK(sock_fd_ < 0);
+    is_playing_ = false;
+    return OK;
+}
+
+bool AAH_RXPlayer::isPlaying() {
+    AutoMutex api_lock(&api_lock_);
+    return is_playing_;
+}
+
+status_t AAH_RXPlayer::seekTo(int msec) {
+    sendEvent(MEDIA_SEEK_COMPLETE);
+    return OK;
+}
+
+status_t AAH_RXPlayer::getCurrentPosition(int *msec) {
+    if (NULL != msec) {
+        *msec = 0;
+    }
+    return OK;
+}
+
+status_t AAH_RXPlayer::getDuration(int *msec) {
+    if (NULL != msec) {
+        *msec = 1;
+    }
+    return OK;
+}
+
+status_t AAH_RXPlayer::reset() {
+    AutoMutex api_lock(&api_lock_);
+    reset_l();
+    return OK;
+}
+
+void AAH_RXPlayer::reset_l() {
+    stopWorkThread();
+    CHECK(sock_fd_ < 0);
+    CHECK(!multicast_joined_);
+    is_playing_ = false;
+    data_source_set_ = false;
+    transmitter_known_ = false;
+    memset(&listen_addr_, 0, sizeof(listen_addr_));
+}
+
+status_t AAH_RXPlayer::setLooping(int loop) {
+    return OK;
+}
+
+player_type AAH_RXPlayer::playerType() {
+    return AAH_RX_PLAYER;
+}
+
+status_t AAH_RXPlayer::setParameter(int key, const Parcel &request) {
+    return ERROR_UNSUPPORTED;
+}
+
+status_t AAH_RXPlayer::getParameter(int key, Parcel *reply) {
+    return ERROR_UNSUPPORTED;
+}
+
+status_t AAH_RXPlayer::invoke(const Parcel& request, Parcel *reply) {
+    if (!reply) {
+        return BAD_VALUE;
+    }
+
+    int32_t magic;
+    status_t err = request.readInt32(&magic);
+    if (err != OK) {
+        reply->writeInt32(err);
+        return OK;
+    }
+
+    if (magic != 0x12345) {
+        reply->writeInt32(BAD_VALUE);
+        return OK;
+    }
+
+    int32_t methodID;
+    err = request.readInt32(&methodID);
+    if (err != OK) {
+        reply->writeInt32(err);
+        return OK;
+    }
+
+    switch (methodID) {
+        // Get Volume
+        case INVOKE_GET_MASTER_VOLUME: {
+            if (audio_flinger_ != NULL) {
+                reply->writeInt32(OK);
+                reply->writeFloat(audio_flinger_->masterVolume());
+            } else {
+                reply->writeInt32(UNKNOWN_ERROR);
+            }
+        } break;
+
+        // Set Volume
+        case INVOKE_SET_MASTER_VOLUME: {
+            float targetVol = request.readFloat();
+            reply->writeInt32(audio_flinger_->setMasterVolume(targetVol));
+        } break;
+
+        default: return BAD_VALUE;
+    }
+
+    return OK;
+}
+
+void AAH_RXPlayer::fetchAudioFlinger() {
+    if (audio_flinger_ == NULL) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder;
+        binder = sm->getService(String16("media.audio_flinger"));
+
+        if (binder == NULL) {
+            ALOGW("AAH_RXPlayer failed to fetch handle to audio flinger."
+                  " Master volume control will not be possible.");
+        }
+
+        audio_flinger_ = interface_cast<IAudioFlinger>(binder);
+    }
+}
+
+}  // namespace android
diff --git a/media/libaah_rtp/aah_rx_player.h b/media/libaah_rtp/aah_rx_player.h
new file mode 100644
index 0000000..7a1b6e3
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player.h
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+#ifndef __AAH_RX_PLAYER_H__
+#define __AAH_RX_PLAYER_H__
+
+#include <common_time/cc_helper.h>
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXClient.h>
+#include <netinet/in.h>
+#include <utils/KeyedVector.h>
+#include <utils/LinearTransform.h>
+#include <utils/threads.h>
+
+#include "aah_decoder_pump.h"
+#include "pipe_event.h"
+
+namespace android {
+
+class AAH_RXPlayer : public MediaPlayerInterface {
+  public:
+    AAH_RXPlayer();
+
+    virtual status_t    initCheck();
+    virtual status_t    setDataSource(const char *url,
+                                      const KeyedVector<String8, String8>*
+                                      headers);
+    virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
+    virtual status_t    setVideoSurface(const sp<Surface>& surface);
+    virtual status_t    setVideoSurfaceTexture(const sp<ISurfaceTexture>&
+                                               surfaceTexture);
+    virtual status_t    prepare();
+    virtual status_t    prepareAsync();
+    virtual status_t    start();
+    virtual status_t    stop();
+    virtual status_t    pause();
+    virtual bool        isPlaying();
+    virtual status_t    seekTo(int msec);
+    virtual status_t    getCurrentPosition(int *msec);
+    virtual status_t    getDuration(int *msec);
+    virtual status_t    reset();
+    virtual status_t    setLooping(int loop);
+    virtual player_type playerType();
+    virtual status_t    setParameter(int key, const Parcel &request);
+    virtual status_t    getParameter(int key, Parcel *reply);
+    virtual status_t    invoke(const Parcel& request, Parcel *reply);
+
+  protected:
+    virtual ~AAH_RXPlayer();
+
+  private:
+    class ThreadWrapper : public Thread {
+      public:
+        friend class AAH_RXPlayer;
+        explicit ThreadWrapper(AAH_RXPlayer& player)
+            : Thread(false /* canCallJava */ )
+            , player_(player) { }
+
+        virtual bool threadLoop() { return player_.threadLoop(); }
+
+      private:
+        AAH_RXPlayer& player_;
+
+        DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper);
+    };
+
+#pragma pack(push, 1)
+    // PacketBuffers are structures used by the RX ring buffer.  The ring buffer
+    // is a ring of pointers to PacketBuffer structures which act as variable
+    // length byte arrays and hold the contents of received UDP packets.  Rather
+    // than make this a structure which hold a length and a pointer to another
+    // allocated structure (which would require two allocations), this struct
+    // uses a structure overlay pattern where allocation for the byte array
+    // consists of allocating (arrayLen + sizeof(ssize_t)) bytes of data from
+    // whatever pool/heap the packet buffer pulls from, and then overlaying the
+    // packed PacketBuffer structure on top of the allocation.  The one-byte
+    // array at the end of the structure serves as an offset to the the data
+    // portion of the allocation; packet buffers are never allocated on the
+    // stack or using the new operator.  Instead, the static allocate-byte-array
+    // and destroy methods handle the allocate and overlay pattern.  They also
+    // allow for a potential future optimization where instead of just
+    // allocating blocks from the process global heap and overlaying, the
+    // allocator is replaced with a different implementation (private heap,
+    // free-list, circular buffer, etc) which reduces potential heap
+    // fragmentation issues which might arise from the frequent allocation and
+    // destruction of the received UDP traffic.
+    struct PacketBuffer {
+        ssize_t length_;
+        uint8_t data_[1];
+
+        // TODO : consider changing this to be some form of ring buffer or free
+        // pool system instead of just using the heap in order to avoid heap
+        // fragmentation.
+        static PacketBuffer* allocate(ssize_t length);
+        static void destroy(PacketBuffer* pb);
+
+      private:
+        // Force people to use allocate/destroy instead of new/delete.
+        PacketBuffer() { }
+        ~PacketBuffer() { }
+    };
+
+    struct RetransRequest {
+        uint32_t magic_;
+        uint32_t mcast_ip_;
+        uint16_t mcast_port_;
+        uint16_t start_seq_;
+        uint16_t end_seq_;
+    };
+#pragma pack(pop)
+
+    enum GapStatus {
+        kGS_NoGap = 0,
+        kGS_NormalGap,
+        kGS_FastStartGap,
+    };
+
+    struct SeqNoGap {
+        uint16_t start_seq_;
+        uint16_t end_seq_;
+    };
+
+    class RXRingBuffer {
+      public:
+        explicit RXRingBuffer(uint32_t capacity);
+        ~RXRingBuffer();
+
+        bool initCheck() const { return (ring_ != NULL); }
+        void reset();
+
+        // Push a packet buffer with a given sequence number into the ring
+        // buffer.  pushBuffer will always consume the buffer pushed to it,
+        // either destroying it because it was a duplicate or overflow, or
+        // holding on to it in the ring.  Callers should not hold any references
+        // to PacketBuffers after they have been pushed to the ring.  Returns
+        // false in the case of a serious error (such as ring overflow).
+        // Callers should consider resetting the pipeline entirely in the event
+        // of a serious error.
+        bool pushBuffer(PacketBuffer* buf, uint16_t seq);
+
+        // Fetch the next buffer in the RTP sequence.  Returns NULL if there is
+        // no buffer to fetch.  If a non-NULL PacketBuffer is returned,
+        // is_discon will be set to indicate whether or not this PacketBuffer is
+        // discontiuous with any previously returned packet buffers.  Packet
+        // buffers returned by fetchBuffer are the caller's responsibility; they
+        // must be certain to destroy the buffers when they are done.
+        PacketBuffer* fetchBuffer(bool* is_discon);
+
+        // Returns true and fills out the gap structure if the read pointer of
+        // the ring buffer is currently pointing to a gap which would stall a
+        // fetchBuffer operation.  Returns false if the read pointer is not
+        // pointing to a gap in the sequence currently.
+        GapStatus fetchCurrentGap(SeqNoGap* gap);
+
+        // Causes the read pointer to skip over any portion of a gap indicated
+        // by nak.  If nak is NULL, any gap currently blocking the read pointer
+        // will be completely skipped.  If any portion of a gap is skipped, the
+        // next successful read from fetch buffer will indicate a discontinuity.
+        void processNAK(const SeqNoGap* nak = NULL);
+
+        // Compute the number of milliseconds until the inactivity timer for
+        // this RTP stream.  Returns -1 if there is no active timeout, or 0 if
+        // the system has already timed out.
+        int computeInactivityTimeout();
+
+      private:
+        Mutex          lock_;
+        PacketBuffer** ring_;
+        uint32_t       capacity_;
+        uint32_t       rd_;
+        uint32_t       wr_;
+
+        uint16_t       rd_seq_;
+        bool           rd_seq_known_;
+        bool           waiting_for_fast_start_;
+        bool           fetched_first_packet_;
+
+        uint64_t       rtp_activity_timeout_;
+        bool           rtp_activity_timeout_valid_;
+
+        DISALLOW_EVIL_CONSTRUCTORS(RXRingBuffer);
+    };
+
+    class Substream : public virtual RefBase {
+      public:
+        Substream(uint32_t ssrc, OMXClient& omx);
+
+        void cleanupBufferInProgress();
+        void shutdown();
+        void processPayloadStart(uint8_t* buf,
+                                 uint32_t amt,
+                                 int32_t ts_lower);
+        void processPayloadCont (uint8_t* buf,
+                                 uint32_t amt);
+        void processTSTransform(const LinearTransform& trans);
+
+        bool     isAboutToUnderflow();
+        uint32_t getSSRC()      const { return ssrc_; }
+        uint16_t getProgramID() const { return (ssrc_ >> 5) & 0x1F; }
+        status_t getStatus() const { return status_; }
+
+      protected:
+        virtual ~Substream() {
+            shutdown();
+        }
+
+      private:
+        void                cleanupDecoder();
+        bool                shouldAbort(const char* log_tag);
+        void                processCompletedBuffer();
+        bool                setupSubstreamType(uint8_t substream_type,
+                                               uint8_t codec_type);
+
+        uint32_t            ssrc_;
+        bool                waiting_for_rap_;
+        status_t            status_;
+
+        bool                substream_details_known_;
+        uint8_t             substream_type_;
+        uint8_t             codec_type_;
+        sp<MetaData>        substream_meta_;
+
+        MediaBuffer*        buffer_in_progress_;
+        uint32_t            expected_buffer_size_;
+        uint32_t            buffer_filled_;
+
+        sp<AAH_DecoderPump> decoder_;
+
+        static int64_t      kAboutToUnderflowThreshold;
+
+        DISALLOW_EVIL_CONSTRUCTORS(Substream);
+    };
+
+    typedef DefaultKeyedVector< uint32_t, sp<Substream> > SubstreamVec;
+
+    status_t            startWorkThread();
+    void                stopWorkThread();
+    virtual bool        threadLoop();
+    bool                setupSocket();
+    void                cleanupSocket();
+    void                resetPipeline();
+    void                reset_l();
+    bool                processRX(PacketBuffer* pb);
+    void                processRingBuffer();
+    void                processCommandPacket(PacketBuffer* pb);
+    bool                processGaps();
+    int                 computeNextGapRetransmitTimeout();
+    void                fetchAudioFlinger();
+
+    PipeEvent           wakeup_work_thread_evt_;
+    sp<ThreadWrapper>   thread_wrapper_;
+    Mutex               api_lock_;
+    bool                is_playing_;
+    bool                data_source_set_;
+
+    struct sockaddr_in  listen_addr_;
+    int                 sock_fd_;
+    bool                multicast_joined_;
+
+    struct sockaddr_in  transmitter_addr_;
+    bool                transmitter_known_;
+
+    uint32_t            current_epoch_;
+    bool                current_epoch_known_;
+
+    SeqNoGap            current_gap_;
+    GapStatus           current_gap_status_;
+    uint64_t            next_retrans_req_time_;
+
+    RXRingBuffer        ring_buffer_;
+    SubstreamVec        substreams_;
+    OMXClient           omx_;
+    CCHelper            cc_helper_;
+
+    // Connection to audio flinger used to hack a path to setMasterVolume.
+    sp<IAudioFlinger>   audio_flinger_;
+
+    static const uint32_t kRTPRingBufferSize;
+    static const uint32_t kRetransRequestMagic;
+    static const uint32_t kFastStartRequestMagic;
+    static const uint32_t kRetransNAKMagic;
+    static const uint32_t kGapRerequestTimeoutUSec;
+    static const uint32_t kFastStartTimeoutUSec;
+    static const uint32_t kRTPActivityTimeoutUSec;
+
+    static const uint32_t INVOKE_GET_MASTER_VOLUME = 3;
+    static const uint32_t INVOKE_SET_MASTER_VOLUME = 4;
+
+    static uint64_t monotonicUSecNow();
+
+    DISALLOW_EVIL_CONSTRUCTORS(AAH_RXPlayer);
+};
+
+}  // namespace android
+
+#endif  // __AAH_RX_PLAYER_H__
diff --git a/media/libaah_rtp/aah_rx_player_core.cpp b/media/libaah_rtp/aah_rx_player_core.cpp
new file mode 100644
index 0000000..d2b3386
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player_core.cpp
@@ -0,0 +1,807 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <utils/misc.h>
+
+#include <media/stagefright/Utils.h>
+
+#include "aah_rx_player.h"
+#include "aah_tx_packet.h"
+
+namespace android {
+
+const uint32_t AAH_RXPlayer::kRetransRequestMagic =
+    FOURCC('T','r','e','q');
+const uint32_t AAH_RXPlayer::kRetransNAKMagic =
+    FOURCC('T','n','a','k');
+const uint32_t AAH_RXPlayer::kFastStartRequestMagic =
+    FOURCC('T','f','s','t');
+const uint32_t AAH_RXPlayer::kGapRerequestTimeoutUSec = 75000;
+const uint32_t AAH_RXPlayer::kFastStartTimeoutUSec = 800000;
+const uint32_t AAH_RXPlayer::kRTPActivityTimeoutUSec = 10000000;
+
+static inline int16_t fetchInt16(uint8_t* data) {
+    return static_cast<int16_t>(U16_AT(data));
+}
+
+static inline int32_t fetchInt32(uint8_t* data) {
+    return static_cast<int32_t>(U32_AT(data));
+}
+
+static inline int64_t fetchInt64(uint8_t* data) {
+    return static_cast<int64_t>(U64_AT(data));
+}
+
+uint64_t AAH_RXPlayer::monotonicUSecNow() {
+    struct timespec now;
+    int res = clock_gettime(CLOCK_MONOTONIC, &now);
+    CHECK(res >= 0);
+
+    uint64_t ret = static_cast<uint64_t>(now.tv_sec) * 1000000;
+    ret += now.tv_nsec / 1000;
+
+    return ret;
+}
+
+status_t AAH_RXPlayer::startWorkThread() {
+    status_t res;
+    stopWorkThread();
+    res = thread_wrapper_->run("TRX_Player", PRIORITY_AUDIO);
+
+    if (res != OK) {
+        ALOGE("Failed to start work thread (res = %d)", res);
+    }
+
+    return res;
+}
+
+void AAH_RXPlayer::stopWorkThread() {
+    thread_wrapper_->requestExit();  // set the exit pending flag
+    wakeup_work_thread_evt_.setEvent();
+
+    status_t res;
+    res = thread_wrapper_->requestExitAndWait(); // block until thread exit.
+    if (res != OK) {
+        ALOGE("Failed to stop work thread (res = %d)", res);
+    }
+
+    wakeup_work_thread_evt_.clearPendingEvents();
+}
+
+void AAH_RXPlayer::cleanupSocket() {
+    if (sock_fd_ >= 0) {
+        if (multicast_joined_) {
+            int res;
+            struct ip_mreq mreq;
+            mreq.imr_multiaddr = listen_addr_.sin_addr;
+            mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+            res = setsockopt(sock_fd_,
+                             IPPROTO_IP,
+                             IP_DROP_MEMBERSHIP,
+                             &mreq, sizeof(mreq));
+            if (res < 0) {
+                ALOGW("Failed to leave multicast group. (%d, %d)", res, errno);
+            }
+            multicast_joined_ = false;
+        }
+
+        close(sock_fd_);
+        sock_fd_ = -1;
+    }
+
+    resetPipeline();
+}
+
+void AAH_RXPlayer::resetPipeline() {
+    ring_buffer_.reset();
+
+    // Explicitly shudown all of the active substreams, then call clear out the
+    // collection.  Failure to clear out a substream can result in its decoder
+    // holding a reference to itself and therefor not going away when the
+    // collection is cleared.
+    for (size_t i = 0; i < substreams_.size(); ++i)
+        substreams_.valueAt(i)->shutdown();
+
+    substreams_.clear();
+
+    current_gap_status_ = kGS_NoGap;
+}
+
+bool AAH_RXPlayer::setupSocket() {
+    long flags;
+    int  res, buf_size;
+    socklen_t opt_size;
+
+    cleanupSocket();
+    CHECK(sock_fd_ < 0);
+
+    // Make the socket
+    sock_fd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    if (sock_fd_ < 0) {
+        ALOGE("Failed to create listen socket (errno %d)", errno);
+        goto bailout;
+    }
+
+    // Set non-blocking operation
+    flags = fcntl(sock_fd_, F_GETFL);
+    res   = fcntl(sock_fd_, F_SETFL, flags | O_NONBLOCK);
+    if (res < 0) {
+        ALOGE("Failed to set socket (%d) to non-blocking mode (errno %d)",
+              sock_fd_, errno);
+        goto bailout;
+    }
+
+    // Bind to our port
+    struct sockaddr_in bind_addr;
+    memset(&bind_addr, 0, sizeof(bind_addr));
+    bind_addr.sin_family = AF_INET;
+    bind_addr.sin_addr.s_addr = INADDR_ANY;
+    bind_addr.sin_port = listen_addr_.sin_port;
+    res = bind(sock_fd_,
+               reinterpret_cast<const sockaddr*>(&bind_addr),
+               sizeof(bind_addr));
+    if (res < 0) {
+        uint32_t a = ntohl(bind_addr.sin_addr.s_addr);
+        uint16_t p = ntohs(bind_addr.sin_port);
+        ALOGE("Failed to bind socket (%d) to %d.%d.%d.%d:%hd. (errno %d)",
+              sock_fd_,
+              (a >> 24) & 0xFF,
+              (a >> 16) & 0xFF,
+              (a >>  8) & 0xFF,
+              (a      ) & 0xFF,
+              p,
+              errno);
+
+        goto bailout;
+    }
+
+    buf_size = 1 << 16;   // 64k
+    res = setsockopt(sock_fd_,
+                     SOL_SOCKET, SO_RCVBUF,
+                     &buf_size, sizeof(buf_size));
+    if (res < 0) {
+        ALOGW("Failed to increase socket buffer size to %d.  (errno %d)",
+              buf_size, errno);
+    }
+
+    buf_size = 0;
+    opt_size = sizeof(buf_size);
+    res = getsockopt(sock_fd_,
+                     SOL_SOCKET, SO_RCVBUF,
+                     &buf_size, &opt_size);
+    if (res < 0) {
+        ALOGW("Failed to fetch socket buffer size.  (errno %d)", errno);
+    } else {
+        ALOGI("RX socket buffer size is now %d bytes",  buf_size);
+    }
+
+    if (listen_addr_.sin_addr.s_addr) {
+        // Join the multicast group and we should be good to go.
+        struct ip_mreq mreq;
+        mreq.imr_multiaddr = listen_addr_.sin_addr;
+        mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+        res = setsockopt(sock_fd_,
+                         IPPROTO_IP,
+                         IP_ADD_MEMBERSHIP,
+                         &mreq, sizeof(mreq));
+        if (res < 0) {
+            ALOGE("Failed to join multicast group. (errno %d)", errno);
+            goto bailout;
+        }
+        multicast_joined_ = true;
+    }
+
+    return true;
+
+bailout:
+    cleanupSocket();
+    return false;
+}
+
+bool AAH_RXPlayer::threadLoop() {
+    struct pollfd poll_fds[2];
+    bool process_more_right_now = false;
+
+    if (!setupSocket()) {
+        sendEvent(MEDIA_ERROR);
+        goto bailout;
+    }
+
+    while (!thread_wrapper_->exitPending()) {
+        // Step 1: Wait until there is something to do.
+        int gap_timeout = computeNextGapRetransmitTimeout();
+        int ring_timeout = ring_buffer_.computeInactivityTimeout();
+        int timeout = -1;
+
+        if (!ring_timeout) {
+            ALOGW("RTP inactivity timeout reached, resetting pipeline.");
+            resetPipeline();
+            timeout = gap_timeout;
+        } else {
+            if (gap_timeout < 0) {
+                timeout = ring_timeout;
+            } else if (ring_timeout < 0) {
+                timeout = gap_timeout;
+            } else {
+                timeout = (gap_timeout < ring_timeout) ? gap_timeout
+                                                       : ring_timeout;
+            }
+        }
+
+        if ((0 != timeout) && (!process_more_right_now)) {
+            // Set up the events to wait on.  Start with the wakeup pipe.
+            memset(&poll_fds, 0, sizeof(poll_fds));
+            poll_fds[0].fd     = wakeup_work_thread_evt_.getWakeupHandle();
+            poll_fds[0].events = POLLIN;
+
+            // Add the RX socket.
+            poll_fds[1].fd     = sock_fd_;
+            poll_fds[1].events = POLLIN;
+
+            // Wait for something interesing to happen.
+            int poll_res = poll(poll_fds, NELEM(poll_fds), timeout);
+            if (poll_res < 0) {
+                ALOGE("Fatal error (%d,%d) while waiting on events",
+                      poll_res, errno);
+                sendEvent(MEDIA_ERROR);
+                goto bailout;
+            }
+        }
+
+        if (thread_wrapper_->exitPending()) {
+            break;
+        }
+
+        wakeup_work_thread_evt_.clearPendingEvents();
+        process_more_right_now = false;
+
+        // Step 2: Do we have data waiting in the socket?  If so, drain the
+        // socket moving valid RTP information into the ring buffer to be
+        // processed.
+        if (poll_fds[1].revents) {
+            struct sockaddr_in from;
+            socklen_t from_len;
+
+            ssize_t res = 0;
+            while (!thread_wrapper_->exitPending()) {
+                // Check the size of any pending packet.
+                res = recv(sock_fd_, NULL, 0, MSG_PEEK | MSG_TRUNC);
+
+                // Error?
+                if (res < 0) {
+                    // If the error is anything other than would block,
+                    // something has gone very wrong.
+                    if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
+                        ALOGE("Fatal socket error during recvfrom (%d, %d)",
+                              (int)res, errno);
+                        goto bailout;
+                    }
+
+                    // Socket is out of data, just break out of processing and
+                    // wait for more.
+                    break;
+                }
+
+                // Allocate a payload.
+                PacketBuffer* pb = PacketBuffer::allocate(res);
+                if (NULL == pb) {
+                    ALOGE("Fatal error, failed to allocate packet buffer of"
+                          " length %u", static_cast<uint32_t>(res));
+                    goto bailout;
+                }
+
+                // Fetch the data.
+                from_len = sizeof(from);
+                res = recvfrom(sock_fd_, pb->data_, pb->length_, 0,
+                               reinterpret_cast<struct sockaddr*>(&from),
+                               &from_len);
+                if (res != pb->length_) {
+                    ALOGE("Fatal error, fetched packet length (%d) does not"
+                          " match peeked packet length (%u).  This should never"
+                          " happen.  (errno = %d)",
+                          static_cast<int>(res),
+                          static_cast<uint32_t>(pb->length_),
+                          errno);
+                }
+
+                bool drop_packet = false;
+                if (transmitter_known_) {
+                    if (from.sin_addr.s_addr !=
+                        transmitter_addr_.sin_addr.s_addr) {
+                        uint32_t a = ntohl(from.sin_addr.s_addr);
+                        uint16_t p = ntohs(from.sin_port);
+                        ALOGV("Dropping packet from unknown transmitter"
+                              " %u.%u.%u.%u:%hu",
+                              ((a >> 24) & 0xFF),
+                              ((a >> 16) & 0xFF),
+                              ((a >>  8) & 0xFF),
+                              ( a        & 0xFF),
+                              p);
+
+                        drop_packet = true;
+                    } else {
+                        transmitter_addr_.sin_port = from.sin_port;
+                    }
+                } else {
+                    memcpy(&transmitter_addr_, &from, sizeof(from));
+                    transmitter_known_ = true;
+                }
+
+                if (!drop_packet) {
+                    bool serious_error = !processRX(pb);
+
+                    if (serious_error) {
+                        // Something went "seriously wrong".  Currently, the
+                        // only trigger for this should be a ring buffer
+                        // overflow.  The current failsafe behavior for when
+                        // something goes seriously wrong is to just reset the
+                        // pipeline.  The system should behave as if this
+                        // AAH_RXPlayer was just set up for the first time.
+                        ALOGE("Something just went seriously wrong with the"
+                              " pipeline.  Resetting.");
+                        resetPipeline();
+                    }
+                } else {
+                    PacketBuffer::destroy(pb);
+                }
+            }
+        }
+
+        // Step 3: Process any data we mave have accumulated in the ring buffer
+        // so far.
+        if (!thread_wrapper_->exitPending()) {
+            processRingBuffer();
+        }
+
+        // Step 4: At this point in time, the ring buffer should either be
+        // empty, or stalled in front of a gap caused by some dropped packets.
+        // Check on the current gap situation and deal with it in an appropriate
+        // fashion.  If processGaps returns true, it means that it has given up
+        // on a gap and that we should try to process some more data
+        // immediately.
+        if (!thread_wrapper_->exitPending()) {
+            process_more_right_now = processGaps();
+        }
+
+        // Step 5: Check for fatal errors.  If any of our substreams has
+        // encountered a fatal, unrecoverable, error, then propagate the error
+        // up to user level and shut down.
+        for (size_t i = 0; i < substreams_.size(); ++i) {
+            status_t status;
+            CHECK(substreams_.valueAt(i) != NULL);
+
+            status = substreams_.valueAt(i)->getStatus();
+            if (OK != status) {
+                ALOGE("Substream index %d has encountered an unrecoverable"
+                      " error (%d).  Signalling application level and shutting"
+                      " down.", i, status);
+                sendEvent(MEDIA_ERROR);
+                goto bailout;
+            }
+        }
+    }
+
+bailout:
+    cleanupSocket();
+    return false;
+}
+
+bool AAH_RXPlayer::processRX(PacketBuffer* pb) {
+    CHECK(NULL != pb);
+
+    uint8_t* data = pb->data_;
+    ssize_t  amt  = pb->length_;
+    uint32_t nak_magic;
+    uint16_t seq_no;
+    uint32_t epoch;
+
+    // Every packet either starts with an RTP header which is at least 12 bytes
+    // long or is a retry NAK which is 14 bytes long.  If there are fewer than
+    // 12 bytes here, this cannot be a proper RTP packet.
+    if (amt < 12) {
+        ALOGV("Dropping packet, too short to contain RTP header (%u bytes)",
+              static_cast<uint32_t>(amt));
+        goto drop_packet;
+    }
+
+    // Check to see if this is the special case of a NAK packet.
+    nak_magic = ntohl(*(reinterpret_cast<uint32_t*>(data)));
+    if (nak_magic == kRetransNAKMagic) {
+        // Looks like a NAK packet; make sure its long enough.
+
+        if (amt < static_cast<ssize_t>(sizeof(RetransRequest))) {
+            ALOGV("Dropping packet, too short to contain NAK payload (%u bytes)",
+                  static_cast<uint32_t>(amt));
+            goto drop_packet;
+        }
+
+        SeqNoGap gap;
+        RetransRequest* rtr = reinterpret_cast<RetransRequest*>(data);
+        gap.start_seq_ = ntohs(rtr->start_seq_);
+        gap.end_seq_   = ntohs(rtr->end_seq_);
+
+        ALOGV("Process NAK for gap at [%hu, %hu]", gap.start_seq_, gap.end_seq_);
+        ring_buffer_.processNAK(&gap);
+
+        return true;
+    }
+
+    // According to the TRTP spec, version should be 2, padding should be 0,
+    // extension should be 0 and CSRCCnt should be 0.  If any of these tests
+    // fail, we chuck the packet.
+    if (data[0] != 0x80) {
+        ALOGV("Dropping packet, bad V/P/X/CSRCCnt field (0x%02x)",
+              data[0]);
+        goto drop_packet;
+    }
+
+    // Check the payload type.  For TRTP, it should always be 100.
+    if ((data[1] & 0x7F) != 100) {
+        ALOGV("Dropping packet, bad payload type. (%u)",
+              data[1] & 0x7F);
+        goto drop_packet;
+    }
+
+    // Check whether the transmitter has begun a new epoch.
+    epoch = (U32_AT(data + 8) >> 10) & 0x3FFFFF;
+    if (current_epoch_known_) {
+        if (epoch != current_epoch_) {
+            ALOGV("%s: new epoch %u", __PRETTY_FUNCTION__, epoch);
+            current_epoch_ = epoch;
+            resetPipeline();
+        }
+    } else {
+        current_epoch_ = epoch;
+        current_epoch_known_ = true;
+    }
+
+    // Extract the sequence number and hand the packet off to the ring buffer
+    // for dropped packet detection and later processing.
+    seq_no = U16_AT(data + 2);
+    return ring_buffer_.pushBuffer(pb, seq_no);
+
+drop_packet:
+    PacketBuffer::destroy(pb);
+    return true;
+}
+
+void AAH_RXPlayer::processRingBuffer() {
+    PacketBuffer* pb;
+    bool is_discon;
+    sp<Substream> substream;
+    LinearTransform trans;
+    bool foundTrans = false;
+
+    while (NULL != (pb = ring_buffer_.fetchBuffer(&is_discon))) {
+        if (is_discon) {
+            // Abort all partially assembled payloads.
+            for (size_t i = 0; i < substreams_.size(); ++i) {
+                CHECK(substreams_.valueAt(i) != NULL);
+                substreams_.valueAt(i)->cleanupBufferInProgress();
+            }
+        }
+
+        uint8_t* data = pb->data_;
+        ssize_t  amt  = pb->length_;
+
+        // Should not have any non-RTP packets in the ring buffer.  RTP packets
+        // must be at least 12 bytes long.
+        CHECK(amt >= 12);
+
+        // Extract the marker bit and the SSRC field.
+        bool     marker = (data[1] & 0x80) != 0;
+        uint32_t ssrc   = U32_AT(data + 8);
+
+        // Is this the start of a new TRTP payload?  If so, the marker bit
+        // should be set and there are some things we should be checking for.
+        if (marker) {
+            // TRTP headers need to have at least a byte for version, a byte for
+            // payload type and flags, and 4 bytes for length.
+            if (amt < 18) {
+                ALOGV("Dropping packet, too short to contain TRTP header"
+                      " (%u bytes)", static_cast<uint32_t>(amt));
+                goto process_next_packet;
+            }
+
+            // Check the TRTP version and extract the payload type/flags.
+            uint8_t trtp_version =  data[12];
+            uint8_t payload_type = (data[13] >> 4) & 0xF;
+            uint8_t trtp_flags   =  data[13]       & 0xF;
+
+            if (1 != trtp_version) {
+                ALOGV("Dropping packet, bad trtp version %hhu", trtp_version);
+                goto process_next_packet;
+            }
+
+            // Is there a timestamp transformation present on this packet?  If
+            // so, extract it and pass it to the appropriate substreams.
+            if (trtp_flags & 0x02) {
+                ssize_t offset = 18 + ((trtp_flags & 0x01) ? 4 : 0);
+                if (amt < (offset + 24)) {
+                    ALOGV("Dropping packet, too short to contain TRTP Timestamp"
+                          " Transformation (%u bytes)",
+                          static_cast<uint32_t>(amt));
+                    goto process_next_packet;
+                }
+
+                trans.a_zero = fetchInt64(data + offset);
+                trans.b_zero = fetchInt64(data + offset + 16);
+                trans.a_to_b_numer = static_cast<int32_t>(
+                        fetchInt32 (data + offset + 8));
+                trans.a_to_b_denom = U32_AT(data + offset + 12);
+                foundTrans = true;
+
+                uint32_t program_id = (ssrc >> 5) & 0x1F;
+                for (size_t i = 0; i < substreams_.size(); ++i) {
+                    sp<Substream> iter = substreams_.valueAt(i);
+                    CHECK(iter != NULL);
+
+                    if (iter->getProgramID() == program_id) {
+                        iter->processTSTransform(trans);
+                    }
+                }
+            }
+
+            // Is this a command packet?  If so, its not necessarily associate
+            // with one particular substream.  Just give it to the command
+            // packet handler and then move on.
+            if (4 == payload_type) {
+                processCommandPacket(pb);
+                goto process_next_packet;
+            }
+        }
+
+        // If we got to here, then we are a normal packet.  Find (or allocate)
+        // the substream we belong to and send the packet off to be processed.
+        substream = substreams_.valueFor(ssrc);
+        if (substream == NULL) {
+            substream = new Substream(ssrc, omx_);
+            if (substream == NULL) {
+                ALOGE("Failed to allocate substream for SSRC 0x%08x", ssrc);
+                goto process_next_packet;
+            }
+            substreams_.add(ssrc, substream);
+
+            if (foundTrans) {
+                substream->processTSTransform(trans);
+            }
+        }
+
+        CHECK(substream != NULL);
+
+        if (marker) {
+            // Start of a new TRTP payload for this substream.  Extract the
+            // lower 32 bits of the timestamp and hand the buffer to the
+            // substream for processing.
+            uint32_t ts_lower = U32_AT(data + 4);
+            substream->processPayloadStart(data + 12, amt - 12, ts_lower);
+        } else {
+            // Continuation of an existing TRTP payload.  Just hand it off to
+            // the substream for processing.
+            substream->processPayloadCont(data + 12, amt - 12);
+        }
+
+process_next_packet:
+        PacketBuffer::destroy(pb);
+    }  // end of main processing while loop.
+}
+
+void AAH_RXPlayer::processCommandPacket(PacketBuffer* pb) {
+    CHECK(NULL != pb);
+
+    uint8_t* data = pb->data_;
+    ssize_t  amt  = pb->length_;
+
+    // verify that this packet meets the minimum length of a command packet
+    if (amt < 20) {
+        return;
+    }
+
+    uint8_t trtp_version =  data[12];
+    uint8_t trtp_flags   =  data[13]       & 0xF;
+
+    if (1 != trtp_version) {
+        ALOGV("Dropping packet, bad trtp version %hhu", trtp_version);
+        return;
+    }
+
+    // calculate the start of the command payload
+    ssize_t offset = 18;
+    if (trtp_flags & 0x01) {
+        // timestamp is present (4 bytes)
+        offset += 4;
+    }
+    if (trtp_flags & 0x02) {
+        // transform is present (24 bytes)
+        offset += 24;
+    }
+
+    // the packet must contain 2 bytes of command payload beyond the TRTP header
+    if (amt < offset + 2) {
+        return;
+    }
+
+    uint16_t command_id = U16_AT(data + offset);
+
+    switch (command_id) {
+        case TRTPControlPacket::kCommandNop:
+            break;
+
+        case TRTPControlPacket::kCommandEOS:
+        case TRTPControlPacket::kCommandFlush: {
+            uint16_t program_id = (U32_AT(data + 8) >> 5) & 0x1F;
+            ALOGI("*** %s flushing program_id=%d",
+                  __PRETTY_FUNCTION__, program_id);
+
+            Vector<uint32_t> substreams_to_remove;
+            for (size_t i = 0; i < substreams_.size(); ++i) {
+                sp<Substream> iter = substreams_.valueAt(i);
+                if (iter->getProgramID() == program_id) {
+                    iter->shutdown();
+                    substreams_to_remove.add(iter->getSSRC());
+                }
+            }
+
+            for (size_t i = 0; i < substreams_to_remove.size(); ++i) {
+                substreams_.removeItem(substreams_to_remove[i]);
+            }
+        } break;
+    }
+}
+
+bool AAH_RXPlayer::processGaps() {
+    // Deal with the current gap situation.  Specifically...
+    //
+    // 1) If a new gap has shown up, send a retransmit request to the
+    //    transmitter.
+    // 2) If a gap we were working on has had a packet in the middle or at
+    //    the end filled in, send another retransmit request for the begining
+    //    portion of the gap.  TRTP was designed for LANs where packet
+    //    re-ordering is very unlikely; so see the middle or end of a gap
+    //    filled in before the begining is an almost certain indication that
+    //    a retransmission packet was also dropped.
+    // 3) If we have been working on a gap for a while and it still has not
+    //    been filled in, send another retransmit request.
+    // 4) If the are no more gaps in the ring, clear the current_gap_status_
+    //    flag to indicate that all is well again.
+
+    // Start by fetching the active gap status.
+    SeqNoGap gap;
+    bool send_retransmit_request = false;
+    bool ret_val = false;
+    GapStatus gap_status;
+    if (kGS_NoGap != (gap_status = ring_buffer_.fetchCurrentGap(&gap))) {
+        // Note: checking for a change in the end sequence number should cover
+        // moving on to an entirely new gap for case #1 as well as resending the
+        // begining of a gap range for case #2.
+        send_retransmit_request = (kGS_NoGap == current_gap_status_) ||
+                                  (current_gap_.end_seq_ != gap.end_seq_);
+
+        // If this is the same gap we have been working on, and it has timed
+        // out, then check to see if our substreams are about to underflow.  If
+        // so, instead of sending another retransmit request, just give up on
+        // this gap and move on.
+        if (!send_retransmit_request &&
+           (kGS_NoGap != current_gap_status_) &&
+           (0 == computeNextGapRetransmitTimeout())) {
+
+            // If out current gap is the fast-start gap, don't bother to skip it
+            // because substreams look like the are about to underflow.
+            if ((kGS_FastStartGap != gap_status) ||
+                (current_gap_.end_seq_ != gap.end_seq_)) {
+                for (size_t i = 0; i < substreams_.size(); ++i) {
+                    if (substreams_.valueAt(i)->isAboutToUnderflow()) {
+                        ALOGV("About to underflow, giving up on gap [%hu, %hu]",
+                              gap.start_seq_, gap.end_seq_);
+                        ring_buffer_.processNAK();
+                        current_gap_status_ = kGS_NoGap;
+                        return true;
+                    }
+                }
+            }
+
+            // Looks like no one is about to underflow.  Just go ahead and send
+            // the request.
+            send_retransmit_request = true;
+        }
+    } else {
+        current_gap_status_ = kGS_NoGap;
+    }
+
+    if (send_retransmit_request) {
+        // If we have been working on a fast start, and it is still not filled
+        // in, even after the extended retransmit time out, give up and skip it.
+        // The system should fall back into its normal slow-start behavior.
+        if ((kGS_FastStartGap == current_gap_status_) &&
+            (current_gap_.end_seq_ == gap.end_seq_)) {
+            ALOGV("Fast start is taking forever; giving up.");
+            ring_buffer_.processNAK();
+            current_gap_status_ = kGS_NoGap;
+            return true;
+        }
+
+        // Send the request.
+        RetransRequest req;
+        uint32_t magic  = (kGS_FastStartGap == gap_status)
+                        ? kFastStartRequestMagic
+                        : kRetransRequestMagic;
+        req.magic_      = htonl(magic);
+        req.mcast_ip_   = listen_addr_.sin_addr.s_addr;
+        req.mcast_port_ = listen_addr_.sin_port;
+        req.start_seq_  = htons(gap.start_seq_);
+        req.end_seq_    = htons(gap.end_seq_);
+
+        {
+            uint32_t a = ntohl(transmitter_addr_.sin_addr.s_addr);
+            uint16_t p = ntohs(transmitter_addr_.sin_port);
+            ALOGV("Sending to transmitter %u.%u.%u.%u:%hu",
+                    ((a >> 24) & 0xFF),
+                    ((a >> 16) & 0xFF),
+                    ((a >>  8) & 0xFF),
+                    ( a        & 0xFF),
+                    p);
+        }
+
+        int res = sendto(sock_fd_, &req, sizeof(req), 0,
+                         reinterpret_cast<struct sockaddr*>(&transmitter_addr_),
+                         sizeof(transmitter_addr_));
+        if (res < 0) {
+            ALOGE("Error when sending retransmit request (%d)", errno);
+        } else {
+            ALOGV("%s request for range [%hu, %hu] sent",
+                  (kGS_FastStartGap == gap_status) ? "Fast Start" : "Retransmit",
+                  gap.start_seq_, gap.end_seq_);
+        }
+
+        // Update the current gap info.
+        current_gap_ = gap;
+        current_gap_status_ = gap_status;
+        next_retrans_req_time_ = monotonicUSecNow() +
+                               ((kGS_FastStartGap == current_gap_status_)
+                                ? kFastStartTimeoutUSec
+                                : kGapRerequestTimeoutUSec);
+    }
+
+    return false;
+}
+
+// Compute when its time to send the next gap retransmission in milliseconds.
+// Returns < 0 for an infinite timeout (no gap) and 0 if its time to retransmit
+// right now.
+int AAH_RXPlayer::computeNextGapRetransmitTimeout() {
+    if (kGS_NoGap == current_gap_status_) {
+        return -1;
+    }
+
+    int64_t timeout_delta = next_retrans_req_time_ - monotonicUSecNow();
+
+    timeout_delta /= 1000;
+    if (timeout_delta <= 0) {
+        return 0;
+    }
+
+    return static_cast<uint32_t>(timeout_delta);
+}
+
+}  // namespace android
diff --git a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp
new file mode 100644
index 0000000..0d8b31f
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp
@@ -0,0 +1,366 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "aah_rx_player.h"
+
+namespace android {
+
+AAH_RXPlayer::RXRingBuffer::RXRingBuffer(uint32_t capacity) {
+    capacity_ = capacity;
+    rd_ = wr_ = 0;
+    ring_ = new PacketBuffer*[capacity];
+    memset(ring_, 0, sizeof(PacketBuffer*) * capacity);
+    reset();
+}
+
+AAH_RXPlayer::RXRingBuffer::~RXRingBuffer() {
+    reset();
+    delete[] ring_;
+}
+
+void AAH_RXPlayer::RXRingBuffer::reset() {
+    AutoMutex lock(&lock_);
+
+    if (NULL != ring_) {
+        while (rd_ != wr_) {
+            CHECK(rd_ < capacity_);
+            if (NULL != ring_[rd_]) {
+                PacketBuffer::destroy(ring_[rd_]);
+                ring_[rd_] = NULL;
+            }
+            rd_ = (rd_ + 1) % capacity_;
+        }
+    }
+
+    rd_ = wr_ = 0;
+    rd_seq_known_ = false;
+    waiting_for_fast_start_ = true;
+    fetched_first_packet_ = false;
+    rtp_activity_timeout_valid_ = false;
+}
+
+bool AAH_RXPlayer::RXRingBuffer::pushBuffer(PacketBuffer* buf,
+                                                uint16_t seq) {
+    AutoMutex lock(&lock_);
+    CHECK(NULL != ring_);
+    CHECK(NULL != buf);
+
+    rtp_activity_timeout_valid_ = true;
+    rtp_activity_timeout_ = monotonicUSecNow() + kRTPActivityTimeoutUSec;
+
+    // If the ring buffer is totally reset (we have never received a single
+    // payload) then we don't know the rd sequence number and this should be
+    // simple.  We just store the payload, advance the wr pointer and record the
+    // initial sequence number.
+    if (!rd_seq_known_) {
+        CHECK(rd_ == wr_);
+        CHECK(NULL == ring_[wr_]);
+        CHECK(wr_ < capacity_);
+
+        ring_[wr_] = buf;
+        wr_ = (wr_ + 1) % capacity_;
+        rd_seq_ = seq;
+        rd_seq_known_ = true;
+        return true;
+    }
+
+    // Compute the seqence number of this payload and of the write pointer,
+    // normalized around the read pointer.  IOW - transform the payload seq no
+    // and the wr pointer seq no into a space where the rd pointer seq no is
+    // zero.  This will define 4 cases we can consider...
+    //
+    // 1) norm_seq == norm_wr_seq
+    //    This payload is contiguous with the last.  All is good.
+    //
+    // 2)  ((norm_seq <  norm_wr_seq) && (norm_seq >= norm_rd_seq)
+    // aka ((norm_seq <  norm_wr_seq) && (norm_seq >= 0)
+    //    This payload is in the past, in the unprocessed region of the ring
+    //    buffer.  It is probably a retransmit intended to fill in a dropped
+    //    payload; it may be a duplicate.
+    //
+    // 3) ((norm_seq - norm_wr_seq) & 0x8000) != 0
+    //    This payload is in the past compared to the write pointer (or so very
+    //    far in the future that it has wrapped the seq no space), but not in
+    //    the unprocessed region of the ring buffer.  This could be a duplicate
+    //    retransmit; we just drop these payloads unless we are waiting for our
+    //    first fast start packet.  If we are waiting for fast start, than this
+    //    packet is probably the first packet of the fast start retransmission.
+    //    If it will fit in the buffer, back up the read pointer to its position
+    //    and clear the fast start flag, otherwise just drop it.
+    //
+    // 4) ((norm_seq - norm_wr_seq) & 0x8000) == 0
+    //    This payload which is ahead of the next write pointer.  This indicates
+    //    that we have missed some payloads and need to request a retransmit.
+    //    If norm_seq >= (capacity - 1), then the gap is so large that it would
+    //    overflow the ring buffer and we should probably start to panic.
+
+    uint16_t norm_wr_seq = ((wr_ + capacity_ - rd_) % capacity_);
+    uint16_t norm_seq    = seq - rd_seq_;
+
+    // Check for overflow first.
+    if ((!(norm_seq & 0x8000)) && (norm_seq >= (capacity_ - 1))) {
+        ALOGW("Ring buffer overflow; cap = %u, [rd, wr] = [%hu, %hu], seq = %hu",
+              capacity_, rd_seq_, norm_wr_seq + rd_seq_, seq);
+        PacketBuffer::destroy(buf);
+        return false;
+    }
+
+    // Check for case #1
+    if (norm_seq == norm_wr_seq) {
+        CHECK(wr_ < capacity_);
+        CHECK(NULL == ring_[wr_]);
+
+        ring_[wr_] = buf;
+        wr_ = (wr_ + 1) % capacity_;
+
+        CHECK(wr_ != rd_);
+        return true;
+    }
+
+    // Check case #2
+    uint32_t ring_pos = (rd_ + norm_seq) % capacity_;
+    if ((norm_seq < norm_wr_seq) && (!(norm_seq & 0x8000))) {
+        // Do we already have a payload for this slot?  If so, then this looks
+        // like a duplicate retransmit.  Just ignore it.
+        if (NULL != ring_[ring_pos]) {
+            ALOGD("RXed duplicate retransmit, seq = %hu", seq);
+            PacketBuffer::destroy(buf);
+        } else {
+            // Looks like we were missing this payload.  Go ahead and store it.
+            ring_[ring_pos] = buf;
+        }
+
+        return true;
+    }
+
+    // Check case #3
+    if ((norm_seq - norm_wr_seq) & 0x8000) {
+        if (!waiting_for_fast_start_) {
+            ALOGD("RXed duplicate retransmit from before rd pointer, seq = %hu",
+                  seq);
+            PacketBuffer::destroy(buf);
+        } else {
+            // Looks like a fast start fill-in.  Go ahead and store it, assuming
+            // that we can fit it in the buffer.
+            uint32_t implied_ring_size = static_cast<uint32_t>(norm_wr_seq)
+                                       + (rd_seq_ - seq);
+
+            if (implied_ring_size >= (capacity_ - 1)) {
+                ALOGD("RXed what looks like a fast start packet (seq = %hu),"
+                      " but packet is too far in the past to fit into the ring"
+                      "  buffer.  Dropping.", seq);
+                PacketBuffer::destroy(buf);
+            } else {
+                ring_pos = (rd_ + capacity_ + seq - rd_seq_) % capacity_;
+                rd_seq_ = seq;
+                rd_ = ring_pos;
+                waiting_for_fast_start_ = false;
+
+                CHECK(ring_pos < capacity_);
+                CHECK(NULL == ring_[ring_pos]);
+                ring_[ring_pos] = buf;
+            }
+
+        }
+        return true;
+    }
+
+    // Must be in case #4 with no overflow.  This packet fits in the current
+    // ring buffer, but is discontiuguous.  Advance the write pointer leaving a
+    // gap behind.
+    uint32_t gap_len = (ring_pos + capacity_ - wr_) % capacity_;
+    ALOGD("Drop detected; %u packets, seq_range [%hu, %hu]",
+          gap_len,
+          rd_seq_ + norm_wr_seq,
+          rd_seq_ + norm_wr_seq + gap_len - 1);
+
+    CHECK(NULL == ring_[ring_pos]);
+    ring_[ring_pos] = buf;
+    wr_ = (ring_pos + 1) % capacity_;
+    CHECK(wr_ != rd_);
+
+    return true;
+}
+
+AAH_RXPlayer::PacketBuffer*
+AAH_RXPlayer::RXRingBuffer::fetchBuffer(bool* is_discon) {
+    AutoMutex lock(&lock_);
+    CHECK(NULL != ring_);
+    CHECK(NULL != is_discon);
+
+    // If the read seqence number is not known, then this ring buffer has not
+    // received a packet since being reset and there cannot be any packets to
+    // return.  If we are still waiting for the first fast start packet to show
+    // up, we don't want to let any buffer be consumed yet because we expect to
+    // see a packet before the initial read sequence number show up shortly.
+    if (!rd_seq_known_ || waiting_for_fast_start_) {
+        *is_discon = false;
+        return NULL;
+    }
+
+    PacketBuffer* ret = NULL;
+    *is_discon = !fetched_first_packet_;
+
+    while ((rd_ != wr_) && (NULL == ret)) {
+        CHECK(rd_ < capacity_);
+
+        // If we hit a gap, stall and do not advance the read pointer.  Let the
+        // higher level code deal with requesting retries and/or deciding to
+        // skip the current gap.
+        ret = ring_[rd_];
+        if (NULL == ret) {
+            break;
+        }
+
+        ring_[rd_] = NULL;
+        rd_ = (rd_ + 1) % capacity_;
+        ++rd_seq_;
+    }
+
+    if (NULL != ret) {
+        fetched_first_packet_ = true;
+    }
+
+    return ret;
+}
+
+AAH_RXPlayer::GapStatus
+AAH_RXPlayer::RXRingBuffer::fetchCurrentGap(SeqNoGap* gap) {
+    AutoMutex lock(&lock_);
+    CHECK(NULL != ring_);
+    CHECK(NULL != gap);
+
+    // If the read seqence number is not known, then this ring buffer has not
+    // received a packet since being reset and there cannot be any gaps.
+    if (!rd_seq_known_) {
+        return kGS_NoGap;
+    }
+
+    // If we are waiting for fast start, then the current gap is a fast start
+    // gap and it includes all packets before the read sequence number.
+    if (waiting_for_fast_start_) {
+        gap->start_seq_ =
+        gap->end_seq_   = rd_seq_ - 1;
+        return kGS_FastStartGap;
+    }
+
+    // If rd == wr, then the buffer is empty and there cannot be any gaps.
+    if (rd_ == wr_) {
+        return kGS_NoGap;
+    }
+
+    // If rd_ is currently pointing at an unprocessed packet, then there is no
+    // current gap.
+    CHECK(rd_ < capacity_);
+    if (NULL != ring_[rd_]) {
+        return kGS_NoGap;
+    }
+
+    // Looks like there must be a gap here.  The start of the gap is the current
+    // rd sequence number, all we need to do now is determine its length in
+    // order to compute the end sequence number.
+    gap->start_seq_ = rd_seq_;
+    uint16_t end = rd_seq_;
+    uint32_t tmp = (rd_ + 1) % capacity_;
+    while ((tmp != wr_) && (NULL == ring_[tmp])) {
+        ++end;
+        tmp = (tmp + 1) % capacity_;
+    }
+    gap->end_seq_ = end;
+
+    return kGS_NormalGap;
+}
+
+void AAH_RXPlayer::RXRingBuffer::processNAK(const SeqNoGap* nak) {
+    AutoMutex lock(&lock_);
+    CHECK(NULL != ring_);
+
+    // If we were waiting for our first fast start fill-in packet, and we
+    // received a NAK, then apparantly we are not getting our fast start.  Just
+    // clear the waiting flag and go back to normal behavior.
+    if (waiting_for_fast_start_) {
+        waiting_for_fast_start_ = false;
+    }
+
+    // If we have not received a packet since last reset, or there is no data in
+    // the ring, then there is nothing to skip.
+    if ((!rd_seq_known_) || (rd_ == wr_)) {
+        return;
+    }
+
+    // If rd_ is currently pointing at an unprocessed packet, then there is no
+    // gap to skip.
+    CHECK(rd_ < capacity_);
+    if (NULL != ring_[rd_]) {
+        return;
+    }
+
+    // Looks like there must be a gap here.  Advance rd until we have passed
+    // over the portion of it indicated by nak (or all of the gap if nak is
+    // NULL).  Then reset fetched_first_packet_ so that the next read will show
+    // up as being discontiguous.
+    uint16_t seq_after_gap = (NULL == nak) ? 0 : nak->end_seq_ + 1;
+    while ((rd_ != wr_) &&
+           (NULL == ring_[rd_]) &&
+          ((NULL == nak) || (seq_after_gap != rd_seq_))) {
+        rd_ = (rd_ + 1) % capacity_;
+        ++rd_seq_;
+    }
+    fetched_first_packet_ = false;
+}
+
+int AAH_RXPlayer::RXRingBuffer::computeInactivityTimeout() {
+    AutoMutex lock(&lock_);
+
+    if (!rtp_activity_timeout_valid_) {
+        return -1;
+    }
+
+    uint64_t now = monotonicUSecNow();
+    if (rtp_activity_timeout_ <= now) {
+        return 0;
+    }
+
+    return (rtp_activity_timeout_ - now) / 1000;
+}
+
+AAH_RXPlayer::PacketBuffer*
+AAH_RXPlayer::PacketBuffer::allocate(ssize_t length) {
+    if (length <= 0) {
+        return NULL;
+    }
+
+    uint32_t alloc_len = sizeof(PacketBuffer) + length;
+    PacketBuffer* ret = reinterpret_cast<PacketBuffer*>(
+                        new uint8_t[alloc_len]);
+
+    if (NULL != ret) {
+        ret->length_ = length;
+    }
+
+    return ret;
+}
+
+void AAH_RXPlayer::PacketBuffer::destroy(PacketBuffer* pb) {
+    uint8_t* kill_me = reinterpret_cast<uint8_t*>(pb);
+    delete[] kill_me;
+}
+
+}  // namespace android
diff --git a/media/libaah_rtp/aah_rx_player_substream.cpp b/media/libaah_rtp/aah_rx_player_substream.cpp
new file mode 100644
index 0000000..1e4c784
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player_substream.cpp
@@ -0,0 +1,498 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include <include/avc_utils.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/Utils.h>
+
+#include "aah_rx_player.h"
+
+namespace android {
+
+int64_t AAH_RXPlayer::Substream::kAboutToUnderflowThreshold =
+    50ull * 1000;
+
+AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) {
+    ssrc_ = ssrc;
+    substream_details_known_ = false;
+    buffer_in_progress_ = NULL;
+    status_ = OK;
+
+    decoder_ = new AAH_DecoderPump(omx);
+    if (decoder_ == NULL) {
+        ALOGE("%s failed to allocate decoder pump!", __PRETTY_FUNCTION__);
+    }
+    if (OK != decoder_->initCheck()) {
+        ALOGE("%s failed to initialize decoder pump!", __PRETTY_FUNCTION__);
+    }
+
+    // cleanupBufferInProgress will reset most of the internal state variables.
+    // Just need to make sure that buffer_in_progress_ is NULL before calling.
+    cleanupBufferInProgress();
+}
+
+
+void AAH_RXPlayer::Substream::shutdown() {
+    substream_meta_ = NULL;
+    status_ = OK;
+    cleanupBufferInProgress();
+    cleanupDecoder();
+}
+
+void AAH_RXPlayer::Substream::cleanupBufferInProgress() {
+    if (NULL != buffer_in_progress_) {
+        buffer_in_progress_->release();
+        buffer_in_progress_ = NULL;
+    }
+
+    expected_buffer_size_ = 0;
+    buffer_filled_ = 0;
+    waiting_for_rap_ = true;
+}
+
+void AAH_RXPlayer::Substream::cleanupDecoder() {
+    if (decoder_ != NULL) {
+        decoder_->shutdown();
+    }
+}
+
+bool AAH_RXPlayer::Substream::shouldAbort(const char* log_tag) {
+    // If we have already encountered a fatal error, do nothing.  We are just
+    // waiting for our owner to shut us down now.
+    if (OK != status_) {
+        ALOGV("Skipping %s, substream has encountered fatal error (%d).",
+                log_tag, status_);
+        return true;
+    }
+
+    return false;
+}
+
+void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf,
+                                                  uint32_t amt,
+                                                  int32_t ts_lower) {
+    uint32_t min_length = 6;
+
+    if (shouldAbort(__PRETTY_FUNCTION__)) {
+        return;
+    }
+
+    // Do we have a buffer in progress already?  If so, abort the buffer.  In
+    // theory, this should never happen.  If there were a discontinutity in the
+    // stream, the discon in the seq_nos at the RTP level should have already
+    // triggered a cleanup of the buffer in progress.  To see a problem at this
+    // level is an indication either of a bug in the transmitter, or some form
+    // of terrible corruption/tampering on the wire.
+    if (NULL != buffer_in_progress_) {
+        ALOGE("processPayloadStart is aborting payload already in progress.");
+        cleanupBufferInProgress();
+    }
+
+    // Parse enough of the header to know where we stand.  Since this is a
+    // payload start, it should begin with a TRTP header which has to be at
+    // least 6 bytes long.
+    if (amt < min_length) {
+        ALOGV("Discarding payload too short to contain TRTP header (len = %u)",
+                amt);
+        return;
+    }
+
+    // Check the TRTP version number.
+    if (0x01 != buf[0]) {
+        ALOGV("Unexpected TRTP version (%u) in header.  Expected %u.",
+                buf[0], 1);
+        return;
+    }
+
+    // Extract the substream type field and make sure its one we understand (and
+    // one that does not conflict with any previously received substream type.
+    uint8_t header_type = (buf[1] >> 4) & 0xF;
+    switch (header_type) {
+        case 0x01:
+            // Audio, yay!  Just break.  We understand audio payloads.
+            break;
+        case 0x02:
+            ALOGV("RXed packet with unhandled TRTP header type (Video).");
+            return;
+        case 0x03:
+            ALOGV("RXed packet with unhandled TRTP header type (Subpicture).");
+            return;
+        case 0x04:
+            ALOGV("RXed packet with unhandled TRTP header type (Control).");
+            return;
+        default:
+            ALOGV("RXed packet with unhandled TRTP header type (%u).",
+                    header_type);
+            return;
+    }
+
+    if (substream_details_known_ && (header_type != substream_type_)) {
+        ALOGV("RXed TRTP Payload for SSRC=0x%08x where header type (%u) does not"
+              " match previously received header type (%u)",
+              ssrc_, header_type, substream_type_);
+        return;
+    }
+
+    // Check the flags to see if there is another 32 bits of timestamp present.
+    uint32_t trtp_header_len = 6;
+    bool ts_valid = buf[1] & 0x1;
+    if (ts_valid) {
+        min_length += 4;
+        trtp_header_len += 4;
+        if (amt < min_length) {
+            ALOGV("Discarding payload too short to contain TRTP timestamp"
+                  " (len = %u)", amt);
+            return;
+        }
+    }
+
+    // Extract the TRTP length field and sanity check it.
+    uint32_t trtp_len;
+    trtp_len = (static_cast<uint32_t>(buf[2]) << 24) |
+        (static_cast<uint32_t>(buf[3]) << 16) |
+        (static_cast<uint32_t>(buf[4]) <<  8) |
+        static_cast<uint32_t>(buf[5]);
+    if (trtp_len < min_length) {
+        ALOGV("TRTP length (%u) is too short to be valid.  Must be at least %u"
+              " bytes.", trtp_len, min_length);
+        return;
+    }
+
+    // Extract the rest of the timestamp field if valid.
+    int64_t ts = 0;
+    uint32_t parse_offset = 6;
+    if (ts_valid) {
+        ts = (static_cast<int64_t>(buf[parse_offset    ]) << 56) |
+            (static_cast<int64_t>(buf[parse_offset + 1]) << 48) |
+            (static_cast<int64_t>(buf[parse_offset + 2]) << 40) |
+            (static_cast<int64_t>(buf[parse_offset + 3]) << 32);
+        ts |= ts_lower;
+        parse_offset += 4;
+    }
+
+    // Check the flags to see if there is another 24 bytes of timestamp
+    // transformation present.
+    if (buf[1] & 0x2) {
+        min_length += 24;
+        parse_offset += 24;
+        trtp_header_len += 24;
+        if (amt < min_length) {
+            ALOGV("Discarding payload too short to contain TRTP timestamp"
+                  " transformation (len = %u)", amt);
+            return;
+        }
+    }
+
+    // TODO : break the parsing into individual parsers for the different
+    // payload types (audio, video, etc).
+    //
+    // At this point in time, we know that this is audio.  Go ahead and parse
+    // the basic header, check the codec type, and find the payload portion of
+    // the packet.
+    min_length += 3;
+    if (trtp_len < min_length) {
+        ALOGV("TRTP length (%u) is too short to be a valid audio payload.  Must"
+              " be at least %u bytes.", trtp_len, min_length);
+        return;
+    }
+
+    if (amt < min_length) {
+        ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain"
+              " entire TRTP header.  TRTP does not currently support fragmenting"
+              " TRTP headers across RTP payloads", amt);
+        return;
+    }
+
+    uint8_t codec_type = buf[parse_offset    ];
+    uint8_t flags      = buf[parse_offset + 1];
+    uint8_t volume     = buf[parse_offset + 2];
+    parse_offset += 3;
+    trtp_header_len += 3;
+
+    if (!setupSubstreamType(header_type, codec_type)) {
+        return;
+    }
+
+    if (decoder_ != NULL) {
+        decoder_->setRenderVolume(volume);
+    }
+
+    // TODO : move all of the constant flag and offset definitions for TRTP up
+    // into some sort of common header file.
+    if (waiting_for_rap_ && !(flags & 0x08)) {
+        ALOGV("Dropping non-RAP TRTP Audio Payload while waiting for RAP.");
+        return;
+    }
+
+    if (flags & 0x10) {
+        ALOGV("Dropping TRTP Audio Payload with aux codec data present (only"
+              " handle MP3 right now, and it has no aux data)");
+        return;
+    }
+
+    // OK - everything left is just payload.  Compute the payload size, start
+    // the buffer in progress and pack as much payload as we can into it.  If
+    // the payload is finished once we are done, go ahead and send the payload
+    // to the decoder.
+    expected_buffer_size_ = trtp_len - trtp_header_len;
+    if (!expected_buffer_size_) {
+        ALOGV("Dropping TRTP Audio Payload with 0 Access Unit length");
+        return;
+    }
+
+    CHECK(amt >= trtp_header_len);
+    uint32_t todo = amt - trtp_header_len;
+    if (expected_buffer_size_ < todo) {
+        ALOGV("Extra data (%u > %u) present in initial TRTP Audio Payload;"
+              " dropping payload.", todo, expected_buffer_size_);
+        return;
+    }
+
+    buffer_filled_ = 0;
+    buffer_in_progress_ = new MediaBuffer(expected_buffer_size_);
+    if ((NULL == buffer_in_progress_) ||
+            (NULL == buffer_in_progress_->data())) {
+        ALOGV("Failed to allocate MediaBuffer of length %u",
+                expected_buffer_size_);
+        cleanupBufferInProgress();
+        return;
+    }
+
+    sp<MetaData> meta = buffer_in_progress_->meta_data();
+    if (meta == NULL) {
+        ALOGV("Missing metadata structure in allocated MediaBuffer; dropping"
+              " payload");
+        cleanupBufferInProgress();
+        return;
+    }
+
+    // TODO : set this based on the codec type indicated in the TRTP stream.
+    // Right now, we only support MP3, so the choice is obvious.
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+    if (ts_valid) {
+        meta->setInt64(kKeyTime, ts);
+    }
+
+    if (amt > 0) {
+        uint8_t* tgt =
+            reinterpret_cast<uint8_t*>(buffer_in_progress_->data());
+        memcpy(tgt + buffer_filled_, buf + trtp_header_len, todo);
+        buffer_filled_ += amt;
+    }
+
+    if (buffer_filled_ >= expected_buffer_size_) {
+        processCompletedBuffer();
+    }
+}
+
+void AAH_RXPlayer::Substream::processPayloadCont(uint8_t* buf,
+                                                 uint32_t amt) {
+    if (shouldAbort(__PRETTY_FUNCTION__)) {
+        return;
+    }
+
+    if (NULL == buffer_in_progress_) {
+        ALOGV("TRTP Receiver skipping payload continuation; no buffer currently"
+              " in progress.");
+        return;
+    }
+
+    CHECK(buffer_filled_ < expected_buffer_size_);
+    uint32_t buffer_left = expected_buffer_size_ - buffer_filled_;
+    if (amt > buffer_left) {
+        ALOGV("Extra data (%u > %u) present in continued TRTP Audio Payload;"
+              " dropping payload.", amt, buffer_left);
+        cleanupBufferInProgress();
+        return;
+    }
+
+    if (amt > 0) {
+        uint8_t* tgt =
+            reinterpret_cast<uint8_t*>(buffer_in_progress_->data());
+        memcpy(tgt + buffer_filled_, buf, amt);
+        buffer_filled_ += amt;
+    }
+
+    if (buffer_filled_ >= expected_buffer_size_) {
+        processCompletedBuffer();
+    }
+}
+
+void AAH_RXPlayer::Substream::processCompletedBuffer() {
+    const uint8_t* buffer_data = NULL;
+    int sample_rate;
+    int channel_count;
+    size_t frame_size;
+    status_t res;
+
+    CHECK(NULL != buffer_in_progress_);
+
+    if (decoder_ == NULL) {
+        ALOGV("Dropping complete buffer, no decoder pump allocated");
+        goto bailout;
+    }
+
+    buffer_data = reinterpret_cast<const uint8_t*>(buffer_in_progress_->data());
+    if (buffer_in_progress_->size() < 4) {
+        ALOGV("MP3 payload too short to contain header, dropping payload.");
+        goto bailout;
+    }
+
+    // Extract the channel count and the sample rate from the MP3 header.  The
+    // stagefright MP3 requires that these be delivered before decoing can
+    // begin.
+    if (!GetMPEGAudioFrameSize(U32_AT(buffer_data),
+                               &frame_size,
+                               &sample_rate,
+                               &channel_count,
+                               NULL,
+                               NULL)) {
+        ALOGV("Failed to parse MP3 header in payload, droping payload.");
+        goto bailout;
+    }
+
+
+    // Make sure that our substream metadata is set up properly.  If there has
+    // been a format change, be sure to reset the underlying decoder.  In
+    // stagefright, it seems like the only way to do this is to destroy and
+    // recreate the decoder.
+    if (substream_meta_ == NULL) {
+        substream_meta_ = new MetaData();
+
+        if (substream_meta_ == NULL) {
+            ALOGE("Failed to allocate MetaData structure for substream");
+            goto bailout;
+        }
+
+        substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+        substream_meta_->setInt32  (kKeyChannelCount, channel_count);
+        substream_meta_->setInt32  (kKeySampleRate,   sample_rate);
+    } else {
+        int32_t prev_sample_rate;
+        int32_t prev_channel_count;
+        substream_meta_->findInt32(kKeySampleRate,   &prev_sample_rate);
+        substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count);
+
+        if ((prev_channel_count != channel_count) ||
+            (prev_sample_rate   != sample_rate)) {
+            ALOGW("Format change detected, forcing decoder reset.");
+            cleanupDecoder();
+
+            substream_meta_->setInt32(kKeyChannelCount, channel_count);
+            substream_meta_->setInt32(kKeySampleRate,   sample_rate);
+        }
+    }
+
+    // If our decoder has not be set up, do so now.
+    res = decoder_->init(substream_meta_);
+    if (OK != res) {
+        ALOGE("Failed to init decoder (res = %d)", res);
+        cleanupDecoder();
+        substream_meta_ = NULL;
+        goto bailout;
+    }
+
+    // Queue the payload for decode.
+    res = decoder_->queueForDecode(buffer_in_progress_);
+
+    if (res != OK) {
+        ALOGD("Failed to queue payload for decode, resetting decoder pump!"
+              " (res = %d)", res);
+        status_ = res;
+        cleanupDecoder();
+        cleanupBufferInProgress();
+    }
+
+    // NULL out buffer_in_progress before calling the cleanup helper.
+    //
+    // MediaBuffers use something of a hybrid ref-counting pattern which prevent
+    // the AAH_DecoderPump's input queue from adding their own reference to the
+    // MediaBuffer.  MediaBuffers start life with a reference count of 0, as
+    // well as an observer which starts as NULL.  Before being given an
+    // observer, the ref count cannot be allowed to become non-zero as it will
+    // cause calls to release() to assert.  Basically, before a MediaBuffer has
+    // an observer, they behave like non-ref counted obects where release()
+    // serves the roll of delete.  After a MediaBuffer has an observer, they
+    // become more like ref counted objects where add ref and release can be
+    // used, and when the ref count hits zero, the MediaBuffer is handed off to
+    // the observer.
+    //
+    // Given all of this, when we give the buffer to the decoder pump to wait in
+    // the to-be-processed queue, the decoder cannot add a ref to the buffer as
+    // it would in a traditional ref counting system.  Instead it needs to
+    // "steal" the non-existent ref.  In the case of queue failure, we need to
+    // make certain to release this non-existent reference so that the buffer is
+    // cleaned up during the cleanupBufferInProgress helper.  In the case of a
+    // successful queue operation, we need to make certain that the
+    // cleanupBufferInProgress helper does not release the buffer since it needs
+    // to remain alive in the queue.  We acomplish this by NULLing out the
+    // buffer pointer before calling the cleanup helper.
+    buffer_in_progress_ = NULL;
+
+bailout:
+    cleanupBufferInProgress();
+}
+
+
+void AAH_RXPlayer::Substream::processTSTransform(const LinearTransform& trans) {
+    if (decoder_ != NULL) {
+        decoder_->setRenderTSTransform(trans);
+    }
+}
+
+bool AAH_RXPlayer::Substream::isAboutToUnderflow() {
+    if (decoder_ == NULL) {
+        return false;
+    }
+
+    return decoder_->isAboutToUnderflow(kAboutToUnderflowThreshold);
+}
+
+bool AAH_RXPlayer::Substream::setupSubstreamType(uint8_t substream_type,
+                                                 uint8_t codec_type) {
+    // Sanity check the codec type.  Right now we only support MP3.  Also check
+    // for conflicts with previously delivered codec types.
+    if (substream_details_known_ && (codec_type != codec_type_)) {
+        ALOGV("RXed TRTP Payload for SSRC=0x%08x where codec type (%u) does not"
+              " match previously received codec type (%u)",
+              ssrc_, codec_type, codec_type_);
+        return false;
+    }
+
+    if (codec_type != 0x03) {
+        ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported codec"
+              " type (%u)", ssrc_, codec_type);
+        return false;
+    }
+
+    if (!substream_details_known_) {
+        substream_type_ = substream_type;
+        codec_type_ = codec_type;
+        substream_details_known_ = true;
+    }
+
+    return true;
+}
+
+}  // namespace android
diff --git a/media/libaah_rtp/aah_tx_packet.cpp b/media/libaah_rtp/aah_tx_packet.cpp
new file mode 100644
index 0000000..3f6e0e9
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_packet.cpp
@@ -0,0 +1,331 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include "aah_tx_packet.h"
+
+namespace android {
+
+const int TRTPPacket::kRTPHeaderLen;
+const uint32_t TRTPPacket::kTRTPEpochMask;
+
+TRTPPacket::~TRTPPacket() {
+    delete mPacket;
+}
+
+/*** TRTP packet properties ***/
+
+void TRTPPacket::setSeqNumber(uint16_t val) {
+    mSeqNumber = val;
+
+    if (mIsPacked) {
+        const int kTRTPSeqNumberOffset = 2;
+        uint16_t* buf = reinterpret_cast<uint16_t*>(
+            mPacket + kTRTPSeqNumberOffset);
+        *buf = htons(mSeqNumber);
+    }
+}
+
+uint16_t TRTPPacket::getSeqNumber() const {
+    return mSeqNumber;
+}
+
+void TRTPPacket::setPTS(int64_t val) {
+    CHECK(!mIsPacked);
+    mPTS = val;
+    mPTSValid = true;
+}
+
+int64_t TRTPPacket::getPTS() const {
+    return mPTS;
+}
+
+void TRTPPacket::setEpoch(uint32_t val) {
+    mEpoch = val;
+
+    if (mIsPacked) {
+        const int kTRTPEpochOffset = 8;
+        uint32_t* buf = reinterpret_cast<uint32_t*>(
+            mPacket + kTRTPEpochOffset);
+        uint32_t val = ntohl(*buf);
+        val &= ~(kTRTPEpochMask << kTRTPEpochShift);
+        val |= (mEpoch & kTRTPEpochMask) << kTRTPEpochShift;
+        *buf = htonl(val);
+    }
+}
+
+void TRTPPacket::setProgramID(uint16_t val) {
+    CHECK(!mIsPacked);
+    mProgramID = val;
+}
+
+void TRTPPacket::setSubstreamID(uint16_t val) {
+    CHECK(!mIsPacked);
+    mSubstreamID = val;
+}
+
+
+void TRTPPacket::setClockTransform(const LinearTransform& trans) {
+    CHECK(!mIsPacked);
+    mClockTranform = trans;
+    mClockTranformValid = true;
+}
+
+uint8_t* TRTPPacket::getPacket() const {
+    CHECK(mIsPacked);
+    return mPacket;
+}
+
+int TRTPPacket::getPacketLen() const {
+    CHECK(mIsPacked);
+    return mPacketLen;
+}
+
+void TRTPPacket::setExpireTime(nsecs_t val) {
+    CHECK(!mIsPacked);
+    mExpireTime = val;
+}
+
+nsecs_t TRTPPacket::getExpireTime() const {
+    return mExpireTime;
+}
+
+/*** TRTP audio packet properties ***/
+
+void TRTPAudioPacket::setCodecType(TRTPAudioCodecType val) {
+    CHECK(!mIsPacked);
+    mCodecType = val;
+}
+
+void TRTPAudioPacket::setRandomAccessPoint(bool val) {
+    CHECK(!mIsPacked);
+    mRandomAccessPoint = val;
+}
+
+void TRTPAudioPacket::setDropable(bool val) {
+    CHECK(!mIsPacked);
+    mDropable = val;
+}
+
+void TRTPAudioPacket::setDiscontinuity(bool val) {
+    CHECK(!mIsPacked);
+    mDiscontinuity = val;
+}
+
+void TRTPAudioPacket::setEndOfStream(bool val) {
+    CHECK(!mIsPacked);
+    mEndOfStream = val;
+}
+
+void TRTPAudioPacket::setVolume(uint8_t val) {
+    CHECK(!mIsPacked);
+    mVolume = val;
+}
+
+void TRTPAudioPacket::setAccessUnitData(void* data, int len) {
+    CHECK(!mIsPacked);
+    mAccessUnitData = data;
+    mAccessUnitLen = len;
+}
+
+/*** TRTP control packet properties ***/
+
+void TRTPControlPacket::setCommandID(TRTPCommandID val) {
+    CHECK(!mIsPacked);
+    mCommandID = val;
+}
+
+/*** TRTP packet serializers ***/
+
+void TRTPPacket::writeU8(uint8_t*& buf, uint8_t val) {
+    *buf = val;
+    buf++;
+}
+
+void TRTPPacket::writeU16(uint8_t*& buf, uint16_t val) {
+    *reinterpret_cast<uint16_t*>(buf) = htons(val);
+    buf += 2;
+}
+
+void TRTPPacket::writeU32(uint8_t*& buf, uint32_t val) {
+    *reinterpret_cast<uint32_t*>(buf) = htonl(val);
+    buf += 4;
+}
+
+void TRTPPacket::writeU64(uint8_t*& buf, uint64_t val) {
+    buf[0] = static_cast<uint8_t>(val >> 56);
+    buf[1] = static_cast<uint8_t>(val >> 48);
+    buf[2] = static_cast<uint8_t>(val >> 40);
+    buf[3] = static_cast<uint8_t>(val >> 32);
+    buf[4] = static_cast<uint8_t>(val >> 24);
+    buf[5] = static_cast<uint8_t>(val >> 16);
+    buf[6] = static_cast<uint8_t>(val >>  8);
+    buf[7] = static_cast<uint8_t>(val);
+    buf += 8;
+}
+
+void TRTPPacket::writeTRTPHeader(uint8_t*& buf,
+                                 bool isFirstFragment,
+                                 int totalPacketLen) {
+    // RTP header
+    writeU8(buf,
+            ((mVersion & 0x03) << 6) |
+            (static_cast<int>(mPadding) << 5) |
+            (static_cast<int>(mExtension) << 4) |
+            (mCsrcCount & 0x0F));
+    writeU8(buf,
+            (static_cast<int>(isFirstFragment) << 7) |
+            (mPayloadType & 0x7F));
+    writeU16(buf, mSeqNumber);
+    if (isFirstFragment && mPTSValid) {
+        writeU32(buf, mPTS & 0xFFFFFFFF);
+    } else {
+        writeU32(buf, 0);
+    }
+    writeU32(buf,
+            ((mEpoch & kTRTPEpochMask) << kTRTPEpochShift) |
+            ((mProgramID & 0x1F) << 5) |
+            (mSubstreamID & 0x1F));
+
+    // TRTP header
+    writeU8(buf, mTRTPVersion);
+    writeU8(buf,
+            ((mTRTPHeaderType & 0x0F) << 4) |
+            (mClockTranformValid ? 0x02 : 0x00) |
+            (mPTSValid ? 0x01 : 0x00));
+    writeU32(buf, totalPacketLen - kRTPHeaderLen);
+    if (mPTSValid) {
+        writeU32(buf, mPTS >> 32);
+    }
+
+    if (mClockTranformValid) {
+        writeU64(buf, mClockTranform.a_zero);
+        writeU32(buf, mClockTranform.a_to_b_numer);
+        writeU32(buf, mClockTranform.a_to_b_denom);
+        writeU64(buf, mClockTranform.b_zero);
+    }
+}
+
+bool TRTPAudioPacket::pack() {
+    if (mIsPacked) {
+        return false;
+    }
+
+    int packetLen = kRTPHeaderLen +
+                    mAccessUnitLen +
+                    TRTPHeaderLen();
+
+    // TODO : support multiple fragments
+    const int kMaxUDPPayloadLen = 65507;
+    if (packetLen > kMaxUDPPayloadLen) {
+        return false;
+    }
+
+    mPacket = new uint8_t[packetLen];
+    if (!mPacket) {
+        return false;
+    }
+
+    mPacketLen = packetLen;
+
+    uint8_t* cur = mPacket;
+
+    writeTRTPHeader(cur, true, packetLen);
+    writeU8(cur, mCodecType);
+    writeU8(cur,
+            (static_cast<int>(mRandomAccessPoint) << 3) |
+            (static_cast<int>(mDropable) << 2) |
+            (static_cast<int>(mDiscontinuity) << 1) |
+            (static_cast<int>(mEndOfStream)));
+    writeU8(cur, mVolume);
+
+    memcpy(cur, mAccessUnitData, mAccessUnitLen);
+
+    mIsPacked = true;
+    return true;
+}
+
+int TRTPPacket::TRTPHeaderLen() const {
+    // 6 bytes for version, payload type, flags and length.  An additional 4 if
+    // there are upper timestamp bits present and another 24 if there is a clock
+    // transformation present.
+    return 6 +
+           (mClockTranformValid ? 24 : 0) +
+           (mPTSValid ? 4 : 0);
+}
+
+int TRTPAudioPacket::TRTPHeaderLen() const {
+    // TRTPPacket::TRTPHeaderLen() for the base TRTPHeader.  3 bytes for audio's
+    // codec type, flags and volume field.  Another 5 bytes if the codec type is
+    // PCM and we are sending sample rate/channel count. as well as however long
+    // the aux data (if present) is.
+
+    int pcmParamLength;
+    switch(mCodecType) {
+        case kCodecPCMBigEndian:
+        case kCodecPCMLittleEndian:
+            pcmParamLength = 5;
+            break;
+
+        default:
+            pcmParamLength = 0;
+            break;
+    }
+
+
+    // TODO : properly compute aux data length.  Currently, nothing
+    // uses aux data, so its length is always 0.
+    int auxDataLength = 0;
+    return TRTPPacket::TRTPHeaderLen() +
+           3 +
+           auxDataLength +
+           pcmParamLength;
+}
+
+bool TRTPControlPacket::pack() {
+    if (mIsPacked) {
+        return false;
+    }
+
+    // command packets contain a 2-byte command ID
+    int packetLen = kRTPHeaderLen +
+                    TRTPHeaderLen() +
+                    2;
+
+    mPacket = new uint8_t[packetLen];
+    if (!mPacket) {
+        return false;
+    }
+
+    mPacketLen = packetLen;
+
+    uint8_t* cur = mPacket;
+
+    writeTRTPHeader(cur, true, packetLen);
+    writeU16(cur, mCommandID);
+
+    mIsPacked = true;
+    return true;
+}
+
+}  // namespace android
diff --git a/media/libaah_rtp/aah_tx_packet.h b/media/libaah_rtp/aah_tx_packet.h
new file mode 100644
index 0000000..833803e
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_packet.h
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+#ifndef __AAH_TX_PACKET_H__
+#define __AAH_TX_PACKET_H__
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/LinearTransform.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+class TRTPPacket : public RefBase {
+  protected:
+    enum TRTPHeaderType {
+        kHeaderTypeAudio = 1,
+        kHeaderTypeVideo = 2,
+        kHeaderTypeSubpicture = 3,
+        kHeaderTypeControl = 4,
+    };
+
+    TRTPPacket(TRTPHeaderType headerType)
+        : mIsPacked(false)
+        , mVersion(2)
+        , mPadding(false)
+        , mExtension(false)
+        , mCsrcCount(0)
+        , mPayloadType(100)
+        , mSeqNumber(0)
+        , mPTSValid(false)
+        , mPTS(0)
+        , mEpoch(0)
+        , mProgramID(0)
+        , mSubstreamID(0)
+        , mClockTranformValid(false)
+        , mTRTPVersion(1)
+        , mTRTPLength(0)
+        , mTRTPHeaderType(headerType)
+        , mPacket(NULL)
+        , mPacketLen(0) { }
+
+  public:
+    virtual ~TRTPPacket();
+
+    void setSeqNumber(uint16_t val);
+    uint16_t getSeqNumber() const;
+
+    void setPTS(int64_t val);
+    int64_t getPTS() const;
+
+    void setEpoch(uint32_t val);
+    void setProgramID(uint16_t val);
+    void setSubstreamID(uint16_t val);
+    void setClockTransform(const LinearTransform& trans);
+
+    uint8_t* getPacket() const;
+    int getPacketLen() const;
+
+    void setExpireTime(nsecs_t val);
+    nsecs_t getExpireTime() const;
+
+    virtual bool pack() = 0;
+
+    // mask for the number of bits in a TRTP epoch
+    static const uint32_t kTRTPEpochMask = (1 << 22) - 1;
+    static const int kTRTPEpochShift = 10;
+
+  protected:
+    static const int kRTPHeaderLen = 12;
+    virtual int TRTPHeaderLen() const;
+
+    void writeTRTPHeader(uint8_t*& buf,
+                         bool isFirstFragment,
+                         int totalPacketLen);
+
+    void writeU8(uint8_t*& buf, uint8_t val);
+    void writeU16(uint8_t*& buf, uint16_t val);
+    void writeU32(uint8_t*& buf, uint32_t val);
+    void writeU64(uint8_t*& buf, uint64_t val);
+
+    bool mIsPacked;
+
+    uint8_t mVersion;
+    bool mPadding;
+    bool mExtension;
+    uint8_t mCsrcCount;
+    uint8_t mPayloadType;
+    uint16_t mSeqNumber;
+    bool mPTSValid;
+    int64_t  mPTS;
+    uint32_t mEpoch;
+    uint16_t mProgramID;
+    uint16_t mSubstreamID;
+    LinearTransform mClockTranform;
+    bool mClockTranformValid;
+    uint8_t mTRTPVersion;
+    uint32_t mTRTPLength;
+    TRTPHeaderType mTRTPHeaderType;
+
+    uint8_t* mPacket;
+    int mPacketLen;
+
+    nsecs_t mExpireTime;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TRTPPacket);
+};
+
+class TRTPAudioPacket : public TRTPPacket {
+  public:
+    TRTPAudioPacket()
+        : TRTPPacket(kHeaderTypeAudio)
+        , mCodecType(kCodecInvalid)
+        , mRandomAccessPoint(false)
+        , mDropable(false)
+        , mDiscontinuity(false)
+        , mEndOfStream(false)
+        , mVolume(0)
+        , mAccessUnitData(NULL) { }
+
+    enum TRTPAudioCodecType {
+        kCodecInvalid = 0,
+        kCodecPCMBigEndian = 1,
+        kCodecPCMLittleEndian = 2,
+        kCodecMPEG1Audio = 3,
+    };
+
+    void setCodecType(TRTPAudioCodecType val);
+    void setRandomAccessPoint(bool val);
+    void setDropable(bool val);
+    void setDiscontinuity(bool val);
+    void setEndOfStream(bool val);
+    void setVolume(uint8_t val);
+    void setAccessUnitData(void* data, int len);
+
+    virtual bool pack();
+
+  protected:
+    virtual int TRTPHeaderLen() const;
+
+  private:
+    TRTPAudioCodecType mCodecType;
+    bool mRandomAccessPoint;
+    bool mDropable;
+    bool mDiscontinuity;
+    bool mEndOfStream;
+    uint8_t mVolume;
+    void* mAccessUnitData;
+    int mAccessUnitLen;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TRTPAudioPacket);
+};
+
+class TRTPControlPacket : public TRTPPacket {
+  public:
+    TRTPControlPacket()
+        : TRTPPacket(kHeaderTypeControl)
+        , mCommandID(kCommandNop) {}
+
+    enum TRTPCommandID {
+        kCommandNop   = 1,
+        kCommandFlush = 2,
+        kCommandEOS   = 3,
+    };
+
+    void setCommandID(TRTPCommandID val);
+
+    virtual bool pack();
+
+  private:
+    TRTPCommandID mCommandID;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TRTPControlPacket);
+};
+
+}  // namespace android
+
+#endif  // __AAH_TX_PLAYER_H__
diff --git a/media/libaah_rtp/aah_tx_player.cpp b/media/libaah_rtp/aah_tx_player.cpp
new file mode 100644
index 0000000..a79a989
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_player.cpp
@@ -0,0 +1,1139 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+#include <utils/Log.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <netdb.h>
+#include <netinet/ip.h>
+
+#include <common_time/cc_helper.h>
+#include <media/IMediaPlayer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/Timers.h>
+
+#include "aah_tx_packet.h"
+#include "aah_tx_player.h"
+
+namespace android {
+
+static int64_t kLowWaterMarkUs = 2000000ll;  // 2secs
+static int64_t kHighWaterMarkUs = 10000000ll;  // 10secs
+static const size_t kLowWaterMarkBytes = 40000;
+static const size_t kHighWaterMarkBytes = 200000;
+
+// When we start up, how much lead time should we put on the first access unit?
+static const int64_t kAAHStartupLeadTimeUs = 300000LL;
+
+// How much time do we attempt to lead the clock by in steady state?
+static const int64_t kAAHBufferTimeUs = 1000000LL;
+
+// how long do we keep data in our retransmit buffer after sending it.
+const int64_t AAH_TXPlayer::kAAHRetryKeepAroundTimeNs =
+    kAAHBufferTimeUs * 1100;
+
+sp<MediaPlayerBase> createAAH_TXPlayer() {
+    sp<MediaPlayerBase> ret = new AAH_TXPlayer();
+    return ret;
+}
+
+template <typename T> static T clamp(T val, T min, T max) {
+    if (val < min) {
+        return min;
+    } else if (val > max) {
+        return max;
+    } else {
+        return val;
+    }
+}
+
+struct AAH_TXEvent : public TimedEventQueue::Event {
+    AAH_TXEvent(AAH_TXPlayer *player,
+                void (AAH_TXPlayer::*method)()) : mPlayer(player)
+                                                , mMethod(method) {}
+
+  protected:
+    virtual ~AAH_TXEvent() {}
+
+    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+        (mPlayer->*mMethod)();
+    }
+
+  private:
+    AAH_TXPlayer *mPlayer;
+    void (AAH_TXPlayer::*mMethod)();
+
+    AAH_TXEvent(const AAH_TXEvent &);
+    AAH_TXEvent& operator=(const AAH_TXEvent &);
+};
+
+AAH_TXPlayer::AAH_TXPlayer()
+        : mQueueStarted(false)
+        , mFlags(0)
+        , mExtractorFlags(0) {
+    DataSource::RegisterDefaultSniffers();
+
+    mBufferingEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onBufferingUpdate);
+    mBufferingEventPending = false;
+
+    mPumpAudioEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onPumpAudio);
+    mPumpAudioEventPending = false;
+
+    reset_l();
+}
+
+AAH_TXPlayer::~AAH_TXPlayer() {
+    if (mQueueStarted) {
+        mQueue.stop();
+    }
+
+    reset_l();
+}
+
+void AAH_TXPlayer::cancelPlayerEvents(bool keepBufferingGoing) {
+    if (!keepBufferingGoing) {
+        mQueue.cancelEvent(mBufferingEvent->eventID());
+        mBufferingEventPending = false;
+
+        mQueue.cancelEvent(mPumpAudioEvent->eventID());
+        mPumpAudioEventPending = false;
+    }
+}
+
+status_t AAH_TXPlayer::initCheck() {
+    // Check for the presense of the common time service by attempting to query
+    // for CommonTime's frequency.  If we get an error back, we cannot talk to
+    // the service at all and should abort now.
+    status_t res;
+    uint64_t freq;
+    res = mCCHelper.getCommonFreq(&freq);
+    if (OK != res) {
+        ALOGE("Failed to connect to common time service! (res %d)", res);
+        return res;
+    }
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::setDataSource(
+        const char *url,
+        const KeyedVector<String8, String8> *headers) {
+    Mutex::Autolock autoLock(mLock);
+    return setDataSource_l(url, headers);
+}
+
+status_t AAH_TXPlayer::setDataSource_l(
+        const char *url,
+        const KeyedVector<String8, String8> *headers) {
+    reset_l();
+
+    // the URL must consist of "aahTX://" followed by the real URL of
+    // the data source
+    const char *kAAHPrefix = "aahTX://";
+    if (strncasecmp(url, kAAHPrefix, strlen(kAAHPrefix))) {
+        return INVALID_OPERATION;
+    }
+
+    mUri.setTo(url + strlen(kAAHPrefix));
+
+    if (headers) {
+        mUriHeaders = *headers;
+
+        ssize_t index = mUriHeaders.indexOfKey(String8("x-hide-urls-from-log"));
+        if (index >= 0) {
+            // Browser is in "incognito" mode, suppress logging URLs.
+
+            // This isn't something that should be passed to the server.
+            mUriHeaders.removeItemsAt(index);
+
+            mFlags |= INCOGNITO;
+        }
+    }
+
+    // The URL may optionally contain a "#" character followed by a Skyjam
+    // cookie.  Ideally the cookie header should just be passed in the headers
+    // argument, but the Java API for supplying headers is apparently not yet
+    // exposed in the SDK used by application developers.
+    const char kSkyjamCookieDelimiter = '#';
+    char* skyjamCookie = strrchr(mUri.string(), kSkyjamCookieDelimiter);
+    if (skyjamCookie) {
+        skyjamCookie++;
+        mUriHeaders.add(String8("Cookie"), String8(skyjamCookie));
+        mUri = String8(mUri.string(), skyjamCookie - mUri.string());
+    }
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
+    Mutex::Autolock autoLock(mLock);
+
+    reset_l();
+
+    sp<DataSource> dataSource = new FileSource(dup(fd), offset, length);
+
+    status_t err = dataSource->initCheck();
+
+    if (err != OK) {
+        return err;
+    }
+
+    mFileSource = dataSource;
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+    if (extractor == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    return setDataSource_l(extractor);
+}
+
+status_t AAH_TXPlayer::setVideoSurface(const sp<Surface>& surface) {
+    return OK;
+}
+
+status_t AAH_TXPlayer::setVideoSurfaceTexture(
+        const sp<ISurfaceTexture>& surfaceTexture) {
+    return OK;
+}
+
+status_t AAH_TXPlayer::prepare() {
+    return INVALID_OPERATION;
+}
+
+status_t AAH_TXPlayer::prepareAsync() {
+    Mutex::Autolock autoLock(mLock);
+
+    return prepareAsync_l();
+}
+
+status_t AAH_TXPlayer::prepareAsync_l() {
+    if (mFlags & PREPARING) {
+        return UNKNOWN_ERROR;  // async prepare already pending
+    }
+
+    mAAH_Sender = AAH_TXSender::GetInstance();
+    if (mAAH_Sender == NULL) {
+        return NO_MEMORY;
+    }
+
+    if (!mQueueStarted) {
+        mQueue.start();
+        mQueueStarted = true;
+    }
+
+    mFlags |= PREPARING;
+    mAsyncPrepareEvent = new AAH_TXEvent(
+            this, &AAH_TXPlayer::onPrepareAsyncEvent);
+
+    mQueue.postEvent(mAsyncPrepareEvent);
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::finishSetDataSource_l() {
+    sp<DataSource> dataSource;
+
+    if (!strncasecmp("http://",  mUri.string(), 7) ||
+        !strncasecmp("https://", mUri.string(), 8)) {
+
+        mConnectingDataSource = HTTPBase::Create(
+                (mFlags & INCOGNITO)
+                    ? HTTPBase::kFlagIncognito
+                    : 0);
+
+        mLock.unlock();
+        status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
+        mLock.lock();
+
+        if (err != OK) {
+            mConnectingDataSource.clear();
+
+            ALOGI("mConnectingDataSource->connect() returned %d", err);
+            return err;
+        }
+
+        mCachedSource = new NuCachedSource2(mConnectingDataSource);
+        mConnectingDataSource.clear();
+
+        dataSource = mCachedSource;
+
+        // We're going to prefill the cache before trying to instantiate
+        // the extractor below, as the latter is an operation that otherwise
+        // could block on the datasource for a significant amount of time.
+        // During that time we'd be unable to abort the preparation phase
+        // without this prefill.
+
+        mLock.unlock();
+
+        for (;;) {
+            status_t finalStatus;
+            size_t cachedDataRemaining =
+                mCachedSource->approxDataRemaining(&finalStatus);
+
+            if (finalStatus != OK ||
+                cachedDataRemaining >= kHighWaterMarkBytes ||
+                (mFlags & PREPARE_CANCELLED)) {
+                break;
+            }
+
+            usleep(200000);
+        }
+
+        mLock.lock();
+
+        if (mFlags & PREPARE_CANCELLED) {
+            ALOGI("Prepare cancelled while waiting for initial cache fill.");
+            return UNKNOWN_ERROR;
+        }
+    } else {
+        dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
+    }
+
+    if (dataSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+    if (extractor == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    return setDataSource_l(extractor);
+}
+
+status_t AAH_TXPlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
+    // Attempt to approximate overall stream bitrate by summing all
+    // tracks' individual bitrates, if not all of them advertise bitrate,
+    // we have to fail.
+
+    int64_t totalBitRate = 0;
+
+    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+        int32_t bitrate;
+        if (!meta->findInt32(kKeyBitRate, &bitrate)) {
+            totalBitRate = -1;
+            break;
+        }
+
+        totalBitRate += bitrate;
+    }
+
+    mBitrate = totalBitRate;
+
+    ALOGV("mBitrate = %lld bits/sec", mBitrate);
+
+    bool haveAudio = false;
+    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+        const char *mime;
+        CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+        if (!strncasecmp(mime, "audio/", 6)) {
+            mAudioSource = extractor->getTrack(i);
+            CHECK(mAudioSource != NULL);
+            haveAudio = true;
+            break;
+        }
+    }
+
+    if (!haveAudio) {
+        return UNKNOWN_ERROR;
+    }
+
+    mExtractorFlags = extractor->flags();
+
+    return OK;
+}
+
+void AAH_TXPlayer::abortPrepare(status_t err) {
+    CHECK(err != OK);
+
+    notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+
+    mPrepareResult = err;
+    mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
+    mPreparedCondition.broadcast();
+}
+
+void AAH_TXPlayer::onPrepareAsyncEvent() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mFlags & PREPARE_CANCELLED) {
+        ALOGI("prepare was cancelled before doing anything");
+        abortPrepare(UNKNOWN_ERROR);
+        return;
+    }
+
+    if (mUri.size() > 0) {
+        status_t err = finishSetDataSource_l();
+
+        if (err != OK) {
+            abortPrepare(err);
+            return;
+        }
+    }
+
+    mAudioSource->getFormat()->findInt64(kKeyDuration, &mDurationUs);
+
+    status_t err = mAudioSource->start();
+    if (err != OK) {
+        ALOGI("failed to start audio source, err=%d", err);
+        abortPrepare(err);
+        return;
+    }
+
+    mFlags |= PREPARING_CONNECTED;
+
+    if (mCachedSource != NULL) {
+        postBufferingEvent_l();
+    } else {
+        finishAsyncPrepare_l();
+    }
+}
+
+void AAH_TXPlayer::finishAsyncPrepare_l() {
+    notifyListener_l(MEDIA_PREPARED);
+
+    mPrepareResult = OK;
+    mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
+    mFlags |= PREPARED;
+    mPreparedCondition.broadcast();
+}
+
+status_t AAH_TXPlayer::start() {
+    Mutex::Autolock autoLock(mLock);
+
+    mFlags &= ~CACHE_UNDERRUN;
+
+    return play_l();
+}
+
+status_t AAH_TXPlayer::play_l() {
+    if (mFlags & PLAYING) {
+        return OK;
+    }
+
+    if (!(mFlags & PREPARED)) {
+        return INVALID_OPERATION;
+    }
+
+    {
+        Mutex::Autolock lock(mEndpointLock);
+        if (!mEndpointValid) {
+            return INVALID_OPERATION;
+        }
+        if (!mEndpointRegistered) {
+            mProgramID = mAAH_Sender->registerEndpoint(mEndpoint);
+            mEndpointRegistered = true;
+        }
+    }
+
+    mFlags |= PLAYING;
+
+    updateClockTransform_l(false);
+
+    postPumpAudioEvent_l(-1);
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::stop() {
+    status_t ret = pause();
+    sendEOS_l();
+    return ret;
+}
+
+status_t AAH_TXPlayer::pause() {
+    Mutex::Autolock autoLock(mLock);
+
+    mFlags &= ~CACHE_UNDERRUN;
+
+    return pause_l();
+}
+
+status_t AAH_TXPlayer::pause_l(bool doClockUpdate) {
+    if (!(mFlags & PLAYING)) {
+        return OK;
+    }
+
+    cancelPlayerEvents(true /* keepBufferingGoing */);
+
+    mFlags &= ~PLAYING;
+
+    if (doClockUpdate) {
+        updateClockTransform_l(true);
+    }
+
+    return OK;
+}
+
+void AAH_TXPlayer::updateClockTransform_l(bool pause) {
+    // record the new pause status so that onPumpAudio knows what rate to apply
+    // when it initializes the transform
+    mPlayRateIsPaused = pause;
+
+    // if we haven't yet established a valid clock transform, then we can't
+    // do anything here
+    if (!mCurrentClockTransformValid) {
+        return;
+    }
+
+    // sample the current common time
+    int64_t commonTimeNow;
+    if (OK != mCCHelper.getCommonTime(&commonTimeNow)) {
+        ALOGE("updateClockTransform_l get common time failed");
+        mCurrentClockTransformValid = false;
+        return;
+    }
+
+    // convert the current common time to media time using the old
+    // transform
+    int64_t mediaTimeNow;
+    if (!mCurrentClockTransform.doReverseTransform(
+            commonTimeNow, &mediaTimeNow)) {
+        ALOGE("updateClockTransform_l reverse transform failed");
+        mCurrentClockTransformValid = false;
+        return;
+    }
+
+    // calculate a new transform that preserves the old transform's
+    // result for the current time
+    mCurrentClockTransform.a_zero = mediaTimeNow;
+    mCurrentClockTransform.b_zero = commonTimeNow;
+    mCurrentClockTransform.a_to_b_numer = 1;
+    mCurrentClockTransform.a_to_b_denom = pause ? 0 : 1;
+
+    // send a packet announcing the new transform
+    sp<TRTPControlPacket> packet = new TRTPControlPacket();
+    packet->setClockTransform(mCurrentClockTransform);
+    packet->setCommandID(TRTPControlPacket::kCommandNop);
+    queuePacketToSender_l(packet);
+}
+
+void AAH_TXPlayer::sendEOS_l() {
+    sp<TRTPControlPacket> packet = new TRTPControlPacket();
+    packet->setCommandID(TRTPControlPacket::kCommandEOS);
+    queuePacketToSender_l(packet);
+}
+
+bool AAH_TXPlayer::isPlaying() {
+    return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN);
+}
+
+status_t AAH_TXPlayer::seekTo(int msec) {
+    if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
+        Mutex::Autolock autoLock(mLock);
+        return seekTo_l(static_cast<int64_t>(msec) * 1000);
+    }
+
+    notifyListener_l(MEDIA_SEEK_COMPLETE);
+    return OK;
+}
+
+status_t AAH_TXPlayer::seekTo_l(int64_t timeUs) {
+    mIsSeeking = true;
+    mSeekTimeUs = timeUs;
+
+    mCurrentClockTransformValid = false;
+    mLastQueuedMediaTimePTSValid = false;
+
+    // send a flush command packet
+    sp<TRTPControlPacket> packet = new TRTPControlPacket();
+    packet->setCommandID(TRTPControlPacket::kCommandFlush);
+    queuePacketToSender_l(packet);
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::getCurrentPosition(int *msec) {
+    if (!msec) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mLock);
+
+    int position;
+
+    if (mIsSeeking) {
+        position = mSeekTimeUs / 1000;
+    } else if (mCurrentClockTransformValid) {
+        // sample the current common time
+        int64_t commonTimeNow;
+        if (OK != mCCHelper.getCommonTime(&commonTimeNow)) {
+            ALOGE("getCurrentPosition get common time failed");
+            return INVALID_OPERATION;
+        }
+
+        int64_t mediaTimeNow;
+        if (!mCurrentClockTransform.doReverseTransform(commonTimeNow,
+                    &mediaTimeNow)) {
+            ALOGE("getCurrentPosition reverse transform failed");
+            return INVALID_OPERATION;
+        }
+
+        position = static_cast<int>(mediaTimeNow / 1000);
+    } else {
+        position = 0;
+    }
+
+    int duration;
+    if (getDuration_l(&duration) == OK) {
+        *msec = clamp(position, 0, duration);
+    } else {
+        *msec = (position >= 0) ? position : 0;
+    }
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::getDuration(int* msec) {
+    if (!msec) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mLock);
+
+    return getDuration_l(msec);
+}
+
+status_t AAH_TXPlayer::getDuration_l(int* msec) {
+    if (mDurationUs < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    *msec = (mDurationUs + 500) / 1000;
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::reset() {
+    Mutex::Autolock autoLock(mLock);
+    reset_l();
+    return OK;
+}
+
+void AAH_TXPlayer::reset_l() {
+    if (mFlags & PREPARING) {
+        mFlags |= PREPARE_CANCELLED;
+        if (mConnectingDataSource != NULL) {
+            ALOGI("interrupting the connection process");
+            mConnectingDataSource->disconnect();
+        }
+
+        if (mFlags & PREPARING_CONNECTED) {
+            // We are basically done preparing, we're just buffering
+            // enough data to start playback, we can safely interrupt that.
+            finishAsyncPrepare_l();
+        }
+    }
+
+    while (mFlags & PREPARING) {
+        mPreparedCondition.wait(mLock);
+    }
+
+    cancelPlayerEvents();
+
+    sendEOS_l();
+
+    mCachedSource.clear();
+
+    if (mAudioSource != NULL) {
+        mAudioSource->stop();
+    }
+    mAudioSource.clear();
+
+    mFlags = 0;
+    mExtractorFlags = 0;
+
+    mDurationUs = -1;
+    mIsSeeking = false;
+    mSeekTimeUs = 0;
+
+    mUri.setTo("");
+    mUriHeaders.clear();
+
+    mFileSource.clear();
+
+    mBitrate = -1;
+
+    {
+        Mutex::Autolock lock(mEndpointLock);
+        if (mAAH_Sender != NULL && mEndpointRegistered) {
+            mAAH_Sender->unregisterEndpoint(mEndpoint);
+        }
+        mEndpointRegistered = false;
+        mEndpointValid = false;
+    }
+
+    mProgramID = 0;
+
+    mAAH_Sender.clear();
+    mLastQueuedMediaTimePTSValid = false;
+    mCurrentClockTransformValid = false;
+    mPlayRateIsPaused = false;
+
+    mTRTPVolume = 255;
+}
+
+status_t AAH_TXPlayer::setLooping(int loop) {
+    return OK;
+}
+
+player_type AAH_TXPlayer::playerType() {
+    return AAH_TX_PLAYER;
+}
+
+status_t AAH_TXPlayer::setParameter(int key, const Parcel &request) {
+    return ERROR_UNSUPPORTED;
+}
+
+status_t AAH_TXPlayer::getParameter(int key, Parcel *reply) {
+    return ERROR_UNSUPPORTED;
+}
+
+status_t AAH_TXPlayer::invoke(const Parcel& request, Parcel *reply) {
+    if (!reply) {
+        return BAD_VALUE;
+    }
+
+    int32_t methodID;
+    status_t err = request.readInt32(&methodID);
+    if (err != android::OK) {
+        return err;
+    }
+
+    switch (methodID) {
+        case kInvokeSetAAHDstIPPort:
+        case kInvokeSetAAHConfigBlob: {
+            if (mEndpointValid) {
+                return INVALID_OPERATION;
+            }
+
+            String8 addr;
+            uint16_t port;
+
+            if (methodID == kInvokeSetAAHDstIPPort) {
+                addr = String8(request.readString16());
+
+                int32_t port32;
+                err = request.readInt32(&port32);
+                if (err != android::OK) {
+                    return err;
+                }
+                port = static_cast<uint16_t>(port32);
+            } else {
+                String8 blob(request.readString16());
+
+                char addr_buf[101];
+                if (sscanf(blob.string(), "V1:%100s %" SCNu16,
+                           addr_buf, &port) != 2) {
+                    return BAD_VALUE;
+                }
+                if (addr.setTo(addr_buf) != OK) {
+                    return NO_MEMORY;
+                }
+            }
+
+            struct hostent* ent = gethostbyname(addr.string());
+            if (ent == NULL) {
+                return ERROR_UNKNOWN_HOST;
+            }
+            if (!(ent->h_addrtype == AF_INET && ent->h_length == 4)) {
+                return BAD_VALUE;
+            }
+
+            Mutex::Autolock lock(mEndpointLock);
+            mEndpoint = AAH_TXSender::Endpoint(
+                        reinterpret_cast<struct in_addr*>(ent->h_addr)->s_addr,
+                        port);
+            mEndpointValid = true;
+            return OK;
+        };
+
+        default:
+            return INVALID_OPERATION;
+    }
+}
+
+status_t AAH_TXPlayer::getMetadata(const media::Metadata::Filter& ids,
+                                   Parcel* records) {
+    using media::Metadata;
+
+    Metadata metadata(records);
+
+    metadata.appendBool(Metadata::kPauseAvailable, true);
+    metadata.appendBool(Metadata::kSeekBackwardAvailable, false);
+    metadata.appendBool(Metadata::kSeekForwardAvailable, false);
+    metadata.appendBool(Metadata::kSeekAvailable, false);
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::setVolume(float leftVolume, float rightVolume) {
+    if (leftVolume != rightVolume) {
+        ALOGE("%s does not support per channel volume: %f, %f",
+              __PRETTY_FUNCTION__, leftVolume, rightVolume);
+    }
+
+    float volume = clamp(leftVolume, 0.0f, 1.0f);
+
+    Mutex::Autolock lock(mLock);
+    mTRTPVolume = static_cast<uint8_t>((leftVolume * 255.0) + 0.5);
+
+    return OK;
+}
+
+status_t AAH_TXPlayer::setAudioStreamType(audio_stream_type_t streamType) {
+    return OK;
+}
+
+void AAH_TXPlayer::notifyListener_l(int msg, int ext1, int ext2) {
+    sendEvent(msg, ext1, ext2);
+}
+
+bool AAH_TXPlayer::getBitrate_l(int64_t *bitrate) {
+    off64_t size;
+    if (mDurationUs >= 0 &&
+        mCachedSource != NULL &&
+        mCachedSource->getSize(&size) == OK) {
+        *bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
+        return true;
+    }
+
+    if (mBitrate >= 0) {
+        *bitrate = mBitrate;
+        return true;
+    }
+
+    *bitrate = 0;
+
+    return false;
+}
+
+// Returns true iff cached duration is available/applicable.
+bool AAH_TXPlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) {
+    int64_t bitrate;
+
+    if (mCachedSource != NULL && getBitrate_l(&bitrate)) {
+        status_t finalStatus;
+        size_t cachedDataRemaining = mCachedSource->approxDataRemaining(
+                                        &finalStatus);
+        *durationUs = cachedDataRemaining * 8000000ll / bitrate;
+        *eos = (finalStatus != OK);
+        return true;
+    }
+
+    return false;
+}
+
+void AAH_TXPlayer::ensureCacheIsFetching_l() {
+    if (mCachedSource != NULL) {
+        mCachedSource->resumeFetchingIfNecessary();
+    }
+}
+
+void AAH_TXPlayer::postBufferingEvent_l() {
+    if (mBufferingEventPending) {
+        return;
+    }
+    mBufferingEventPending = true;
+    mQueue.postEventWithDelay(mBufferingEvent, 1000000ll);
+}
+
+void AAH_TXPlayer::postPumpAudioEvent_l(int64_t delayUs) {
+    if (mPumpAudioEventPending) {
+        return;
+    }
+    mPumpAudioEventPending = true;
+    mQueue.postEventWithDelay(mPumpAudioEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void AAH_TXPlayer::onBufferingUpdate() {
+    Mutex::Autolock autoLock(mLock);
+    if (!mBufferingEventPending) {
+        return;
+    }
+    mBufferingEventPending = false;
+
+    if (mCachedSource != NULL) {
+        status_t finalStatus;
+        size_t cachedDataRemaining = mCachedSource->approxDataRemaining(
+                                        &finalStatus);
+        bool eos = (finalStatus != OK);
+
+        if (eos) {
+            if (finalStatus == ERROR_END_OF_STREAM) {
+                notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+            }
+            if (mFlags & PREPARING) {
+                ALOGV("cache has reached EOS, prepare is done.");
+                finishAsyncPrepare_l();
+            }
+        } else {
+            int64_t bitrate;
+            if (getBitrate_l(&bitrate)) {
+                size_t cachedSize = mCachedSource->cachedSize();
+                int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
+
+                int percentage = (100.0 * (double) cachedDurationUs)
+                               / mDurationUs;
+                if (percentage > 100) {
+                    percentage = 100;
+                }
+
+                notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
+            } else {
+                // We don't know the bitrate of the stream, use absolute size
+                // limits to maintain the cache.
+
+                if ((mFlags & PLAYING) &&
+                    !eos &&
+                    (cachedDataRemaining < kLowWaterMarkBytes)) {
+                    ALOGI("cache is running low (< %d) , pausing.",
+                          kLowWaterMarkBytes);
+                    mFlags |= CACHE_UNDERRUN;
+                    pause_l();
+                    ensureCacheIsFetching_l();
+                    notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+                } else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
+                    if (mFlags & CACHE_UNDERRUN) {
+                        ALOGI("cache has filled up (> %d), resuming.",
+                              kHighWaterMarkBytes);
+                        mFlags &= ~CACHE_UNDERRUN;
+                        play_l();
+                        notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+                    } else if (mFlags & PREPARING) {
+                        ALOGV("cache has filled up (> %d), prepare is done",
+                              kHighWaterMarkBytes);
+                        finishAsyncPrepare_l();
+                    }
+                }
+            }
+        }
+    }
+
+    int64_t cachedDurationUs;
+    bool eos;
+    if (getCachedDuration_l(&cachedDurationUs, &eos)) {
+        ALOGV("cachedDurationUs = %.2f secs, eos=%d",
+              cachedDurationUs / 1E6, eos);
+
+        if ((mFlags & PLAYING) &&
+            !eos &&
+            (cachedDurationUs < kLowWaterMarkUs)) {
+            ALOGI("cache is running low (%.2f secs) , pausing.",
+                  cachedDurationUs / 1E6);
+            mFlags |= CACHE_UNDERRUN;
+            pause_l();
+            ensureCacheIsFetching_l();
+            notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+        } else if (eos || cachedDurationUs > kHighWaterMarkUs) {
+            if (mFlags & CACHE_UNDERRUN) {
+                ALOGI("cache has filled up (%.2f secs), resuming.",
+                      cachedDurationUs / 1E6);
+                mFlags &= ~CACHE_UNDERRUN;
+                play_l();
+                notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+            } else if (mFlags & PREPARING) {
+                ALOGV("cache has filled up (%.2f secs), prepare is done",
+                        cachedDurationUs / 1E6);
+                finishAsyncPrepare_l();
+            }
+        }
+    }
+
+    postBufferingEvent_l();
+}
+
+void AAH_TXPlayer::onPumpAudio() {
+    while (true) {
+        Mutex::Autolock autoLock(mLock);
+        // If this flag is clear, its because someone has externally canceled
+        // this pump operation (probably because we a resetting/shutting down).
+        // Get out immediately, do not reschedule ourselves.
+        if (!mPumpAudioEventPending) {
+            return;
+        }
+
+        // Start by checking if there is still work to be doing.  If we have
+        // never queued a payload (so we don't know what the last queued PTS is)
+        // or we have never established a MediaTime->CommonTime transformation,
+        // then we have work to do (one time through this loop should establish
+        // both).  Otherwise, we want to keep a fixed amt of presentation time
+        // worth of data buffered.  If we cannot get common time (service is
+        // unavailable, or common time is undefined)) then we don't have a lot
+        // of good options here.  For now, signal an error up to the app level
+        // and shut down the transmission pump.
+        int64_t commonTimeNow;
+        if (OK != mCCHelper.getCommonTime(&commonTimeNow)) {
+            // Failed to get common time; either the service is down or common
+            // time is not synced.  Raise an error and shutdown the player.
+            ALOGE("*** Cannot pump audio, unable to fetch common time."
+                  "  Shutting down.");
+            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, UNKNOWN_ERROR);
+            mPumpAudioEventPending = false;
+            break;
+        }
+
+        if (mCurrentClockTransformValid && mLastQueuedMediaTimePTSValid) {
+            int64_t mediaTimeNow;
+            bool conversionResult = mCurrentClockTransform.doReverseTransform(
+                                        commonTimeNow,
+                                        &mediaTimeNow);
+            CHECK(conversionResult);
+
+            if ((mediaTimeNow +
+                 kAAHBufferTimeUs -
+                 mLastQueuedMediaTimePTS) <= 0) {
+                break;
+            }
+        }
+
+        MediaSource::ReadOptions options;
+        if (mIsSeeking) {
+            options.setSeekTo(mSeekTimeUs);
+        }
+
+        MediaBuffer* mediaBuffer;
+        status_t err = mAudioSource->read(&mediaBuffer, &options);
+        if (err != NO_ERROR) {
+            if (err == ERROR_END_OF_STREAM) {
+                ALOGI("*** %s reached end of stream", __PRETTY_FUNCTION__);
+                notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+                notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
+                pause_l(false);
+                sendEOS_l();
+            } else {
+                ALOGE("*** %s read failed err=%d", __PRETTY_FUNCTION__, err);
+            }
+            return;
+        }
+
+        if (mIsSeeking) {
+            mIsSeeking = false;
+            notifyListener_l(MEDIA_SEEK_COMPLETE);
+        }
+
+        uint8_t* data = (static_cast<uint8_t*>(mediaBuffer->data()) +
+                mediaBuffer->range_offset());
+        ALOGV("*** %s got media buffer data=[%02hhx %02hhx %02hhx %02hhx]"
+              " offset=%d length=%d", __PRETTY_FUNCTION__,
+              data[0], data[1], data[2], data[3],
+              mediaBuffer->range_offset(), mediaBuffer->range_length());
+
+        int64_t mediaTimeUs;
+        CHECK(mediaBuffer->meta_data()->findInt64(kKeyTime, &mediaTimeUs));
+        ALOGV("*** timeUs=%lld", mediaTimeUs);
+
+        if (!mCurrentClockTransformValid) {
+            if (OK == mCCHelper.getCommonTime(&commonTimeNow)) {
+                mCurrentClockTransform.a_zero = mediaTimeUs;
+                mCurrentClockTransform.b_zero = commonTimeNow +
+                                                kAAHStartupLeadTimeUs;
+                mCurrentClockTransform.a_to_b_numer = 1;
+                mCurrentClockTransform.a_to_b_denom = mPlayRateIsPaused ? 0 : 1;
+                mCurrentClockTransformValid = true;
+            } else {
+                // Failed to get common time; either the service is down or
+                // common time is not synced.  Raise an error and shutdown the
+                // player.
+                ALOGE("*** Cannot begin transmission, unable to fetch common"
+                      " time. Dropping sample with pts=%lld", mediaTimeUs);
+                notifyListener_l(MEDIA_ERROR,
+                                 MEDIA_ERROR_UNKNOWN,
+                                 UNKNOWN_ERROR);
+                mPumpAudioEventPending = false;
+                break;
+            }
+        }
+
+        ALOGV("*** transmitting packet with pts=%lld", mediaTimeUs);
+
+        sp<TRTPAudioPacket> packet = new TRTPAudioPacket();
+        packet->setPTS(mediaTimeUs);
+        packet->setSubstreamID(1);
+
+        packet->setCodecType(TRTPAudioPacket::kCodecMPEG1Audio);
+        packet->setVolume(mTRTPVolume);
+        // TODO : introduce a throttle for this so we can control the
+        // frequency with which transforms get sent.
+        packet->setClockTransform(mCurrentClockTransform);
+        packet->setAccessUnitData(data, mediaBuffer->range_length());
+        packet->setRandomAccessPoint(true);
+
+        queuePacketToSender_l(packet);
+        mediaBuffer->release();
+
+        mLastQueuedMediaTimePTSValid = true;
+        mLastQueuedMediaTimePTS = mediaTimeUs;
+    }
+
+    { // Explicit scope for the autolock pattern.
+        Mutex::Autolock autoLock(mLock);
+
+        // If someone externally has cleared this flag, its because we should be
+        // shutting down.  Do not reschedule ourselves.
+        if (!mPumpAudioEventPending) {
+            return;
+        }
+
+        // Looks like no one canceled us explicitly.  Clear our flag and post a
+        // new event to ourselves.
+        mPumpAudioEventPending = false;
+        postPumpAudioEvent_l(10000);
+    }
+}
+
+void AAH_TXPlayer::queuePacketToSender_l(const sp<TRTPPacket>& packet) {
+    if (mAAH_Sender == NULL) {
+        return;
+    }
+
+    sp<AMessage> message = new AMessage(AAH_TXSender::kWhatSendPacket,
+                                        mAAH_Sender->handlerID());
+
+    {
+        Mutex::Autolock lock(mEndpointLock);
+        if (!mEndpointValid) {
+            return;
+        }
+
+        message->setInt32(AAH_TXSender::kSendPacketIPAddr, mEndpoint.addr);
+        message->setInt32(AAH_TXSender::kSendPacketPort, mEndpoint.port);
+    }
+
+    packet->setProgramID(mProgramID);
+    packet->setExpireTime(systemTime() + kAAHRetryKeepAroundTimeNs);
+    packet->pack();
+
+    message->setObject(AAH_TXSender::kSendPacketTRTPPacket, packet);
+
+    message->post();
+}
+
+}  // namespace android
diff --git a/media/libaah_rtp/aah_tx_player.h b/media/libaah_rtp/aah_tx_player.h
new file mode 100644
index 0000000..64cf5dc1
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_player.h
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+#ifndef __AAH_TX_PLAYER_H__
+#define __AAH_TX_PLAYER_H__
+
+#include <common_time/cc_helper.h>
+#include <libstagefright/include/HTTPBase.h>
+#include <libstagefright/include/NuCachedSource2.h>
+#include <libstagefright/include/TimedEventQueue.h>
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/LinearTransform.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include "aah_tx_sender.h"
+
+namespace android {
+
+class AAH_TXPlayer : public MediaPlayerHWInterface {
+  public:
+    AAH_TXPlayer();
+
+    virtual status_t    initCheck();
+    virtual status_t    setDataSource(const char *url,
+                                      const KeyedVector<String8, String8>*
+                                      headers);
+    virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
+    virtual status_t    setVideoSurface(const sp<Surface>& surface);
+    virtual status_t    setVideoSurfaceTexture(const sp<ISurfaceTexture>&
+                                               surfaceTexture);
+    virtual status_t    prepare();
+    virtual status_t    prepareAsync();
+    virtual status_t    start();
+    virtual status_t    stop();
+    virtual status_t    pause();
+    virtual bool        isPlaying();
+    virtual status_t    seekTo(int msec);
+    virtual status_t    getCurrentPosition(int *msec);
+    virtual status_t    getDuration(int *msec);
+    virtual status_t    reset();
+    virtual status_t    setLooping(int loop);
+    virtual player_type playerType();
+    virtual status_t    setParameter(int key, const Parcel &request);
+    virtual status_t    getParameter(int key, Parcel *reply);
+    virtual status_t    invoke(const Parcel& request, Parcel *reply);
+    virtual status_t    getMetadata(const media::Metadata::Filter& ids,
+                                    Parcel* records);
+    virtual status_t    setVolume(float leftVolume, float rightVolume);
+    virtual status_t    setAudioStreamType(audio_stream_type_t streamType);
+
+    // invoke method IDs
+    enum {
+        // set the IP address and port of the A@H receiver
+        kInvokeSetAAHDstIPPort = 1,
+
+        // set the destination IP address and port (and perhaps any additional
+        // parameters added in the future) packaged in one string
+        kInvokeSetAAHConfigBlob,
+    };
+
+    static const int64_t kAAHRetryKeepAroundTimeNs;
+
+  protected:
+    virtual ~AAH_TXPlayer();
+
+  private:
+    friend struct AwesomeEvent;
+
+    enum {
+        PLAYING             = 1,
+        PREPARING           = 8,
+        PREPARED            = 16,
+        PREPARE_CANCELLED   = 64,
+        CACHE_UNDERRUN      = 128,
+
+        // We are basically done preparing but are currently buffering
+        // sufficient data to begin playback and finish the preparation
+        // phase for good.
+        PREPARING_CONNECTED = 2048,
+
+        INCOGNITO           = 32768,
+    };
+
+    status_t setDataSource_l(const char *url,
+                             const KeyedVector<String8, String8> *headers);
+    status_t setDataSource_l(const sp<MediaExtractor>& extractor);
+    status_t finishSetDataSource_l();
+    status_t prepareAsync_l();
+    void onPrepareAsyncEvent();
+    void finishAsyncPrepare_l();
+    void abortPrepare(status_t err);
+    status_t play_l();
+    status_t pause_l(bool doClockUpdate = true);
+    status_t seekTo_l(int64_t timeUs);
+    void updateClockTransform_l(bool pause);
+    void sendEOS_l();
+    void cancelPlayerEvents(bool keepBufferingGoing = false);
+    void reset_l();
+    void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0);
+    bool getBitrate_l(int64_t* bitrate);
+    status_t getDuration_l(int* msec);
+    bool getCachedDuration_l(int64_t* durationUs, bool* eos);
+    void ensureCacheIsFetching_l();
+    void postBufferingEvent_l();
+    void postPumpAudioEvent_l(int64_t delayUs);
+    void onBufferingUpdate();
+    void onPumpAudio();
+    void queuePacketToSender_l(const sp<TRTPPacket>& packet);
+
+    Mutex mLock;
+
+    TimedEventQueue mQueue;
+    bool mQueueStarted;
+
+    sp<TimedEventQueue::Event> mBufferingEvent;
+    bool mBufferingEventPending;
+
+    uint32_t mFlags;
+    uint32_t mExtractorFlags;
+
+    String8 mUri;
+    KeyedVector<String8, String8> mUriHeaders;
+
+    sp<DataSource> mFileSource;
+
+    sp<TimedEventQueue::Event> mAsyncPrepareEvent;
+    Condition mPreparedCondition;
+    status_t mPrepareResult;
+
+    bool mIsSeeking;
+    int64_t mSeekTimeUs;
+
+    sp<TimedEventQueue::Event> mPumpAudioEvent;
+    bool mPumpAudioEventPending;
+
+    sp<HTTPBase> mConnectingDataSource;
+    sp<NuCachedSource2> mCachedSource;
+
+    sp<MediaSource> mAudioSource;
+    int64_t mDurationUs;
+    int64_t mBitrate;
+
+    sp<AAH_TXSender> mAAH_Sender;
+    LinearTransform  mCurrentClockTransform;
+    bool             mCurrentClockTransformValid;
+    int64_t          mLastQueuedMediaTimePTS;
+    bool             mLastQueuedMediaTimePTSValid;
+    bool             mPlayRateIsPaused;
+    CCHelper         mCCHelper;
+
+    Mutex mEndpointLock;
+    AAH_TXSender::Endpoint mEndpoint;
+    bool mEndpointValid;
+    bool mEndpointRegistered;
+    uint16_t mProgramID;
+    uint8_t mTRTPVolume;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AAH_TXPlayer);
+};
+
+}  // namespace android
+
+#endif  // __AAH_TX_PLAYER_H__
diff --git a/media/libaah_rtp/aah_tx_sender.cpp b/media/libaah_rtp/aah_tx_sender.cpp
new file mode 100644
index 0000000..d991ea7
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_sender.cpp
@@ -0,0 +1,602 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <utils/misc.h>
+
+#include "aah_tx_player.h"
+#include "aah_tx_sender.h"
+
+namespace android {
+
+const char* AAH_TXSender::kSendPacketIPAddr = "ipaddr";
+const char* AAH_TXSender::kSendPacketPort = "port";
+const char* AAH_TXSender::kSendPacketTRTPPacket = "trtp";
+
+const int AAH_TXSender::kRetryTrimIntervalUs = 100000;
+const int AAH_TXSender::kHeartbeatIntervalUs = 1000000;
+const int AAH_TXSender::kRetryBufferCapacity = 100;
+const nsecs_t AAH_TXSender::kHeartbeatTimeout = 600ull * 1000000000ull;
+
+Mutex AAH_TXSender::sLock;
+wp<AAH_TXSender> AAH_TXSender::sInstance;
+uint32_t AAH_TXSender::sNextEpoch;
+bool AAH_TXSender::sNextEpochValid = false;
+
+AAH_TXSender::AAH_TXSender() : mSocket(-1) {
+    mLastSentPacketTime = systemTime();
+}
+
+sp<AAH_TXSender> AAH_TXSender::GetInstance() {
+    Mutex::Autolock autoLock(sLock);
+
+    sp<AAH_TXSender> sender = sInstance.promote();
+
+    if (sender == NULL) {
+        sender = new AAH_TXSender();
+        if (sender == NULL) {
+            return NULL;
+        }
+
+        sender->mLooper = new ALooper();
+        if (sender->mLooper == NULL) {
+            return NULL;
+        }
+
+        sender->mReflector = new AHandlerReflector<AAH_TXSender>(sender.get());
+        if (sender->mReflector == NULL) {
+            return NULL;
+        }
+
+        sender->mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+        if (sender->mSocket == -1) {
+            ALOGW("%s unable to create socket", __PRETTY_FUNCTION__);
+            return NULL;
+        }
+
+        struct sockaddr_in bind_addr;
+        memset(&bind_addr, 0, sizeof(bind_addr));
+        bind_addr.sin_family = AF_INET;
+        if (bind(sender->mSocket,
+                 reinterpret_cast<const sockaddr*>(&bind_addr),
+                 sizeof(bind_addr)) < 0) {
+            ALOGW("%s unable to bind socket (errno %d)",
+                  __PRETTY_FUNCTION__, errno);
+            return NULL;
+        }
+
+        sender->mRetryReceiver = new RetryReceiver(sender.get());
+        if (sender->mRetryReceiver == NULL) {
+            return NULL;
+        }
+
+        sender->mLooper->setName("AAH_TXSender");
+        sender->mLooper->registerHandler(sender->mReflector);
+        sender->mLooper->start(false, false, PRIORITY_AUDIO);
+
+        if (sender->mRetryReceiver->run("AAH_TXSenderRetry", PRIORITY_AUDIO)
+                != OK) {
+            ALOGW("%s unable to start retry thread", __PRETTY_FUNCTION__);
+            return NULL;
+        }
+
+        sInstance = sender;
+    }
+
+    return sender;
+}
+
+AAH_TXSender::~AAH_TXSender() {
+    mLooper->stop();
+    mLooper->unregisterHandler(mReflector->id());
+
+    if (mRetryReceiver != NULL) {
+        mRetryReceiver->requestExit();
+        mRetryReceiver->mWakeupEvent.setEvent();
+        if (mRetryReceiver->requestExitAndWait() != OK) {
+            ALOGW("%s shutdown of retry receiver failed", __PRETTY_FUNCTION__);
+        }
+        mRetryReceiver->mSender = NULL;
+        mRetryReceiver.clear();
+    }
+
+    if (mSocket != -1) {
+        close(mSocket);
+    }
+}
+
+// Return the next epoch number usable for a newly instantiated endpoint.
+uint32_t AAH_TXSender::getNextEpoch() {
+    Mutex::Autolock autoLock(sLock);
+
+    if (sNextEpochValid) {
+        sNextEpoch = (sNextEpoch + 1) & TRTPPacket::kTRTPEpochMask;
+    } else {
+        sNextEpoch = ns2ms(systemTime()) & TRTPPacket::kTRTPEpochMask;
+        sNextEpochValid = true;
+    }
+
+    return sNextEpoch;
+}
+
+// Notify the sender that a player has started sending to this endpoint.
+// Returns a program ID for use by the calling player.
+uint16_t AAH_TXSender::registerEndpoint(const Endpoint& endpoint) {
+    Mutex::Autolock lock(mEndpointLock);
+
+    EndpointState* eps = mEndpointMap.valueFor(endpoint);
+    if (eps) {
+        eps->playerRefCount++;
+    } else {
+        eps = new EndpointState(getNextEpoch());
+        mEndpointMap.add(endpoint, eps);
+    }
+
+    // if this is the first registered endpoint, then send a message to start
+    // trimming retry buffers and a message to start sending heartbeats.
+    if (mEndpointMap.size() == 1) {
+        sp<AMessage> trimMessage = new AMessage(kWhatTrimRetryBuffers,
+                                                handlerID());
+        trimMessage->post(kRetryTrimIntervalUs);
+
+        sp<AMessage> heartbeatMessage = new AMessage(kWhatSendHeartbeats,
+                                                     handlerID());
+        heartbeatMessage->post(kHeartbeatIntervalUs);
+    }
+
+    eps->nextProgramID++;
+    return eps->nextProgramID;
+}
+
+// Notify the sender that a player has ceased sending to this endpoint.
+// An endpoint's state can not be deleted until all of the endpoint's
+// registered players have called unregisterEndpoint.
+void AAH_TXSender::unregisterEndpoint(const Endpoint& endpoint) {
+    Mutex::Autolock lock(mEndpointLock);
+
+    EndpointState* eps = mEndpointMap.valueFor(endpoint);
+    if (eps) {
+        eps->playerRefCount--;
+        CHECK(eps->playerRefCount >= 0);
+    }
+}
+
+void AAH_TXSender::onMessageReceived(const sp<AMessage>& msg) {
+    switch (msg->what()) {
+        case kWhatSendPacket:
+            onSendPacket(msg);
+            break;
+
+        case kWhatTrimRetryBuffers:
+            trimRetryBuffers();
+            break;
+
+        case kWhatSendHeartbeats:
+            sendHeartbeats();
+            break;
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void AAH_TXSender::onSendPacket(const sp<AMessage>& msg) {
+    sp<RefBase> obj;
+    CHECK(msg->findObject(kSendPacketTRTPPacket, &obj));
+    sp<TRTPPacket> packet = static_cast<TRTPPacket*>(obj.get());
+
+    uint32_t ipAddr;
+    CHECK(msg->findInt32(kSendPacketIPAddr,
+                         reinterpret_cast<int32_t*>(&ipAddr)));
+
+    int32_t port32;
+    CHECK(msg->findInt32(kSendPacketPort, &port32));
+    uint16_t port = port32;
+
+    Mutex::Autolock lock(mEndpointLock);
+    doSendPacket_l(packet, Endpoint(ipAddr, port));
+    mLastSentPacketTime = systemTime();
+}
+
+void AAH_TXSender::doSendPacket_l(const sp<TRTPPacket>& packet,
+                                  const Endpoint& endpoint) {
+    EndpointState* eps = mEndpointMap.valueFor(endpoint);
+    if (!eps) {
+        // the endpoint state has disappeared, so the player that sent this
+        // packet must be dead.
+        return;
+    }
+
+    // assign the packet's sequence number
+    packet->setEpoch(eps->epoch);
+    packet->setSeqNumber(eps->trtpSeqNumber++);
+
+    // add the packet to the retry buffer
+    RetryBuffer& retry = eps->retry;
+    retry.push_back(packet);
+
+    // send the packet
+    struct sockaddr_in addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = endpoint.addr;
+    addr.sin_port = htons(endpoint.port);
+
+    ssize_t result = sendto(mSocket,
+                            packet->getPacket(),
+                            packet->getPacketLen(),
+                            0,
+                            (const struct sockaddr *) &addr,
+                            sizeof(addr));
+    if (result == -1) {
+        ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+    }
+}
+
+void AAH_TXSender::trimRetryBuffers() {
+    Mutex::Autolock lock(mEndpointLock);
+
+    nsecs_t localTimeNow = systemTime();
+
+    Vector<Endpoint> endpointsToRemove;
+
+    for (size_t i = 0; i < mEndpointMap.size(); i++) {
+        EndpointState* eps = mEndpointMap.editValueAt(i);
+        RetryBuffer& retry = eps->retry;
+
+        while (!retry.isEmpty()) {
+            if (retry[0]->getExpireTime() < localTimeNow) {
+                retry.pop_front();
+            } else {
+                break;
+            }
+        }
+
+        if (retry.isEmpty() && eps->playerRefCount == 0) {
+            endpointsToRemove.add(mEndpointMap.keyAt(i));
+        }
+    }
+
+    // remove the state for any endpoints that are no longer in use
+    for (size_t i = 0; i < endpointsToRemove.size(); i++) {
+        Endpoint& e = endpointsToRemove.editItemAt(i);
+        ALOGD("*** %s removing endpoint addr=%08x", __PRETTY_FUNCTION__, e.addr);
+        size_t index = mEndpointMap.indexOfKey(e);
+        delete mEndpointMap.valueAt(index);
+        mEndpointMap.removeItemsAt(index);
+    }
+
+    // schedule the next trim
+    if (mEndpointMap.size()) {
+        sp<AMessage> trimMessage = new AMessage(kWhatTrimRetryBuffers,
+                                                handlerID());
+        trimMessage->post(kRetryTrimIntervalUs);
+    }
+}
+
+void AAH_TXSender::sendHeartbeats() {
+    Mutex::Autolock lock(mEndpointLock);
+
+    if (shouldSendHeartbeats_l()) {
+        for (size_t i = 0; i < mEndpointMap.size(); i++) {
+            EndpointState* eps = mEndpointMap.editValueAt(i);
+            const Endpoint& ep = mEndpointMap.keyAt(i);
+
+            sp<TRTPControlPacket> packet = new TRTPControlPacket();
+            packet->setCommandID(TRTPControlPacket::kCommandNop);
+
+            packet->setExpireTime(systemTime() +
+                                  AAH_TXPlayer::kAAHRetryKeepAroundTimeNs);
+            packet->pack();
+
+            doSendPacket_l(packet, ep);
+        }
+    }
+
+    // schedule the next heartbeat
+    if (mEndpointMap.size()) {
+        sp<AMessage> heartbeatMessage = new AMessage(kWhatSendHeartbeats,
+                                                     handlerID());
+        heartbeatMessage->post(kHeartbeatIntervalUs);
+    }
+}
+
+bool AAH_TXSender::shouldSendHeartbeats_l() {
+    // assert(holding endpoint lock)
+    return (systemTime() < (mLastSentPacketTime + kHeartbeatTimeout));
+}
+
+// Receiver
+
+// initial 4-byte ID of a retry request packet
+const uint32_t AAH_TXSender::RetryReceiver::kRetryRequestID = 'Treq';
+
+// initial 4-byte ID of a retry NAK packet
+const uint32_t AAH_TXSender::RetryReceiver::kRetryNakID = 'Tnak';
+
+// initial 4-byte ID of a fast start request packet
+const uint32_t AAH_TXSender::RetryReceiver::kFastStartRequestID = 'Tfst';
+
+AAH_TXSender::RetryReceiver::RetryReceiver(AAH_TXSender* sender)
+        : Thread(false),
+    mSender(sender) {}
+
+    AAH_TXSender::RetryReceiver::~RetryReceiver() {
+        mWakeupEvent.clearPendingEvents();
+    }
+
+// Returns true if val is within the interval bounded inclusively by
+// start and end.  Also handles the case where there is a rollover of the
+// range between start and end.
+template <typename T>
+static inline bool withinIntervalWithRollover(T val, T start, T end) {
+    return ((start <= end && val >= start && val <= end) ||
+            (start > end && (val >= start || val <= end)));
+}
+
+bool AAH_TXSender::RetryReceiver::threadLoop() {
+    struct pollfd pollFds[2];
+    pollFds[0].fd = mSender->mSocket;
+    pollFds[0].events = POLLIN;
+    pollFds[0].revents = 0;
+    pollFds[1].fd = mWakeupEvent.getWakeupHandle();
+    pollFds[1].events = POLLIN;
+    pollFds[1].revents = 0;
+
+    int pollResult = poll(pollFds, NELEM(pollFds), -1);
+    if (pollResult == -1) {
+        ALOGE("%s poll failed", __PRETTY_FUNCTION__);
+        return false;
+    }
+
+    if (exitPending()) {
+        ALOGI("*** %s exiting", __PRETTY_FUNCTION__);
+        return false;
+    }
+
+    if (pollFds[0].revents) {
+        handleRetryRequest();
+    }
+
+    return true;
+}
+
+void AAH_TXSender::RetryReceiver::handleRetryRequest() {
+    ALOGV("*** RX %s start", __PRETTY_FUNCTION__);
+
+    RetryPacket request;
+    struct sockaddr requestSrcAddr;
+    socklen_t requestSrcAddrLen = sizeof(requestSrcAddr);
+
+    ssize_t result = recvfrom(mSender->mSocket, &request, sizeof(request), 0,
+                              &requestSrcAddr, &requestSrcAddrLen);
+    if (result == -1) {
+        ALOGE("%s recvfrom failed, errno=%d", __PRETTY_FUNCTION__, errno);
+        return;
+    }
+
+    if (static_cast<size_t>(result) < sizeof(RetryPacket)) {
+        ALOGW("%s short packet received", __PRETTY_FUNCTION__);
+        return;
+    }
+
+    uint32_t host_request_id = ntohl(request.id);
+    if ((host_request_id != kRetryRequestID) &&
+        (host_request_id != kFastStartRequestID)) {
+        ALOGW("%s received retry request with bogus ID (%08x)",
+                __PRETTY_FUNCTION__, host_request_id);
+        return;
+    }
+
+    Endpoint endpoint(request.endpointIP, ntohs(request.endpointPort));
+
+    Mutex::Autolock lock(mSender->mEndpointLock);
+
+    EndpointState* eps = mSender->mEndpointMap.valueFor(endpoint);
+
+    if (eps == NULL || eps->retry.isEmpty()) {
+        // we have no retry buffer or an empty retry buffer for this endpoint,
+        // so NAK the entire request
+        RetryPacket nak = request;
+        nak.id = htonl(kRetryNakID);
+        result = sendto(mSender->mSocket, &nak, sizeof(nak), 0,
+                        &requestSrcAddr, requestSrcAddrLen);
+        if (result == -1) {
+            ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+        }
+        return;
+    }
+
+    RetryBuffer& retry = eps->retry;
+
+    uint16_t startSeq = ntohs(request.seqStart);
+    uint16_t endSeq = ntohs(request.seqEnd);
+
+    uint16_t retryFirstSeq = retry[0]->getSeqNumber();
+    uint16_t retryLastSeq = retry[retry.size() - 1]->getSeqNumber();
+
+    // If this is a fast start, then force the start of the retry to match the
+    // start of the retransmit ring buffer (unless the end of the retransmit
+    // ring buffer is already past the point of fast start)
+    if ((host_request_id == kFastStartRequestID) &&
+        !((startSeq - retryFirstSeq) & 0x8000)) {
+        startSeq = retryFirstSeq;
+    }
+
+    int startIndex;
+    if (withinIntervalWithRollover(startSeq, retryFirstSeq, retryLastSeq)) {
+        startIndex = static_cast<uint16_t>(startSeq - retryFirstSeq);
+    } else {
+        startIndex = -1;
+    }
+
+    int endIndex;
+    if (withinIntervalWithRollover(endSeq, retryFirstSeq, retryLastSeq)) {
+        endIndex = static_cast<uint16_t>(endSeq - retryFirstSeq);
+    } else {
+        endIndex = -1;
+    }
+
+    if (startIndex == -1 && endIndex == -1) {
+        // no part of the request range is found in the retry buffer
+        RetryPacket nak = request;
+        nak.id = htonl(kRetryNakID);
+        result = sendto(mSender->mSocket, &nak, sizeof(nak), 0,
+                        &requestSrcAddr, requestSrcAddrLen);
+        if (result == -1) {
+            ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+        }
+        return;
+    }
+
+    if (startIndex == -1) {
+        // NAK a subrange at the front of the request range
+        RetryPacket nak = request;
+        nak.id = htonl(kRetryNakID);
+        nak.seqEnd = htons(retryFirstSeq - 1);
+        result = sendto(mSender->mSocket, &nak, sizeof(nak), 0,
+                        &requestSrcAddr, requestSrcAddrLen);
+        if (result == -1) {
+            ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+        }
+
+        startIndex = 0;
+    } else if (endIndex == -1) {
+        // NAK a subrange at the back of the request range
+        RetryPacket nak = request;
+        nak.id = htonl(kRetryNakID);
+        nak.seqStart = htons(retryLastSeq + 1);
+        result = sendto(mSender->mSocket, &nak, sizeof(nak), 0,
+                        &requestSrcAddr, requestSrcAddrLen);
+        if (result == -1) {
+            ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+        }
+
+        endIndex = retry.size() - 1;
+    }
+
+    // send the retry packets
+    for (int i = startIndex; i <= endIndex; i++) {
+        const sp<TRTPPacket>& replyPacket = retry[i];
+
+        result = sendto(mSender->mSocket,
+                        replyPacket->getPacket(),
+                        replyPacket->getPacketLen(),
+                        0,
+                        &requestSrcAddr,
+                        requestSrcAddrLen);
+
+        if (result == -1) {
+            ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+        }
+    }
+}
+
+// Endpoint
+
+AAH_TXSender::Endpoint::Endpoint()
+        : addr(0)
+        , port(0) { }
+
+AAH_TXSender::Endpoint::Endpoint(uint32_t a, uint16_t p)
+        : addr(a)
+        , port(p) {}
+
+bool AAH_TXSender::Endpoint::operator<(const Endpoint& other) const {
+    return ((addr < other.addr) ||
+            (addr == other.addr && port < other.port));
+}
+
+// EndpointState
+
+AAH_TXSender::EndpointState::EndpointState(uint32_t _epoch)
+    : retry(kRetryBufferCapacity)
+    , playerRefCount(1)
+    , trtpSeqNumber(0)
+    , nextProgramID(0)
+    , epoch(_epoch) { }
+
+// CircularBuffer
+
+template <typename T>
+CircularBuffer<T>::CircularBuffer(size_t capacity)
+        : mCapacity(capacity)
+        , mHead(0)
+        , mTail(0)
+        , mFillCount(0) {
+    mBuffer = new T[capacity];
+}
+
+template <typename T>
+CircularBuffer<T>::~CircularBuffer() {
+    delete [] mBuffer;
+}
+
+template <typename T>
+void CircularBuffer<T>::push_back(const T& item) {
+    if (this->isFull()) {
+        this->pop_front();
+    }
+    mBuffer[mHead] = item;
+    mHead = (mHead + 1) % mCapacity;
+    mFillCount++;
+}
+
+template <typename T>
+void CircularBuffer<T>::pop_front() {
+    CHECK(!isEmpty());
+    mBuffer[mTail] = T();
+    mTail = (mTail + 1) % mCapacity;
+    mFillCount--;
+}
+
+template <typename T>
+size_t CircularBuffer<T>::size() const {
+    return mFillCount;
+}
+
+template <typename T>
+bool CircularBuffer<T>::isFull() const {
+    return (mFillCount == mCapacity);
+}
+
+template <typename T>
+bool CircularBuffer<T>::isEmpty() const {
+    return (mFillCount == 0);
+}
+
+template <typename T>
+const T& CircularBuffer<T>::itemAt(size_t index) const {
+    CHECK(index < mFillCount);
+    return mBuffer[(mTail + index) % mCapacity];
+}
+
+template <typename T>
+const T& CircularBuffer<T>::operator[](size_t index) const {
+    return itemAt(index);
+}
+
+}  // namespace android
diff --git a/media/libaah_rtp/aah_tx_sender.h b/media/libaah_rtp/aah_tx_sender.h
new file mode 100644
index 0000000..74206c4
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_sender.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#ifndef __AAH_TX_SENDER_H__
+#define __AAH_TX_SENDER_H__
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+#include "aah_tx_packet.h"
+#include "pipe_event.h"
+
+namespace android {
+
+template <typename T> class CircularBuffer {
+  public:
+    CircularBuffer(size_t capacity);
+    ~CircularBuffer();
+    void push_back(const T& item);;
+    void pop_front();
+    size_t size() const;
+    bool isFull() const;
+    bool isEmpty() const;
+    const T& itemAt(size_t index) const;
+    const T& operator[](size_t index) const;
+
+  private:
+    T* mBuffer;
+    size_t mCapacity;
+    size_t mHead;
+    size_t mTail;
+    size_t mFillCount;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CircularBuffer);
+};
+
+class AAH_TXSender : public virtual RefBase {
+  public:
+    ~AAH_TXSender();
+
+    static sp<AAH_TXSender> GetInstance();
+
+    ALooper::handler_id handlerID() { return mReflector->id(); }
+
+    // an IP address and port
+    struct Endpoint {
+        Endpoint();
+        Endpoint(uint32_t a, uint16_t p);
+        bool operator<(const Endpoint& other) const;
+
+        uint32_t addr;
+        uint16_t port;
+    };
+
+    uint16_t registerEndpoint(const Endpoint& endpoint);
+    void unregisterEndpoint(const Endpoint& endpoint);
+
+    enum {
+        kWhatSendPacket,
+        kWhatTrimRetryBuffers,
+        kWhatSendHeartbeats,
+    };
+
+    // fields for SendPacket messages
+    static const char* kSendPacketIPAddr;
+    static const char* kSendPacketPort;
+    static const char* kSendPacketTRTPPacket;
+
+  private:
+    AAH_TXSender();
+
+    static Mutex sLock;
+    static wp<AAH_TXSender> sInstance;
+    static uint32_t sNextEpoch;
+    static bool sNextEpochValid;
+
+    static uint32_t getNextEpoch();
+
+    typedef CircularBuffer<sp<TRTPPacket> > RetryBuffer;
+
+    // state maintained on a per-endpoint basis
+    struct EndpointState {
+        EndpointState(uint32_t epoch);
+        RetryBuffer retry;
+        int playerRefCount;
+        uint16_t trtpSeqNumber;
+        uint16_t nextProgramID;
+        uint32_t epoch;
+    };
+
+    friend class AHandlerReflector<AAH_TXSender>;
+    void onMessageReceived(const sp<AMessage>& msg);
+    void onSendPacket(const sp<AMessage>& msg);
+    void doSendPacket_l(const sp<TRTPPacket>& packet,
+                        const Endpoint& endpoint);
+    void trimRetryBuffers();
+    void sendHeartbeats();
+    bool shouldSendHeartbeats_l();
+
+    sp<ALooper> mLooper;
+    sp<AHandlerReflector<AAH_TXSender> > mReflector;
+
+    int mSocket;
+    nsecs_t mLastSentPacketTime;
+
+    DefaultKeyedVector<Endpoint, EndpointState*> mEndpointMap;
+    Mutex mEndpointLock;
+
+    static const int kRetryTrimIntervalUs;
+    static const int kHeartbeatIntervalUs;
+    static const int kRetryBufferCapacity;
+    static const nsecs_t kHeartbeatTimeout;
+
+    class RetryReceiver : public Thread {
+      private:
+        friend class AAH_TXSender;
+
+        RetryReceiver(AAH_TXSender* sender);
+        virtual ~RetryReceiver();
+        virtual bool threadLoop();
+        void handleRetryRequest();
+
+        static const int kMaxReceiverPacketLen;
+        static const uint32_t kRetryRequestID;
+        static const uint32_t kFastStartRequestID;
+        static const uint32_t kRetryNakID;
+
+        AAH_TXSender* mSender;
+        PipeEvent mWakeupEvent;
+    };
+
+    sp<RetryReceiver> mRetryReceiver;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AAH_TXSender);
+};
+
+struct RetryPacket {
+    uint32_t id;
+    uint32_t endpointIP;
+    uint16_t endpointPort;
+    uint16_t seqStart;
+    uint16_t seqEnd;
+} __attribute__((packed));
+
+}  // namespace android
+
+#endif  // __AAH_TX_SENDER_H__
diff --git a/media/libaah_rtp/pipe_event.cpp b/media/libaah_rtp/pipe_event.cpp
new file mode 100644
index 0000000..b8e6960
--- /dev/null
+++ b/media/libaah_rtp/pipe_event.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "pipe_event.h"
+
+namespace android {
+
+PipeEvent::PipeEvent() {
+    pipe_[0] = -1;
+    pipe_[1] = -1;
+
+    // Create the pipe.
+    if (pipe(pipe_) >= 0) {
+        // Set non-blocking mode on the read side of the pipe so we can
+        // easily drain it whenever we wakeup.
+        fcntl(pipe_[0], F_SETFL, O_NONBLOCK);
+    } else {
+        ALOGE("Failed to create pipe event %d %d %d",
+              pipe_[0], pipe_[1], errno);
+        pipe_[0] = -1;
+        pipe_[1] = -1;
+    }
+}
+
+PipeEvent::~PipeEvent() {
+    if (pipe_[0] >= 0) {
+        close(pipe_[0]);
+    }
+
+    if (pipe_[1] >= 0) {
+        close(pipe_[1]);
+    }
+}
+
+void PipeEvent::clearPendingEvents() {
+    char drain_buffer[16];
+    while (read(pipe_[0], drain_buffer, sizeof(drain_buffer)) > 0) {
+        // No body.
+    }
+}
+
+bool PipeEvent::wait(int timeout) {
+    struct pollfd wait_fd;
+
+    wait_fd.fd = getWakeupHandle();
+    wait_fd.events = POLLIN;
+    wait_fd.revents = 0;
+
+    int res = poll(&wait_fd, 1, timeout);
+
+    if (res < 0) {
+        ALOGE("Wait error in PipeEvent; sleeping to prevent overload!");
+        usleep(1000);
+    }
+
+    return (res > 0);
+}
+
+void PipeEvent::setEvent() {
+    char foo = 'q';
+    write(pipe_[1], &foo, 1);
+}
+
+}  // namespace android
+
diff --git a/media/libaah_rtp/pipe_event.h b/media/libaah_rtp/pipe_event.h
new file mode 100644
index 0000000..e53b0fd
--- /dev/null
+++ b/media/libaah_rtp/pipe_event.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef __PIPE_EVENT_H__
+#define __PIPE_EVENT_H__
+
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+class PipeEvent {
+  public:
+    PipeEvent();
+   ~PipeEvent();
+
+    bool initCheck() const {
+        return ((pipe_[0] >= 0) && (pipe_[1] >= 0));
+    }
+
+    int getWakeupHandle() const { return pipe_[0]; }
+
+    // block until the event fires; returns true if the event fired and false if
+    // the wait timed out.  Timeout is expressed in milliseconds; negative
+    // values mean wait forever.
+    bool wait(int timeout = -1);
+
+    void clearPendingEvents();
+    void setEvent();
+
+  private:
+    int pipe_[2];
+
+    DISALLOW_EVIL_CONSTRUCTORS(PipeEvent);
+};
+
+}  // namespace android
+
+#endif  // __PIPE_EVENT_H__
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index 9f6599f..59cd9e3 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -53,8 +53,8 @@
 static lib_entry_t *getLibrary(const char *path);
 static void resetEffectEnumeration();
 static uint32_t updateNumEffects();
-static int findEffect(effect_uuid_t *type,
-               effect_uuid_t *uuid,
+static int findEffect(const effect_uuid_t *type,
+               const effect_uuid_t *uuid,
                lib_entry_t **lib,
                effect_descriptor_t **desc);
 static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
@@ -236,7 +236,7 @@
     return ret;
 }
 
-int EffectGetDescriptor(effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
+int EffectGetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
 {
     lib_entry_t *l = NULL;
     effect_descriptor_t *d = NULL;
@@ -257,7 +257,7 @@
     return ret;
 }
 
-int EffectCreate(effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
+int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
 {
     list_elem_t *e = gLibraryList;
     lib_entry_t *l = NULL;
@@ -372,7 +372,7 @@
     return ret;
 }
 
-int EffectIsNullUuid(effect_uuid_t *uuid)
+int EffectIsNullUuid(const effect_uuid_t *uuid)
 {
     if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
         return 0;
@@ -628,8 +628,8 @@
     return cnt;
 }
 
-int findEffect(effect_uuid_t *type,
-               effect_uuid_t *uuid,
+int findEffect(const effect_uuid_t *type,
+               const effect_uuid_t *uuid,
                lib_entry_t **lib,
                effect_descriptor_t **desc)
 {
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 108d36a..3714283 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -195,7 +195,7 @@
     return 0;
 }     /* end EffectQueryEffect */
 
-extern "C" int EffectCreate(effect_uuid_t       *uuid,
+extern "C" int EffectCreate(const effect_uuid_t *uuid,
                             int32_t             sessionId,
                             int32_t             ioId,
                             effect_handle_t  *pHandle){
@@ -471,7 +471,7 @@
 
 } /* end EffectRelease */
 
-extern "C" int EffectGetDescriptor(effect_uuid_t       *uuid,
+extern "C" int EffectGetDescriptor(const effect_uuid_t *uuid,
                                    effect_descriptor_t *pDescriptor) {
     const effect_descriptor_t *desc = NULL;
 
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 09cd5cc..358357e 100755
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -210,7 +210,7 @@
     return 0;
 }     /* end EffectQueryEffect */
 
-extern "C" int EffectCreate(effect_uuid_t       *uuid,
+extern "C" int EffectCreate(const effect_uuid_t *uuid,
                             int32_t             sessionId,
                             int32_t             ioId,
                             effect_handle_t  *pHandle){
@@ -317,7 +317,7 @@
     return 0;
 } /* end EffectRelease */
 
-extern "C" int EffectGetDescriptor(effect_uuid_t       *uuid,
+extern "C" int EffectGetDescriptor(const effect_uuid_t *uuid,
                                    effect_descriptor_t *pDescriptor) {
     int i;
     int length = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 9fd6764..098a1a2 100755
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -1072,7 +1072,7 @@
     return sInitStatus;
 }
 
-const effect_descriptor_t *PreProc_GetDescriptor(effect_uuid_t *uuid)
+const effect_descriptor_t *PreProc_GetDescriptor(const effect_uuid_t *uuid)
 {
     size_t i;
     for (i = 0; i < PREPROC_NUM_EFFECTS; i++) {
@@ -1298,7 +1298,7 @@
                 return -EINVAL;
             }
 
-            Session_GetConfig(effect->session, (effect_config_t *)pCmdData);
+            Session_GetConfig(effect->session, (effect_config_t *)pReplyData);
             break;
 
         case EFFECT_CMD_SET_CONFIG_REVERSE:
@@ -1568,7 +1568,7 @@
     return 0;
 }
 
-int PreProcessingLib_Create(effect_uuid_t       *uuid,
+int PreProcessingLib_Create(const effect_uuid_t *uuid,
                             int32_t             sessionId,
                             int32_t             ioId,
                             effect_handle_t  *pInterface)
@@ -1620,7 +1620,7 @@
     return Session_ReleaseEffect(fx->session, fx);
 }
 
-int PreProcessingLib_GetDescriptor(effect_uuid_t       *uuid,
+int PreProcessingLib_GetDescriptor(const effect_uuid_t *uuid,
                                    effect_descriptor_t *pDescriptor) {
 
     if (pDescriptor == NULL || uuid == NULL){
diff --git a/media/libeffects/testlibs/EffectEqualizer.cpp b/media/libeffects/testlibs/EffectEqualizer.cpp
index 5241660..35a4a61 100644
--- a/media/libeffects/testlibs/EffectEqualizer.cpp
+++ b/media/libeffects/testlibs/EffectEqualizer.cpp
@@ -140,7 +140,7 @@
     return 0;
 } /* end EffectQueryNext */
 
-extern "C" int EffectCreate(effect_uuid_t *uuid,
+extern "C" int EffectCreate(const effect_uuid_t *uuid,
                             int32_t sessionId,
                             int32_t ioId,
                             effect_handle_t *pHandle) {
@@ -195,7 +195,7 @@
     return 0;
 } /* end EffectRelease */
 
-extern "C" int EffectGetDescriptor(effect_uuid_t       *uuid,
+extern "C" int EffectGetDescriptor(const effect_uuid_t *uuid,
                                    effect_descriptor_t *pDescriptor) {
 
     if (pDescriptor == NULL || uuid == NULL){
diff --git a/media/libeffects/testlibs/EffectReverb.c b/media/libeffects/testlibs/EffectReverb.c
index ebb72c1..8351712 100644
--- a/media/libeffects/testlibs/EffectReverb.c
+++ b/media/libeffects/testlibs/EffectReverb.c
@@ -111,7 +111,7 @@
     return 0;
 }
 
-int EffectCreate(effect_uuid_t *uuid,
+int EffectCreate(const effect_uuid_t *uuid,
         int32_t sessionId,
         int32_t ioId,
         effect_handle_t *pHandle) {
@@ -182,7 +182,7 @@
     return 0;
 }
 
-int EffectGetDescriptor(effect_uuid_t       *uuid,
+int EffectGetDescriptor(const effect_uuid_t *uuid,
                         effect_descriptor_t *pDescriptor) {
     int i;
     int length = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
diff --git a/media/libeffects/testlibs/EffectReverb.h b/media/libeffects/testlibs/EffectReverb.h
index 5137074..1fb14a7 100644
--- a/media/libeffects/testlibs/EffectReverb.h
+++ b/media/libeffects/testlibs/EffectReverb.h
@@ -303,12 +303,12 @@
 int EffectQueryNumberEffects(uint32_t *pNumEffects);
 int EffectQueryEffect(uint32_t index,
                       effect_descriptor_t *pDescriptor);
-int EffectCreate(effect_uuid_t *effectUID,
+int EffectCreate(const effect_uuid_t *effectUID,
                  int32_t sessionId,
                  int32_t ioId,
                  effect_handle_t *pHandle);
 int EffectRelease(effect_handle_t handle);
-int EffectGetDescriptor(effect_uuid_t       *uuid,
+int EffectGetDescriptor(const effect_uuid_t *uuid,
                         effect_descriptor_t *pDescriptor);
 
 static int Reverb_Process(effect_handle_t self,
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 5d70a9b..51c8b68 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -190,7 +190,7 @@
     return 0;
 }
 
-int VisualizerLib_Create(effect_uuid_t *uuid,
+int VisualizerLib_Create(const effect_uuid_t *uuid,
                          int32_t sessionId,
                          int32_t ioId,
                          effect_handle_t *pHandle) {
@@ -240,7 +240,7 @@
     return 0;
 }
 
-int VisualizerLib_GetDescriptor(effect_uuid_t       *uuid,
+int VisualizerLib_GetDescriptor(const effect_uuid_t *uuid,
                                 effect_descriptor_t *pDescriptor) {
 
     if (pDescriptor == NULL || uuid == NULL){
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index a242846..6808aa2 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -159,7 +159,7 @@
     mCblk->buffer = (uint8_t *)mCblk + bufOffset;
 
     iEffect->asBinder()->linkToDeath(mIEffectClient);
-    ALOGV("set() %p OK effect: %s id: %d status %d enabled %d, ", this, mDescriptor.name, mId, mStatus, mEnabled);
+    ALOGV("set() %p OK effect: %s id: %d status %d enabled %d", this, mDescriptor.name, mId, mStatus, mEnabled);
 
     return mStatus;
 }
@@ -202,7 +202,7 @@
 status_t AudioEffect::setEnabled(bool enabled)
 {
     if (mStatus != NO_ERROR) {
-        return INVALID_OPERATION;
+        return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
     }
 
     status_t status = NO_ERROR;
@@ -231,7 +231,7 @@
 {
     if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
         ALOGV("command() bad status %d", mStatus);
-        return INVALID_OPERATION;
+        return mStatus;
     }
 
     if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) {
@@ -263,7 +263,7 @@
 status_t AudioEffect::setParameter(effect_param_t *param)
 {
     if (mStatus != NO_ERROR) {
-        return INVALID_OPERATION;
+        return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
     }
 
     if (param == NULL || param->psize == 0 || param->vsize == 0) {
@@ -281,7 +281,7 @@
 status_t AudioEffect::setParameterDeferred(effect_param_t *param)
 {
     if (mStatus != NO_ERROR) {
-        return INVALID_OPERATION;
+        return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
     }
 
     if (param == NULL || param->psize == 0 || param->vsize == 0) {
@@ -307,7 +307,7 @@
 status_t AudioEffect::setParameterCommit()
 {
     if (mStatus != NO_ERROR) {
-        return INVALID_OPERATION;
+        return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
     }
 
     Mutex::Autolock _l(mCblk->lock);
@@ -321,7 +321,7 @@
 status_t AudioEffect::getParameter(effect_param_t *param)
 {
     if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
-        return INVALID_OPERATION;
+        return mStatus;
     }
 
     if (param == NULL || param->psize == 0 || param->vsize == 0) {
@@ -341,7 +341,7 @@
 void AudioEffect::binderDied()
 {
     ALOGW("IEffect died");
-    mStatus = NO_INIT;
+    mStatus = DEAD_OBJECT;
     if (mCbf != NULL) {
         status_t status = DEAD_OBJECT;
         mCbf(EVENT_ERROR, mUserData, &status);
@@ -412,7 +412,8 @@
     return af->queryEffect(index, descriptor);
 }
 
-status_t AudioEffect::getEffectDescriptor(effect_uuid_t *uuid, effect_descriptor_t *descriptor)
+status_t AudioEffect::getEffectDescriptor(const effect_uuid_t *uuid,
+        effect_descriptor_t *descriptor) /*const*/
 {
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
     if (af == 0) return PERMISSION_DENIED;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index c96bc76..a4068ff 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -293,7 +293,6 @@
                 return WOULD_BLOCK;
             }
         }
-        t->mLock.lock();
      }
 
     AutoMutex lock(mLock);
@@ -305,10 +304,25 @@
     if (mActive == 0) {
         mActive = 1;
 
+        pid_t tid;
+        if (t != 0) {
+            mReadyToRun = WOULD_BLOCK;
+            t->run("ClientRecordThread", ANDROID_PRIORITY_AUDIO);
+            tid = t->getTid();  // pid_t is unknown until run()
+            ALOGV("getTid=%d", tid);
+            if (tid == -1) {
+                tid = 0;
+            }
+            // thread blocks in readyToRun()
+        } else {
+            tid = 0;    // not gettid()
+        }
+
         cblk->lock.lock();
         if (!(cblk->flags & CBLK_INVALID_MSK)) {
             cblk->lock.unlock();
-            ret = mAudioRecord->start();
+            ALOGV("mAudioRecord->start(tid=%d)", tid);
+            ret = mAudioRecord->start(tid);
             cblk->lock.lock();
             if (ret == DEAD_OBJECT) {
                 android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
@@ -323,7 +337,9 @@
             cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
             cblk->waitTimeMs = 0;
             if (t != 0) {
-                t->run("ClientRecordThread", ANDROID_PRIORITY_AUDIO);
+                // thread unblocks in readyToRun() and returns NO_ERROR
+                mReadyToRun = NO_ERROR;
+                mCondition.signal();
             } else {
                 mPreviousPriority = getpriority(PRIO_PROCESS, 0);
                 mPreviousSchedulingGroup = androidGetThreadSchedulingGroup(0);
@@ -331,13 +347,12 @@
             }
         } else {
             mActive = 0;
+            // thread unblocks in readyToRun() and returns NO_INIT
+            mReadyToRun = NO_INIT;
+            mCondition.signal();
         }
     }
 
-    if (t != 0) {
-        t->mLock.unlock();
-    }
-
     return ret;
 }
 
@@ -347,10 +362,6 @@
 
     ALOGV("stop");
 
-    if (t != 0) {
-        t->mLock.lock();
-    }
-
     AutoMutex lock(mLock);
     if (mActive == 1) {
         mActive = 0;
@@ -367,10 +378,6 @@
         }
     }
 
-    if (t != 0) {
-        t->mLock.unlock();
-    }
-
     return NO_ERROR;
 }
 
@@ -535,7 +542,7 @@
                     ALOGW(   "obtainBuffer timed out (is the CPU pegged?) "
                             "user=%08x, server=%08x", cblk->user, cblk->server);
                     cblk->lock.unlock();
-                    result = mAudioRecord->start();
+                    result = mAudioRecord->start(0);    // callback thread hasn't changed
                     cblk->lock.lock();
                     if (result == DEAD_OBJECT) {
                         android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
@@ -773,7 +780,7 @@
         result = openRecord_l(cblk->sampleRate, mFormat, mChannelMask,
                 mFrameCount, mFlags, getInput_l());
         if (result == NO_ERROR) {
-            result = mAudioRecord->start();
+            result = mAudioRecord->start(0);    // callback thread hasn't changed
         }
         if (result != NO_ERROR) {
             mActive = false;
@@ -824,6 +831,15 @@
     return mReceiver.processAudioBuffer(this);
 }
 
+status_t AudioRecord::ClientRecordThread::readyToRun()
+{
+    AutoMutex(mReceiver.mLock);
+    while (mReceiver.mReadyToRun == WOULD_BLOCK) {
+        mReceiver.mCondition.wait(mReceiver.mLock);
+    }
+    return mReceiver.mReadyToRun;
+}
+
 // -------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 110a294..ec4c044 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -121,7 +121,8 @@
     return NO_ERROR;
 }
 
-status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value, int output)
+status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,
+        audio_io_handle_t output)
 {
     if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
@@ -139,7 +140,8 @@
     return NO_ERROR;
 }
 
-status_t AudioSystem::getStreamVolume(audio_stream_type_t stream, float* volume, int output)
+status_t AudioSystem::getStreamVolume(audio_stream_type_t stream, float* volume,
+        audio_io_handle_t output)
 {
     if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
@@ -402,7 +404,8 @@
     ALOGW("AudioFlinger server died!");
 }
 
-void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, int ioHandle, void *param2) {
+void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle_t ioHandle,
+        void *param2) {
     ALOGV("ioConfigChanged() event %d", event);
     OutputDescriptor *desc;
     audio_stream_type_t stream;
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 8c33f41..74c97ed 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1,4 +1,4 @@
-/* frameworks/base/media/libmedia/AudioTrack.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -80,7 +80,9 @@
 
 AudioTrack::AudioTrack()
     : mStatus(NO_INIT),
-      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
+      mIsTimed(false),
+      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
+      mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
 {
 }
 
@@ -96,7 +98,9 @@
         int notificationFrames,
         int sessionId)
     : mStatus(NO_INIT),
-      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
+      mIsTimed(false),
+      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
+      mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
 {
     mStatus = set(streamType, sampleRate, format, channelMask,
             frameCount, flags, cbf, user, notificationFrames,
@@ -134,7 +138,9 @@
         int notificationFrames,
         int sessionId)
     : mStatus(NO_INIT),
-      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
+      mIsTimed(false),
+      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
+      mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
 {
     mStatus = set(streamType, sampleRate, format, channelMask,
             0, flags, cbf, user, notificationFrames,
@@ -345,7 +351,6 @@
                 return;
             }
         }
-        t->mLock.lock();
      }
 
     AutoMutex lock(mLock);
@@ -363,18 +368,26 @@
         cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
         cblk->waitTimeMs = 0;
         android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
+        pid_t tid;
         if (t != 0) {
             t->run("AudioTrackThread", ANDROID_PRIORITY_AUDIO);
+            tid = t->getTid();  // pid_t is unknown until run()
+            ALOGV("getTid=%d", tid);
+            if (tid == -1) {
+                tid = 0;
+            }
         } else {
             mPreviousPriority = getpriority(PRIO_PROCESS, 0);
             mPreviousSchedulingGroup = androidGetThreadSchedulingGroup(0);
             androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
+            tid = 0;    // not gettid()
         }
 
         ALOGV("start %p before lock cblk %p", this, mCblk);
         if (!(cblk->flags & CBLK_INVALID_MSK)) {
             cblk->lock.unlock();
-            status = mAudioTrack->start();
+            ALOGV("mAudioTrack->start(tid=%d)", tid);
+            status = mAudioTrack->start(tid);
             cblk->lock.lock();
             if (status == DEAD_OBJECT) {
                 android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
@@ -396,9 +409,6 @@
         }
     }
 
-    if (t != 0) {
-        t->mLock.unlock();
-    }
 }
 
 void AudioTrack::stop()
@@ -406,9 +416,6 @@
     sp<AudioTrackThread> t = mAudioTrackThread;
 
     ALOGV("stop %p", this);
-    if (t != 0) {
-        t->mLock.lock();
-    }
 
     AutoMutex lock(mLock);
     if (mActive) {
@@ -434,9 +441,6 @@
         }
     }
 
-    if (t != 0) {
-        t->mLock.unlock();
-    }
 }
 
 bool AudioTrack::stopped() const
@@ -506,7 +510,7 @@
     return NO_ERROR;
 }
 
-void AudioTrack::getVolume(float* left, float* right)
+void AudioTrack::getVolume(float* left, float* right) const
 {
     if (left != NULL) {
         *left  = mVolume[LEFT];
@@ -531,7 +535,7 @@
     return NO_ERROR;
 }
 
-void AudioTrack::getAuxEffectSendLevel(float* level)
+void AudioTrack::getAuxEffectSendLevel(float* level) const
 {
     if (level != NULL) {
         *level  = mSendLevel;
@@ -542,6 +546,10 @@
 {
     int afSamplingRate;
 
+    if (mIsTimed) {
+        return INVALID_OPERATION;
+    }
+
     if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) {
         return NO_INIT;
     }
@@ -553,8 +561,12 @@
     return NO_ERROR;
 }
 
-uint32_t AudioTrack::getSampleRate()
+uint32_t AudioTrack::getSampleRate() const
 {
+    if (mIsTimed) {
+        return INVALID_OPERATION;
+    }
+
     AutoMutex lock(mLock);
     return mCblk->sampleRate;
 }
@@ -580,6 +592,10 @@
         return NO_ERROR;
     }
 
+    if (mIsTimed) {
+        return INVALID_OPERATION;
+    }
+
     if (loopStart >= loopEnd ||
         loopEnd - loopStart > cblk->frameCount ||
         cblk->server > loopStart) {
@@ -601,26 +617,6 @@
     return NO_ERROR;
 }
 
-status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount)
-{
-    AutoMutex lock(mLock);
-    if (loopStart != NULL) {
-        *loopStart = mCblk->loopStart;
-    }
-    if (loopEnd != NULL) {
-        *loopEnd = mCblk->loopEnd;
-    }
-    if (loopCount != NULL) {
-        if (mCblk->loopCount < 0) {
-            *loopCount = -1;
-        } else {
-            *loopCount = mCblk->loopCount;
-        }
-    }
-
-    return NO_ERROR;
-}
-
 status_t AudioTrack::setMarkerPosition(uint32_t marker)
 {
     if (mCbf == NULL) return INVALID_OPERATION;
@@ -631,7 +627,7 @@
     return NO_ERROR;
 }
 
-status_t AudioTrack::getMarkerPosition(uint32_t *marker)
+status_t AudioTrack::getMarkerPosition(uint32_t *marker) const
 {
     if (marker == NULL) return BAD_VALUE;
 
@@ -652,7 +648,7 @@
     return NO_ERROR;
 }
 
-status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod)
+status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const
 {
     if (updatePeriod == NULL) return BAD_VALUE;
 
@@ -663,6 +659,8 @@
 
 status_t AudioTrack::setPosition(uint32_t position)
 {
+    if (mIsTimed) return INVALID_OPERATION;
+
     AutoMutex lock(mLock);
 
     if (!stopped_l()) return INVALID_OPERATION;
@@ -712,7 +710,7 @@
             mCblk->sampleRate, mFormat, mChannelMask, (audio_policy_output_flags_t)mFlags);
 }
 
-int AudioTrack::getSessionId()
+int AudioTrack::getSessionId() const
 {
     return mSessionId;
 }
@@ -794,7 +792,7 @@
                 }
             }
         } else {
-            // Ensure that buffer alignment matches channelcount
+            // Ensure that buffer alignment matches channelCount
             int channelCount = popcount(channelMask);
             if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) {
                 ALOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount);
@@ -813,6 +811,7 @@
                                                       ((uint16_t)flags) << 16,
                                                       sharedBuffer,
                                                       output,
+                                                      mIsTimed,
                                                       &mSessionId,
                                                       &status);
 
@@ -905,7 +904,7 @@
                                 "user=%08x, server=%08x", this, cblk->user, cblk->server);
                         //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
                         cblk->lock.unlock();
-                        result = mAudioTrack->start();
+                        result = mAudioTrack->start(0); // callback thread hasn't changed
                         cblk->lock.lock();
                         if (result == DEAD_OBJECT) {
                             android_atomic_or(CBLK_INVALID_ON, &cblk->flags);
@@ -937,7 +936,7 @@
     if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
         android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
         ALOGW("obtainBuffer() track %p disabled, restarting", this);
-        mAudioTrack->start();
+        mAudioTrack->start(0);  // callback thread hasn't changed
     }
 
     cblk->waitTimeMs = 0;
@@ -979,9 +978,11 @@
 {
 
     if (mSharedBuffer != 0) return INVALID_OPERATION;
+    if (mIsTimed) return INVALID_OPERATION;
 
     if (ssize_t(userSize) < 0) {
-        // sanity-check. user is most-likely passing an error code.
+        // Sanity-check: user is most-likely passing an error code, and it would
+        // make the return value ambiguous (actualSize vs error).
         ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)",
                 buffer, userSize, userSize);
         return BAD_VALUE;
@@ -1004,8 +1005,6 @@
     do {
         audioBuffer.frameCount = userSize/frameSz;
 
-        // Calling obtainBuffer() with a negative wait count causes
-        // an (almost) infinite wait time.
         status_t err = obtainBuffer(&audioBuffer, -1);
         if (err < 0) {
             // out of buffers, return #bytes written
@@ -1036,6 +1035,59 @@
 
 // -------------------------------------------------------------------------
 
+TimedAudioTrack::TimedAudioTrack() {
+    mIsTimed = true;
+}
+
+status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp<IMemory>* buffer)
+{
+    status_t result = UNKNOWN_ERROR;
+
+    // If the track is not invalid already, try to allocate a buffer.  alloc
+    // fails indicating that the server is dead, flag the track as invalid so
+    // we can attempt to restore in in just a bit.
+    if (!(mCblk->flags & CBLK_INVALID_MSK)) {
+        result = mAudioTrack->allocateTimedBuffer(size, buffer);
+        if (result == DEAD_OBJECT) {
+            android_atomic_or(CBLK_INVALID_ON, &mCblk->flags);
+        }
+    }
+
+    // If the track is invalid at this point, attempt to restore it. and try the
+    // allocation one more time.
+    if (mCblk->flags & CBLK_INVALID_MSK) {
+        mCblk->lock.lock();
+        result = restoreTrack_l(mCblk, false);
+        mCblk->lock.unlock();
+
+        if (result == OK)
+            result = mAudioTrack->allocateTimedBuffer(size, buffer);
+    }
+
+    return result;
+}
+
+status_t TimedAudioTrack::queueTimedBuffer(const sp<IMemory>& buffer,
+                                           int64_t pts)
+{
+    // restart track if it was disabled by audioflinger due to previous underrun
+    if (mActive && (mCblk->flags & CBLK_DISABLED_MSK)) {
+        android_atomic_and(~CBLK_DISABLED_ON, &mCblk->flags);
+        ALOGW("queueTimedBuffer() track %p disabled, restarting", this);
+        mAudioTrack->start(0);
+    }
+
+    return mAudioTrack->queueTimedBuffer(buffer, pts);
+}
+
+status_t TimedAudioTrack::setMediaTimeTransform(const LinearTransform& xform,
+                                                TargetTimeline target)
+{
+    return mAudioTrack->setMediaTimeTransform(xform, target);
+}
+
+// -------------------------------------------------------------------------
+
 bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
 {
     Buffer audioBuffer;
@@ -1095,6 +1147,9 @@
         frames = mRemainingFrames;
     }
 
+    // See description of waitCount parameter at declaration of obtainBuffer().
+    // The logic below prevents us from being stuck below at obtainBuffer()
+    // not being able to handle timed events (position, markers, loops).
     int32_t waitCount = -1;
     if (mUpdatePeriod || (!mMarkerReached && mMarkerPosition) || mLoopCount) {
         waitCount = 1;
@@ -1104,9 +1159,6 @@
 
         audioBuffer.frameCount = frames;
 
-        // Calling obtainBuffer() with a wait count of 1
-        // limits wait time to WAIT_PERIOD_MS. This prevents from being
-        // stuck here not being able to handle timed events (position, markers, loops).
         status_t err = obtainBuffer(&audioBuffer, waitCount);
         if (err < NO_ERROR) {
             if (err != TIMED_OUT) {
@@ -1228,7 +1280,7 @@
                 }
             }
             if (mActive) {
-                result = mAudioTrack->start();
+                result = mAudioTrack->start(0); // callback thread hasn't changed
                 ALOGW_IF(result != NO_ERROR, "restoreTrack_l() start() failed status %d", result);
             }
             if (fromStart && result == NO_ERROR) {
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index fc5520f..ebadbfa 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -89,7 +89,8 @@
                                 int frameCount,
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
-                                int output,
+                                audio_io_handle_t output,
+                                bool isTimed,
                                 int *sessionId,
                                 status_t *status)
     {
@@ -104,7 +105,8 @@
         data.writeInt32(frameCount);
         data.writeInt32(flags);
         data.writeStrongBinder(sharedBuffer->asBinder());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
+        data.writeInt32(isTimed);
         int lSessionId = 0;
         if (sessionId != NULL) {
             lSessionId = *sessionId;
@@ -129,7 +131,7 @@
 
     virtual sp<IAudioRecord> openRecord(
                                 pid_t pid,
-                                int input,
+                                audio_io_handle_t input,
                                 uint32_t sampleRate,
                                 audio_format_t format,
                                 uint32_t channelMask,
@@ -142,7 +144,7 @@
         sp<IAudioRecord> record;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
         data.writeInt32(pid);
-        data.writeInt32(input);
+        data.writeInt32((int32_t) input);
         data.writeInt32(sampleRate);
         data.writeInt32(format);
         data.writeInt32(channelMask);
@@ -170,47 +172,47 @@
         return record;
     }
 
-    virtual uint32_t sampleRate(int output) const
+    virtual uint32_t sampleRate(audio_io_handle_t output) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(SAMPLE_RATE, data, &reply);
         return reply.readInt32();
     }
 
-    virtual int channelCount(int output) const
+    virtual int channelCount(audio_io_handle_t output) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(CHANNEL_COUNT, data, &reply);
         return reply.readInt32();
     }
 
-    virtual audio_format_t format(int output) const
+    virtual audio_format_t format(audio_io_handle_t output) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(FORMAT, data, &reply);
         return (audio_format_t) reply.readInt32();
     }
 
-    virtual size_t frameCount(int output) const
+    virtual size_t frameCount(audio_io_handle_t output) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(FRAME_COUNT, data, &reply);
         return reply.readInt32();
     }
 
-    virtual uint32_t latency(int output) const
+    virtual uint32_t latency(audio_io_handle_t output) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(LATENCY, data, &reply);
         return reply.readInt32();
     }
@@ -249,13 +251,14 @@
         return reply.readInt32();
     }
 
-    virtual status_t setStreamVolume(audio_stream_type_t stream, float value, int output)
+    virtual status_t setStreamVolume(audio_stream_type_t stream, float value,
+            audio_io_handle_t output)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
         data.writeInt32((int32_t) stream);
         data.writeFloat(value);
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(SET_STREAM_VOLUME, data, &reply);
         return reply.readInt32();
     }
@@ -270,12 +273,12 @@
         return reply.readInt32();
     }
 
-    virtual float streamVolume(audio_stream_type_t stream, int output) const
+    virtual float streamVolume(audio_stream_type_t stream, audio_io_handle_t output) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
         data.writeInt32((int32_t) stream);
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(STREAM_VOLUME, data, &reply);
         return reply.readFloat();
     }
@@ -315,21 +318,21 @@
         return reply.readInt32();
     }
 
-    virtual status_t setParameters(int ioHandle, const String8& keyValuePairs)
+    virtual status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(ioHandle);
+        data.writeInt32((int32_t) ioHandle);
         data.writeString8(keyValuePairs);
         remote()->transact(SET_PARAMETERS, data, &reply);
         return reply.readInt32();
     }
 
-    virtual String8 getParameters(int ioHandle, const String8& keys)
+    virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(ioHandle);
+        data.writeInt32((int32_t) ioHandle);
         data.writeString8(keys);
         remote()->transact(GET_PARAMETERS, data, &reply);
         return reply.readString8();
@@ -343,7 +346,7 @@
         remote()->transact(REGISTER_CLIENT, data, &reply);
     }
 
-    virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount)
+    virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
@@ -354,7 +357,7 @@
         return reply.readInt32();
     }
 
-    virtual int openOutput(uint32_t *pDevices,
+    virtual audio_io_handle_t openOutput(uint32_t *pDevices,
                             uint32_t *pSamplingRate,
                             audio_format_t *pFormat,
                             uint32_t *pChannels,
@@ -376,8 +379,8 @@
         data.writeInt32(latency);
         data.writeInt32(flags);
         remote()->transact(OPEN_OUTPUT, data, &reply);
-        int  output = reply.readInt32();
-        ALOGV("openOutput() returned output, %p", output);
+        audio_io_handle_t output = (audio_io_handle_t) reply.readInt32();
+        ALOGV("openOutput() returned output, %d", output);
         devices = reply.readInt32();
         if (pDevices) *pDevices = devices;
         samplingRate = reply.readInt32();
@@ -391,44 +394,45 @@
         return output;
     }
 
-    virtual int openDuplicateOutput(int output1, int output2)
+    virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
+            audio_io_handle_t output2)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output1);
-        data.writeInt32(output2);
+        data.writeInt32((int32_t) output1);
+        data.writeInt32((int32_t) output2);
         remote()->transact(OPEN_DUPLICATE_OUTPUT, data, &reply);
-        return reply.readInt32();
+        return (audio_io_handle_t) reply.readInt32();
     }
 
-    virtual status_t closeOutput(int output)
+    virtual status_t closeOutput(audio_io_handle_t output)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(CLOSE_OUTPUT, data, &reply);
         return reply.readInt32();
     }
 
-    virtual status_t suspendOutput(int output)
+    virtual status_t suspendOutput(audio_io_handle_t output)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(SUSPEND_OUTPUT, data, &reply);
         return reply.readInt32();
     }
 
-    virtual status_t restoreOutput(int output)
+    virtual status_t restoreOutput(audio_io_handle_t output)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(RESTORE_OUTPUT, data, &reply);
         return reply.readInt32();
     }
 
-    virtual int openInput(uint32_t *pDevices,
+    virtual audio_io_handle_t openInput(uint32_t *pDevices,
                             uint32_t *pSamplingRate,
                             audio_format_t *pFormat,
                             uint32_t *pChannels,
@@ -447,7 +451,7 @@
         data.writeInt32(channels);
         data.writeInt32((int32_t) acoustics);
         remote()->transact(OPEN_INPUT, data, &reply);
-        int input = reply.readInt32();
+        audio_io_handle_t input = (audio_io_handle_t) reply.readInt32();
         devices = reply.readInt32();
         if (pDevices) *pDevices = devices;
         samplingRate = reply.readInt32();
@@ -468,12 +472,12 @@
         return reply.readInt32();
     }
 
-    virtual status_t setStreamOutput(audio_stream_type_t stream, int output)
+    virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
         data.writeInt32((int32_t) stream);
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(SET_STREAM_OUTPUT, data, &reply);
         return reply.readInt32();
     }
@@ -487,11 +491,12 @@
         return reply.readInt32();
     }
 
-    virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output)
+    virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
+            audio_io_handle_t output) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         remote()->transact(GET_RENDER_POSITION, data, &reply);
         status_t status = reply.readInt32();
         if (status == NO_ERROR) {
@@ -507,11 +512,11 @@
         return status;
     }
 
-    virtual unsigned int getInputFramesLost(int ioHandle)
+    virtual unsigned int getInputFramesLost(audio_io_handle_t ioHandle) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(ioHandle);
+        data.writeInt32((int32_t) ioHandle);
         remote()->transact(GET_INPUT_FRAMES_LOST, data, &reply);
         return reply.readInt32();
     }
@@ -544,7 +549,7 @@
         remote()->transact(RELEASE_AUDIO_SESSION_ID, data, &reply);
     }
 
-    virtual status_t queryNumberEffects(uint32_t *numEffects)
+    virtual status_t queryNumberEffects(uint32_t *numEffects) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
@@ -556,13 +561,13 @@
         if (status != NO_ERROR) {
             return status;
         }
-        if (numEffects) {
+        if (numEffects != NULL) {
             *numEffects = (uint32_t)reply.readInt32();
         }
         return NO_ERROR;
     }
 
-    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
+    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) const
     {
         if (pDescriptor == NULL) {
             return BAD_VALUE;
@@ -582,7 +587,8 @@
         return NO_ERROR;
     }
 
-    virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *pDescriptor)
+    virtual status_t getEffectDescriptor(const effect_uuid_t *pUuid,
+            effect_descriptor_t *pDescriptor) const
     {
         if (pUuid == NULL || pDescriptor == NULL) {
             return BAD_VALUE;
@@ -606,7 +612,7 @@
                                     effect_descriptor_t *pDesc,
                                     const sp<IEffectClient>& client,
                                     int32_t priority,
-                                    int output,
+                                    audio_io_handle_t output,
                                     int sessionId,
                                     status_t *status,
                                     int *id,
@@ -627,7 +633,7 @@
         data.write(pDesc, sizeof(effect_descriptor_t));
         data.writeStrongBinder(client->asBinder());
         data.writeInt32(priority);
-        data.writeInt32(output);
+        data.writeInt32((int32_t) output);
         data.writeInt32(sessionId);
 
         status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
@@ -653,13 +659,14 @@
         return effect;
     }
 
-    virtual status_t moveEffects(int session, int srcOutput, int dstOutput)
+    virtual status_t moveEffects(int session, audio_io_handle_t srcOutput,
+            audio_io_handle_t dstOutput)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
         data.writeInt32(session);
-        data.writeInt32(srcOutput);
-        data.writeInt32(dstOutput);
+        data.writeInt32((int32_t) srcOutput);
+        data.writeInt32((int32_t) dstOutput);
         remote()->transact(MOVE_EFFECTS, data, &reply);
         return reply.readInt32();
     }
@@ -683,12 +690,13 @@
             size_t bufferCount = data.readInt32();
             uint32_t flags = data.readInt32();
             sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder());
-            int output = data.readInt32();
+            audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
+            bool isTimed = data.readInt32();
             int sessionId = data.readInt32();
             status_t status;
             sp<IAudioTrack> track = createTrack(pid,
                     (audio_stream_type_t) streamType, sampleRate, format,
-                    channelCount, bufferCount, flags, buffer, output, &sessionId, &status);
+                    channelCount, bufferCount, flags, buffer, output, isTimed, &sessionId, &status);
             reply->writeInt32(sessionId);
             reply->writeInt32(status);
             reply->writeStrongBinder(track->asBinder());
@@ -697,7 +705,7 @@
         case OPEN_RECORD: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             pid_t pid = data.readInt32();
-            int input = data.readInt32();
+            audio_io_handle_t input = (audio_io_handle_t) data.readInt32();
             uint32_t sampleRate = data.readInt32();
             audio_format_t format = (audio_format_t) data.readInt32();
             int channelCount = data.readInt32();
@@ -714,27 +722,27 @@
         } break;
         case SAMPLE_RATE: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32( sampleRate(data.readInt32()) );
+            reply->writeInt32( sampleRate((audio_io_handle_t) data.readInt32()) );
             return NO_ERROR;
         } break;
         case CHANNEL_COUNT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32( channelCount(data.readInt32()) );
+            reply->writeInt32( channelCount((audio_io_handle_t) data.readInt32()) );
             return NO_ERROR;
         } break;
         case FORMAT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32( format(data.readInt32()) );
+            reply->writeInt32( format((audio_io_handle_t) data.readInt32()) );
             return NO_ERROR;
         } break;
         case FRAME_COUNT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32( frameCount(data.readInt32()) );
+            reply->writeInt32( frameCount((audio_io_handle_t) data.readInt32()) );
             return NO_ERROR;
         } break;
         case LATENCY: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32( latency(data.readInt32()) );
+            reply->writeInt32( latency((audio_io_handle_t) data.readInt32()) );
             return NO_ERROR;
         } break;
          case SET_MASTER_VOLUME: {
@@ -761,7 +769,7 @@
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             int stream = data.readInt32();
             float volume = data.readFloat();
-            int output = data.readInt32();
+            audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
             reply->writeInt32( setStreamVolume((audio_stream_type_t) stream, volume, output) );
             return NO_ERROR;
         } break;
@@ -803,14 +811,14 @@
         } break;
         case SET_PARAMETERS: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            int ioHandle = data.readInt32();
+            audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
             String8 keyValuePairs(data.readString8());
             reply->writeInt32(setParameters(ioHandle, keyValuePairs));
             return NO_ERROR;
          } break;
         case GET_PARAMETERS: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            int ioHandle = data.readInt32();
+            audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
             String8 keys(data.readString8());
             reply->writeString8(getParameters(ioHandle, keys));
             return NO_ERROR;
@@ -838,14 +846,14 @@
             uint32_t channels = data.readInt32();
             uint32_t latency = data.readInt32();
             uint32_t flags = data.readInt32();
-            int output = openOutput(&devices,
+            audio_io_handle_t output = openOutput(&devices,
                                      &samplingRate,
                                      &format,
                                      &channels,
                                      &latency,
                                      flags);
             ALOGV("OPEN_OUTPUT output, %p", output);
-            reply->writeInt32(output);
+            reply->writeInt32((int32_t) output);
             reply->writeInt32(devices);
             reply->writeInt32(samplingRate);
             reply->writeInt32(format);
@@ -855,24 +863,24 @@
         } break;
         case OPEN_DUPLICATE_OUTPUT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            int output1 = data.readInt32();
-            int output2 = data.readInt32();
-            reply->writeInt32(openDuplicateOutput(output1, output2));
+            audio_io_handle_t output1 = (audio_io_handle_t) data.readInt32();
+            audio_io_handle_t output2 = (audio_io_handle_t) data.readInt32();
+            reply->writeInt32((int32_t) openDuplicateOutput(output1, output2));
             return NO_ERROR;
         } break;
         case CLOSE_OUTPUT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32(closeOutput(data.readInt32()));
+            reply->writeInt32(closeOutput((audio_io_handle_t) data.readInt32()));
             return NO_ERROR;
         } break;
         case SUSPEND_OUTPUT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32(suspendOutput(data.readInt32()));
+            reply->writeInt32(suspendOutput((audio_io_handle_t) data.readInt32()));
             return NO_ERROR;
         } break;
         case RESTORE_OUTPUT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32(restoreOutput(data.readInt32()));
+            reply->writeInt32(restoreOutput((audio_io_handle_t) data.readInt32()));
             return NO_ERROR;
         } break;
         case OPEN_INPUT: {
@@ -883,12 +891,12 @@
             uint32_t channels = data.readInt32();
             audio_in_acoustics_t acoustics = (audio_in_acoustics_t) data.readInt32();
 
-            int input = openInput(&devices,
+            audio_io_handle_t input = openInput(&devices,
                                      &samplingRate,
                                      &format,
                                      &channels,
                                      acoustics);
-            reply->writeInt32(input);
+            reply->writeInt32((int32_t) input);
             reply->writeInt32(devices);
             reply->writeInt32(samplingRate);
             reply->writeInt32(format);
@@ -897,13 +905,13 @@
         } break;
         case CLOSE_INPUT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            reply->writeInt32(closeInput(data.readInt32()));
+            reply->writeInt32(closeInput((audio_io_handle_t) data.readInt32()));
             return NO_ERROR;
         } break;
         case SET_STREAM_OUTPUT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             uint32_t stream = data.readInt32();
-            int output = data.readInt32();
+            audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
             reply->writeInt32(setStreamOutput((audio_stream_type_t) stream, output));
             return NO_ERROR;
         } break;
@@ -915,7 +923,7 @@
         } break;
         case GET_RENDER_POSITION: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            int output = data.readInt32();
+            audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
             uint32_t halFrames;
             uint32_t dspFrames;
             status_t status = getRenderPosition(&halFrames, &dspFrames, output);
@@ -928,7 +936,7 @@
         }
         case GET_INPUT_FRAMES_LOST: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            int ioHandle = data.readInt32();
+            audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
             reply->writeInt32(getInputFramesLost(ioHandle));
             return NO_ERROR;
         } break;
@@ -988,7 +996,7 @@
             data.read(&desc, sizeof(effect_descriptor_t));
             sp<IEffectClient> client = interface_cast<IEffectClient>(data.readStrongBinder());
             int32_t priority = data.readInt32();
-            int output = data.readInt32();
+            audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
             int sessionId = data.readInt32();
             status_t status;
             int id;
@@ -1005,8 +1013,8 @@
         case MOVE_EFFECTS: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             int session = data.readInt32();
-            int srcOutput = data.readInt32();
-            int dstOutput = data.readInt32();
+            audio_io_handle_t srcOutput = (audio_io_handle_t) data.readInt32();
+            audio_io_handle_t dstOutput = (audio_io_handle_t) data.readInt32();
             reply->writeInt32(moveEffects(session, srcOutput, dstOutput));
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 9458bc0..ce28b33 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -39,12 +39,12 @@
     {
     }
 
-    void ioConfigChanged(int event, int ioHandle, void *param2)
+    void ioConfigChanged(int event, audio_io_handle_t ioHandle, void *param2)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
         data.writeInt32(event);
-        data.writeInt32(ioHandle);
+        data.writeInt32((int32_t) ioHandle);
         if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
             uint32_t stream = *(uint32_t *)param2;
             ALOGV("ioConfigChanged stream %d", stream);
@@ -72,7 +72,7 @@
     case IO_CONFIG_CHANGED: {
             CHECK_INTERFACE(IAudioFlingerClient, data, reply);
             int event = data.readInt32();
-            int ioHandle = data.readInt32();
+            audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
             void *param2 = NULL;
             AudioSystem::OutputDescriptor desc;
             uint32_t stream;
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index 8c7a960..6b473c9 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -42,10 +42,11 @@
     {
     }
     
-    virtual status_t start()
+    virtual status_t start(pid_t tid)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor());
+        data.writeInt32(tid);
         status_t status = remote()->transact(START, data, &reply);
         if (status == NO_ERROR) {
             status = reply.readInt32();
@@ -90,7 +91,7 @@
         } break;
         case START: {
             CHECK_INTERFACE(IAudioRecord, data, reply);
-            reply->writeInt32(start());
+            reply->writeInt32(start(data.readInt32()));
             return NO_ERROR;
         } break;
         case STOP: {
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index e618619..28ebbbfc 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -1,4 +1,4 @@
-/* //device/extlibs/pv/android/IAudioTrack.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -35,7 +35,10 @@
     FLUSH,
     MUTE,
     PAUSE,
-    ATTACH_AUX_EFFECT
+    ATTACH_AUX_EFFECT,
+    ALLOCATE_TIMED_BUFFER,
+    QUEUE_TIMED_BUFFER,
+    SET_MEDIA_TIME_TRANSFORM,
 };
 
 class BpAudioTrack : public BpInterface<IAudioTrack>
@@ -58,10 +61,11 @@
         return cblk;
     }
 
-    virtual status_t start()
+    virtual status_t start(pid_t tid)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+        data.writeInt32(tid);
         status_t status = remote()->transact(START, data, &reply);
         if (status == NO_ERROR) {
             status = reply.readInt32();
@@ -113,6 +117,52 @@
         }
         return status;
     }
+
+    virtual status_t allocateTimedBuffer(size_t size, sp<IMemory>* buffer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+        data.writeInt32(size);
+        status_t status = remote()->transact(ALLOCATE_TIMED_BUFFER,
+                                             data, &reply);
+        if (status == NO_ERROR) {
+            status = reply.readInt32();
+            if (status == NO_ERROR) {
+                *buffer = interface_cast<IMemory>(reply.readStrongBinder());
+            }
+        }
+        return status;
+    }
+
+    virtual status_t queueTimedBuffer(const sp<IMemory>& buffer,
+                                      int64_t pts) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+        data.writeStrongBinder(buffer->asBinder());
+        data.writeInt64(pts);
+        status_t status = remote()->transact(QUEUE_TIMED_BUFFER,
+                                             data, &reply);
+        if (status == NO_ERROR) {
+            status = reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t setMediaTimeTransform(const LinearTransform& xform,
+                                           int target) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+        data.writeInt64(xform.a_zero);
+        data.writeInt64(xform.b_zero);
+        data.writeInt32(xform.a_to_b_numer);
+        data.writeInt32(xform.a_to_b_denom);
+        data.writeInt32(target);
+        status_t status = remote()->transact(SET_MEDIA_TIME_TRANSFORM,
+                                             data, &reply);
+        if (status == NO_ERROR) {
+            status = reply.readInt32();
+        }
+        return status;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
@@ -130,7 +180,7 @@
         } break;
         case START: {
             CHECK_INTERFACE(IAudioTrack, data, reply);
-            reply->writeInt32(start());
+            reply->writeInt32(start(data.readInt32()));
             return NO_ERROR;
         } break;
         case STOP: {
@@ -158,10 +208,38 @@
             reply->writeInt32(attachAuxEffect(data.readInt32()));
             return NO_ERROR;
         } break;
+        case ALLOCATE_TIMED_BUFFER: {
+            CHECK_INTERFACE(IAudioTrack, data, reply);
+            sp<IMemory> buffer;
+            status_t status = allocateTimedBuffer(data.readInt32(), &buffer);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeStrongBinder(buffer->asBinder());
+            }
+            return NO_ERROR;
+        } break;
+        case QUEUE_TIMED_BUFFER: {
+            CHECK_INTERFACE(IAudioTrack, data, reply);
+            sp<IMemory> buffer = interface_cast<IMemory>(
+                data.readStrongBinder());
+            uint64_t pts = data.readInt64();
+            reply->writeInt32(queueTimedBuffer(buffer, pts));
+            return NO_ERROR;
+        } break;
+        case SET_MEDIA_TIME_TRANSFORM: {
+            CHECK_INTERFACE(IAudioTrack, data, reply);
+            LinearTransform xform;
+            xform.a_zero = data.readInt64();
+            xform.b_zero = data.readInt64();
+            xform.a_to_b_numer = data.readInt32();
+            xform.a_to_b_denom = data.readInt32();
+            int target = data.readInt32();
+            reply->writeInt32(setMediaTimeTransform(xform, target));
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
 }
 
 }; // namespace android
-
diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp
index d469e28..5d40cc8 100644
--- a/media/libmedia/IEffect.cpp
+++ b/media/libmedia/IEffect.cpp
@@ -83,8 +83,15 @@
             size = *pReplySize;
         }
         data.writeInt32(size);
-        remote()->transact(COMMAND, data, &reply);
-        status_t status = reply.readInt32();
+
+        status_t status = remote()->transact(COMMAND, data, &reply);
+        if (status != NO_ERROR) {
+            if (pReplySize != NULL)
+                *pReplySize = 0;
+            return status;
+        }
+
+        status = reply.readInt32();
         size = reply.readInt32();
         if (size != 0 && pReplyData != NULL && pReplySize != NULL) {
             reply.read(pReplyData, size);
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 8456db5..6cb5b82 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -246,14 +246,12 @@
     }//while (1)
 
 threadExit:
-    if (mAudioTrack) {
+    if (mAudioTrack != NULL) {
         mAudioTrack->stop();
         mAudioTrack->flush();
     }
-    if (mAudioBuffer) {
-        delete [] mAudioBuffer;
-        mAudioBuffer = NULL;
-    }
+    delete [] mAudioBuffer;
+    mAudioBuffer = NULL;
     mMutex.lock();
     mTid = -1;
     mCondition.signal();
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index c905762..93ddca8 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -25,7 +25,7 @@
 #include <cutils/properties.h>
 #include <expat.h>
 #include <media/MediaProfiles.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/openmax/OMX_Video.h>
 
 namespace android {
@@ -349,7 +349,7 @@
 {
     CHECK(!strcmp("quality", atts[0]));
     int quality = atoi(atts[1]);
-    ALOGV("%s: cameraId=%d, quality=%d\n", __func__, cameraId, quality);
+    ALOGV("%s: cameraId=%d, quality=%d", __func__, cameraId, quality);
     ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId);
 
     if (levels == NULL) {
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index 79cab74..73d4519 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -143,7 +143,7 @@
     if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
         strcpy(fileSpot, ".nomedia");
         if (access(path, F_OK) == 0) {
-            ALOGV("found .nomedia, setting noMedia flag\n");
+            ALOGV("found .nomedia, setting noMedia flag");
             noMedia = true;
         }
 
diff --git a/media/libmedia/MediaScannerClient.cpp b/media/libmedia/MediaScannerClient.cpp
index 9fe1820..cdfd477 100644
--- a/media/libmedia/MediaScannerClient.cpp
+++ b/media/libmedia/MediaScannerClient.cpp
@@ -142,12 +142,12 @@
 
         UConverter *conv = ucnv_open(enc, &status);
         if (U_FAILURE(status)) {
-            ALOGE("could not create UConverter for %s\n", enc);
+            ALOGE("could not create UConverter for %s", enc);
             return;
         }
         UConverter *utf8Conv = ucnv_open("UTF-8", &status);
         if (U_FAILURE(status)) {
-            ALOGE("could not create UConverter for UTF-8\n");
+            ALOGE("could not create UConverter for UTF-8");
             ucnv_close(conv);
             return;
         }
@@ -181,7 +181,7 @@
             ucnv_convertEx(utf8Conv, conv, &target, target + targetLength,
                     &source, (const char *)dest, NULL, NULL, NULL, NULL, TRUE, TRUE, &status);
             if (U_FAILURE(status)) {
-                ALOGE("ucnv_convertEx failed: %d\n", status);
+                ALOGE("ucnv_convertEx failed: %d", status);
                 mValues->setEntry(i, "???");
             } else {
                 // zero terminate
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index e6e989d..54eb98a 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -791,7 +791,7 @@
 //        generators, instantiates output audio track.
 //
 //    Input:
-//        streamType:        Type of stream used for tone playback (enum AudioTrack::stream_type)
+//        streamType:        Type of stream used for tone playback
 //        volume:            volume applied to tone (0.0 to 1.0)
 //
 //    Output:
@@ -800,7 +800,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava) {
 
-    ALOGV("ToneGenerator constructor: streamType=%d, volume=%f\n", streamType, volume);
+    ALOGV("ToneGenerator constructor: streamType=%d, volume=%f", streamType, volume);
 
     mState = TONE_IDLE;
 
@@ -829,9 +829,9 @@
     }
 
     if (initAudioTrack()) {
-        ALOGV("ToneGenerator INIT OK, time: %d\n", (unsigned int)(systemTime()/1000000));
+        ALOGV("ToneGenerator INIT OK, time: %d", (unsigned int)(systemTime()/1000000));
     } else {
-        ALOGV("!!!ToneGenerator INIT FAILED!!!\n");
+        ALOGV("!!!ToneGenerator INIT FAILED!!!");
     }
 }
 
@@ -853,11 +853,11 @@
 //
 ////////////////////////////////////////////////////////////////////////////////
 ToneGenerator::~ToneGenerator() {
-    ALOGV("ToneGenerator destructor\n");
+    ALOGV("ToneGenerator destructor");
 
     if (mpAudioTrack != NULL) {
         stopTone();
-        ALOGV("Delete Track: %p\n", mpAudioTrack);
+        ALOGV("Delete Track: %p", mpAudioTrack);
         delete mpAudioTrack;
     }
 }
@@ -892,7 +892,7 @@
         }
     }
 
-    ALOGV("startTone\n");
+    ALOGV("startTone");
 
     mLock.lock();
 
@@ -915,7 +915,7 @@
 
     if (mState == TONE_INIT) {
         if (prepareWave()) {
-            ALOGV("Immediate start, time %d\n", (unsigned int)(systemTime()/1000000));
+            ALOGV("Immediate start, time %d", (unsigned int)(systemTime()/1000000));
             lResult = true;
             mState = TONE_STARTING;
             mLock.unlock();
@@ -934,7 +934,7 @@
             mState = TONE_IDLE;
         }
     } else {
-        ALOGV("Delayed start\n");
+        ALOGV("Delayed start");
         mState = TONE_RESTARTING;
         lStatus = mWaitCbkCond.waitRelative(mLock, seconds(3));
         if (lStatus == NO_ERROR) {
@@ -949,8 +949,8 @@
     }
     mLock.unlock();
 
-    ALOGV_IF(lResult, "Tone started, time %d\n", (unsigned int)(systemTime()/1000000));
-    ALOGW_IF(!lResult, "Tone start failed!!!, time %d\n", (unsigned int)(systemTime()/1000000));
+    ALOGV_IF(lResult, "Tone started, time %d", (unsigned int)(systemTime()/1000000));
+    ALOGW_IF(!lResult, "Tone start failed!!!, time %d", (unsigned int)(systemTime()/1000000));
 
     return lResult;
 }
@@ -1017,7 +1017,7 @@
 
    // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size
     mpAudioTrack = new AudioTrack();
-    ALOGV("Create Track: %p\n", mpAudioTrack);
+    ALOGV("Create Track: %p", mpAudioTrack);
 
     mpAudioTrack->set(mStreamType,
                       0,
@@ -1045,8 +1045,8 @@
 initAudioTrack_exit:
 
     // Cleanup
-    if (mpAudioTrack) {
-        ALOGV("Delete Track I: %p\n", mpAudioTrack);
+    if (mpAudioTrack != NULL) {
+        ALOGV("Delete Track I: %p", mpAudioTrack);
         delete mpAudioTrack;
         mpAudioTrack = NULL;
     }
@@ -1141,7 +1141,7 @@
         if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) {
             // Time to go to next sequence segment
 
-            ALOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000));
+            ALOGV("End Segment, time: %d", (unsigned int)(systemTime()/1000000));
 
             lGenSmp = lReqSmp;
 
@@ -1156,13 +1156,13 @@
                     lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd);
                     lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[++lFreqIdx];
                 }
-                ALOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp);
+                ALOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d", lGenSmp, lReqSmp);
             }
 
             // check if we need to loop and loop for the reqd times
             if (lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt) {
                 if (lpToneGen->mLoopCounter < lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt) {
-                    ALOGV ("in if loop loopCnt(%d) loopctr(%d), CurSeg(%d) \n",
+                    ALOGV ("in if loop loopCnt(%d) loopctr(%d), CurSeg(%d)",
                           lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt,
                           lpToneGen->mLoopCounter,
                           lpToneGen->mCurSegment);
@@ -1172,14 +1172,14 @@
                     // completed loop. go to next segment
                     lpToneGen->mLoopCounter = 0;
                     lpToneGen->mCurSegment++;
-                    ALOGV ("in else loop loopCnt(%d) loopctr(%d), CurSeg(%d) \n",
+                    ALOGV ("in else loop loopCnt(%d) loopctr(%d), CurSeg(%d)",
                           lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt,
                           lpToneGen->mLoopCounter,
                           lpToneGen->mCurSegment);
                 }
             } else {
                 lpToneGen->mCurSegment++;
-                ALOGV ("Goto next seg loopCnt(%d) loopctr(%d), CurSeg(%d) \n",
+                ALOGV ("Goto next seg loopCnt(%d) loopctr(%d), CurSeg(%d)",
                       lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt,
                       lpToneGen->mLoopCounter,
                       lpToneGen->mCurSegment);
@@ -1188,32 +1188,32 @@
 
             // Handle loop if last segment reached
             if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) {
-                ALOGV("Last Seg: %d\n", lpToneGen->mCurSegment);
+                ALOGV("Last Seg: %d", lpToneGen->mCurSegment);
 
                 // Pre increment loop count and restart if total count not reached. Stop sequence otherwise
                 if (++lpToneGen->mCurCount <= lpToneDesc->repeatCnt) {
-                    ALOGV("Repeating Count: %d\n", lpToneGen->mCurCount);
+                    ALOGV("Repeating Count: %d", lpToneGen->mCurCount);
 
                     lpToneGen->mCurSegment = lpToneDesc->repeatSegment;
                     if (lpToneDesc->segments[lpToneDesc->repeatSegment].waveFreq[0] != 0) {
                         lWaveCmd = WaveGenerator::WAVEGEN_START;
                     }
 
-                    ALOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment,
+                    ALOGV("New segment %d, Next Time: %d", lpToneGen->mCurSegment,
                             (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate);
 
                 } else {
                     lGenSmp = 0;
-                    ALOGV("End repeat, time: %d\n", (unsigned int)(systemTime()/1000000));
+                    ALOGV("End repeat, time: %d", (unsigned int)(systemTime()/1000000));
                 }
             } else {
-                ALOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment,
+                ALOGV("New segment %d, Next Time: %d", lpToneGen->mCurSegment,
                         (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate);
                 if (lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[0] != 0) {
                     // If next segment is not silent,  OFF -> ON transition : reset wave generator
                     lWaveCmd = WaveGenerator::WAVEGEN_START;
 
-                    ALOGV("OFF->ON, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp);
+                    ALOGV("OFF->ON, lGenSmp: %d, lReqSmp: %d", lGenSmp, lReqSmp);
                 } else {
                     lGenSmp = 0;
                 }
@@ -1251,13 +1251,13 @@
 
         switch (lpToneGen->mState) {
         case TONE_RESTARTING:
-            ALOGV("Cbk restarting track\n");
+            ALOGV("Cbk restarting track");
             if (lpToneGen->prepareWave()) {
                 lpToneGen->mState = TONE_STARTING;
                 // must reload lpToneDesc as prepareWave() may change mpToneDesc
                 lpToneDesc = lpToneGen->mpToneDesc;
             } else {
-                ALOGW("Cbk restarting prepareWave() failed\n");
+                ALOGW("Cbk restarting prepareWave() failed");
                 lpToneGen->mState = TONE_IDLE;
                 lpToneGen->mpAudioTrack->stop();
                 // Force loop exit
@@ -1266,14 +1266,14 @@
             lSignal = true;
             break;
         case TONE_STOPPING:
-            ALOGV("Cbk Stopping\n");
+            ALOGV("Cbk Stopping");
             lpToneGen->mState = TONE_STOPPED;
             // Force loop exit
             lNumSmp = 0;
             break;
         case TONE_STOPPED:
             lpToneGen->mState = TONE_INIT;
-            ALOGV("Cbk Stopped track\n");
+            ALOGV("Cbk Stopped track");
             lpToneGen->mpAudioTrack->stop();
             // Force loop exit
             lNumSmp = 0;
@@ -1281,7 +1281,7 @@
             lSignal = true;
             break;
         case TONE_STARTING:
-            ALOGV("Cbk starting track\n");
+            ALOGV("Cbk starting track");
             lpToneGen->mState = TONE_PLAYING;
             lSignal = true;
            break;
@@ -1491,7 +1491,7 @@
         d0 = 32767;
     mA1_Q14 = (short) d0;
 
-    ALOGV("WaveGenerator init, mA1_Q14: %d, mS2_0: %d, mAmplitude_Q15: %d\n",
+    ALOGV("WaveGenerator init, mA1_Q14: %d, mS2_0: %d, mAmplitude_Q15: %d",
             mA1_Q14, mS2_0, mAmplitude_Q15);
 }
 
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 13b64e9..70f8c0c 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -168,7 +168,7 @@
         uint32_t replySize = mCaptureSize;
         status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform);
         ALOGV("getWaveForm() command returned %d", status);
-        if (replySize == 0) {
+        if ((status == NO_ERROR) && (replySize == 0)) {
             status = NOT_ENOUGH_DATA;
         }
     } else {
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index f1c47dd..250425b 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -1,4 +1,4 @@
-/* mediaplayer.cpp
+/*
 **
 ** Copyright 2006, The Android Open Source Project
 **
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index a3e2517..e521648 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -29,7 +29,8 @@
 	libstagefright_omx    			\
 	libstagefright_foundation       \
 	libgui                          \
-	libdl
+	libdl                           \
+	libaah_rtp
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_nuplayer                 \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index a0c20ae..764eddc 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -70,6 +70,11 @@
 
 #include <OMX.h>
 
+namespace android {
+sp<MediaPlayerBase> createAAH_TXPlayer();
+sp<MediaPlayerBase> createAAH_RXPlayer();
+}
+
 namespace {
 using android::media::Metadata;
 using android::status_t;
@@ -593,6 +598,14 @@
         return NU_PLAYER;
     }
 
+    if (!strncasecmp("aahRX://", url, 8)) {
+        return AAH_RX_PLAYER;
+    }
+
+    if (!strncasecmp("aahTX://", url, 8)) {
+        return AAH_TX_PLAYER;
+    }
+
     // use MidiFile for MIDI extensions
     int lenURL = strlen(url);
     for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
@@ -629,6 +642,14 @@
             ALOGV("Create Test Player stub");
             p = new TestPlayerStub();
             break;
+        case AAH_RX_PLAYER:
+            ALOGV(" create A@H RX Player");
+            p = createAAH_RXPlayer();
+            break;
+        case AAH_TX_PLAYER:
+            ALOGV(" create A@H TX Player");
+            p = createAAH_TXPlayer();
+            break;
         default:
             ALOGE("Unknown player type: %d", playerType);
             return NULL;
@@ -1031,9 +1052,21 @@
 status_t MediaPlayerService::Client::setVolume(float leftVolume, float rightVolume)
 {
     ALOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume);
-    // TODO: for hardware output, call player instead
-    Mutex::Autolock l(mLock);
-    if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
+
+    // for hardware output, call player instead
+    sp<MediaPlayerBase> p = getPlayer();
+    {
+      Mutex::Autolock l(mLock);
+      if (p != 0 && p->hardwareOutput()) {
+          MediaPlayerHWInterface* hwp =
+                  reinterpret_cast<MediaPlayerHWInterface*>(p.get());
+          return hwp->setVolume(leftVolume, rightVolume);
+      } else {
+          if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
+          return NO_ERROR;
+      }
+    }
+
     return NO_ERROR;
 }
 
@@ -1265,6 +1298,8 @@
     mStreamType = AUDIO_STREAM_MUSIC;
     mLeftVolume = 1.0;
     mRightVolume = 1.0;
+    mPlaybackRatePermille = 1000;
+    mSampleRateHz = 0;
     mLatency = 0;
     mMsecsPerFrame = 0;
     mAuxEffectId = 0;
@@ -1402,10 +1437,15 @@
     ALOGV("setVolume");
     t->setVolume(mLeftVolume, mRightVolume);
 
-    mMsecsPerFrame = 1.e3 / (float) sampleRate;
+    mSampleRateHz = sampleRate;
+    mMsecsPerFrame = mPlaybackRatePermille / (float) sampleRate;
     mLatency = t->latency();
     mTrack = t;
 
+    status_t res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000);
+    if (res != NO_ERROR) {
+        return res;
+    }
     t->setAuxEffectSendLevel(mSendLevel);
     return t->attachAuxEffect(mAuxEffectId);;
 }
@@ -1469,6 +1509,22 @@
     }
 }
 
+status_t MediaPlayerService::AudioOutput::setPlaybackRatePermille(int32_t ratePermille)
+{
+    ALOGV("setPlaybackRatePermille(%d)", ratePermille);
+    status_t res = NO_ERROR;
+    if (mTrack) {
+        res = mTrack->setSampleRate(ratePermille * mSampleRateHz / 1000);
+    } else {
+        res = NO_INIT;
+    }
+    mPlaybackRatePermille = ratePermille;
+    if (mSampleRateHz != 0) {
+        mMsecsPerFrame = mPlaybackRatePermille / (float) mSampleRateHz;
+    }
+    return res;
+}
+
 status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level)
 {
     ALOGV("setAuxEffectSendLevel(%f)", level);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index fa71d11..52af64d 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -95,6 +95,7 @@
         virtual void            close();
                 void            setAudioStreamType(audio_stream_type_t streamType) { mStreamType = streamType; }
                 void            setVolume(float left, float right);
+        virtual status_t        setPlaybackRatePermille(int32_t ratePermille);
                 status_t        setAuxEffectSendLevel(float level);
                 status_t        attachAuxEffect(int effectId);
         virtual status_t        dump(int fd, const Vector<String16>& args) const;
@@ -112,6 +113,8 @@
         audio_stream_type_t     mStreamType;
         float                   mLeftVolume;
         float                   mRightVolume;
+        int32_t                 mPlaybackRatePermille;
+        uint32_t                mSampleRateHz; // sample rate of the content, as set in open()
         float                   mMsecsPerFrame;
         uint32_t                mLatency;
         int                     mSessionId;
@@ -152,6 +155,7 @@
         virtual void            close() {}
                 void            setAudioStreamType(audio_stream_type_t streamType) {}
                 void            setVolume(float left, float right) {}
+        virtual status_t        setPlaybackRatePermille(int32_t ratePermille) { return INVALID_OPERATION; }
                 uint32_t        sampleRate() const { return mSampleRate; }
                 audio_format_t  format() const { return mFormat; }
                 size_t          size() const { return mSize; }
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 6d7771a..052ebf0 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -176,7 +176,7 @@
 }
 
 status_t StagefrightPlayer::setParameter(int key, const Parcel &request) {
-    ALOGV("setParameter");
+    ALOGV("setParameter(key=%d)", key);
     return mPlayer->setParameter(key, request);
 }
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 4632016..c5f4f86 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -24,6 +24,7 @@
 #include <binder/IServiceManager.h>
 
 #include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AudioSource.h>
 #include <media/stagefright/AMRWriter.h>
 #include <media/stagefright/AACWriter.h>
@@ -31,7 +32,6 @@
 #include <media/stagefright/CameraSourceTimeLapse.h>
 #include <media/stagefright/MPEG2TSWriter.h>
 #include <media/stagefright/MPEG4Writer.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXClient.h>
@@ -241,8 +241,8 @@
 status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) {
     ALOGV("setOutputFile: %d, %lld, %lld", fd, offset, length);
     // These don't make any sense, do they?
-    CHECK_EQ(offset, 0);
-    CHECK_EQ(length, 0);
+    CHECK_EQ(offset, 0ll);
+    CHECK_EQ(length, 0ll);
 
     if (fd < 0) {
         ALOGE("Invalid file descriptor: %d", fd);
@@ -734,7 +734,7 @@
 }
 
 status_t StagefrightRecorder::start() {
-    CHECK(mOutputFd >= 0);
+    CHECK_GE(mOutputFd, 0);
 
     if (mWriter != NULL) {
         ALOGE("File writer is not avaialble");
@@ -837,7 +837,7 @@
     }
 
     OMXClient client;
-    CHECK_EQ(client.connect(), OK);
+    CHECK_EQ(client.connect(), (status_t)OK);
 
     sp<MediaSource> audioEncoder =
         OMXCodec::Create(client.interface(), encMeta,
@@ -850,9 +850,9 @@
 status_t StagefrightRecorder::startAACRecording() {
     // FIXME:
     // Add support for OUTPUT_FORMAT_AAC_ADIF
-    CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADTS);
+    CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_AAC_ADTS);
 
-    CHECK(mAudioEncoder == AUDIO_ENCODER_AAC);
+    CHECK_EQ(mAudioEncoder, AUDIO_ENCODER_AAC);
     CHECK(mAudioSource != AUDIO_SOURCE_CNT);
 
     mWriter = new AACWriter(mOutputFd);
@@ -1291,6 +1291,12 @@
     videoSize.width = mVideoWidth;
     videoSize.height = mVideoHeight;
     if (mCaptureTimeLapse) {
+        if (mTimeBetweenTimeLapseFrameCaptureUs < 0) {
+            ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld",
+                mTimeBetweenTimeLapseFrameCaptureUs);
+            return BAD_VALUE;
+        }
+
         mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
                 mCamera, mCameraProxy, mCameraId,
                 videoSize, mFrameRate, mPreviewSurface,
@@ -1386,7 +1392,7 @@
     }
 
     OMXClient client;
-    CHECK_EQ(client.connect(), OK);
+    CHECK_EQ(client.connect(), (status_t)OK);
 
     uint32_t encoder_flags = 0;
     if (mIsMetaDataStoredInVideoBuffers) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index b731d0f..dec1c08c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -387,10 +387,10 @@
                      audio ? "audio" : "video");
 
                 mRenderer->queueEOS(audio, UNKNOWN_ERROR);
-            } else {
-                CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
-
+            } else if (what == ACodec::kWhatDrainThisBuffer) {
                 renderBuffer(audio, codecRequest);
+            } else {
+                ALOGV("Unhandled codec notification %d.", what);
             }
 
             break;
@@ -768,7 +768,7 @@
          mediaTimeUs / 1E6);
 #endif
 
-    reply->setObject("buffer", accessUnit);
+    reply->setBuffer("buffer", accessUnit);
     reply->post();
 
     return OK;
@@ -793,10 +793,8 @@
         return;
     }
 
-    sp<RefBase> obj;
-    CHECK(msg->findObject("buffer", &obj));
-
-    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+    sp<ABuffer> buffer;
+    CHECK(msg->findBuffer("buffer", &buffer));
 
     int64_t &skipUntilMediaTimeUs =
         audio
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 56c2773..2a51829 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -214,8 +214,6 @@
 
         buffer->meta()->setInt32("csd", true);
         mCSD.push(buffer);
-
-        msg->setObject("csd", buffer);
     } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
         ESDS esds((const char *)data, size);
         CHECK_EQ(esds.InitCheck(), (status_t)OK);
@@ -242,9 +240,8 @@
     CHECK(msg->findMessage("reply", &reply));
 
 #if 0
-    sp<RefBase> obj;
-    CHECK(msg->findObject("buffer", &obj));
-    sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+    sp<ABuffer> outBuffer;
+    CHECK(msg->findBuffer("buffer", &outBuffer));
 #else
     sp<ABuffer> outBuffer;
 #endif
@@ -253,7 +250,7 @@
         outBuffer = mCSD.editItemAt(mCSDIndex++);
         outBuffer->meta()->setInt64("timeUs", 0);
 
-        reply->setObject("buffer", outBuffer);
+        reply->setBuffer("buffer", outBuffer);
         reply->post();
         return;
     }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 15259cb..5738ecb 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -60,7 +60,7 @@
         const sp<AMessage> &notifyConsumed) {
     sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
     msg->setInt32("audio", static_cast<int32_t>(audio));
-    msg->setObject("buffer", buffer);
+    msg->setBuffer("buffer", buffer);
     msg->setMessage("notifyConsumed", notifyConsumed);
     msg->post();
 }
@@ -411,9 +411,8 @@
         return;
     }
 
-    sp<RefBase> obj;
-    CHECK(msg->findObject("buffer", &obj));
-    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+    sp<ABuffer> buffer;
+    CHECK(msg->findBuffer("buffer", &buffer));
 
     sp<AMessage> notifyConsumed;
     CHECK(msg->findMessage("notifyConsumed", &notifyConsumed));
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 6eb0d07..4c65b65 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -218,10 +218,8 @@
             CHECK(msg->findSize("trackIndex", &trackIndex));
             CHECK_LT(trackIndex, mTracks.size());
 
-            sp<RefBase> obj;
-            CHECK(msg->findObject("accessUnit", &obj));
-
-            sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+            sp<ABuffer> accessUnit;
+            CHECK(msg->findBuffer("accessUnit", &accessUnit));
 
             int32_t damaged;
             if (accessUnit->meta()->findInt32("damaged", &damaged)
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
index 33f22f2..4d1072f 100644
--- a/media/libstagefright/AACExtractor.cpp
+++ b/media/libstagefright/AACExtractor.cpp
@@ -23,9 +23,9 @@
 
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ca44ea3..c91fbe6 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -171,6 +171,9 @@
 
 private:
     void onSetup(const sp<AMessage> &msg);
+    void onAllocateComponent(const sp<AMessage> &msg);
+    void onConfigureComponent(const sp<AMessage> &msg);
+    void onStart();
 
     DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
 };
@@ -265,6 +268,8 @@
 private:
     void changeStateIfWeOwnAllBuffers();
 
+    bool mComponentNowIdle;
+
     DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState);
 };
 
@@ -309,7 +314,8 @@
 
 ACodec::ACodec()
     : mNode(NULL),
-      mSentFormat(false) {
+      mSentFormat(false),
+      mIsEncoder(false) {
     mUninitializedState = new UninitializedState(this);
     mLoadedToIdleState = new LoadedToIdleState(this);
     mIdleToExecutingState = new IdleToExecutingState(this);
@@ -341,6 +347,22 @@
     msg->post();
 }
 
+void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) {
+    msg->setWhat(kWhatAllocateComponent);
+    msg->setTarget(id());
+    msg->post();
+}
+
+void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) {
+    msg->setWhat(kWhatConfigureComponent);
+    msg->setTarget(id());
+    msg->post();
+}
+
+void ACodec::initiateStart() {
+    (new AMessage(kWhatStart, id()))->post();
+}
+
 void ACodec::signalFlush() {
     ALOGV("[%s] signalFlush", mComponentName.c_str());
     (new AMessage(kWhatFlush, id()))->post();
@@ -360,62 +382,75 @@
     CHECK(mDealer[portIndex] == NULL);
     CHECK(mBuffers[portIndex].isEmpty());
 
+    status_t err;
     if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
-        return allocateOutputBuffersFromNativeWindow();
+        err = allocateOutputBuffersFromNativeWindow();
+    } else {
+        OMX_PARAM_PORTDEFINITIONTYPE def;
+        InitOMXParams(&def);
+        def.nPortIndex = portIndex;
+
+        err = mOMX->getParameter(
+                mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+        if (err == OK) {
+            ALOGV("[%s] Allocating %lu buffers of size %lu on %s port",
+                    mComponentName.c_str(),
+                    def.nBufferCountActual, def.nBufferSize,
+                    portIndex == kPortIndexInput ? "input" : "output");
+
+            size_t totalSize = def.nBufferCountActual * def.nBufferSize;
+            mDealer[portIndex] = new MemoryDealer(totalSize, "ACodec");
+
+            for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+                sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
+                CHECK(mem.get() != NULL);
+
+                IOMX::buffer_id buffer;
+
+                if (!strncasecmp(
+                            mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.", 21)) {
+                    if (portIndex == kPortIndexInput && i == 0) {
+                        // Only log this warning once per allocation round.
+
+                        ALOGW("OMX.TI.DUCATI1.VIDEO.* require the use of "
+                             "OMX_AllocateBuffer instead of the preferred "
+                             "OMX_UseBuffer. Vendor must fix this.");
+                    }
+
+                    err = mOMX->allocateBufferWithBackup(
+                            mNode, portIndex, mem, &buffer);
+                } else {
+                    err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+                }
+
+                BufferInfo info;
+                info.mBufferID = buffer;
+                info.mStatus = BufferInfo::OWNED_BY_US;
+                info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
+                mBuffers[portIndex].push(info);
+            }
+        }
     }
 
-    OMX_PARAM_PORTDEFINITIONTYPE def;
-    InitOMXParams(&def);
-    def.nPortIndex = portIndex;
-
-    status_t err = mOMX->getParameter(
-            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
     if (err != OK) {
         return err;
     }
 
-    ALOGV("[%s] Allocating %lu buffers of size %lu on %s port",
-            mComponentName.c_str(),
-            def.nBufferCountActual, def.nBufferSize,
-            portIndex == kPortIndexInput ? "input" : "output");
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatBuffersAllocated);
 
-    size_t totalSize = def.nBufferCountActual * def.nBufferSize;
-    mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");
+    notify->setInt32("portIndex", portIndex);
+    for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+        AString name = StringPrintf("buffer-id_%d", i);
+        notify->setPointer(name.c_str(), mBuffers[portIndex][i].mBufferID);
 
-    for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
-        sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
-        CHECK(mem.get() != NULL);
-
-        IOMX::buffer_id buffer;
-
-        if (!strcasecmp(
-                    mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.DECODER")) {
-            if (portIndex == kPortIndexInput && i == 0) {
-                // Only log this warning once per allocation round.
-
-                ALOGW("OMX.TI.DUCATI1.VIDEO.DECODER requires the use of "
-                     "OMX_AllocateBuffer instead of the preferred "
-                     "OMX_UseBuffer. Vendor must fix this.");
-            }
-
-            err = mOMX->allocateBufferWithBackup(
-                    mNode, portIndex, mem, &buffer);
-        } else {
-            err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
-        }
-
-        if (err != OK) {
-            return err;
-        }
-
-        BufferInfo info;
-        info.mBufferID = buffer;
-        info.mStatus = BufferInfo::OWNED_BY_US;
-        info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
-        mBuffers[portIndex].push(info);
+        name = StringPrintf("data_%d", i);
+        notify->setBuffer(name.c_str(), mBuffers[portIndex][i].mData);
     }
 
+    notify->post();
+
     return OK;
 }
 
@@ -671,7 +706,7 @@
     return NULL;
 }
 
-void ACodec::setComponentRole(
+status_t ACodec::setComponentRole(
         bool isEncoder, const char *mime) {
     struct MimeToRole {
         const char *mime;
@@ -700,6 +735,8 @@
             "video_decoder.mpeg4", "video_encoder.mpeg4" },
         { MEDIA_MIMETYPE_VIDEO_H263,
             "video_decoder.h263", "video_encoder.h263" },
+        { MEDIA_MIMETYPE_VIDEO_VPX,
+            "video_decoder.vpx", "video_encoder.vpx" },
     };
 
     static const size_t kNumMimeToRole =
@@ -713,7 +750,7 @@
     }
 
     if (i == kNumMimeToRole) {
-        return;
+        return ERROR_UNSUPPORTED;
     }
 
     const char *role =
@@ -736,50 +773,83 @@
         if (err != OK) {
             ALOGW("[%s] Failed to set standard component role '%s'.",
                  mComponentName.c_str(), role);
+
+            return err;
         }
     }
+
+    return OK;
 }
 
-void ACodec::configureCodec(
+status_t ACodec::configureCodec(
         const char *mime, const sp<AMessage> &msg) {
-    setComponentRole(false /* isEncoder */, mime);
+    int32_t encoder;
+    if (!msg->findInt32("encoder", &encoder)) {
+        encoder = false;
+    }
+
+    mIsEncoder = encoder;
+
+    status_t err = setComponentRole(encoder /* isEncoder */, mime);
+
+    if (err != OK) {
+        return err;
+    }
+
+    int32_t bitRate = 0;
+    if (encoder && !msg->findInt32("bitrate", &bitRate)) {
+        return INVALID_OPERATION;
+    }
 
     if (!strncasecmp(mime, "video/", 6)) {
-        int32_t width, height;
-        CHECK(msg->findInt32("width", &width));
-        CHECK(msg->findInt32("height", &height));
-
-        CHECK_EQ(setupVideoDecoder(mime, width, height),
-                 (status_t)OK);
+        if (encoder) {
+            err = setupVideoEncoder(mime, msg);
+        } else {
+            int32_t width, height;
+            if (!msg->findInt32("width", &width)
+                    || !msg->findInt32("height", &height)) {
+                err = INVALID_OPERATION;
+            } else {
+                err = setupVideoDecoder(mime, width, height);
+            }
+        }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
         int32_t numChannels, sampleRate;
-        CHECK(msg->findInt32("channel-count", &numChannels));
-        CHECK(msg->findInt32("sample-rate", &sampleRate));
-
-        CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK);
+        if (!msg->findInt32("channel-count", &numChannels)
+                || !msg->findInt32("sample-rate", &sampleRate)) {
+            err = INVALID_OPERATION;
+        } else {
+            err = setupAACCodec(encoder, numChannels, sampleRate, bitRate);
+        }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
-        CHECK_EQ(setupAMRDecoder(false /* isWAMR */), (status_t)OK);
+        err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
-        CHECK_EQ(setupAMRDecoder(true /* isWAMR */), (status_t)OK);
+        err = setupAMRCodec(encoder, true /* isWAMR */, bitRate);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)
             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) {
         // These are PCM-like formats with a fixed sample rate but
         // a variable number of channels.
 
         int32_t numChannels;
-        CHECK(msg->findInt32("channel-count", &numChannels));
+        if (!msg->findInt32("channel-count", &numChannels)) {
+            err = INVALID_OPERATION;
+        } else {
+            err = setupG711Codec(encoder, numChannels);
+        }
+    }
 
-        CHECK_EQ(setupG711Decoder(numChannels), (status_t)OK);
+    if (err != OK) {
+        return err;
     }
 
     int32_t maxInputSize;
     if (msg->findInt32("max-input-size", &maxInputSize)) {
-        CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize),
-                 (status_t)OK);
+        err = setMinBufferSize(kPortIndexInput, (size_t)maxInputSize);
     } else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) {
-        CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192),  // XXX
-                 (status_t)OK);
+        err = setMinBufferSize(kPortIndexInput, 8192);  // XXX
     }
+
+    return err;
 }
 
 status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
@@ -819,12 +889,113 @@
     return OK;
 }
 
-status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
+status_t ACodec::selectAudioPortFormat(
+        OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat) {
+    OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+    InitOMXParams(&format);
+
+    format.nPortIndex = portIndex;
+    for (OMX_U32 index = 0;; ++index) {
+        format.nIndex = index;
+
+        status_t err = mOMX->getParameter(
+                mNode, OMX_IndexParamAudioPortFormat,
+                &format, sizeof(format));
+
+        if (err != OK) {
+            return err;
+        }
+
+        if (format.eEncoding == desiredFormat) {
+            break;
+        }
+    }
+
+    return mOMX->setParameter(
+            mNode, OMX_IndexParamAudioPortFormat, &format, sizeof(format));
+}
+
+status_t ACodec::setupAACCodec(
+        bool encoder,
+        int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
+    status_t err = setupRawAudioFormat(
+            encoder ? kPortIndexInput : kPortIndexOutput,
+            sampleRate,
+            numChannels);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (encoder) {
+        err = selectAudioPortFormat(kPortIndexOutput, OMX_AUDIO_CodingAAC);
+
+        if (err != OK) {
+            return err;
+        }
+
+        OMX_PARAM_PORTDEFINITIONTYPE def;
+        InitOMXParams(&def);
+        def.nPortIndex = kPortIndexOutput;
+
+        err = mOMX->getParameter(
+                mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+        if (err != OK) {
+            return err;
+        }
+
+        def.format.audio.bFlagErrorConcealment = OMX_TRUE;
+        def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+        err = mOMX->setParameter(
+                mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+        if (err != OK) {
+            return err;
+        }
+
+        OMX_AUDIO_PARAM_AACPROFILETYPE profile;
+        InitOMXParams(&profile);
+        profile.nPortIndex = kPortIndexOutput;
+
+        err = mOMX->getParameter(
+                mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+        if (err != OK) {
+            return err;
+        }
+
+        profile.nChannels = numChannels;
+
+        profile.eChannelMode =
+            (numChannels == 1)
+                ? OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo;
+
+        profile.nSampleRate = sampleRate;
+        profile.nBitRate = bitRate;
+        profile.nAudioBandWidth = 0;
+        profile.nFrameLength = 0;
+        profile.nAACtools = OMX_AUDIO_AACToolAll;
+        profile.nAACERtools = OMX_AUDIO_AACERNone;
+        profile.eAACProfile = OMX_AUDIO_AACObjectLC;
+        profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+
+        err = mOMX->setParameter(
+                mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+        if (err != OK) {
+            return err;
+        }
+
+        return err;
+    }
+
     OMX_AUDIO_PARAM_AACPROFILETYPE profile;
     InitOMXParams(&profile);
     profile.nPortIndex = kPortIndexInput;
 
-    status_t err = mOMX->getParameter(
+    err = mOMX->getParameter(
             mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
 
     if (err != OK) {
@@ -835,16 +1006,59 @@
     profile.nSampleRate = sampleRate;
     profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
 
-    err = mOMX->setParameter(
+    return mOMX->setParameter(
             mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
-
-    return err;
 }
 
-status_t ACodec::setupAMRDecoder(bool isWAMR) {
+static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(
+        bool isAMRWB, int32_t bps) {
+    if (isAMRWB) {
+        if (bps <= 6600) {
+            return OMX_AUDIO_AMRBandModeWB0;
+        } else if (bps <= 8850) {
+            return OMX_AUDIO_AMRBandModeWB1;
+        } else if (bps <= 12650) {
+            return OMX_AUDIO_AMRBandModeWB2;
+        } else if (bps <= 14250) {
+            return OMX_AUDIO_AMRBandModeWB3;
+        } else if (bps <= 15850) {
+            return OMX_AUDIO_AMRBandModeWB4;
+        } else if (bps <= 18250) {
+            return OMX_AUDIO_AMRBandModeWB5;
+        } else if (bps <= 19850) {
+            return OMX_AUDIO_AMRBandModeWB6;
+        } else if (bps <= 23050) {
+            return OMX_AUDIO_AMRBandModeWB7;
+        }
+
+        // 23850 bps
+        return OMX_AUDIO_AMRBandModeWB8;
+    } else {  // AMRNB
+        if (bps <= 4750) {
+            return OMX_AUDIO_AMRBandModeNB0;
+        } else if (bps <= 5150) {
+            return OMX_AUDIO_AMRBandModeNB1;
+        } else if (bps <= 5900) {
+            return OMX_AUDIO_AMRBandModeNB2;
+        } else if (bps <= 6700) {
+            return OMX_AUDIO_AMRBandModeNB3;
+        } else if (bps <= 7400) {
+            return OMX_AUDIO_AMRBandModeNB4;
+        } else if (bps <= 7950) {
+            return OMX_AUDIO_AMRBandModeNB5;
+        } else if (bps <= 10200) {
+            return OMX_AUDIO_AMRBandModeNB6;
+        }
+
+        // 12200 bps
+        return OMX_AUDIO_AMRBandModeNB7;
+    }
+}
+
+status_t ACodec::setupAMRCodec(bool encoder, bool isWAMR, int32_t bitrate) {
     OMX_AUDIO_PARAM_AMRTYPE def;
     InitOMXParams(&def);
-    def.nPortIndex = kPortIndexInput;
+    def.nPortIndex = encoder ? kPortIndexOutput : kPortIndexInput;
 
     status_t err =
         mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
@@ -854,14 +1068,24 @@
     }
 
     def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+    def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitrate);
 
-    def.eAMRBandMode =
-        isWAMR ? OMX_AUDIO_AMRBandModeWB0 : OMX_AUDIO_AMRBandModeNB0;
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
 
-    return mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+    if (err != OK) {
+        return err;
+    }
+
+    return setupRawAudioFormat(
+            encoder ? kPortIndexInput : kPortIndexOutput,
+            isWAMR ? 16000 : 8000 /* sampleRate */,
+            1 /* numChannels */);
 }
 
-status_t ACodec::setupG711Decoder(int32_t numChannels) {
+status_t ACodec::setupG711Codec(bool encoder, int32_t numChannels) {
+    CHECK(!encoder);  // XXX TODO
+
     return setupRawAudioFormat(
             kPortIndexInput, 8000 /* sampleRate */, numChannels);
 }
@@ -1001,22 +1225,36 @@
             &format, sizeof(format));
 }
 
-status_t ACodec::setupVideoDecoder(
-        const char *mime, int32_t width, int32_t height) {
-    OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+static status_t GetVideoCodingTypeFromMime(
+        const char *mime, OMX_VIDEO_CODINGTYPE *codingType) {
     if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
-        compressionFormat = OMX_VIDEO_CodingAVC;
+        *codingType = OMX_VIDEO_CodingAVC;
     } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
-        compressionFormat = OMX_VIDEO_CodingMPEG4;
+        *codingType = OMX_VIDEO_CodingMPEG4;
     } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
-        compressionFormat = OMX_VIDEO_CodingH263;
+        *codingType = OMX_VIDEO_CodingH263;
     } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) {
-        compressionFormat = OMX_VIDEO_CodingMPEG2;
+        *codingType = OMX_VIDEO_CodingMPEG2;
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) {
+        *codingType = OMX_VIDEO_CodingVPX;
     } else {
-        TRESPASS();
+        *codingType = OMX_VIDEO_CodingUnused;
+        return ERROR_UNSUPPORTED;
     }
 
-    status_t err = setVideoPortFormatType(
+    return OK;
+}
+
+status_t ACodec::setupVideoDecoder(
+        const char *mime, int32_t width, int32_t height) {
+    OMX_VIDEO_CODINGTYPE compressionFormat;
+    status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat);
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = setVideoPortFormatType(
             kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
 
     if (err != OK) {
@@ -1046,6 +1284,489 @@
     return OK;
 }
 
+status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) {
+    int32_t tmp;
+    if (!msg->findInt32("color-format", &tmp)) {
+        return INVALID_OPERATION;
+    }
+
+    OMX_COLOR_FORMATTYPE colorFormat =
+        static_cast<OMX_COLOR_FORMATTYPE>(tmp);
+
+    status_t err = setVideoPortFormatType(
+            kPortIndexInput, OMX_VIDEO_CodingUnused, colorFormat);
+
+    if (err != OK) {
+        ALOGE("[%s] does not support color format %d",
+              mComponentName.c_str(), colorFormat);
+
+        return err;
+    }
+
+    /* Input port configuration */
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+    def.nPortIndex = kPortIndexInput;
+
+    err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    int32_t width, height, bitrate;
+    if (!msg->findInt32("width", &width)
+            || !msg->findInt32("height", &height)
+            || !msg->findInt32("bitrate", &bitrate)) {
+        return INVALID_OPERATION;
+    }
+
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+
+    int32_t stride;
+    if (!msg->findInt32("stride", &stride)) {
+        stride = width;
+    }
+
+    video_def->nStride = stride;
+
+    int32_t sliceHeight;
+    if (!msg->findInt32("slice-height", &sliceHeight)) {
+        sliceHeight = height;
+    }
+
+    video_def->nSliceHeight = sliceHeight;
+
+    def.nBufferSize = (video_def->nStride * video_def->nSliceHeight * 3) / 2;
+
+    float frameRate;
+    if (!msg->findFloat("frame-rate", &frameRate)) {
+        int32_t tmp;
+        if (!msg->findInt32("frame-rate", &tmp)) {
+            return INVALID_OPERATION;
+        }
+        frameRate = (float)tmp;
+    }
+
+    video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
+    video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
+    video_def->eColorFormat = colorFormat;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        ALOGE("[%s] failed to set input port definition parameters.",
+              mComponentName.c_str());
+
+        return err;
+    }
+
+    /* Output port configuration */
+
+    OMX_VIDEO_CODINGTYPE compressionFormat;
+    err = GetVideoCodingTypeFromMime(mime, &compressionFormat);
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = setVideoPortFormatType(
+            kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused);
+
+    if (err != OK) {
+        ALOGE("[%s] does not support compression format %d",
+             mComponentName.c_str(), compressionFormat);
+
+        return err;
+    }
+
+    def.nPortIndex = kPortIndexOutput;
+
+    err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+    video_def->xFramerate = 0;
+    video_def->nBitrate = bitrate;
+    video_def->eCompressionFormat = compressionFormat;
+    video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        ALOGE("[%s] failed to set output port definition parameters.",
+              mComponentName.c_str());
+
+        return err;
+    }
+
+    switch (compressionFormat) {
+        case OMX_VIDEO_CodingMPEG4:
+            err = setupMPEG4EncoderParameters(msg);
+            break;
+
+        case OMX_VIDEO_CodingH263:
+            err = setupH263EncoderParameters(msg);
+            break;
+
+        case OMX_VIDEO_CodingAVC:
+            err = setupAVCEncoderParameters(msg);
+            break;
+
+        default:
+            break;
+    }
+
+    ALOGI("setupVideoEncoder succeeded");
+
+    return err;
+}
+
+static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) {
+    if (iFramesInterval < 0) {
+        return 0xFFFFFFFF;
+    } else if (iFramesInterval == 0) {
+        return 0;
+    }
+    OMX_U32 ret = frameRate * iFramesInterval;
+    CHECK(ret > 1);
+    return ret;
+}
+
+status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) {
+    int32_t bitrate, iFrameInterval;
+    if (!msg->findInt32("bitrate", &bitrate)
+            || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+        return INVALID_OPERATION;
+    }
+
+    float frameRate;
+    if (!msg->findFloat("frame-rate", &frameRate)) {
+        int32_t tmp;
+        if (!msg->findInt32("frame-rate", &tmp)) {
+            return INVALID_OPERATION;
+        }
+        frameRate = (float)tmp;
+    }
+
+    OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type;
+    InitOMXParams(&mpeg4type);
+    mpeg4type.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
+
+    if (err != OK) {
+        return err;
+    }
+
+    mpeg4type.nSliceHeaderSpacing = 0;
+    mpeg4type.bSVH = OMX_FALSE;
+    mpeg4type.bGov = OMX_FALSE;
+
+    mpeg4type.nAllowedPictureTypes =
+        OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+    mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+    if (mpeg4type.nPFrames == 0) {
+        mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+    }
+    mpeg4type.nBFrames = 0;
+    mpeg4type.nIDCVLCThreshold = 0;
+    mpeg4type.bACPred = OMX_TRUE;
+    mpeg4type.nMaxPacketSize = 256;
+    mpeg4type.nTimeIncRes = 1000;
+    mpeg4type.nHeaderExtension = 0;
+    mpeg4type.bReversibleVLC = OMX_FALSE;
+
+    int32_t profile;
+    if (msg->findInt32("profile", &profile)) {
+        int32_t level;
+        if (!msg->findInt32("level", &level)) {
+            return INVALID_OPERATION;
+        }
+
+        err = verifySupportForProfileAndLevel(profile, level);
+
+        if (err != OK) {
+            return err;
+        }
+
+        mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profile);
+        mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(level);
+    }
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = configureBitrate(bitrate);
+
+    if (err != OK) {
+        return err;
+    }
+
+    return setupErrorCorrectionParameters();
+}
+
+status_t ACodec::setupH263EncoderParameters(const sp<AMessage> &msg) {
+    int32_t bitrate, iFrameInterval;
+    if (!msg->findInt32("bitrate", &bitrate)
+            || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+        return INVALID_OPERATION;
+    }
+
+    float frameRate;
+    if (!msg->findFloat("frame-rate", &frameRate)) {
+        int32_t tmp;
+        if (!msg->findInt32("frame-rate", &tmp)) {
+            return INVALID_OPERATION;
+        }
+        frameRate = (float)tmp;
+    }
+
+    OMX_VIDEO_PARAM_H263TYPE h263type;
+    InitOMXParams(&h263type);
+    h263type.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
+
+    if (err != OK) {
+        return err;
+    }
+
+    h263type.nAllowedPictureTypes =
+        OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+    h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+    if (h263type.nPFrames == 0) {
+        h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+    }
+    h263type.nBFrames = 0;
+
+    int32_t profile;
+    if (msg->findInt32("profile", &profile)) {
+        int32_t level;
+        if (!msg->findInt32("level", &level)) {
+            return INVALID_OPERATION;
+        }
+
+        err = verifySupportForProfileAndLevel(profile, level);
+
+        if (err != OK) {
+            return err;
+        }
+
+        h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profile);
+        h263type.eLevel = static_cast<OMX_VIDEO_H263LEVELTYPE>(level);
+    }
+
+    h263type.bPLUSPTYPEAllowed = OMX_FALSE;
+    h263type.bForceRoundingTypeToZero = OMX_FALSE;
+    h263type.nPictureHeaderRepetition = 0;
+    h263type.nGOBHeaderInterval = 0;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = configureBitrate(bitrate);
+
+    if (err != OK) {
+        return err;
+    }
+
+    return setupErrorCorrectionParameters();
+}
+
+status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
+    int32_t bitrate, iFrameInterval;
+    if (!msg->findInt32("bitrate", &bitrate)
+            || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+        return INVALID_OPERATION;
+    }
+
+    float frameRate;
+    if (!msg->findFloat("frame-rate", &frameRate)) {
+        int32_t tmp;
+        if (!msg->findInt32("frame-rate", &tmp)) {
+            return INVALID_OPERATION;
+        }
+        frameRate = (float)tmp;
+    }
+
+    OMX_VIDEO_PARAM_AVCTYPE h264type;
+    InitOMXParams(&h264type);
+    h264type.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+    if (err != OK) {
+        return err;
+    }
+
+    h264type.nAllowedPictureTypes =
+        OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+    int32_t profile;
+    if (msg->findInt32("profile", &profile)) {
+        int32_t level;
+        if (!msg->findInt32("level", &level)) {
+            return INVALID_OPERATION;
+        }
+
+        err = verifySupportForProfileAndLevel(profile, level);
+
+        if (err != OK) {
+            return err;
+        }
+
+        h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profile);
+        h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(level);
+    }
+
+    // XXX
+    if (!strncmp(mComponentName.c_str(), "OMX.TI.DUCATI1", 14)) {
+        h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
+    }
+
+    if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) {
+        h264type.nSliceHeaderSpacing = 0;
+        h264type.bUseHadamard = OMX_TRUE;
+        h264type.nRefFrames = 1;
+        h264type.nBFrames = 0;
+        h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+        if (h264type.nPFrames == 0) {
+            h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+        }
+        h264type.nRefIdx10ActiveMinus1 = 0;
+        h264type.nRefIdx11ActiveMinus1 = 0;
+        h264type.bEntropyCodingCABAC = OMX_FALSE;
+        h264type.bWeightedPPrediction = OMX_FALSE;
+        h264type.bconstIpred = OMX_FALSE;
+        h264type.bDirect8x8Inference = OMX_FALSE;
+        h264type.bDirectSpatialTemporal = OMX_FALSE;
+        h264type.nCabacInitIdc = 0;
+    }
+
+    if (h264type.nBFrames != 0) {
+        h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
+    }
+
+    h264type.bEnableUEP = OMX_FALSE;
+    h264type.bEnableFMO = OMX_FALSE;
+    h264type.bEnableASO = OMX_FALSE;
+    h264type.bEnableRS = OMX_FALSE;
+    h264type.bFrameMBsOnly = OMX_TRUE;
+    h264type.bMBAFF = OMX_FALSE;
+    h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
+
+    if (!strcasecmp("OMX.Nvidia.h264.encoder", mComponentName.c_str())) {
+        h264type.eLevel = OMX_VIDEO_AVCLevelMax;
+    }
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+    if (err != OK) {
+        return err;
+    }
+
+    return configureBitrate(bitrate);
+}
+
+status_t ACodec::verifySupportForProfileAndLevel(
+        int32_t profile, int32_t level) {
+    OMX_VIDEO_PARAM_PROFILELEVELTYPE params;
+    InitOMXParams(&params);
+    params.nPortIndex = kPortIndexOutput;
+
+    for (params.nProfileIndex = 0;; ++params.nProfileIndex) {
+        status_t err = mOMX->getParameter(
+                mNode,
+                OMX_IndexParamVideoProfileLevelQuerySupported,
+                &params,
+                sizeof(params));
+
+        if (err != OK) {
+            return err;
+        }
+
+        int32_t supportedProfile = static_cast<int32_t>(params.eProfile);
+        int32_t supportedLevel = static_cast<int32_t>(params.eLevel);
+
+        if (profile == supportedProfile && level <= supportedLevel) {
+            return OK;
+        }
+    }
+}
+
+status_t ACodec::configureBitrate(int32_t bitrate) {
+    OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
+    InitOMXParams(&bitrateType);
+    bitrateType.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoBitrate,
+            &bitrateType, sizeof(bitrateType));
+
+    if (err != OK) {
+        return err;
+    }
+
+    bitrateType.eControlRate = OMX_Video_ControlRateVariable;
+    bitrateType.nTargetBitrate = bitrate;
+
+    return mOMX->setParameter(
+            mNode, OMX_IndexParamVideoBitrate,
+            &bitrateType, sizeof(bitrateType));
+}
+
+status_t ACodec::setupErrorCorrectionParameters() {
+    OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType;
+    InitOMXParams(&errorCorrectionType);
+    errorCorrectionType.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoErrorCorrection,
+            &errorCorrectionType, sizeof(errorCorrectionType));
+
+    if (err != OK) {
+        return OK;  // Optional feature. Ignore this failure
+    }
+
+    errorCorrectionType.bEnableHEC = OMX_FALSE;
+    errorCorrectionType.bEnableResync = OMX_TRUE;
+    errorCorrectionType.nResynchMarkerSpacing = 256;
+    errorCorrectionType.bEnableDataPartitioning = OMX_FALSE;
+    errorCorrectionType.bEnableRVLC = OMX_FALSE;
+
+    return mOMX->setParameter(
+            mNode, OMX_IndexParamVideoErrorCorrection,
+            &errorCorrectionType, sizeof(errorCorrectionType));
+}
+
 status_t ACodec::setVideoFormatOnPort(
         OMX_U32 portIndex,
         int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) {
@@ -1166,6 +1887,9 @@
             notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW);
             notify->setInt32("width", videoDef->nFrameWidth);
             notify->setInt32("height", videoDef->nFrameHeight);
+            notify->setInt32("stride", videoDef->nStride);
+            notify->setInt32("slice-height", videoDef->nSliceHeight);
+            notify->setInt32("color-format", videoDef->eColorFormat);
 
             OMX_CONFIG_RECTTYPE rect;
             InitOMXParams(&rect);
@@ -1241,10 +1965,11 @@
     mSentFormat = true;
 }
 
-void ACodec::signalError(OMX_ERRORTYPE error) {
+void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) {
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", ACodec::kWhatError);
     notify->setInt32("omx-error", error);
+    notify->setInt32("err", internalError);
     notify->post();
 }
 
@@ -1417,7 +2142,7 @@
     notify->setPointer("buffer-id", info->mBufferID);
 
     info->mData->meta()->clear();
-    notify->setObject("buffer", info->mData);
+    notify->setBuffer("buffer", info->mData);
 
     sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
     reply->setPointer("buffer-id", info->mBufferID);
@@ -1433,18 +2158,26 @@
     IOMX::buffer_id bufferID;
     CHECK(msg->findPointer("buffer-id", &bufferID));
 
-    sp<RefBase> obj;
+    sp<ABuffer> buffer;
     int32_t err = OK;
-    if (!msg->findObject("buffer", &obj)) {
+    bool eos = false;
+
+    if (!msg->findBuffer("buffer", &buffer)) {
         CHECK(msg->findInt32("err", &err));
 
         ALOGV("[%s] saw error %d instead of an input buffer",
              mCodec->mComponentName.c_str(), err);
 
-        obj.clear();
+        buffer.clear();
+
+        eos = true;
     }
 
-    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+    int32_t tmp;
+    if (buffer != NULL && buffer->meta()->findInt32("eos", &tmp) && tmp) {
+        eos = true;
+        err = ERROR_END_OF_STREAM;
+    }
 
     BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID);
     CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_UPSTREAM);
@@ -1456,7 +2189,7 @@
     switch (mode) {
         case KEEP_BUFFERS:
         {
-            if (buffer == NULL) {
+            if (eos) {
                 if (!mCodec->mPortEOS[kPortIndexInput]) {
                     mCodec->mPortEOS[kPortIndexInput] = true;
                     mCodec->mInputEOSResult = err;
@@ -1467,9 +2200,7 @@
 
         case RESUBMIT_BUFFERS:
         {
-            if (buffer != NULL) {
-                CHECK(!mCodec->mPortEOS[kPortIndexInput]);
-
+            if (buffer != NULL && !mCodec->mPortEOS[kPortIndexInput]) {
                 int64_t timeUs;
                 CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
@@ -1480,6 +2211,10 @@
                     flags |= OMX_BUFFERFLAG_CODECCONFIG;
                 }
 
+                if (eos) {
+                    flags |= OMX_BUFFERFLAG_EOS;
+                }
+
                 if (buffer != info->mData) {
                     if (0 && !(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
                         ALOGV("[%s] Needs to copy input data.",
@@ -1493,6 +2228,9 @@
                 if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
                     ALOGV("[%s] calling emptyBuffer %p w/ codec specific data",
                          mCodec->mComponentName.c_str(), bufferID);
+                } else if (flags & OMX_BUFFERFLAG_EOS) {
+                    ALOGV("[%s] calling emptyBuffer %p w/ EOS",
+                         mCodec->mComponentName.c_str(), bufferID);
                 } else {
                     ALOGV("[%s] calling emptyBuffer %p w/ time %lld us",
                          mCodec->mComponentName.c_str(), bufferID, timeUs);
@@ -1509,7 +2247,15 @@
 
                 info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
 
-                getMoreInputDataIfPossible();
+                if (!eos) {
+                    getMoreInputDataIfPossible();
+                } else {
+                    ALOGV("[%s] Signalled EOS on the input port",
+                         mCodec->mComponentName.c_str());
+
+                    mCodec->mPortEOS[kPortIndexInput] = true;
+                    mCodec->mInputEOSResult = err;
+                }
             } else if (!mCodec->mPortEOS[kPortIndexInput]) {
                 if (err != ERROR_END_OF_STREAM) {
                     ALOGV("[%s] Signalling EOS on the input port "
@@ -1582,8 +2328,8 @@
         int64_t timeUs,
         void *platformPrivate,
         void *dataPtr) {
-    ALOGV("[%s] onOMXFillBufferDone %p time %lld us",
-         mCodec->mComponentName.c_str(), bufferID, timeUs);
+    ALOGV("[%s] onOMXFillBufferDone %p time %lld us, flags = 0x%08lx",
+         mCodec->mComponentName.c_str(), bufferID, timeUs, flags);
 
     ssize_t index;
     BufferInfo *info =
@@ -1601,46 +2347,48 @@
 
         case RESUBMIT_BUFFERS:
         {
-            if (rangeLength == 0) {
-                if (!(flags & OMX_BUFFERFLAG_EOS)) {
-                    ALOGV("[%s] calling fillBuffer %p",
-                         mCodec->mComponentName.c_str(), info->mBufferID);
+            if (rangeLength == 0 && !(flags & OMX_BUFFERFLAG_EOS)) {
+                ALOGV("[%s] calling fillBuffer %p",
+                     mCodec->mComponentName.c_str(), info->mBufferID);
 
-                    CHECK_EQ(mCodec->mOMX->fillBuffer(
-                                mCodec->mNode, info->mBufferID),
-                             (status_t)OK);
+                CHECK_EQ(mCodec->mOMX->fillBuffer(
+                            mCodec->mNode, info->mBufferID),
+                         (status_t)OK);
 
-                    info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
-                }
-            } else {
-                if (!mCodec->mSentFormat) {
-                    mCodec->sendFormatChange();
-                }
-
-                if (mCodec->mNativeWindow == NULL) {
-                    info->mData->setRange(rangeOffset, rangeLength);
-                }
-
-                info->mData->meta()->setInt64("timeUs", timeUs);
-
-                sp<AMessage> notify = mCodec->mNotify->dup();
-                notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
-                notify->setPointer("buffer-id", info->mBufferID);
-                notify->setObject("buffer", info->mData);
-
-                sp<AMessage> reply =
-                    new AMessage(kWhatOutputBufferDrained, mCodec->id());
-
-                reply->setPointer("buffer-id", info->mBufferID);
-
-                notify->setMessage("reply", reply);
-
-                notify->post();
-
-                info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
+                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+                break;
             }
 
+            if (!mCodec->mIsEncoder && !mCodec->mSentFormat) {
+                mCodec->sendFormatChange();
+            }
+
+            if (mCodec->mNativeWindow == NULL) {
+                info->mData->setRange(rangeOffset, rangeLength);
+            }
+
+            info->mData->meta()->setInt64("timeUs", timeUs);
+
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+            notify->setPointer("buffer-id", info->mBufferID);
+            notify->setBuffer("buffer", info->mData);
+            notify->setInt32("flags", flags);
+
+            sp<AMessage> reply =
+                new AMessage(kWhatOutputBufferDrained, mCodec->id());
+
+            reply->setPointer("buffer-id", info->mBufferID);
+
+            notify->setMessage("reply", reply);
+
+            notify->post();
+
+            info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
+
             if (flags & OMX_BUFFERFLAG_EOS) {
+                ALOGV("[%s] saw output EOS", mCodec->mComponentName.c_str());
+
                 sp<AMessage> notify = mCodec->mNotify->dup();
                 notify->setInt32("what", ACodec::kWhatEOS);
                 notify->setInt32("err", mCodec->mInputEOSResult);
@@ -1678,12 +2426,13 @@
             && msg->findInt32("render", &render) && render != 0) {
         // The client wants this buffer to be rendered.
 
-        if (mCodec->mNativeWindow->queueBuffer(
+        status_t err;
+        if ((err = mCodec->mNativeWindow->queueBuffer(
                     mCodec->mNativeWindow.get(),
-                    info->mGraphicBuffer.get()) == OK) {
+                    info->mGraphicBuffer.get())) == OK) {
             info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
         } else {
-            mCodec->signalError();
+            mCodec->signalError(OMX_ErrorUndefined, err);
             info->mStatus = BufferInfo::OWNED_BY_US;
         }
     } else {
@@ -1758,6 +2507,27 @@
             break;
         }
 
+        case ACodec::kWhatAllocateComponent:
+        {
+            onAllocateComponent(msg);
+            handled = true;
+            break;
+        }
+
+        case ACodec::kWhatConfigureComponent:
+        {
+            onConfigureComponent(msg);
+            handled = true;
+            break;
+        }
+
+        case ACodec::kWhatStart:
+        {
+            onStart();
+            handled = true;
+            break;
+        }
+
         case ACodec::kWhatShutdown:
         {
             sp<AMessage> notify = mCodec->mNotify->dup();
@@ -1787,27 +2557,54 @@
 
 void ACodec::UninitializedState::onSetup(
         const sp<AMessage> &msg) {
+    onAllocateComponent(msg);
+    onConfigureComponent(msg);
+    onStart();
+}
+
+void ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
+    ALOGV("onAllocateComponent");
+
+    if (mCodec->mNode != NULL) {
+        CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
+
+        mCodec->mNativeWindow.clear();
+        mCodec->mNode = NULL;
+        mCodec->mOMX.clear();
+        mCodec->mComponentName.clear();
+    }
+
     OMXClient client;
     CHECK_EQ(client.connect(), (status_t)OK);
 
     sp<IOMX> omx = client.interface();
 
-    AString mime;
-    CHECK(msg->findString("mime", &mime));
-
     Vector<String8> matchingCodecs;
-    OMXCodec::findMatchingCodecs(
-            mime.c_str(),
-            false, // createEncoder
-            NULL,  // matchComponentName
-            0,     // flags
-            &matchingCodecs);
+
+    AString mime;
+
+    AString componentName;
+    if (msg->findString("componentName", &componentName)) {
+        matchingCodecs.push_back(String8(componentName.c_str()));
+    } else {
+        CHECK(msg->findString("mime", &mime));
+
+        int32_t encoder;
+        if (!msg->findInt32("encoder", &encoder)) {
+            encoder = false;
+        }
+
+        OMXCodec::findMatchingCodecs(
+                mime.c_str(),
+                encoder, // createEncoder
+                NULL,  // matchComponentName
+                0,     // flags
+                &matchingCodecs);
+    }
 
     sp<CodecObserver> observer = new CodecObserver;
     IOMX::node_id node = NULL;
 
-    AString componentName;
-
     for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
             ++matchIndex) {
         componentName = matchingCodecs.itemAt(matchIndex).string();
@@ -1826,7 +2623,12 @@
     }
 
     if (node == NULL) {
-        ALOGE("Unable to instantiate a decoder for type '%s'.", mime.c_str());
+        if (!mime.empty()) {
+            ALOGE("Unable to instantiate a decoder for type '%s'.",
+                 mime.c_str());
+        } else {
+            ALOGE("Unable to instantiate decoder '%s'.", componentName.c_str());
+        }
 
         mCodec->signalError(OMX_ErrorComponentNotFound);
         return;
@@ -1844,20 +2646,52 @@
 
     mCodec->mInputEOSResult = OK;
 
-    mCodec->configureCodec(mime.c_str(), msg);
+    {
+        sp<AMessage> notify = mCodec->mNotify->dup();
+        notify->setInt32("what", ACodec::kWhatComponentAllocated);
+        notify->setString("componentName", mCodec->mComponentName.c_str());
+        notify->post();
+    }
+}
+
+void ACodec::UninitializedState::onConfigureComponent(
+        const sp<AMessage> &msg) {
+    ALOGV("onConfigureComponent");
+
+    CHECK(mCodec->mNode != NULL);
+
+    AString mime;
+    CHECK(msg->findString("mime", &mime));
+
+    status_t err = mCodec->configureCodec(mime.c_str(), msg);
+
+    if (err != OK) {
+        mCodec->signalError(OMX_ErrorUndefined, err);
+        return;
+    }
 
     sp<RefBase> obj;
     if (msg->findObject("native-window", &obj)
-            && strncmp("OMX.google.", componentName.c_str(), 11)) {
+            && strncmp("OMX.google.", mCodec->mComponentName.c_str(), 11)) {
         sp<NativeWindowWrapper> nativeWindow(
                 static_cast<NativeWindowWrapper *>(obj.get()));
         CHECK(nativeWindow != NULL);
         mCodec->mNativeWindow = nativeWindow->getNativeWindow();
     }
-
     CHECK_EQ((status_t)OK, mCodec->initNativeWindow());
 
-    CHECK_EQ(omx->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle),
+    {
+        sp<AMessage> notify = mCodec->mNotify->dup();
+        notify->setInt32("what", ACodec::kWhatComponentConfigured);
+        notify->post();
+    }
+}
+
+void ACodec::UninitializedState::onStart() {
+    ALOGV("onStart");
+
+    CHECK_EQ(mCodec->mOMX->sendCommand(
+                mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
              (status_t)OK);
 
     mCodec->changeState(mCodec->mLoadedToIdleState);
@@ -1878,7 +2712,7 @@
              "(error 0x%08x)",
              err);
 
-        mCodec->signalError();
+        mCodec->signalError(OMX_ErrorUndefined, err);
     }
 }
 
@@ -2202,7 +3036,7 @@
                          "port reconfiguration (error 0x%08x)",
                          err);
 
-                    mCodec->signalError();
+                    mCodec->signalError(OMX_ErrorUndefined, err);
 
                     // This is technically not correct, since we were unable
                     // to allocate output buffers and therefore the output port
@@ -2240,7 +3074,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec)
-    : BaseState(codec) {
+    : BaseState(codec),
+      mComponentNowIdle(false) {
 }
 
 bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
@@ -2274,6 +3109,7 @@
 void ACodec::ExecutingToIdleState::stateEntered() {
     ALOGV("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
 
+    mComponentNowIdle = false;
     mCodec->mSentFormat = false;
 }
 
@@ -2285,6 +3121,8 @@
             CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
             CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
 
+            mComponentNowIdle = true;
+
             changeStateIfWeOwnAllBuffers();
 
             return true;
@@ -2303,7 +3141,7 @@
 }
 
 void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
-    if (mCodec->allYourBuffersAreBelongToUs()) {
+    if (mComponentNowIdle && mCodec->allYourBuffersAreBelongToUs()) {
         CHECK_EQ(mCodec->mOMX->sendCommand(
                     mCodec->mNode, OMX_CommandStateSet, OMX_StateLoaded),
                  (status_t)OK);
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 5a28347..03dcbf9 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -20,9 +20,9 @@
 
 #include "include/AMRExtractor.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 59b4ca7..ca85640 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AMRWriter.h>
 #include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 483e5ab..cfb1e29 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -29,12 +29,14 @@
         MPEG4Writer.cpp                   \
         MediaBuffer.cpp                   \
         MediaBufferGroup.cpp              \
+        MediaCodec.cpp                    \
         MediaDefs.cpp                     \
         MediaExtractor.cpp                \
         MediaSource.cpp                   \
         MediaSourceSplitter.cpp           \
         MetaData.cpp                      \
         NuCachedSource2.cpp               \
+        NuMediaExtractor.cpp              \
         OMXClient.cpp                     \
         OMXCodec.cpp                      \
         OggExtractor.cpp                  \
@@ -61,20 +63,26 @@
         $(TOP)/external/openssl/include \
 
 LOCAL_SHARED_LIBRARIES := \
-        libbinder         \
-        libmedia          \
-        libutils          \
-        libcutils         \
-        libui             \
-        libsonivox        \
-        libvorbisidec     \
+        libbinder \
+        libmedia \
+        libutils \
+        libcutils \
+        libui \
+        libsonivox \
+        libvorbisidec \
         libstagefright_yuv \
         libcamera_client \
-        libdrmframework  \
-        libcrypto        \
-        libssl           \
-        libgui           \
+        libdrmframework \
+        libcrypto \
+        libssl \
+        libgui \
         libstagefright_omx \
+        liblog \
+        libicuuc \
+        libicui18n \
+        libz \
+        libdl \
+        libchromium_net \
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_color_conversion \
@@ -88,57 +96,14 @@
         libstagefright_httplive \
         libstagefright_id3 \
         libFLAC \
+        libstagefright_chromium_http \
 
-################################################################################
-
-# The following was shamelessly copied from external/webkit/Android.mk and
-# currently must follow the same logic to determine how webkit was built and
-# if it's safe to link against libchromium.net
-
-# V8 also requires an ARMv7 CPU, and since we must use jsc, we cannot
-# use the Chrome http stack either.
-ifneq ($(strip $(ARCH_ARM_HAVE_ARMV7A)),true)
-  USE_ALT_HTTP := true
-endif
-
-# See if the user has specified a stack they want to use
-HTTP_STACK = $(HTTP)
-# We default to the Chrome HTTP stack.
-DEFAULT_HTTP = chrome
-ALT_HTTP = android
-
-ifneq ($(HTTP_STACK),chrome)
-  ifneq ($(HTTP_STACK),android)
-    # No HTTP stack is specified, pickup the one we want as default.
-    ifeq ($(USE_ALT_HTTP),true)
-      HTTP_STACK = $(ALT_HTTP)
-    else
-      HTTP_STACK = $(DEFAULT_HTTP)
-    endif
-  endif
-endif
-
-ifeq ($(HTTP_STACK),chrome)
-
-LOCAL_SHARED_LIBRARIES += \
-        liblog           \
-        libicuuc         \
-        libicui18n       \
-        libz             \
-        libdl            \
-
-LOCAL_STATIC_LIBRARIES += \
-        libstagefright_chromium_http
-
-LOCAL_SHARED_LIBRARIES += libstlport libchromium_net
+LOCAL_SHARED_LIBRARIES += libstlport
 include external/stlport/libstlport.mk
 
+# TODO: Chromium is always available, so this flag can be removed.
 LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
 
-endif  # ifeq ($(HTTP_STACK),chrome)
-
-################################################################################
-
 LOCAL_SHARED_LIBRARIES += \
         libstagefright_enc_common \
         libstagefright_avc_common \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 9a9c3ef..df27566 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -268,6 +268,16 @@
     return mReachedEOS;
 }
 
+status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) {
+    if (mAudioSink.get() != NULL) {
+        return mAudioSink->setPlaybackRatePermille(ratePermille);
+    } else if (mAudioTrack != NULL){
+        return mAudioTrack->setSampleRate(ratePermille * mSampleRate / 1000);
+    } else {
+        return NO_INIT;
+    }
+}
+
 // static
 size_t AudioPlayer::AudioSinkCallback(
         MediaPlayerBase::AudioSink *audioSink,
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index fef2a00..5b2ea1f 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -282,8 +282,6 @@
         mPrevSampleTimeUs = mStartTimeUs;
     }
 
-    int64_t timestampUs = mPrevSampleTimeUs;
-
     size_t numLostBytes = 0;
     if (mNumFramesReceived > 0) {  // Ignore earlier frame lost
         // getInputFramesLost() returns the number of lost frames.
@@ -293,37 +291,58 @@
 
     CHECK_EQ(numLostBytes & 1, 0u);
     CHECK_EQ(audioBuffer.size & 1, 0u);
-    size_t bufferSize = numLostBytes + audioBuffer.size;
-    MediaBuffer *buffer = new MediaBuffer(bufferSize);
     if (numLostBytes > 0) {
-        memset(buffer->data(), 0, numLostBytes);
-        memcpy((uint8_t *) buffer->data() + numLostBytes,
-                    audioBuffer.i16, audioBuffer.size);
-    } else {
-        if (audioBuffer.size == 0) {
-            ALOGW("Nothing is available from AudioRecord callback buffer");
-            buffer->release();
-            return OK;
-        }
-        memcpy((uint8_t *) buffer->data(),
-                audioBuffer.i16, audioBuffer.size);
+        // Loss of audio frames should happen rarely; thus the LOGW should
+        // not cause a logging spam
+        ALOGW("Lost audio record data: %d bytes", numLostBytes);
     }
 
+    while (numLostBytes > 0) {
+        size_t bufferSize = numLostBytes;
+        if (numLostBytes > kMaxBufferSize) {
+            numLostBytes -= kMaxBufferSize;
+            bufferSize = kMaxBufferSize;
+        } else {
+            numLostBytes = 0;
+        }
+        MediaBuffer *lostAudioBuffer = new MediaBuffer(bufferSize);
+        memset(lostAudioBuffer->data(), 0, bufferSize);
+        lostAudioBuffer->set_range(0, bufferSize);
+        queueInputBuffer_l(lostAudioBuffer, timeUs);
+    }
+
+    if (audioBuffer.size == 0) {
+        ALOGW("Nothing is available from AudioRecord callback buffer");
+        return OK;
+    }
+
+    const size_t bufferSize = audioBuffer.size;
+    MediaBuffer *buffer = new MediaBuffer(bufferSize);
+    memcpy((uint8_t *) buffer->data(),
+            audioBuffer.i16, audioBuffer.size);
     buffer->set_range(0, bufferSize);
-    timestampUs += ((1000000LL * (bufferSize >> 1)) +
-                    (mSampleRate >> 1)) / mSampleRate;
+    queueInputBuffer_l(buffer, timeUs);
+    return OK;
+}
+
+void AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) {
+    const size_t bufferSize = buffer->range_length();
+    const size_t frameSize = mRecord->frameSize();
+    const int64_t timestampUs =
+                mPrevSampleTimeUs +
+                    ((1000000LL * (bufferSize / frameSize)) +
+                        (mSampleRate >> 1)) / mSampleRate;
 
     if (mNumFramesReceived == 0) {
         buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
     }
+
     buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
     buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs);
     mPrevSampleTimeUs = timestampUs;
-    mNumFramesReceived += buffer->range_length() / sizeof(int16_t);
+    mNumFramesReceived += bufferSize / frameSize;
     mBuffersReceived.push_back(buffer);
     mFrameAvailableCondition.signal();
-
-    return OK;
 }
 
 void AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8073af8..70945e3 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1360,7 +1360,7 @@
     mAudioTrack = source;
 }
 
-void AwesomePlayer::addTextSource(sp<MediaSource> source) {
+void AwesomePlayer::addTextSource(const sp<MediaSource>& source) {
     Mutex::Autolock autoLock(mTimedTextLock);
     CHECK(source != NULL);
 
@@ -1609,7 +1609,7 @@
                     mSeekTimeUs,
                     mSeeking == SEEK_VIDEO_ONLY
                         ? MediaSource::ReadOptions::SEEK_NEXT_SYNC
-                        : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
+                        : MediaSource::ReadOptions::SEEK_CLOSEST);
         }
         for (;;) {
             status_t err = mVideoSource->read(&mVideoBuffer, &options);
@@ -2251,6 +2251,14 @@
         {
             return setCacheStatCollectFreq(request);
         }
+        case KEY_PARAMETER_PLAYBACK_RATE_PERMILLE:
+        {
+            if (mAudioPlayer != NULL) {
+                return mAudioPlayer->setPlaybackRatePermille(request.readInt32());
+            } else {
+                return NO_INIT;
+            }
+        }
         default:
         {
             return ERROR_UNSUPPORTED;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 228659c..ed1d5f4 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -20,8 +20,8 @@
 
 #include <OMX_Component.h>
 #include <binder/IPCThreadState.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/CameraSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -114,7 +114,7 @@
     ALOGE("Uknown color format (%s), please add it to "
          "CameraSource::getColorFormat", colorFormat);
 
-    CHECK_EQ(0, "Unknown color format");
+    CHECK(!"Unknown color format");
 }
 
 CameraSource *CameraSource::Create() {
@@ -517,7 +517,7 @@
 
     // This CHECK is good, since we just passed the lock/unlock
     // check earlier by calling mCamera->setParameters().
-    CHECK_EQ(OK, mCamera->setPreviewDisplay(mSurface));
+    CHECK_EQ((status_t)OK, mCamera->setPreviewDisplay(mSurface));
 
     // By default, do not store metadata in video buffers
     mIsMetaDataStoredInVideoBuffers = false;
@@ -566,7 +566,8 @@
     if (mCameraFlags & FLAGS_HOT_CAMERA) {
         mCamera->unlock();
         mCamera.clear();
-        CHECK_EQ(OK, mCameraRecordingProxy->startRecording(new ProxyListener(this)));
+        CHECK_EQ((status_t)OK,
+            mCameraRecordingProxy->startRecording(new ProxyListener(this)));
     } else {
         mCamera->setListener(new CameraSourceListener(this));
         mCamera->startRecording();
@@ -718,7 +719,7 @@
             return;
         }
     }
-    CHECK_EQ(0, "signalBufferReturned: bogus buffer");
+    CHECK(!"signalBufferReturned: bogus buffer");
 }
 
 status_t CameraSource::read(
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 83d67b9..26ce7ae 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -20,9 +20,9 @@
 #include <binder/IPCThreadState.h>
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/CameraSource.h>
 #include <media/stagefright/CameraSourceTimeLapse.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MetaData.h>
 #include <camera/Camera.h>
 #include <camera/CameraParameters.h>
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
index afc4a80..524c3aa 100644
--- a/media/libstagefright/DRMExtractor.cpp
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -23,6 +23,7 @@
 
 #include <arpa/inet.h>
 #include <utils/String8.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/Utils.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaSource.h>
@@ -30,7 +31,6 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
 
 #include <drm/drm_framework_common.h>
 #include <utils/Errors.h>
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index 01f53e4..73c8d03 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <sys/types.h>
diff --git a/media/libstagefright/JPEGSource.cpp b/media/libstagefright/JPEGSource.cpp
index e818115..bafa4b2 100644
--- a/media/libstagefright/JPEGSource.cpp
+++ b/media/libstagefright/JPEGSource.cpp
@@ -18,10 +18,10 @@
 #define LOG_TAG "JPEGSource"
 #include <utils/Log.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/JPEGSource.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -59,7 +59,7 @@
       mWidth(0),
       mHeight(0),
       mOffset(0) {
-    CHECK_EQ(parseJPEG(), OK);
+    CHECK_EQ(parseJPEG(), (status_t)OK);
     CHECK(mSource->getSize(&mSize) == OK);
 }
 
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 0b4ecbe..f702376 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -244,7 +244,7 @@
 
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kNotifyBuffer);
-    notify->setObject("buffer", out);
+    notify->setBuffer("buffer", out);
     notify->setInt32("oob", true);
     notify->post();
 }
@@ -270,7 +270,7 @@
         copy->meta()->setInt32("isSync", true);
     }
 
-    notify->setObject("buffer", copy);
+    notify->setBuffer("buffer", copy);
     notify->post();
 }
 
@@ -351,7 +351,7 @@
 
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kNotifyBuffer);
-    notify->setObject("buffer", mAACBuffer);
+    notify->setBuffer("buffer", mAACBuffer);
     notify->post();
 
     mAACBuffer.clear();
@@ -614,10 +614,8 @@
 
                 ++mNumSourcesDone;
             } else if (what == SourceInfo::kNotifyBuffer) {
-                sp<RefBase> obj;
-                CHECK(msg->findObject("buffer", &obj));
-
-                sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+                sp<ABuffer> buffer;
+                CHECK(msg->findBuffer("buffer", &buffer));
 
                 int32_t oob;
                 if (msg->findInt32("oob", &oob) && oob) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 068660b..7ebbe1d 100755
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -23,10 +23,10 @@
 #include <pthread.h>
 #include <sys/prctl.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MPEG4Writer.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MetaData.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
@@ -70,6 +70,10 @@
     status_t dump(int fd, const Vector<String16>& args) const;
 
 private:
+    enum {
+        kMaxCttsOffsetTimeUs = 1000000LL,  // 1 second
+    };
+
     MPEG4Writer *mOwner;
     sp<MetaData> mMeta;
     sp<MediaSource> mSource;
@@ -137,11 +141,12 @@
             : sampleCount(count), sampleDuration(timescaledDur) {}
 
         uint32_t sampleCount;
-        int32_t sampleDuration;  // time scale based
+        uint32_t sampleDuration;  // time scale based
     };
-    bool          mHasNegativeCttsDeltaDuration;
     size_t        mNumCttsTableEntries;
     List<CttsTableEntry> mCttsTableEntries;
+    int64_t mMinCttsOffsetTimeUs;
+    int64_t mMaxCttsOffsetTimeUs;
 
     // Sequence parameter set or picture parameter set
     struct AVCParamSet {
@@ -172,6 +177,8 @@
     // Update the audio track's drift information.
     void updateDriftTime(const sp<MetaData>& meta);
 
+    int32_t getStartTimeOffsetScaledTime() const;
+
     static void *ThreadWrapper(void *me);
     status_t threadEntry();
 
@@ -471,7 +478,7 @@
         !param->findInt32(kKeyTimeScale, &mTimeScale)) {
         mTimeScale = 1000;
     }
-    CHECK(mTimeScale > 0);
+    CHECK_GT(mTimeScale, 0);
     ALOGV("movie time scale: %d", mTimeScale);
 
     mStreamableFile = true;
@@ -490,7 +497,7 @@
         }
         mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
     }
-    CHECK(mEstimatedMoovBoxSize >= 8);
+    CHECK_GE(mEstimatedMoovBoxSize, 8);
     lseek64(mFd, mFreeBoxOffset, SEEK_SET);
     writeInt32(mEstimatedMoovBoxSize);
     write("free", 4);
@@ -684,7 +691,7 @@
 
     mWriteMoovBoxToMemory = false;
     if (mStreamableFile) {
-        CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
+        CHECK_LE(mMoovBoxBufferOffset + 8, mEstimatedMoovBoxSize);
 
         // Moov box
         lseek64(mFd, mFreeBoxOffset, SEEK_SET);
@@ -856,7 +863,7 @@
 
         mOffset += length + 4;
     } else {
-        CHECK(length < 65536);
+        CHECK_LT(length, 65536);
 
         uint8_t x = length >> 8;
         ::write(mFd, &x, 1);
@@ -1085,7 +1092,7 @@
 
 void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
     ALOGI("setStartTimestampUs: %lld", timeUs);
-    CHECK(timeUs >= 0);
+    CHECK_GE(timeUs, 0ll);
     Mutex::Autolock autoLock(mLock);
     if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
         mStartTimestampUs = timeUs;
@@ -1186,9 +1193,6 @@
     if (mIsAudio) {
         return;
     }
-    if (duration < 0 && !mHasNegativeCttsDeltaDuration) {
-        mHasNegativeCttsDeltaDuration = true;
-    }
     CttsTableEntry cttsEntry(sampleCount, duration);
     mCttsTableEntries.push_back(cttsEntry);
     ++mNumCttsTableEntries;
@@ -1218,7 +1222,7 @@
         mTimeScale = timeScale;
     }
 
-    CHECK(mTimeScale > 0);
+    CHECK_GT(mTimeScale, 0);
 }
 
 void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
@@ -1299,7 +1303,7 @@
         }
     }
 
-    CHECK("Received a chunk for a unknown track" == 0);
+    CHECK(!"Received a chunk for a unknown track");
 }
 
 void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
@@ -1509,7 +1513,6 @@
     mMdatSizeBytes = 0;
 
     mMaxChunkDurationUs = 0;
-    mHasNegativeCttsDeltaDuration = false;
 
     pthread_create(&mThread, &attr, ThreadWrapper, this);
     pthread_attr_destroy(&attr);
@@ -1833,29 +1836,18 @@
     int32_t nChunks = 0;
     int32_t nZeroLengthFrames = 0;
     int64_t lastTimestampUs = 0;      // Previous sample time stamp
-    int64_t lastCttsTimeUs = 0;       // Previous sample time stamp
     int64_t lastDurationUs = 0;       // Between the previous two samples
     int64_t currDurationTicks = 0;    // Timescale based ticks
     int64_t lastDurationTicks = 0;    // Timescale based ticks
     int32_t sampleCount = 1;          // Sample count in the current stts table entry
-    int64_t currCttsDurTicks = 0;     // Timescale based ticks
-    int64_t lastCttsDurTicks = 0;     // Timescale based ticks
-    int32_t cttsSampleCount = 1;      // Sample count in the current ctts table entry
-    uint32_t previousSampleSize = 0;      // Size of the previous sample
+    uint32_t previousSampleSize = 0;  // Size of the previous sample
     int64_t previousPausedDurationUs = 0;
     int64_t timestampUs = 0;
-    int64_t cttsDeltaTimeUs = 0;
-    bool hasBFrames = false;
+    int64_t cttsOffsetTimeUs = 0;
+    int64_t currCttsOffsetTimeTicks = 0;   // Timescale based ticks
+    int64_t lastCttsOffsetTimeTicks = -1;  // Timescale based ticks
+    int32_t cttsSampleCount = 0;           // Sample count in the current ctts table entry
 
-#if 1
-    // XXX: Samsung's video encoder's output buffer timestamp
-    // is not correct. see bug 4724339
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("rw.media.record.hasb", value, NULL) &&
-        (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) {
-        hasBFrames = true;
-    }
-#endif
     if (mIsAudio) {
         prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
     } else {
@@ -1897,7 +1889,7 @@
                         (const uint8_t *)buffer->data()
                             + buffer->range_offset(),
                         buffer->range_length());
-                CHECK_EQ(OK, err);
+                CHECK_EQ((status_t)OK, err);
             } else if (mIsMPEG4) {
                 mCodecSpecificDataSize = buffer->range_length();
                 mCodecSpecificData = malloc(mCodecSpecificDataSize);
@@ -1963,32 +1955,64 @@
 
         if (mResumed) {
             int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
-            CHECK(durExcludingEarlierPausesUs >= 0);
+            CHECK_GE(durExcludingEarlierPausesUs, 0ll);
             int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
-            CHECK(pausedDurationUs >= lastDurationUs);
+            CHECK_GE(pausedDurationUs, lastDurationUs);
             previousPausedDurationUs += pausedDurationUs - lastDurationUs;
             mResumed = false;
         }
 
         timestampUs -= previousPausedDurationUs;
-        CHECK(timestampUs >= 0);
-        if (!mIsAudio && hasBFrames) {
+        CHECK_GE(timestampUs, 0ll);
+        if (!mIsAudio) {
             /*
              * Composition time: timestampUs
              * Decoding time: decodingTimeUs
-             * Composition time delta = composition time - decoding time
-             *
-             * We save picture decoding time stamp delta in stts table entries,
-             * and composition time delta duration in ctts table entries.
+             * Composition time offset = composition time - decoding time
              */
             int64_t decodingTimeUs;
             CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
             decodingTimeUs -= previousPausedDurationUs;
-            int64_t timeUs = decodingTimeUs;
-            cttsDeltaTimeUs = timestampUs - decodingTimeUs;
+            cttsOffsetTimeUs =
+                    timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
+            CHECK_GE(cttsOffsetTimeUs, 0ll);
             timestampUs = decodingTimeUs;
-            ALOGV("decoding time: %lld and ctts delta time: %lld",
-                timestampUs, cttsDeltaTimeUs);
+            ALOGV("decoding time: %lld and ctts offset time: %lld",
+                timestampUs, cttsOffsetTimeUs);
+
+            // Update ctts box table if necessary
+            currCttsOffsetTimeTicks =
+                    (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL;
+            CHECK_LE(currCttsOffsetTimeTicks, 0x0FFFFFFFFLL);
+            if (mNumSamples == 0) {
+                // Force the first ctts table entry to have one single entry
+                // so that we can do adjustment for the initial track start
+                // time offset easily in writeCttsBox().
+                lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
+                addOneCttsTableEntry(1, currCttsOffsetTimeTicks);
+                cttsSampleCount = 0;      // No sample in ctts box is pending
+            } else {
+                if (currCttsOffsetTimeTicks != lastCttsOffsetTimeTicks) {
+                    addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+                    lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
+                    cttsSampleCount = 1;  // One sample in ctts box is pending
+                } else {
+                    ++cttsSampleCount;
+                }
+            }
+
+            // Update ctts time offset range
+            if (mNumSamples == 0) {
+                mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+                mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+            } else {
+                if (currCttsOffsetTimeTicks > mMaxCttsOffsetTimeUs) {
+                    mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+                } else if (currCttsOffsetTimeTicks < mMinCttsOffsetTimeUs) {
+                    mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;
+                }
+            }
+
         }
 
         if (mIsRealTimeRecording) {
@@ -1997,7 +2021,7 @@
             }
         }
 
-        CHECK(timestampUs >= 0);
+        CHECK_GE(timestampUs, 0ll);
         ALOGV("%s media time stamp: %lld and previous paused duration %lld",
                 mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
         if (timestampUs > mTrackDurationUs) {
@@ -2012,6 +2036,7 @@
         currDurationTicks =
             ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
                 (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+        CHECK_GE(currDurationTicks, 0ll);
 
         mSampleSizes.push_back(sampleSize);
         ++mNumSamples;
@@ -2020,25 +2045,12 @@
             // Force the first sample to have its own stts entry so that
             // we can adjust its value later to maintain the A/V sync.
             if (mNumSamples == 3 || currDurationTicks != lastDurationTicks) {
-                ALOGV("%s lastDurationUs: %lld us, currDurationTicks: %lld us",
-                        mIsAudio? "Audio": "Video", lastDurationUs, currDurationTicks);
                 addOneSttsTableEntry(sampleCount, lastDurationTicks);
                 sampleCount = 1;
             } else {
                 ++sampleCount;
             }
 
-            if (!mIsAudio) {
-                currCttsDurTicks =
-                     ((cttsDeltaTimeUs * mTimeScale + 500000LL) / 1000000LL -
-                     (lastCttsTimeUs * mTimeScale + 500000LL) / 1000000LL);
-                if (currCttsDurTicks != lastCttsDurTicks) {
-                    addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks);
-                    cttsSampleCount = 1;
-                } else {
-                    ++cttsSampleCount;
-                }
-            }
         }
         if (mSamplesHaveSameSize) {
             if (mNumSamples >= 2 && previousSampleSize != sampleSize) {
@@ -2052,11 +2064,6 @@
         lastDurationTicks = currDurationTicks;
         lastTimestampUs = timestampUs;
 
-        if (!mIsAudio) {
-            lastCttsDurTicks = currCttsDurTicks;
-            lastCttsTimeUs = cttsDeltaTimeUs;
-        }
-
         if (isSync != 0) {
             addOneStssTableEntry(mNumSamples);
         }
@@ -2125,10 +2132,8 @@
     if (mNumSamples == 1) {
         lastDurationUs = 0;  // A single sample's duration
         lastDurationTicks = 0;
-        lastCttsDurTicks = 0;
     } else {
         ++sampleCount;  // Count for the last sample
-        ++cttsSampleCount;
     }
 
     if (mNumSamples <= 2) {
@@ -2140,7 +2145,14 @@
         addOneSttsTableEntry(sampleCount, lastDurationTicks);
     }
 
-    addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks);
+    // The last ctts box may not have been written yet, and this
+    // is to make sure that we write out the last ctts box.
+    if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
+        if (cttsSampleCount > 0) {
+            addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+        }
+    }
+
     mTrackDurationUs += lastDurationUs;
     mReachedEOS = true;
 
@@ -2406,7 +2418,7 @@
     mOwner->writeInt16(0x18);        // depth
     mOwner->writeInt16(-1);          // predefined
 
-    CHECK(23 + mCodecSpecificDataSize < 128);
+    CHECK_LT(23 + mCodecSpecificDataSize, 128);
 
     if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
         writeMp4vEsdsBox();
@@ -2465,10 +2477,10 @@
 void MPEG4Writer::Track::writeMp4aEsdsBox() {
     mOwner->beginBox("esds");
     CHECK(mCodecSpecificData);
-    CHECK(mCodecSpecificDataSize > 0);
+    CHECK_GT(mCodecSpecificDataSize, 0);
 
     // Make sure all sizes encode to a single byte.
-    CHECK(mCodecSpecificDataSize + 23 < 128);
+    CHECK_LT(mCodecSpecificDataSize + 23, 128);
 
     mOwner->writeInt32(0);     // version=0, flags=0
     mOwner->writeInt8(0x03);   // ES_DescrTag
@@ -2502,7 +2514,7 @@
 
 void MPEG4Writer::Track::writeMp4vEsdsBox() {
     CHECK(mCodecSpecificData);
-    CHECK(mCodecSpecificDataSize > 0);
+    CHECK_GT(mCodecSpecificDataSize, 0);
     mOwner->beginBox("esds");
 
     mOwner->writeInt32(0);    // version=0, flags=0
@@ -2662,7 +2674,7 @@
 
 void MPEG4Writer::Track::writeAvccBox() {
     CHECK(mCodecSpecificData);
-    CHECK(mCodecSpecificDataSize >= 5);
+    CHECK_GE(mCodecSpecificDataSize, 5);
 
     // Patch avcc's lengthSize field to match the number
     // of bytes we use to indicate the size of a nal unit.
@@ -2690,23 +2702,26 @@
     mOwner->endBox();  // pasp
 }
 
+int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const {
+    int64_t trackStartTimeOffsetUs = 0;
+    int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
+    if (mStartTimestampUs != moovStartTimeUs) {
+        CHECK_GT(mStartTimestampUs, moovStartTimeUs);
+        trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
+    }
+    return (trackStartTimeOffsetUs *  mTimeScale + 500000LL) / 1000000LL;
+}
+
 void MPEG4Writer::Track::writeSttsBox() {
     mOwner->beginBox("stts");
     mOwner->writeInt32(0);  // version=0, flags=0
     mOwner->writeInt32(mNumSttsTableEntries);
 
     // Compensate for small start time difference from different media tracks
-    int64_t trackStartTimeOffsetUs = 0;
-    int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
-    if (mStartTimestampUs != moovStartTimeUs) {
-        CHECK(mStartTimestampUs > moovStartTimeUs);
-        trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
-    }
     List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
     CHECK(it != mSttsTableEntries.end() && it->sampleCount == 1);
     mOwner->writeInt32(it->sampleCount);
-    int32_t dur = (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL;
-    mOwner->writeInt32(dur + it->sampleDuration);
+    mOwner->writeInt32(getStartTimeOffsetScaledTime() + it->sampleDuration);
 
     int64_t totalCount = 1;
     while (++it != mSttsTableEntries.end()) {
@@ -2714,7 +2729,7 @@
         mOwner->writeInt32(it->sampleDuration);
         totalCount += it->sampleCount;
     }
-    CHECK(totalCount == mNumSamples);
+    CHECK_EQ(totalCount, mNumSamples);
     mOwner->endBox();  // stts
 }
 
@@ -2723,6 +2738,11 @@
         return;
     }
 
+    // There is no B frame at all
+    if (mMinCttsOffsetTimeUs == mMaxCttsOffsetTimeUs) {
+        return;
+    }
+
     // Do not write ctts box when there is no need to have it.
     if ((mNumCttsTableEntries == 1 &&
         mCttsTableEntries.begin()->sampleDuration == 0) ||
@@ -2730,24 +2750,29 @@
         return;
     }
 
-    ALOGV("ctts box has %d entries", mNumCttsTableEntries);
+    ALOGD("ctts box has %d entries with range [%lld, %lld]",
+            mNumCttsTableEntries, mMinCttsOffsetTimeUs, mMaxCttsOffsetTimeUs);
 
     mOwner->beginBox("ctts");
-    if (mHasNegativeCttsDeltaDuration) {
-        mOwner->writeInt32(0x00010000);  // version=1, flags=0
-    } else {
-        mOwner->writeInt32(0);  // version=0, flags=0
-    }
+    // Version 1 allows to use negative offset time value, but
+    // we are sticking to version 0 for now.
+    mOwner->writeInt32(0);  // version=0, flags=0
     mOwner->writeInt32(mNumCttsTableEntries);
 
-    int64_t totalCount = 0;
-    for (List<CttsTableEntry>::iterator it = mCttsTableEntries.begin();
-         it != mCttsTableEntries.end(); ++it) {
+    // Compensate for small start time difference from different media tracks
+    List<CttsTableEntry>::iterator it = mCttsTableEntries.begin();
+    CHECK(it != mCttsTableEntries.end() && it->sampleCount == 1);
+    mOwner->writeInt32(it->sampleCount);
+    mOwner->writeInt32(getStartTimeOffsetScaledTime() +
+            it->sampleDuration - mMinCttsOffsetTimeUs);
+
+    int64_t totalCount = 1;
+    while (++it != mCttsTableEntries.end()) {
         mOwner->writeInt32(it->sampleCount);
-        mOwner->writeInt32(it->sampleDuration);
+        mOwner->writeInt32(it->sampleDuration - mMinCttsOffsetTimeUs);
         totalCount += it->sampleCount;
     }
-    CHECK(totalCount == mNumSamples);
+    CHECK_EQ(totalCount, mNumSamples);
     mOwner->endBox();  // ctts
 }
 
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index 96271e4..11b80bf 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -22,8 +22,8 @@
 #include <stdlib.h>
 
 #include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MetaData.h>
 
 #include <ui/GraphicBuffer.h>
@@ -157,7 +157,7 @@
 }
 
 MediaBuffer::~MediaBuffer() {
-    CHECK_EQ(mObserver, NULL);
+    CHECK(mObserver == NULL);
 
     if (mOwnsData && mData != NULL) {
         free(mData);
@@ -188,7 +188,7 @@
 }
 
 MediaBuffer *MediaBuffer::clone() {
-    CHECK_EQ(mGraphicBuffer, NULL);
+    CHECK(mGraphicBuffer == NULL);
 
     MediaBuffer *buffer = new MediaBuffer(mData, mSize);
     buffer->set_range(mRangeOffset, mRangeLength);
diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp
index c8d05f4..80aae51 100644
--- a/media/libstagefright/MediaBufferGroup.cpp
+++ b/media/libstagefright/MediaBufferGroup.cpp
@@ -17,9 +17,9 @@
 #define LOG_TAG "MediaBufferGroup"
 #include <utils/Log.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 
 namespace android {
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
new file mode 100644
index 0000000..e14b1c4
--- /dev/null
+++ b/media/libstagefright/MediaCodec.cpp
@@ -0,0 +1,1179 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodec"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaCodec.h>
+
+#include "include/SoftwareRenderer.h"
+
+#include <gui/SurfaceTextureClient.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/NativeWindowWrapper.h>
+
+namespace android {
+
+// static
+sp<MediaCodec> MediaCodec::CreateByType(
+        const sp<ALooper> &looper, const char *mime, bool encoder) {
+    sp<MediaCodec> codec = new MediaCodec(looper);
+    if (codec->init(mime, true /* nameIsType */, encoder) != OK) {
+        return NULL;
+    }
+
+    return codec;
+}
+
+// static
+sp<MediaCodec> MediaCodec::CreateByComponentName(
+        const sp<ALooper> &looper, const char *name) {
+    sp<MediaCodec> codec = new MediaCodec(looper);
+    if (codec->init(name, false /* nameIsType */, false /* encoder */) != OK) {
+        return NULL;
+    }
+
+    return codec;
+}
+
+MediaCodec::MediaCodec(const sp<ALooper> &looper)
+    : mState(UNINITIALIZED),
+      mLooper(looper),
+      mCodec(new ACodec),
+      mFlags(0),
+      mSoftRenderer(NULL),
+      mDequeueInputTimeoutGeneration(0),
+      mDequeueInputReplyID(0),
+      mDequeueOutputTimeoutGeneration(0),
+      mDequeueOutputReplyID(0) {
+}
+
+MediaCodec::~MediaCodec() {
+    CHECK_EQ(mState, UNINITIALIZED);
+}
+
+// static
+status_t MediaCodec::PostAndAwaitResponse(
+        const sp<AMessage> &msg, sp<AMessage> *response) {
+    status_t err = msg->postAndAwaitResponse(response);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (!(*response)->findInt32("err", &err)) {
+        err = OK;
+    }
+
+    return err;
+}
+
+status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) {
+    // Current video decoders do not return from OMX_FillThisBuffer
+    // quickly, violating the OpenMAX specs, until that is remedied
+    // we need to invest in an extra looper to free the main event
+    // queue.
+    bool needDedicatedLooper = false;
+    if (nameIsType && !strncasecmp(name, "video/", 6)) {
+        needDedicatedLooper = true;
+    } else if (!nameIsType && !strncmp(name, "OMX.TI.DUCATI1.VIDEO.", 21)) {
+        needDedicatedLooper = true;
+    }
+
+    if (needDedicatedLooper) {
+        if (mCodecLooper == NULL) {
+            mCodecLooper = new ALooper;
+            mCodecLooper->setName("CodecLooper");
+            mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+        }
+
+        mCodecLooper->registerHandler(mCodec);
+    } else {
+        mLooper->registerHandler(mCodec);
+    }
+
+    mLooper->registerHandler(this);
+
+    mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, id()));
+
+    sp<AMessage> msg = new AMessage(kWhatInit, id());
+    msg->setString("name", name);
+    msg->setInt32("nameIsType", nameIsType);
+
+    if (nameIsType) {
+        msg->setInt32("encoder", encoder);
+    }
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::configure(
+        const sp<AMessage> &format,
+        const sp<SurfaceTextureClient> &nativeWindow,
+        uint32_t flags) {
+    sp<AMessage> msg = new AMessage(kWhatConfigure, id());
+
+    msg->setMessage("format", format);
+    msg->setInt32("flags", flags);
+
+    if (nativeWindow != NULL) {
+        if (!(mFlags & kFlagIsSoftwareCodec)) {
+            msg->setObject(
+                    "native-window",
+                    new NativeWindowWrapper(nativeWindow));
+        } else {
+            mNativeWindow = nativeWindow;
+        }
+    }
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::start() {
+    sp<AMessage> msg = new AMessage(kWhatStart, id());
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::stop() {
+    sp<AMessage> msg = new AMessage(kWhatStop, id());
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::queueInputBuffer(
+        size_t index,
+        size_t offset,
+        size_t size,
+        int64_t presentationTimeUs,
+        uint32_t flags) {
+    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id());
+    msg->setSize("index", index);
+    msg->setSize("offset", offset);
+    msg->setSize("size", size);
+    msg->setInt64("timeUs", presentationTimeUs);
+    msg->setInt32("flags", flags);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
+    sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id());
+    msg->setInt64("timeoutUs", timeoutUs);
+
+    sp<AMessage> response;
+    status_t err;
+    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+        return err;
+    }
+
+    CHECK(response->findSize("index", index));
+
+    return OK;
+}
+
+status_t MediaCodec::dequeueOutputBuffer(
+        size_t *index,
+        size_t *offset,
+        size_t *size,
+        int64_t *presentationTimeUs,
+        uint32_t *flags,
+        int64_t timeoutUs) {
+    sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, id());
+    msg->setInt64("timeoutUs", timeoutUs);
+
+    sp<AMessage> response;
+    status_t err;
+    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+        return err;
+    }
+
+    CHECK(response->findSize("index", index));
+    CHECK(response->findSize("offset", offset));
+    CHECK(response->findSize("size", size));
+    CHECK(response->findInt64("timeUs", presentationTimeUs));
+    CHECK(response->findInt32("flags", (int32_t *)flags));
+
+    return OK;
+}
+
+status_t MediaCodec::renderOutputBufferAndRelease(size_t index) {
+    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+    msg->setSize("index", index);
+    msg->setInt32("render", true);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::releaseOutputBuffer(size_t index) {
+    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+    msg->setSize("index", index);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
+    sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id());
+
+    sp<AMessage> response;
+    status_t err;
+    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+        return err;
+    }
+
+    CHECK(response->findMessage("format", format));
+
+    return OK;
+}
+
+status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
+    sp<AMessage> msg = new AMessage(kWhatGetBuffers, id());
+    msg->setInt32("portIndex", kPortIndexInput);
+    msg->setPointer("buffers", buffers);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::getOutputBuffers(Vector<sp<ABuffer> > *buffers) const {
+    sp<AMessage> msg = new AMessage(kWhatGetBuffers, id());
+    msg->setInt32("portIndex", kPortIndexOutput);
+    msg->setPointer("buffers", buffers);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::flush() {
+    sp<AMessage> msg = new AMessage(kWhatFlush, id());
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void MediaCodec::cancelPendingDequeueOperations() {
+    if (mFlags & kFlagDequeueInputPending) {
+        sp<AMessage> response = new AMessage;
+        response->setInt32("err", INVALID_OPERATION);
+        response->postReply(mDequeueInputReplyID);
+
+        ++mDequeueInputTimeoutGeneration;
+        mDequeueInputReplyID = 0;
+        mFlags &= ~kFlagDequeueInputPending;
+    }
+
+    if (mFlags & kFlagDequeueOutputPending) {
+        sp<AMessage> response = new AMessage;
+        response->setInt32("err", INVALID_OPERATION);
+        response->postReply(mDequeueOutputReplyID);
+
+        ++mDequeueOutputTimeoutGeneration;
+        mDequeueOutputReplyID = 0;
+        mFlags &= ~kFlagDequeueOutputPending;
+    }
+}
+
+bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) {
+    if (mState != STARTED
+            || (mFlags & kFlagStickyError)
+            || (newRequest && (mFlags & kFlagDequeueInputPending))) {
+        sp<AMessage> response = new AMessage;
+        response->setInt32("err", INVALID_OPERATION);
+
+        response->postReply(replyID);
+
+        return true;
+    }
+
+    ssize_t index = dequeuePortBuffer(kPortIndexInput);
+
+    if (index < 0) {
+        CHECK_EQ(index, -EAGAIN);
+        return false;
+    }
+
+    sp<AMessage> response = new AMessage;
+    response->setSize("index", index);
+    response->postReply(replyID);
+
+    return true;
+}
+
+bool MediaCodec::handleDequeueOutputBuffer(uint32_t replyID, bool newRequest) {
+    sp<AMessage> response = new AMessage;
+
+    if (mState != STARTED
+            || (mFlags & kFlagStickyError)
+            || (newRequest && (mFlags & kFlagDequeueOutputPending))) {
+        response->setInt32("err", INVALID_OPERATION);
+    } else if (mFlags & kFlagOutputBuffersChanged) {
+        response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED);
+        mFlags &= ~kFlagOutputBuffersChanged;
+    } else if (mFlags & kFlagOutputFormatChanged) {
+        response->setInt32("err", INFO_FORMAT_CHANGED);
+        mFlags &= ~kFlagOutputFormatChanged;
+    } else {
+        ssize_t index = dequeuePortBuffer(kPortIndexOutput);
+
+        if (index < 0) {
+            CHECK_EQ(index, -EAGAIN);
+            return false;
+        }
+
+        const sp<ABuffer> &buffer =
+            mPortBuffers[kPortIndexOutput].itemAt(index).mData;
+
+        response->setSize("index", index);
+        response->setSize("offset", buffer->offset());
+        response->setSize("size", buffer->size());
+
+        int64_t timeUs;
+        CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+        response->setInt64("timeUs", timeUs);
+
+        int32_t omxFlags;
+        CHECK(buffer->meta()->findInt32("omxFlags", &omxFlags));
+
+        uint32_t flags = 0;
+        if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
+            flags |= BUFFER_FLAG_SYNCFRAME;
+        }
+        if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+            flags |= BUFFER_FLAG_CODECCONFIG;
+        }
+        if (omxFlags & OMX_BUFFERFLAG_EOS) {
+            flags |= BUFFER_FLAG_EOS;
+        }
+
+        response->setInt32("flags", flags);
+    }
+
+    response->postReply(replyID);
+
+    return true;
+}
+
+void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatCodecNotify:
+        {
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            switch (what) {
+                case ACodec::kWhatError:
+                {
+                    int32_t omxError, internalError;
+                    CHECK(msg->findInt32("omx-error", &omxError));
+                    CHECK(msg->findInt32("err", &internalError));
+
+                    ALOGE("Codec reported an error. "
+                          "(omx error 0x%08x, internalError %d)",
+                          omxError, internalError);
+
+                    bool sendErrorReponse = true;
+
+                    switch (mState) {
+                        case INITIALIZING:
+                        {
+                            setState(UNINITIALIZED);
+                            break;
+                        }
+
+                        case CONFIGURING:
+                        {
+                            setState(INITIALIZED);
+                            break;
+                        }
+
+                        case STARTING:
+                        {
+                            setState(CONFIGURED);
+                            break;
+                        }
+
+                        case STOPPING:
+                        {
+                            // Ignore the error, assuming we'll still get
+                            // the shutdown complete notification.
+
+                            sendErrorReponse = false;
+                            break;
+                        }
+
+                        case FLUSHING:
+                        {
+                            setState(STARTED);
+                            break;
+                        }
+
+                        case STARTED:
+                        {
+                            sendErrorReponse = false;
+
+                            mFlags |= kFlagStickyError;
+
+                            cancelPendingDequeueOperations();
+                            break;
+                        }
+
+                        default:
+                        {
+                            sendErrorReponse = false;
+
+                            mFlags |= kFlagStickyError;
+                            break;
+                        }
+                    }
+
+                    if (sendErrorReponse) {
+                        sp<AMessage> response = new AMessage;
+                        response->setInt32("err", UNKNOWN_ERROR);
+
+                        response->postReply(mReplyID);
+                    }
+                    break;
+                }
+
+                case ACodec::kWhatComponentAllocated:
+                {
+                    CHECK_EQ(mState, INITIALIZING);
+                    setState(INITIALIZED);
+
+                    AString componentName;
+                    CHECK(msg->findString("componentName", &componentName));
+
+                    if (componentName.startsWith("OMX.google.")) {
+                        mFlags |= kFlagIsSoftwareCodec;
+                    } else {
+                        mFlags &= ~kFlagIsSoftwareCodec;
+                    }
+
+                    (new AMessage)->postReply(mReplyID);
+                    break;
+                }
+
+                case ACodec::kWhatComponentConfigured:
+                {
+                    CHECK_EQ(mState, CONFIGURING);
+                    setState(CONFIGURED);
+
+                    (new AMessage)->postReply(mReplyID);
+                    break;
+                }
+
+                case ACodec::kWhatBuffersAllocated:
+                {
+                    int32_t portIndex;
+                    CHECK(msg->findInt32("portIndex", &portIndex));
+
+                    ALOGV("%s buffers allocated",
+                          portIndex == kPortIndexInput ? "input" : "output");
+
+                    CHECK(portIndex == kPortIndexInput
+                            || portIndex == kPortIndexOutput);
+
+                    mPortBuffers[portIndex].clear();
+
+                    Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+                    for (size_t i = 0;; ++i) {
+                        AString name = StringPrintf("buffer-id_%d", i);
+
+                        void *bufferID;
+                        if (!msg->findPointer(name.c_str(), &bufferID)) {
+                            break;
+                        }
+
+                        name = StringPrintf("data_%d", i);
+
+                        BufferInfo info;
+                        info.mBufferID = bufferID;
+                        info.mOwnedByClient = false;
+                        CHECK(msg->findBuffer(name.c_str(), &info.mData));
+
+                        buffers->push_back(info);
+                    }
+
+                    if (portIndex == kPortIndexOutput) {
+                        if (mState == STARTING) {
+                            // We're always allocating output buffers after
+                            // allocating input buffers, so this is a good
+                            // indication that now all buffers are allocated.
+                            setState(STARTED);
+                            (new AMessage)->postReply(mReplyID);
+                        } else {
+                            mFlags |= kFlagOutputBuffersChanged;
+                        }
+                    }
+                    break;
+                }
+
+                case ACodec::kWhatOutputFormatChanged:
+                {
+                    ALOGV("codec output format changed");
+
+                    if ((mFlags & kFlagIsSoftwareCodec)
+                            && mNativeWindow != NULL) {
+                        AString mime;
+                        CHECK(msg->findString("mime", &mime));
+
+                        if (!strncasecmp("video/", mime.c_str(), 6)) {
+                            delete mSoftRenderer;
+                            mSoftRenderer = NULL;
+
+                            int32_t width, height;
+                            CHECK(msg->findInt32("width", &width));
+                            CHECK(msg->findInt32("height", &height));
+
+                            int32_t colorFormat;
+                            CHECK(msg->findInt32(
+                                        "color-format", &colorFormat));
+
+                            sp<MetaData> meta = new MetaData;
+                            meta->setInt32(kKeyWidth, width);
+                            meta->setInt32(kKeyHeight, height);
+                            meta->setInt32(kKeyColorFormat, colorFormat);
+
+                            mSoftRenderer =
+                                new SoftwareRenderer(mNativeWindow, meta);
+                        }
+                    }
+
+                    mOutputFormat = msg;
+                    mFlags |= kFlagOutputFormatChanged;
+                    break;
+                }
+
+                case ACodec::kWhatFillThisBuffer:
+                {
+                    /* size_t index = */updateBuffers(kPortIndexInput, msg);
+
+                    if (mState == FLUSHING || mState == STOPPING) {
+                        returnBuffersToCodecOnPort(kPortIndexInput);
+                        break;
+                    }
+
+                    if (mFlags & kFlagDequeueInputPending) {
+                        CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));
+
+                        ++mDequeueInputTimeoutGeneration;
+                        mFlags &= ~kFlagDequeueInputPending;
+                        mDequeueInputReplyID = 0;
+                    }
+                    break;
+                }
+
+                case ACodec::kWhatDrainThisBuffer:
+                {
+                    /* size_t index = */updateBuffers(kPortIndexOutput, msg);
+
+                    if (mState == FLUSHING || mState == STOPPING) {
+                        returnBuffersToCodecOnPort(kPortIndexOutput);
+                        break;
+                    }
+
+                    sp<ABuffer> buffer;
+                    CHECK(msg->findBuffer("buffer", &buffer));
+
+                    int32_t omxFlags;
+                    CHECK(msg->findInt32("flags", &omxFlags));
+
+                    buffer->meta()->setInt32("omxFlags", omxFlags);
+
+                    if (mFlags & kFlagDequeueOutputPending) {
+                        CHECK(handleDequeueOutputBuffer(mDequeueOutputReplyID));
+
+                        ++mDequeueOutputTimeoutGeneration;
+                        mFlags &= ~kFlagDequeueOutputPending;
+                        mDequeueOutputReplyID = 0;
+                    }
+                    break;
+                }
+
+                case ACodec::kWhatEOS:
+                {
+                    // We already notify the client of this by using the
+                    // corresponding flag in "onOutputBufferReady".
+                    break;
+                }
+
+                case ACodec::kWhatShutdownCompleted:
+                {
+                    CHECK_EQ(mState, STOPPING);
+                    setState(UNINITIALIZED);
+
+                    (new AMessage)->postReply(mReplyID);
+                    break;
+                }
+
+                case ACodec::kWhatFlushCompleted:
+                {
+                    CHECK_EQ(mState, FLUSHING);
+                    setState(STARTED);
+
+                    mCodec->signalResume();
+
+                    (new AMessage)->postReply(mReplyID);
+                    break;
+                }
+
+                default:
+                    TRESPASS();
+            }
+            break;
+        }
+
+        case kWhatInit:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != UNINITIALIZED) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            setState(INITIALIZING);
+
+            AString name;
+            CHECK(msg->findString("name", &name));
+
+            int32_t nameIsType;
+            int32_t encoder = false;
+            CHECK(msg->findInt32("nameIsType", &nameIsType));
+            if (nameIsType) {
+                CHECK(msg->findInt32("encoder", &encoder));
+            }
+
+            sp<AMessage> format = new AMessage;
+
+            if (nameIsType) {
+                format->setString("mime", name.c_str());
+                format->setInt32("encoder", encoder);
+            } else {
+                format->setString("componentName", name.c_str());
+            }
+
+            mCodec->initiateAllocateComponent(format);
+            break;
+        }
+
+        case kWhatConfigure:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != INITIALIZED) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            setState(CONFIGURING);
+
+            sp<RefBase> obj;
+            if (!msg->findObject("native-window", &obj)) {
+                obj.clear();
+            }
+
+            sp<AMessage> format;
+            CHECK(msg->findMessage("format", &format));
+
+            if (obj != NULL) {
+                format->setObject("native-window", obj);
+            }
+
+            uint32_t flags;
+            CHECK(msg->findInt32("flags", (int32_t *)&flags));
+
+            if (flags & CONFIGURE_FLAG_ENCODE) {
+                format->setInt32("encoder", true);
+            }
+
+            mCodec->initiateConfigureComponent(format);
+            break;
+        }
+
+        case kWhatStart:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != CONFIGURED) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            setState(STARTING);
+
+            mCodec->initiateStart();
+            break;
+        }
+
+        case kWhatStop:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != INITIALIZED
+                    && mState != CONFIGURED && mState != STARTED) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            setState(STOPPING);
+
+            mCodec->initiateShutdown();
+            returnBuffersToCodec();
+            break;
+        }
+
+        case kWhatDequeueInputBuffer:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (handleDequeueInputBuffer(replyID, true /* new request */)) {
+                break;
+            }
+
+            int64_t timeoutUs;
+            CHECK(msg->findInt64("timeoutUs", &timeoutUs));
+
+            if (timeoutUs == 0ll) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", -EAGAIN);
+                response->postReply(replyID);
+                break;
+            }
+
+            mFlags |= kFlagDequeueInputPending;
+            mDequeueInputReplyID = replyID;
+
+            if (timeoutUs > 0ll) {
+                sp<AMessage> timeoutMsg =
+                    new AMessage(kWhatDequeueInputTimedOut, id());
+                timeoutMsg->setInt32(
+                        "generation", ++mDequeueInputTimeoutGeneration);
+                timeoutMsg->post(timeoutUs);
+            }
+            break;
+        }
+
+        case kWhatDequeueInputTimedOut:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mDequeueInputTimeoutGeneration) {
+                // Obsolete
+                break;
+            }
+
+            CHECK(mFlags & kFlagDequeueInputPending);
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", -EAGAIN);
+            response->postReply(mDequeueInputReplyID);
+
+            mFlags &= ~kFlagDequeueInputPending;
+            mDequeueInputReplyID = 0;
+            break;
+        }
+
+        case kWhatQueueInputBuffer:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != STARTED || (mFlags & kFlagStickyError)) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            status_t err = onQueueInputBuffer(msg);
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatDequeueOutputBuffer:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (handleDequeueOutputBuffer(replyID, true /* new request */)) {
+                break;
+            }
+
+            int64_t timeoutUs;
+            CHECK(msg->findInt64("timeoutUs", &timeoutUs));
+
+            if (timeoutUs == 0ll) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", -EAGAIN);
+                response->postReply(replyID);
+                break;
+            }
+
+            mFlags |= kFlagDequeueOutputPending;
+            mDequeueOutputReplyID = replyID;
+
+            if (timeoutUs > 0ll) {
+                sp<AMessage> timeoutMsg =
+                    new AMessage(kWhatDequeueOutputTimedOut, id());
+                timeoutMsg->setInt32(
+                        "generation", ++mDequeueOutputTimeoutGeneration);
+                timeoutMsg->post(timeoutUs);
+            }
+            break;
+        }
+
+        case kWhatDequeueOutputTimedOut:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mDequeueOutputTimeoutGeneration) {
+                // Obsolete
+                break;
+            }
+
+            CHECK(mFlags & kFlagDequeueOutputPending);
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", -EAGAIN);
+            response->postReply(mDequeueOutputReplyID);
+
+            mFlags &= ~kFlagDequeueOutputPending;
+            mDequeueOutputReplyID = 0;
+            break;
+        }
+
+        case kWhatReleaseOutputBuffer:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != STARTED || (mFlags & kFlagStickyError)) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            status_t err = onReleaseOutputBuffer(msg);
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatGetBuffers:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != STARTED || (mFlags & kFlagStickyError)) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            int32_t portIndex;
+            CHECK(msg->findInt32("portIndex", &portIndex));
+
+            Vector<sp<ABuffer> > *dstBuffers;
+            CHECK(msg->findPointer("buffers", (void **)&dstBuffers));
+
+            dstBuffers->clear();
+            const Vector<BufferInfo> &srcBuffers = mPortBuffers[portIndex];
+
+            for (size_t i = 0; i < srcBuffers.size(); ++i) {
+                const BufferInfo &info = srcBuffers.itemAt(i);
+
+                dstBuffers->push_back(info.mData);
+            }
+
+            (new AMessage)->postReply(replyID);
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != STARTED || (mFlags & kFlagStickyError)) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            setState(FLUSHING);
+
+            mCodec->signalFlush();
+            returnBuffersToCodec();
+            break;
+        }
+
+        case kWhatGetOutputFormat:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if ((mState != STARTED && mState != FLUSHING)
+                    || (mFlags & kFlagStickyError)) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            sp<AMessage> response = new AMessage;
+            response->setMessage("format", mOutputFormat);
+            response->postReply(replyID);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaCodec::setState(State newState) {
+    if (newState == UNINITIALIZED) {
+        delete mSoftRenderer;
+        mSoftRenderer = NULL;
+
+        mNativeWindow.clear();
+
+        mOutputFormat.clear();
+        mFlags &= ~kFlagOutputFormatChanged;
+        mFlags &= ~kFlagOutputBuffersChanged;
+        mFlags &= ~kFlagStickyError;
+    }
+
+    mState = newState;
+
+    cancelPendingDequeueOperations();
+}
+
+void MediaCodec::returnBuffersToCodec() {
+    returnBuffersToCodecOnPort(kPortIndexInput);
+    returnBuffersToCodecOnPort(kPortIndexOutput);
+}
+
+void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex) {
+    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+    Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+    for (size_t i = 0; i < buffers->size(); ++i) {
+        BufferInfo *info = &buffers->editItemAt(i);
+
+        if (info->mNotify != NULL) {
+            sp<AMessage> msg = info->mNotify;
+            info->mNotify = NULL;
+            info->mOwnedByClient = false;
+
+            if (portIndex == kPortIndexInput) {
+                msg->setInt32("err", ERROR_END_OF_STREAM);
+            }
+            msg->post();
+        }
+    }
+
+    mAvailPortBuffers[portIndex].clear();
+}
+
+size_t MediaCodec::updateBuffers(
+        int32_t portIndex, const sp<AMessage> &msg) {
+    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+    void *bufferID;
+    CHECK(msg->findPointer("buffer-id", &bufferID));
+
+    Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+    for (size_t i = 0; i < buffers->size(); ++i) {
+        BufferInfo *info = &buffers->editItemAt(i);
+
+        if (info->mBufferID == bufferID) {
+            CHECK(info->mNotify == NULL);
+            CHECK(msg->findMessage("reply", &info->mNotify));
+
+            mAvailPortBuffers[portIndex].push_back(i);
+
+            return i;
+        }
+    }
+
+    TRESPASS();
+
+    return 0;
+}
+
+status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
+    size_t index;
+    size_t offset;
+    size_t size;
+    int64_t timeUs;
+    uint32_t flags;
+    CHECK(msg->findSize("index", &index));
+    CHECK(msg->findSize("offset", &offset));
+    CHECK(msg->findSize("size", &size));
+    CHECK(msg->findInt64("timeUs", &timeUs));
+    CHECK(msg->findInt32("flags", (int32_t *)&flags));
+
+    if (index >= mPortBuffers[kPortIndexInput].size()) {
+        return -ERANGE;
+    }
+
+    BufferInfo *info = &mPortBuffers[kPortIndexInput].editItemAt(index);
+
+    if (info->mNotify == NULL || !info->mOwnedByClient) {
+        return -EACCES;
+    }
+
+    if (offset + size > info->mData->capacity()) {
+        return -EINVAL;
+    }
+
+    sp<AMessage> reply = info->mNotify;
+    info->mNotify = NULL;
+    info->mOwnedByClient = false;
+
+    info->mData->setRange(offset, size);
+    info->mData->meta()->setInt64("timeUs", timeUs);
+
+    if (flags & BUFFER_FLAG_EOS) {
+        info->mData->meta()->setInt32("eos", true);
+    }
+
+    if (flags & BUFFER_FLAG_CODECCONFIG) {
+        info->mData->meta()->setInt32("csd", true);
+    }
+
+    reply->setBuffer("buffer", info->mData);
+    reply->post();
+
+    return OK;
+}
+
+status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
+    size_t index;
+    CHECK(msg->findSize("index", &index));
+
+    int32_t render;
+    if (!msg->findInt32("render", &render)) {
+        render = 0;
+    }
+
+    if (mState != STARTED) {
+        return -EINVAL;
+    }
+
+    if (index >= mPortBuffers[kPortIndexOutput].size()) {
+        return -ERANGE;
+    }
+
+    BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);
+
+    if (info->mNotify == NULL || !info->mOwnedByClient) {
+        return -EACCES;
+    }
+
+    if (render) {
+        info->mNotify->setInt32("render", true);
+
+        if (mSoftRenderer != NULL) {
+            mSoftRenderer->render(
+                    info->mData->data(), info->mData->size(), NULL);
+        }
+    }
+
+    info->mNotify->post();
+    info->mNotify = NULL;
+    info->mOwnedByClient = false;
+
+    return OK;
+}
+
+ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
+    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+    List<size_t> *availBuffers = &mAvailPortBuffers[portIndex];
+
+    if (availBuffers->empty()) {
+        return -EAGAIN;
+    }
+
+    size_t index = *availBuffers->begin();
+    availBuffers->erase(availBuffers->begin());
+
+    BufferInfo *info = &mPortBuffers[portIndex].editItemAt(index);
+    CHECK(!info->mOwnedByClient);
+    info->mOwnedByClient = true;
+
+    return index;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MediaSourceSplitter.cpp b/media/libstagefright/MediaSourceSplitter.cpp
index 8af0694..3b64ded 100644
--- a/media/libstagefright/MediaSourceSplitter.cpp
+++ b/media/libstagefright/MediaSourceSplitter.cpp
@@ -18,8 +18,8 @@
 #define LOG_TAG "MediaSourceSplitter"
 #include <utils/Log.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaSourceSplitter.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MetaData.h>
 
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
index 884f3b4..66dec90 100644
--- a/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/MetaData.cpp
@@ -17,7 +17,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MetaData.h>
 
 namespace android {
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
new file mode 100644
index 0000000..afd4763
--- /dev/null
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuMediaExtractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/NuMediaExtractor.h>
+
+#include "include/ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+NuMediaExtractor::NuMediaExtractor() {
+}
+
+NuMediaExtractor::~NuMediaExtractor() {
+    releaseTrackSamples();
+
+    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+        TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+        CHECK_EQ((status_t)OK, info->mSource->stop());
+    }
+
+    mSelectedTracks.clear();
+}
+
+status_t NuMediaExtractor::setDataSource(const char *path) {
+    sp<DataSource> dataSource = DataSource::CreateFromURI(path);
+
+    if (dataSource == NULL) {
+        return -ENOENT;
+    }
+
+    mImpl = MediaExtractor::Create(dataSource);
+
+    if (mImpl == NULL) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    return OK;
+}
+
+size_t NuMediaExtractor::countTracks() const {
+    return mImpl == NULL ? 0 : mImpl->countTracks();
+}
+
+status_t NuMediaExtractor::getTrackFormat(
+        size_t index, sp<AMessage> *format) const {
+    *format = NULL;
+
+    if (mImpl == NULL) {
+        return -EINVAL;
+    }
+
+    if (index >= mImpl->countTracks()) {
+        return -ERANGE;
+    }
+
+    sp<MetaData> meta = mImpl->getTrackMetaData(index);
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    sp<AMessage> msg = new AMessage;
+    msg->setString("mime", mime);
+
+    if (!strncasecmp("video/", mime, 6)) {
+        int32_t width, height;
+        CHECK(meta->findInt32(kKeyWidth, &width));
+        CHECK(meta->findInt32(kKeyHeight, &height));
+
+        msg->setInt32("width", width);
+        msg->setInt32("height", height);
+    } else {
+        CHECK(!strncasecmp("audio/", mime, 6));
+
+        int32_t numChannels, sampleRate;
+        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+        msg->setInt32("channel-count", numChannels);
+        msg->setInt32("sample-rate", sampleRate);
+    }
+
+    int32_t maxInputSize;
+    if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+        msg->setInt32("max-input-size", maxInputSize);
+    }
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+        // Parse the AVCDecoderConfigurationRecord
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size >= 7);
+        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+        uint8_t profile = ptr[1];
+        uint8_t level = ptr[3];
+
+        // There is decodable content out there that fails the following
+        // assertion, let's be lenient for now...
+        // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
+
+        size_t lengthSize = 1 + (ptr[4] & 3);
+
+        // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+        // violates it...
+        // CHECK((ptr[5] >> 5) == 7);  // reserved
+
+        size_t numSeqParameterSets = ptr[5] & 31;
+
+        ptr += 6;
+        size -= 6;
+
+        sp<ABuffer> buffer = new ABuffer(1024);
+        buffer->setRange(0, 0);
+
+        for (size_t i = 0; i < numSeqParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+            buffer->setRange(0, buffer->size() + 4 + length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+
+        msg->setBuffer("csd-0", buffer);
+
+        buffer = new ABuffer(1024);
+        buffer->setRange(0, 0);
+
+        CHECK(size >= 1);
+        size_t numPictureParameterSets = *ptr;
+        ++ptr;
+        --size;
+
+        for (size_t i = 0; i < numPictureParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+            buffer->setRange(0, buffer->size() + 4 + length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-1", buffer);
+    } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+        ESDS esds((const char *)data, size);
+        CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+        const void *codec_specific_data;
+        size_t codec_specific_data_size;
+        esds.getCodecSpecificInfo(
+                &codec_specific_data, &codec_specific_data_size);
+
+        sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+        memcpy(buffer->data(), codec_specific_data,
+               codec_specific_data_size);
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-0", buffer);
+    } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
+        sp<ABuffer> buffer = new ABuffer(size);
+        memcpy(buffer->data(), data, size);
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-0", buffer);
+
+        if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
+            return -EINVAL;
+        }
+
+        buffer = new ABuffer(size);
+        memcpy(buffer->data(), data, size);
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-1", buffer);
+    }
+
+    *format = msg;
+
+    return OK;
+}
+
+status_t NuMediaExtractor::selectTrack(size_t index) {
+    if (mImpl == NULL) {
+        return -EINVAL;
+    }
+
+    if (index >= mImpl->countTracks()) {
+        return -ERANGE;
+    }
+
+    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+        TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+        if (info->mTrackIndex == index) {
+            // This track has already been selected.
+            return OK;
+        }
+    }
+
+    sp<MediaSource> source = mImpl->getTrack(index);
+
+    CHECK_EQ((status_t)OK, source->start());
+
+    mSelectedTracks.push();
+    TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
+
+    info->mSource = source;
+    info->mTrackIndex = index;
+    info->mFinalResult = OK;
+    info->mSample = NULL;
+    info->mSampleTimeUs = -1ll;
+    info->mFlags = 0;
+
+    const char *mime;
+    CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
+        info->mFlags |= kIsVorbis;
+    }
+
+    return OK;
+}
+
+void NuMediaExtractor::releaseTrackSamples() {
+    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+        TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+        if (info->mSample != NULL) {
+            info->mSample->release();
+            info->mSample = NULL;
+
+            info->mSampleTimeUs = -1ll;
+        }
+    }
+}
+
+ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
+    TrackInfo *minInfo = NULL;
+    ssize_t minIndex = -1;
+
+    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+        TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+        if (seekTimeUs >= 0ll) {
+            info->mFinalResult = OK;
+
+            if (info->mSample != NULL) {
+                info->mSample->release();
+                info->mSample = NULL;
+                info->mSampleTimeUs = -1ll;
+            }
+        } else if (info->mFinalResult != OK) {
+            continue;
+        }
+
+        if (info->mSample == NULL) {
+            MediaSource::ReadOptions options;
+            if (seekTimeUs >= 0ll) {
+                options.setSeekTo(seekTimeUs);
+            }
+            status_t err = info->mSource->read(&info->mSample, &options);
+
+            if (err != OK) {
+                CHECK(info->mSample == NULL);
+
+                info->mFinalResult = err;
+                info->mSampleTimeUs = -1ll;
+                continue;
+            } else {
+                CHECK(info->mSample != NULL);
+                CHECK(info->mSample->meta_data()->findInt64(
+                            kKeyTime, &info->mSampleTimeUs));
+            }
+        }
+
+        if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
+            minInfo = info;
+            minIndex = i;
+        }
+    }
+
+    return minIndex;
+}
+
+status_t NuMediaExtractor::seekTo(int64_t timeUs) {
+    return fetchTrackSamples(timeUs);
+}
+
+status_t NuMediaExtractor::advance() {
+    ssize_t minIndex = fetchTrackSamples();
+
+    if (minIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+
+    info->mSample->release();
+    info->mSample = NULL;
+    info->mSampleTimeUs = -1ll;
+
+    return OK;
+}
+
+status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
+    ssize_t minIndex = fetchTrackSamples();
+
+    if (minIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+
+    size_t sampleSize = info->mSample->range_length();
+
+    if (info->mFlags & kIsVorbis) {
+        // Each sample's data is suffixed by the number of page samples
+        // or -1 if not available.
+        sampleSize += sizeof(int32_t);
+    }
+
+    if (buffer->capacity() < sampleSize) {
+        return -ENOMEM;
+    }
+
+    const uint8_t *src =
+        (const uint8_t *)info->mSample->data()
+            + info->mSample->range_offset();
+
+    memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
+
+    if (info->mFlags & kIsVorbis) {
+        int32_t numPageSamples;
+        if (!info->mSample->meta_data()->findInt32(
+                    kKeyValidSamples, &numPageSamples)) {
+            numPageSamples = -1;
+        }
+
+        memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
+               &numPageSamples,
+               sizeof(numPageSamples));
+    }
+
+    buffer->setRange(0, sampleSize);
+
+    return OK;
+}
+
+status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
+    ssize_t minIndex = fetchTrackSamples();
+
+    if (minIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+    *trackIndex = info->mTrackIndex;
+
+    return OK;
+}
+
+status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
+    ssize_t minIndex = fetchTrackSamples();
+
+    if (minIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+    *sampleTimeUs = info->mSampleTimeUs;
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 7a805aa..7cdb793 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -335,6 +335,10 @@
 }
 
 void OMXClient::disconnect() {
+    if (mOMX.get() != NULL) {
+        mOMX.clear();
+        mOMX = NULL;
+    }
 }
 
 }  // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 381320b..1325462 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1541,6 +1541,8 @@
             "video_decoder.mpeg4", "video_encoder.mpeg4" },
         { MEDIA_MIMETYPE_VIDEO_H263,
             "video_decoder.h263", "video_encoder.h263" },
+        { MEDIA_MIMETYPE_VIDEO_VPX,
+            "video_decoder.vpx", "video_encoder.vpx" },
     };
 
     static const size_t kNumMimeToRole =
@@ -2187,7 +2189,7 @@
     }
 }
 
-int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) {
+int64_t OMXCodec::getDecodingTimeUs() {
     CHECK(mIsEncoder && mIsVideo);
 
     if (mDecodingTimeList.empty()) {
@@ -2199,12 +2201,7 @@
 
     List<int64_t>::iterator it = mDecodingTimeList.begin();
     int64_t timeUs = *it;
-
-    // If the output buffer is codec specific configuration,
-    // do not remove the decoding time from the list.
-    if (!isCodecSpecific) {
-        mDecodingTimeList.erase(it);
-    }
+    mDecodingTimeList.erase(it);
     return timeUs;
 }
 
@@ -2384,7 +2381,7 @@
                 }
 
                 if (mIsEncoder && mIsVideo) {
-                    int64_t decodingTimeUs = retrieveDecodingTimeUs(isCodecSpecific);
+                    int64_t decodingTimeUs = isCodecSpecific? 0: getDecodingTimeUs();
                     buffer->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
                 }
 
@@ -3561,6 +3558,7 @@
         //////////////// output port ////////////////////
         // format
         OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+        InitOMXParams(&format);
         format.nPortIndex = kPortIndexOutput;
         format.nIndex = 0;
         status_t err = OMX_ErrorNone;
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 73efc27..5e79e78 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -21,10 +21,10 @@
 #include "include/OggExtractor.h"
 
 #include <cutils/properties.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
index 81ec5c1..eae721b 100644
--- a/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/SampleIterator.cpp
@@ -22,8 +22,8 @@
 
 #include <arpa/inet.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/Utils.h>
 
 #include "include/SampleTable.h"
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 43bfd9e..35f9c1f 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -20,10 +20,10 @@
 
 #include "include/StagefrightMetadataRetriever.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/ColorConverter.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXCodec.h>
@@ -37,7 +37,7 @@
     ALOGV("StagefrightMetadataRetriever()");
 
     DataSource::RegisterDefaultSniffers();
-    CHECK_EQ(mClient.connect(), OK);
+    CHECK_EQ(mClient.connect(), (status_t)OK);
 }
 
 StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
@@ -169,7 +169,7 @@
              || (buffer != NULL && buffer->range_length() == 0));
 
     if (err != OK) {
-        CHECK_EQ(buffer, NULL);
+        CHECK(buffer == NULL);
 
         ALOGV("decoding frame failed.");
         decoder->stop();
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index d068381..aa047d6 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -16,14 +16,14 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "SurfaceMediaSource"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/SurfaceMediaSource.h>
-#include <ui/GraphicBuffer.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/openmax/OMX_IVCommon.h>
 #include <media/stagefright/MetadataBufferType.h>
 
+#include <ui/GraphicBuffer.h>
 #include <surfaceflinger/ISurfaceComposer.h>
 #include <surfaceflinger/SurfaceComposerClient.h>
 #include <surfaceflinger/IGraphicBufferAlloc.h>
@@ -855,7 +855,7 @@
     }
 
     if (!foundBuffer) {
-        CHECK_EQ(0, "signalBufferReturned: bogus buffer");
+        CHECK(!"signalBufferReturned: bogus buffer");
     }
 }
 
diff --git a/media/libstagefright/ThrottledSource.cpp b/media/libstagefright/ThrottledSource.cpp
index 88e07b0..b1fcafd 100644
--- a/media/libstagefright/ThrottledSource.cpp
+++ b/media/libstagefright/ThrottledSource.cpp
@@ -16,7 +16,7 @@
 
 #include "include/ThrottledSource.h"
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
 
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 12c9c36..f4b5d4f 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -31,7 +31,7 @@
 #include <sys/prctl.h>
 #include <sys/time.h>
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 
 #ifdef ANDROID_SIMULATOR
 #include <jni.h>
diff --git a/media/libstagefright/VideoSourceDownSampler.cpp b/media/libstagefright/VideoSourceDownSampler.cpp
index 1b669904..90a42c9 100644
--- a/media/libstagefright/VideoSourceDownSampler.cpp
+++ b/media/libstagefright/VideoSourceDownSampler.cpp
@@ -17,9 +17,9 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "VideoSourceDownSampler"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/VideoSourceDownSampler.h>
 #include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/YUVImage.h>
 #include <media/stagefright/YUVCanvas.h>
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 0bcaf08..501f4806 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -20,9 +20,9 @@
 
 #include "include/WAVExtractor.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
@@ -217,7 +217,7 @@
                                 kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
                         break;
                     default:
-                        CHECK_EQ(mWaveFormat, WAVE_FORMAT_MULAW);
+                        CHECK_EQ(mWaveFormat, (uint16_t)WAVE_FORMAT_MULAW);
                         mTrackMeta->setCString(
                                 kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
                         break;
@@ -362,7 +362,7 @@
             // Convert 8-bit unsigned samples to 16-bit signed.
 
             MediaBuffer *tmp;
-            CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
+            CHECK_EQ(mGroup->acquire_buffer(&tmp), (status_t)OK);
 
             // The new buffer holds the sample number of samples, but each
             // one is 2 bytes wide.
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 1e4e049..c7ad513 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -21,6 +21,7 @@
 
 #include <arpa/inet.h>
 #include <utils/String8.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/Utils.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaSource.h>
@@ -28,7 +29,6 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
 #include <dlfcn.h>
 
 #include <utils/Errors.h>
diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
index 2b8633d..8b5007e 100644
--- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
@@ -22,8 +22,8 @@
 #include "voAAC.h"
 #include "cmnMemory.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -114,8 +114,8 @@
     ALOGV("setAudioSpecificConfigData: %d hz, %d bps, and %d channels",
          mSampleRate, mBitRate, mChannels);
 
-    int32_t index;
-    CHECK_EQ(OK, getSampleRateTableIndex(mSampleRate, index));
+    int32_t index = 0;
+    CHECK_EQ((status_t)OK, getSampleRateTableIndex(mSampleRate, index));
     if (mChannels > 2 || mChannels <= 0) {
         ALOGE("Unsupported number of channels(%d)", mChannels);
         return UNKNOWN_ERROR;
@@ -142,7 +142,7 @@
     mBufferGroup = new MediaBufferGroup;
     mBufferGroup->add_buffer(new MediaBuffer(2048));
 
-    CHECK_EQ(OK, initCheck());
+    CHECK_EQ((status_t)OK, initCheck());
 
     mNumInputSamples = 0;
     mAnchorTimeUs = 0;
@@ -183,7 +183,7 @@
 
     mSource->stop();
     if (mEncoderHandle) {
-        CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+        CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
         mEncoderHandle = NULL;
     }
     delete mApiHandle;
@@ -223,7 +223,7 @@
     CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode));
 
     MediaBuffer *buffer;
-    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK);
     uint8_t *outPtr = (uint8_t *)buffer->data();
     bool readFromSource = false;
     int64_t wallClockTimeUs = -1;
@@ -255,7 +255,7 @@
             }
 
             size_t align = mInputBuffer->range_length() % sizeof(int16_t);
-            CHECK_EQ(align, 0);
+            CHECK_EQ(align, (size_t)0);
 
             int64_t timeUs;
             if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) {
diff --git a/media/libstagefright/codecs/aacenc/basic_op/basic_op.h b/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
index ef3c31b..e878bba 100644
--- a/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
+++ b/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
@@ -228,7 +228,7 @@
 __inline Word32 ASM_L_shr(Word32 L_var1, Word16 var2)
 {
 	Word32 result;
-	asm volatile(
+	asm (
 		"MOV %[result], %[L_var1], ASR %[var2] \n"
 		:[result]"=r"(result)
 		:[L_var1]"r"(L_var1), [var2]"r"(var2)
@@ -239,15 +239,12 @@
 __inline Word32 ASM_L_shl(Word32 L_var1, Word16 var2)
 {
 	Word32 result;
-	asm volatile(
-		"MOV	r2, %[L_var1] \n"
-		"MOV	r3, #0x7fffffff\n"
+	asm (
 		"MOV	%[result], %[L_var1], ASL %[var2] \n"
-		"TEQ	r2, %[result], ASR %[var2]\n"
-		"EORNE  %[result],r3,r2,ASR#31\n"
-		:[result]"+r"(result)
-		:[L_var1]"r"(L_var1), [var2]"r"(var2)
-		:"r2", "r3"
+		"TEQ	%[L_var1], %[result], ASR %[var2]\n"
+		"EORNE  %[result], %[mask], %[L_var1], ASR #31\n"
+		:[result]"=&r"(result)
+		:[L_var1]"r"(L_var1), [var2]"r"(var2), [mask]"r"(0x7fffffff)
 		);
 	return result;
 }
@@ -255,10 +252,10 @@
 __inline Word32 ASM_shr(Word32 L_var1, Word16 var2)
 {
 	Word32 result;
-	asm volatile(
+	asm (
 		"CMP	%[var2], #15\n"
-		"MOVGE  %[var2], #15\n"
-		"MOV	%[result], %[L_var1], ASR %[var2]\n"
+		"MOVLT	%[result], %[L_var1], ASR %[var2]\n"
+		"MOVGE	%[result], %[L_var1], ASR #15\n"
 		:[result]"=r"(result)
 		:[L_var1]"r"(L_var1), [var2]"r"(var2)
 		);
@@ -268,18 +265,16 @@
 __inline Word32 ASM_shl(Word32 L_var1, Word16 var2)
 {
 	Word32 result;
-	asm volatile(
+	Word32 tmp;
+	asm (
 		"CMP	%[var2], #16\n"
-		"MOVGE  %[var2], #16\n"
-		"MOV    %[result], %[L_var1], ASL %[var2]\n"
-		"MOV    r3, #1\n"
-        "MOV    r2, %[result], ASR #15\n"
-        "RSB    r3,r3,r3,LSL #15 \n"
-        "TEQ    r2, %[result], ASR #31 \n"
-        "EORNE  %[result], r3, %[result],ASR #31"
-		:[result]"+r"(result)
-		:[L_var1]"r"(L_var1), [var2]"r"(var2)
-		:"r2", "r3"
+		"MOVLT  %[result], %[L_var1], ASL %[var2]\n"
+		"MOVGE  %[result], %[L_var1], ASL #16\n"
+        "MOV    %[tmp], %[result], ASR #15\n"
+        "TEQ    %[tmp], %[result], ASR #31 \n"
+        "EORNE  %[result], %[mask], %[result],ASR #31"
+		:[result]"=&r"(result), [tmp]"=&r"(tmp)
+		:[L_var1]"r"(L_var1), [var2]"r"(var2), [mask]"r"(0x7fff)
 		);
 	return result;
 }
@@ -295,16 +290,14 @@
 {
 #if ARMV5TE_SAT
 	Word16 result;
+	Word32 tmp;
 	asm volatile (
-		"MOV	%[result], %[L_var1]\n"
-		"MOV	r3, #1\n"
-		"MOV	r2,%[L_var1],ASR#15\n"
-		"RSB	r3, r3, r3, LSL #15\n"
-		"TEQ	r2,%[L_var1],ASR#31\n"
-		"EORNE	%[result],r3,%[L_var1],ASR#31\n"
-		:[result]"+r"(result)
-		:[L_var1]"r"(L_var1)
-		:"r2", "r3"
+		"MOV	%[tmp], %[L_var1],ASR#15\n"
+		"TEQ	%[tmp], %[L_var1],ASR#31\n"
+		"EORNE	%[result], %[mask],%[L_var1],ASR#31\n"
+		"MOVEQ	%[result], %[L_var1]\n"
+		:[result]"=&r"(result), [tmp]"=&r"(tmp)
+		:[L_var1]"r"(L_var1), [mask]"r"(0x7fff)
 	);
 
 	return result;
@@ -420,10 +413,10 @@
 {
 #if ARMV5TE_L_MULT
 	Word32 result;
-	asm volatile(
+	asm (
 		"SMULBB %[result], %[var1], %[var2] \n"
 		"QADD %[result], %[result], %[result] \n"
-		:[result]"+r"(result)
+		:[result]"=r"(result)
 		:[var1]"r"(var1), [var2]"r"(var2)
 		);
 	return result;
@@ -450,11 +443,11 @@
 {
 #if ARMV5TE_L_MSU
 	Word32 result;
-	asm volatile(
+	asm (
 		"SMULBB %[result], %[var1], %[var2] \n"
 		"QADD %[result], %[result], %[result] \n"
 		"QSUB %[result], %[L_var3], %[result]\n"
-		:[result]"+r"(result)
+		:[result]"=&r"(result)
 		:[L_var3]"r"(L_var3), [var1]"r"(var1), [var2]"r"(var2)
 		);
 	return result;
@@ -474,9 +467,9 @@
 {
 #if ARMV5TE_L_SUB
 	Word32 result;
-	asm volatile(
+	asm (
 		"QSUB %[result], %[L_var1], %[L_var2]\n"
-		:[result]"+r"(result)
+		:[result]"=r"(result)
 		:[L_var1]"r"(L_var1), [L_var2]"r"(L_var2)
 		);
 	return result;
@@ -589,16 +582,14 @@
 {
 #if ARMV5TE_ADD
 	Word32 result;
-	asm volatile(
+	Word32 tmp;
+	asm (
 		"ADD  %[result], %[var1], %[var2] \n"
-		"MOV  r3, #0x1\n"
-		"MOV  r2, %[result], ASR #15\n"
-		"RSB  r3, r3, r3, LSL, #15\n"
-		"TEQ  r2, %[result], ASR #31\n"
-		"EORNE %[result], r3, %[result], ASR #31"
-		:[result]"+r"(result)
-		:[var1]"r"(var1), [var2]"r"(var2)
-		:"r2", "r3"
+		"MOV  %[tmp], %[result], ASR #15 \n"
+		"TEQ  %[tmp], %[result], ASR #31 \n"
+		"EORNE %[result], %[mask], %[result], ASR #31"
+		:[result]"=&r"(result), [tmp]"=&r"(tmp)
+		:[var1]"r"(var1), [var2]"r"(var2), [mask]"r"(0x7fff)
 		);
 	return result;
 #else
@@ -619,16 +610,14 @@
 {
 #if ARMV5TE_SUB
 	Word32 result;
-	asm volatile(
-		"MOV   r3, #1\n"
+	Word32 tmp;
+	asm (
 		"SUB   %[result], %[var1], %[var2] \n"
-		"RSB   r3,r3,r3,LSL#15\n"
-		"MOV   r2, %[var1], ASR #15 \n"
-		"TEQ   r2, %[var1], ASR #31 \n"
-		"EORNE %[result], r3, %[result], ASR #31 \n"
-		:[result]"+r"(result)
-		:[var1]"r"(var1), [var2]"r"(var2)
-		:"r2", "r3"
+		"MOV   %[tmp], %[var1], ASR #15 \n"
+		"TEQ   %[tmp], %[var1], ASR #31 \n"
+		"EORNE %[result], %[mask], %[result], ASR #31 \n"
+		:[result]"=&r"(result), [tmp]"=&r"(tmp)
+		:[var1]"r"(var1), [var2]"r"(var2), [mask]"r"(0x7fff)
 		);
 	return result;
 #else
@@ -683,18 +672,15 @@
 __inline Word16 mult (Word16 var1, Word16 var2)
 {
 #if ARMV5TE_MULT
-	Word32 result;
-	asm volatile(
-		"SMULBB r2, %[var1], %[var2] \n"
-		"MOV	r3, #1\n"
-		"MOV	%[result], r2, ASR #15\n"
-		"RSB	r3, r3, r3, LSL #15\n"
-		"MOV	r2, %[result], ASR #15\n"
-		"TEQ	r2, %[result], ASR #31\n"
-		"EORNE  %[result], r3, %[result], ASR #31 \n"
-		:[result]"+r"(result)
-		:[var1]"r"(var1), [var2]"r"(var2)
-		:"r2", "r3"
+	Word32 result, tmp;
+	asm (
+		"SMULBB %[tmp], %[var1], %[var2] \n"
+		"MOV	%[result], %[tmp], ASR #15\n"
+		"MOV	%[tmp], %[result], ASR #15\n"
+		"TEQ	%[tmp], %[result], ASR #31\n"
+		"EORNE  %[result], %[mask], %[result], ASR #31 \n"
+		:[result]"=&r"(result), [tmp]"=&r"(tmp)
+		:[var1]"r"(var1), [var2]"r"(var2), [mask]"r"(0x7fff)
 		);
 	return result;
 #else
@@ -719,18 +705,17 @@
 {
 #if ARMV5TE_NORM_S
 	Word16 result;
-	asm volatile(
-		"MOV   r2,%[var1] \n"
-		"CMP   r2, #0\n"
-		"RSBLT %[var1], %[var1], #0 \n"
-		"CLZNE %[result], %[var1]\n"
+	Word32 tmp;
+	asm (
+		"RSBS  %[tmp], %[var1], #0 \n"
+		"CLZLT %[result], %[var1]\n"
+		"CLZGT %[result], %[tmp]\n"
 		"SUBNE %[result], %[result], #17\n"
 		"MOVEQ %[result], #0\n"
-		"CMP   r2, #-1\n"
+		"CMP   %[var1], #-1\n"
 		"MOVEQ %[result], #15\n"
-		:[result]"+r"(result)
+		:[result]"=&r"(result), [tmp]"=&r"(tmp)
 		:[var1]"r"(var1)
-		:"r2"
 		);
 	return result;
 #else
@@ -774,7 +759,7 @@
 		"CLZNE  %[result], %[L_var1]\n"
 		"SUBNE  %[result], %[result], #1\n"
 		"MOVEQ  %[result], #0\n"
-		:[result]"+r"(result)
+		:[result]"=r"(result)
 		:[L_var1]"r"(L_var1)
 		);
 	return result;
@@ -979,13 +964,11 @@
 {
 #if ARMV5TE_ROUND
 	Word16 result;
-	asm volatile(
-		"MOV   r1,#0x00008000\n"
-		"QADD  %[result], %[L_var1], r1\n"
+	asm (
+		"QADD  %[result], %[L_var1], %[bias]\n"
 		"MOV   %[result], %[result], ASR #16 \n"
-		:[result]"+r"(result)
-		:[L_var1]"r"(L_var1)
-		:"r1"
+		:[result]"=r"(result)
+		:[L_var1]"r"(L_var1), [bias]"r"(0x8000)
 		);
 	return result;
 #else
@@ -1005,11 +988,11 @@
 {
 #if ARMV5TE_L_MAC
 	Word32 result;
-	asm volatile(
+	asm (
 		"SMULBB %[result], %[var1], %[var2]\n"
 		"QADD	%[result], %[result], %[result]\n"
 		"QADD   %[result], %[result], %[L_var3]\n"
-		:[result]"+r"(result)
+		:[result]"=&r"(result)
 		: [L_var3]"r"(L_var3), [var1]"r"(var1), [var2]"r"(var2)
 		);
 	return result;
@@ -1029,9 +1012,9 @@
 {
 #if ARMV5TE_L_ADD
 	Word32 result;
-	asm volatile(
+	asm (
 		"QADD %[result], %[L_var1], %[L_var2]\n"
-		:[result]"+r"(result)
+		:[result]"=r"(result)
 		:[L_var1]"r"(L_var1), [L_var2]"r"(L_var2)
 		);
 	return result;
diff --git a/media/libstagefright/codecs/aacenc/basic_op/oper_32b.h b/media/libstagefright/codecs/aacenc/basic_op/oper_32b.h
index 9ebd1c2..6e5844f 100644
--- a/media/libstagefright/codecs/aacenc/basic_op/oper_32b.h
+++ b/media/libstagefright/codecs/aacenc/basic_op/oper_32b.h
@@ -63,7 +63,7 @@
 	Word32 result;
 	asm volatile(
 		"SMULWB  %[result], %[L_var2], %[var1] \n"
-		:[result]"+r"(result)
+		:[result]"=r"(result)
 		:[L_var2]"r"(L_var2), [var1]"r"(var1)
 		);
 	return result;
diff --git a/media/libstagefright/codecs/aacenc/inc/aacenc_core.h b/media/libstagefright/codecs/aacenc/inc/aacenc_core.h
index 1acdbbc..bb75b6d 100644
--- a/media/libstagefright/codecs/aacenc/inc/aacenc_core.h
+++ b/media/libstagefright/codecs/aacenc/inc/aacenc_core.h
@@ -102,7 +102,7 @@
                     const UWord8       *ancBytes,      /*!< pointer to ancillary data bytes */
                     Word16             *numAncBytes,   /*!< number of ancillary Data Bytes, send as fill element  */
                     UWord8             *outBytes,      /*!< pointer to output buffer            */
-                    Word32             *numOutBytes    /*!< number of bytes in output buffer */
+                    VO_U32             *numOutBytes    /*!< number of bytes in output buffer */
                     );
 
 /*---------------------------------------------------------------------------
diff --git a/media/libstagefright/codecs/aacenc/inc/bitbuffer.h b/media/libstagefright/codecs/aacenc/inc/bitbuffer.h
index e538064..7c79f07 100644
--- a/media/libstagefright/codecs/aacenc/inc/bitbuffer.h
+++ b/media/libstagefright/codecs/aacenc/inc/bitbuffer.h
@@ -76,7 +76,7 @@
 
 
 Word16 WriteBits(HANDLE_BIT_BUF hBitBuf,
-                 Word32 writeValue,
+                 UWord32 writeValue,
                  Word16 noBitsToWrite);
 
 void ResetBitBuf(HANDLE_BIT_BUF hBitBuf,
diff --git a/media/libstagefright/codecs/aacenc/inc/psy_configuration.h b/media/libstagefright/codecs/aacenc/inc/psy_configuration.h
index 9abfc99..f6981fa 100644
--- a/media/libstagefright/codecs/aacenc/inc/psy_configuration.h
+++ b/media/libstagefright/codecs/aacenc/inc/psy_configuration.h
@@ -31,7 +31,7 @@
 
   Word16 sfbCnt;
   Word16 sfbActive;   /* number of sf bands containing energy after lowpass */
-  Word16 *sfbOffset;
+  const Word16 *sfbOffset;
 
   Word32 sfbThresholdQuiet[MAX_SFB_LONG];
 
@@ -61,7 +61,7 @@
 
   Word16 sfbCnt;
   Word16 sfbActive;   /* number of sf bands containing energy after lowpass */
-  Word16 *sfbOffset;
+  const Word16 *sfbOffset;
 
   Word32 sfbThresholdQuiet[MAX_SFB_SHORT];
 
diff --git a/media/libstagefright/codecs/aacenc/src/aacenc_core.c b/media/libstagefright/codecs/aacenc/src/aacenc_core.c
index 2b3bd48..cecbc8f 100644
--- a/media/libstagefright/codecs/aacenc/src/aacenc_core.c
+++ b/media/libstagefright/codecs/aacenc/src/aacenc_core.c
@@ -146,7 +146,7 @@
                     const UWord8 *ancBytes,     /*!< pointer to ancillary data bytes */
                     Word16 *numAncBytes,		/*!< number of ancillary Data Bytes */
                     UWord8 *outBytes,           /*!< pointer to output buffer (must be large MINBITS_COEF/8*MAX_CHANNELS bytes) */
-                    Word32 *numOutBytes         /*!< number of bytes in output buffer after processing */
+                    VO_U32 *numOutBytes         /*!< number of bytes in output buffer after processing */
                     )
 {
   ELEMENT_INFO *elInfo = &aacEnc->elInfo;
diff --git a/media/libstagefright/codecs/aacenc/src/adj_thr.c b/media/libstagefright/codecs/aacenc/src/adj_thr.c
index a8ab809..373b063 100644
--- a/media/libstagefright/codecs/aacenc/src/adj_thr.c
+++ b/media/libstagefright/codecs/aacenc/src/adj_thr.c
@@ -26,6 +26,7 @@
 #include "adj_thr.h"
 #include "qc_data.h"
 #include "line_pe.h"
+#include <string.h>
 
 
 #define  minSnrLimit    0x6666 /* 1 dB */
@@ -1138,6 +1139,7 @@
   Word16 maxBitresBits = elBits->maxBits;
   Word16 sideInfoBits = (qcOE->staticBitsUsed + qcOE->ancBitsUsed);
   Word16 ch;
+  memset(&peData, 0, sizeof(peData));
 
   prepareSfbPe(&peData, psyOutChannel, logSfbEnergy, sfbNRelevantLines, nChannels, AdjThrStateElement->peOffset);
 
diff --git a/media/libstagefright/codecs/aacenc/src/bitbuffer.c b/media/libstagefright/codecs/aacenc/src/bitbuffer.c
index 5615ac3..a706893 100644
--- a/media/libstagefright/codecs/aacenc/src/bitbuffer.c
+++ b/media/libstagefright/codecs/aacenc/src/bitbuffer.c
@@ -138,7 +138,7 @@
 *
 *****************************************************************************/
 Word16 WriteBits(HANDLE_BIT_BUF hBitBuf,
-                 Word32 writeValue,
+                 UWord32 writeValue,
                  Word16 noBitsToWrite)
 {
   Word16 wBitPos;
diff --git a/media/libstagefright/codecs/aacenc/src/dyn_bits.c b/media/libstagefright/codecs/aacenc/src/dyn_bits.c
index 3d2efdc..7769188 100644
--- a/media/libstagefright/codecs/aacenc/src/dyn_bits.c
+++ b/media/libstagefright/codecs/aacenc/src/dyn_bits.c
@@ -281,7 +281,7 @@
                  const Word32 blockType)
 {
   Word32 grpNdx, i;
-  Word16 *sideInfoTab = NULL;
+  const Word16 *sideInfoTab = NULL;
   SECTION_INFO *sectionInfo;
 
   /*
diff --git a/media/libstagefright/codecs/aacenc/src/interface.c b/media/libstagefright/codecs/aacenc/src/interface.c
index f2472d8..d0ad433 100644
--- a/media/libstagefright/codecs/aacenc/src/interface.c
+++ b/media/libstagefright/codecs/aacenc/src/interface.c
@@ -99,8 +99,8 @@
     Word32 i;
     Word32 accuSumMS=0;
     Word32 accuSumLR=0;
-	Word32 *pSumMS = sfbEnergySumMS.sfbShort;
-	Word32 *pSumLR = sfbEnergySumLR.sfbShort;
+    const Word32 *pSumMS = sfbEnergySumMS.sfbShort;
+    const Word32 *pSumLR = sfbEnergySumLR.sfbShort;
 
     for (i=TRANS_FAC; i; i--) {
       accuSumLR = L_add(accuSumLR, *pSumLR); pSumLR++;
diff --git a/media/libstagefright/codecs/aacenc/src/psy_configuration.c b/media/libstagefright/codecs/aacenc/src/psy_configuration.c
index 02d92ab..dd40f9b 100644
--- a/media/libstagefright/codecs/aacenc/src/psy_configuration.c
+++ b/media/libstagefright/codecs/aacenc/src/psy_configuration.c
@@ -139,7 +139,7 @@
 *
 *****************************************************************************/
 static void initThrQuiet(Word16  numPb,
-                         Word16 *pbOffset,
+                         const Word16 *pbOffset,
                          Word16 *pbBarcVal,
                          Word32 *pbThresholdQuiet) {
   Word16 i;
@@ -250,7 +250,7 @@
 *
 *****************************************************************************/
 static void initBarcValues(Word16  numPb,
-                           Word16 *pbOffset,
+                           const Word16 *pbOffset,
                            Word16  numLines,
                            Word32  samplingFrequency,
                            Word16 *pbBval)
diff --git a/media/libstagefright/codecs/aacenc/src/psy_main.c b/media/libstagefright/codecs/aacenc/src/psy_main.c
index 085acb8..4e9218c 100644
--- a/media/libstagefright/codecs/aacenc/src/psy_main.c
+++ b/media/libstagefright/codecs/aacenc/src/psy_main.c
@@ -658,7 +658,8 @@
   Word32 normEnergyShift = (psyData->mdctScale + 1) << 1; /* in reference code, mdct spectrum must be multipied with 2, so +1 */
   Word32 clipEnergy = hPsyConfShort->clipEnergy >> normEnergyShift;
   Word32 wOffset = 0;
-  Word32 *data0, *data1;
+  Word32 *data0;
+  const Word32 *data1;
 
   for(w = 0; w < TRANS_FAC; w++) {
     Word32 i, tdata;
diff --git a/media/libstagefright/codecs/aacenc/src/quantize.c b/media/libstagefright/codecs/aacenc/src/quantize.c
index 54add2f..0d0f550 100644
--- a/media/libstagefright/codecs/aacenc/src/quantize.c
+++ b/media/libstagefright/codecs/aacenc/src/quantize.c
@@ -110,7 +110,7 @@
   Word32 m = gain&3;
   Word32 g = (gain >> 2) + 4;
   Word32 mdctSpeL;
-  Word16 *pquat;
+  const Word16 *pquat;
     /* gain&3 */
 
   pquat = quantBorders[m];
@@ -333,7 +333,7 @@
   Word32 m = gain&3;
   Word32 g = (gain >> 2) + 4;
   Word32 g2 = (g << 1) + 1;
-  Word16 *pquat, *repquat;
+  const Word16 *pquat, *repquat;
     /* gain&3 */
 
   pquat = quantBorders[m];
diff --git a/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp b/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp
index bd99b30..4135f30 100644
--- a/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp
@@ -299,7 +299,7 @@
     t0 += (Word32) * (p_f) << 13;
 
 
-    if ((UWord32)(t0 - 0xfe000000L) < 0x01ffffffL -  0xfe000000L)
+    if ((UWord32)(t0 - 0xfe000000L) < (UWord32)0x03ffffffL)
     {
         cheb = (Word16)(t0 >> 10);
     }
diff --git a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
index 3afbc4f..27d7e4d 100644
--- a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
@@ -18,8 +18,8 @@
 
 #include "gsmamr_enc.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -210,7 +210,7 @@
     }
 
     MediaBuffer *buffer;
-    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK);
 
     uint8_t *outPtr = (uint8_t *)buffer->data();
 
diff --git a/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp b/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp
index dedf91a..d626de3 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp
@@ -552,10 +552,10 @@
         else
         {
             *(p_sign--) = -32767;                     /* sign = -1 */
-            cor = - (cor);
+            cor = negate(cor);
 
             /* modify dn[] according to the fixed sign */
-            dn[i] = - val;
+            dn[i] = negate(val);
         }
 
         *(p_en--) = cor;
diff --git a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
index 60b1163..7fd3a95 100644
--- a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
+++ b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
@@ -22,8 +22,8 @@
 #include "voAMRWB.h"
 #include "cmnMemory.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -134,7 +134,7 @@
     // The largest buffer size is header + 477 bits
     mBufferGroup->add_buffer(new MediaBuffer(1024));
 
-    CHECK_EQ(OK, initCheck());
+    CHECK_EQ((status_t)OK, initCheck());
 
     mNumFramesOutput = 0;
 
@@ -163,7 +163,7 @@
     mBufferGroup = NULL;
 
 
-    CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+    CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
     mEncoderHandle = NULL;
 
     delete mApiHandle;
@@ -222,7 +222,7 @@
             }
 
             size_t align = mInputBuffer->range_length() % sizeof(int16_t);
-            CHECK_EQ(align, 0);
+            CHECK_EQ(align, (size_t)0);
 
             int64_t timeUs;
             if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) {
@@ -271,7 +271,7 @@
     CHECK(VO_ERR_NONE == mApiHandle->SetInputData(mEncoderHandle,&inputData));
 
     MediaBuffer *buffer;
-    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK);
     uint8_t *outPtr = (uint8_t *)buffer->data();
 
     VO_CODECBUFFER outputData;
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index e202a2b..7533f07 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -24,8 +24,8 @@
 #include "avcenc_int.h"
 #include "OMX_Video.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -417,7 +417,7 @@
     *out = NULL;
 
     MediaBuffer *outputBuffer;
-    CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
+    CHECK_EQ((status_t)OK, mGroup->acquire_buffer(&outputBuffer));
     uint8_t *outPtr = (uint8_t *) outputBuffer->data();
     uint32_t dataLength = outputBuffer->size();
 
@@ -557,9 +557,9 @@
     encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
     if (encoderStatus == AVCENC_SUCCESS) {
         outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, mIsIDRFrame);
-        CHECK_EQ(NULL, PVAVCEncGetOverrunBuffer(mHandle));
+        CHECK(NULL == PVAVCEncGetOverrunBuffer(mHandle));
     } else if (encoderStatus == AVCENC_PICTURE_READY) {
-        CHECK_EQ(NULL, PVAVCEncGetOverrunBuffer(mHandle));
+        CHECK(NULL == PVAVCEncGetOverrunBuffer(mHandle));
         if (mIsIDRFrame) {
             outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, mIsIDRFrame);
             mIsIDRFrame = 0;
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index d538603..20b0f8d 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -23,8 +23,8 @@
 #include "mp4enc_api.h"
 #include "OMX_Video.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -379,7 +379,7 @@
     *out = NULL;
 
     MediaBuffer *outputBuffer;
-    CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
+    CHECK_EQ((status_t)OK, mGroup->acquire_buffer(&outputBuffer));
     uint8_t *outPtr = (uint8_t *) outputBuffer->data();
     int32_t dataLength = outputBuffer->size();
 
@@ -467,7 +467,7 @@
         mInputBuffer = NULL;
         return UNKNOWN_ERROR;
     }
-    CHECK_EQ(NULL, PVGetOverrunBuffer(mHandle));
+    CHECK(NULL == PVGetOverrunBuffer(mHandle));
     if (hintTrack.CodeType == 0) {  // I-frame serves as sync frame
         outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
     }
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index f3ef3de..597167f 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -18,8 +18,8 @@
 #define LOG_TAG "ColorConverter"
 #include <utils/Log.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/ColorConverter.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaErrors.h>
 
 namespace android {
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 0a6776ef..9a00186 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -19,6 +19,7 @@
 #include <ctype.h>
 
 #include "AAtomizer.h"
+#include "ABuffer.h"
 #include "ADebug.h"
 #include "ALooperRoster.h"
 #include "AString.h"
@@ -157,14 +158,23 @@
     item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
 }
 
-void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
+void AMessage::setObjectInternal(
+        const char *name, const sp<RefBase> &obj, Type type) {
     Item *item = allocateItem(name);
-    item->mType = kTypeObject;
+    item->mType = type;
 
     if (obj != NULL) { obj->incStrong(this); }
     item->u.refValue = obj.get();
 }
 
+void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
+    setObjectInternal(name, obj, kTypeObject);
+}
+
+void AMessage::setBuffer(const char *name, const sp<ABuffer> &buffer) {
+    setObjectInternal(name, sp<RefBase>(buffer), kTypeBuffer);
+}
+
 void AMessage::setMessage(const char *name, const sp<AMessage> &obj) {
     Item *item = allocateItem(name);
     item->mType = kTypeMessage;
@@ -203,6 +213,15 @@
     return false;
 }
 
+bool AMessage::findBuffer(const char *name, sp<ABuffer> *buf) const {
+    const Item *item = findItem(name, kTypeBuffer);
+    if (item) {
+        *buf = (ABuffer *)(item->u.refValue);
+        return true;
+    }
+    return false;
+}
+
 bool AMessage::findMessage(const char *name, sp<AMessage> *obj) const {
     const Item *item = findItem(name, kTypeMessage);
     if (item) {
@@ -542,4 +561,20 @@
     }
 }
 
+size_t AMessage::countEntries() const {
+    return mNumItems;
+}
+
+const char *AMessage::getEntryNameAt(size_t index, Type *type) const {
+    if (index >= mNumItems) {
+        *type = kTypeInt32;
+
+        return NULL;
+    }
+
+    *type = mItems[index].mType;
+
+    return mItems[index].mName;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index 23c8e44..ff35d4a 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -16,7 +16,7 @@
 	testid3.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright libutils libbinder
+	libstagefright libutils libbinder libstagefright_foundation
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_id3
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 6dde9d8..2e92926 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -20,8 +20,8 @@
 
 #include "../include/ID3.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/Utils.h>
 #include <utils/String8.h>
 #include <byteswap.h>
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
index 0741045..bc4572c 100644
--- a/media/libstagefright/id3/testid3.cpp
+++ b/media/libstagefright/id3/testid3.cpp
@@ -23,7 +23,7 @@
 
 #include <binder/ProcessState.h>
 #include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 
 #define MAXPATHLEN 256
 
@@ -70,7 +70,7 @@
 
 void scanFile(const char *path) {
     sp<FileSource> file = new FileSource(path);
-    CHECK_EQ(file->initCheck(), OK);
+    CHECK_EQ(file->initCheck(), (status_t)OK);
 
     ID3 tag(file);
     if (!tag.isValid()) {
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index a7a3d47..4c7bfa6 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -258,7 +258,7 @@
     void setVideoSource(sp<MediaSource> source);
     status_t initVideoDecoder(uint32_t flags = 0);
 
-    void addTextSource(sp<MediaSource> source);
+    void addTextSource(const sp<MediaSource>& source);
 
     void onStreamDone();
 
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index a1644d2..a0db719 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -93,7 +93,10 @@
 
     void advance();
     void reset();
-    void seek(int64_t seekTimeUs, bool seekToKeyFrame);
+
+    void seek(
+            int64_t seekTimeUs, bool seekToKeyFrame,
+            int64_t *actualFrameTimeUs);
 
     const mkvparser::Block *block() const;
     int64_t blockTimeUs() const;
@@ -303,22 +306,52 @@
     } while (!eos() && block()->GetTrackNumber() != mTrackNum);
 }
 
-void BlockIterator::seek(int64_t seekTimeUs, bool seekToKeyFrame) {
+void BlockIterator::seek(
+        int64_t seekTimeUs, bool seekToKeyFrame,
+        int64_t *actualFrameTimeUs) {
     Mutex::Autolock autoLock(mExtractor->mLock);
 
-    mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll);
+    *actualFrameTimeUs = -1ll;
+
+    int64_t seekTimeNs = seekTimeUs * 1000ll;
+
+    mCluster = mExtractor->mSegment->FindCluster(seekTimeNs);
     mBlockEntry = NULL;
     mBlockEntryIndex = 0;
 
-    do {
-        advance_l();
-    }
-    while (!eos() && block()->GetTrackNumber() != mTrackNum);
+    long prevKeyFrameBlockEntryIndex = -1;
 
-    if (seekToKeyFrame) {
-        while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
-            advance_l();
+    for (;;) {
+        advance_l();
+
+        if (eos()) {
+            break;
         }
+
+        if (block()->GetTrackNumber() != mTrackNum) {
+            continue;
+        }
+
+        if (block()->IsKey()) {
+            prevKeyFrameBlockEntryIndex = mBlockEntryIndex - 1;
+        }
+
+        int64_t timeNs = block()->GetTime(mCluster);
+
+        if (timeNs >= seekTimeNs) {
+            *actualFrameTimeUs = (timeNs + 500ll) / 1000ll;
+            break;
+        }
+    }
+
+    if (eos()) {
+        return;
+    }
+
+    if (seekToKeyFrame && !block()->IsKey()) {
+        CHECK_GE(prevKeyFrameBlockEntryIndex, 0);
+        mBlockEntryIndex = prevKeyFrameBlockEntryIndex;
+        advance_l();
     }
 }
 
@@ -397,6 +430,8 @@
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
 
+    int64_t targetSampleTimeUs = -1ll;
+
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)
@@ -406,10 +441,14 @@
         // Apparently keyframe indication in audio tracks is unreliable,
         // fortunately in all our currently supported audio encodings every
         // frame is effectively a keyframe.
-        mBlockIter.seek(seekTimeUs, !mIsAudio);
+        int64_t actualFrameTimeUs;
+        mBlockIter.seek(seekTimeUs, !mIsAudio, &actualFrameTimeUs);
+
+        if (mode == ReadOptions::SEEK_CLOSEST) {
+            targetSampleTimeUs = actualFrameTimeUs;
+        }
     }
 
-again:
     while (mPendingFrames.empty()) {
         status_t err = readBlock();
 
@@ -424,6 +463,11 @@
     mPendingFrames.erase(mPendingFrames.begin());
 
     if (mType != AVC) {
+        if (targetSampleTimeUs >= 0ll) {
+            frame->meta_data()->setInt64(
+                    kKeyTargetTime, targetSampleTimeUs);
+        }
+
         *out = frame;
 
         return OK;
@@ -506,6 +550,11 @@
     frame->release();
     frame = NULL;
 
+    if (targetSampleTimeUs >= 0ll) {
+        buffer->meta_data()->setInt64(
+                kKeyTargetTime, targetSampleTimeUs);
+    }
+
     *out = buffer;
 
     return OK;
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 03033f5..e1589b4 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -22,8 +22,8 @@
 #include "include/LiveSession.h"
 #include "include/NuCachedSource2.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index ace883c..f11fcd2 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -25,7 +25,7 @@
 #include "../include/OMXNodeInstance.h"
 
 #include <binder/IMemory.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <utils/threads.h>
 
 #include "OMXMaster.h"
@@ -102,7 +102,7 @@
     if (status != WOULD_BLOCK) {
         // Other than join to self, the only other error return codes are
         // whatever readyToRun() returns, and we don't override that
-        CHECK_EQ(status, NO_ERROR);
+        CHECK_EQ(status, (status_t)NO_ERROR);
     }
 }
 
diff --git a/media/libstagefright/omx/OMXComponentBase.cpp b/media/libstagefright/omx/OMXComponentBase.cpp
index 35227a0..7d11dce 100644
--- a/media/libstagefright/omx/OMXComponentBase.cpp
+++ b/media/libstagefright/omx/OMXComponentBase.cpp
@@ -18,7 +18,7 @@
 
 #include <stdlib.h>
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
 
@@ -33,7 +33,7 @@
 OMXComponentBase::~OMXComponentBase() {}
 
 void OMXComponentBase::setComponentHandle(OMX_COMPONENTTYPE *handle) {
-    CHECK_EQ(mComponentHandle, NULL);
+    CHECK(mComponentHandle == NULL);
     mComponentHandle = handle;
 }
 
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index d698939..6b6d0ab 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -24,7 +24,7 @@
 
 #include <dlfcn.h>
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
 
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 8938e33..099c4f5 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -24,8 +24,8 @@
 #include <OMX_Component.h>
 
 #include <binder/IMemory.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/HardwareAPI.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaErrors.h>
 
 namespace android {
@@ -91,11 +91,11 @@
 }
 
 OMXNodeInstance::~OMXNodeInstance() {
-    CHECK_EQ(mHandle, NULL);
+    CHECK(mHandle == NULL);
 }
 
 void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) {
-    CHECK_EQ(mHandle, NULL);
+    CHECK(mHandle == NULL);
     mNodeID = node_id;
     mHandle = handle;
 }
diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk
index 41c08be..0c0a70c 100644
--- a/media/libstagefright/omx/tests/Android.mk
+++ b/media/libstagefright/omx/tests/Android.mk
@@ -5,7 +5,7 @@
 	OMXHarness.cpp  \
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright libbinder libmedia libutils
+	libstagefright libbinder libmedia libutils libstagefright_foundation
 
 LOCAL_C_INCLUDES := \
 	$(JNI_H_INCLUDE) \
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 8faf544..fab1771 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -26,9 +26,9 @@
 #include <binder/IServiceManager.h>
 #include <binder/MemoryDealer.h>
 #include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -155,7 +155,7 @@
         if (err == TIMED_OUT) {
             return err;
         }
-        CHECK_EQ(err, OK);
+        CHECK_EQ(err, (status_t)OK);
     }
 }
 
@@ -317,7 +317,7 @@
     EXPECT_SUCCESS(err, "allocatePortBuffers(input)");
 
     err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
-    CHECK_EQ(err, TIMED_OUT);
+    CHECK_EQ(err, (status_t)TIMED_OUT);
 
     Vector<Buffer> outputBuffers;
     err = allocatePortBuffers(dealer, node, 1, &outputBuffers);
@@ -412,7 +412,7 @@
     // Make sure node doesn't just transition to loaded before we are done
     // freeing all input and output buffers.
     err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
-    CHECK_EQ(err, TIMED_OUT);
+    CHECK_EQ(err, (status_t)TIMED_OUT);
 
     for (size_t i = 0; i < inputBuffers.size(); ++i) {
         err = mOMX->freeBuffer(node, 0, inputBuffers[i].mID);
@@ -420,7 +420,7 @@
     }
 
     err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
-    CHECK_EQ(err, TIMED_OUT);
+    CHECK_EQ(err, (status_t)TIMED_OUT);
 
     for (size_t i = 0; i < outputBuffers.size(); ++i) {
         err = mOMX->freeBuffer(node, 1, outputBuffers[i].mID);
@@ -584,7 +584,7 @@
         return UNKNOWN_ERROR;
     }
 
-    CHECK_EQ(seekSource->start(), OK);
+    CHECK_EQ(seekSource->start(), (status_t)OK);
 
     sp<MediaSource> codec = OMXCodec::Create(
             mOMX, source->getFormat(), false /* createEncoder */,
@@ -592,7 +592,7 @@
 
     CHECK(codec != NULL);
 
-    CHECK_EQ(codec->start(), OK);
+    CHECK_EQ(codec->start(), (status_t)OK);
 
     int64_t durationUs;
     CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
@@ -638,7 +638,7 @@
                     requestedSeekTimeUs, MediaSource::ReadOptions::SEEK_NEXT_SYNC);
 
             if (seekSource->read(&buffer, &options) != OK) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
                 actualSeekTimeUs = -1;
             } else {
                 CHECK(buffer != NULL);
@@ -659,7 +659,7 @@
             err = codec->read(&buffer, &options);
             options.clearSeekTo();
             if (err == INFO_FORMAT_CHANGED) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
                 continue;
             }
             if (err == OK) {
@@ -670,7 +670,7 @@
                     continue;
                 }
             } else {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
             }
 
             break;
@@ -679,7 +679,7 @@
         if (requestedSeekTimeUs < 0) {
             // Linear read.
             if (err != OK) {
-                CHECK_EQ(buffer, NULL);
+                CHECK(buffer == NULL);
             } else {
                 CHECK(buffer != NULL);
                 buffer->release();
@@ -694,8 +694,8 @@
                    "We attempted to seek beyond EOS and expected "
                    "ERROR_END_OF_STREAM to be returned, but instead "
                    "we found some other error.");
-            CHECK_EQ(err, ERROR_END_OF_STREAM);
-            CHECK_EQ(buffer, NULL);
+            CHECK_EQ(err, (status_t)ERROR_END_OF_STREAM);
+            CHECK(buffer == NULL);
         } else {
             EXPECT(err == OK,
                    "Expected a valid buffer to be returned from "
@@ -715,7 +715,7 @@
                 buffer->release();
                 buffer = NULL;
 
-                CHECK_EQ(codec->stop(), OK);
+                CHECK_EQ(codec->stop(), (status_t)OK);
 
                 return UNKNOWN_ERROR;
             }
@@ -725,7 +725,7 @@
         }
     }
 
-    CHECK_EQ(codec->stop(), OK);
+    CHECK_EQ(codec->stop(), (status_t)OK);
 
     return OK;
 }
@@ -841,7 +841,7 @@
     srand(seed);
 
     sp<Harness> h = new Harness;
-    CHECK_EQ(h->initCheck(), OK);
+    CHECK_EQ(h->initCheck(), (status_t)OK);
 
     if (argc == 0) {
         h->testAll();
diff --git a/media/libstagefright/rtsp/AAMRAssembler.cpp b/media/libstagefright/rtsp/AAMRAssembler.cpp
index 9d72b1f..fb8abc5 100644
--- a/media/libstagefright/rtsp/AAMRAssembler.cpp
+++ b/media/libstagefright/rtsp/AAMRAssembler.cpp
@@ -211,7 +211,7 @@
     }
 
     sp<AMessage> msg = mNotifyMsg->dup();
-    msg->setObject("access-unit", accessUnit);
+    msg->setBuffer("access-unit", accessUnit);
     msg->post();
 
     queue->erase(queue->begin());
diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp
index ed8b1df..7ea132e 100644
--- a/media/libstagefright/rtsp/AAVCAssembler.cpp
+++ b/media/libstagefright/rtsp/AAVCAssembler.cpp
@@ -345,7 +345,7 @@
     mAccessUnitDamaged = false;
 
     sp<AMessage> msg = mNotifyMsg->dup();
-    msg->setObject("access-unit", accessUnit);
+    msg->setBuffer("access-unit", accessUnit);
     msg->post();
 }
 
diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp
index 498295c..ded70fa 100644
--- a/media/libstagefright/rtsp/AH263Assembler.cpp
+++ b/media/libstagefright/rtsp/AH263Assembler.cpp
@@ -166,7 +166,7 @@
     mAccessUnitDamaged = false;
 
     sp<AMessage> msg = mNotifyMsg->dup();
-    msg->setObject("access-unit", accessUnit);
+    msg->setBuffer("access-unit", accessUnit);
     msg->post();
 }
 
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index b0c7007..24c2f30 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -571,7 +571,7 @@
     mAccessUnitDamaged = false;
 
     sp<AMessage> msg = mNotifyMsg->dup();
-    msg->setObject("access-unit", accessUnit);
+    msg->setBuffer("access-unit", accessUnit);
     msg->post();
 }
 
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 2f2e2c2..687d72b 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -368,7 +368,7 @@
     mAccessUnitDamaged = false;
 
     sp<AMessage> msg = mNotifyMsg->dup();
-    msg->setObject("access-unit", accessUnit);
+    msg->setBuffer("access-unit", accessUnit);
     msg->post();
 }
 
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 8c9dd8d..44988a3 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -639,7 +639,7 @@
 void ARTPConnection::injectPacket(int index, const sp<ABuffer> &buffer) {
     sp<AMessage> msg = new AMessage(kWhatInjectPacket, id());
     msg->setInt32("index", index);
-    msg->setObject("buffer", buffer);
+    msg->setBuffer("buffer", buffer);
     msg->post();
 }
 
@@ -647,10 +647,8 @@
     int32_t index;
     CHECK(msg->findInt32("index", &index));
 
-    sp<RefBase> obj;
-    CHECK(msg->findObject("buffer", &obj));
-
-    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+    sp<ABuffer> buffer;
+    CHECK(msg->findBuffer("buffer", &buffer));
 
     List<StreamInfo>::iterator it = mStreams.begin();
     while (it != mStreams.end()
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 7a05b88..ba4e33c 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -145,10 +145,8 @@
                 break;
             }
 
-            sp<RefBase> obj;
-            CHECK(msg->findObject("access-unit", &obj));
-
-            sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+            sp<ABuffer> accessUnit;
+            CHECK(msg->findBuffer("access-unit", &accessUnit));
 
             uint64_t ntpTime;
             CHECK(accessUnit->meta()->findInt64(
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index 80a010e..539a888 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -612,7 +612,7 @@
 
         if (mObserveBinaryMessage != NULL) {
             sp<AMessage> notify = mObserveBinaryMessage->dup();
-            notify->setObject("buffer", buffer);
+            notify->setBuffer("buffer", buffer);
             notify->post();
         } else {
             ALOGW("received binary data, but no one cares.");
diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.cpp b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
index 98bee82..0da5dd2 100644
--- a/media/libstagefright/rtsp/ARawAudioAssembler.cpp
+++ b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
@@ -94,7 +94,7 @@
     }
 
     sp<AMessage> msg = mNotifyMsg->dup();
-    msg->setObject("access-unit", buffer);
+    msg->setBuffer("access-unit", buffer);
     msg->post();
 
     queue->erase(queue->begin());
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 9a7dd70..deee30f 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -857,10 +857,8 @@
                     return;
                 }
 
-                sp<RefBase> obj;
-                CHECK(msg->findObject("access-unit", &obj));
-
-                sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+                sp<ABuffer> accessUnit;
+                CHECK(msg->findBuffer("access-unit", &accessUnit));
 
                 uint32_t seqNum = (uint32_t)accessUnit->int32Data();
 
@@ -1005,9 +1003,8 @@
 
             case 'biny':
             {
-                sp<RefBase> obj;
-                CHECK(msg->findObject("buffer", &obj));
-                sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+                sp<ABuffer> buffer;
+                CHECK(msg->findBuffer("buffer", &buffer));
 
                 int32_t index;
                 CHECK(buffer->meta()->findInt32("index", &index));
@@ -1488,7 +1485,7 @@
         sp<AMessage> msg = mNotify->dup();
         msg->setInt32("what", kWhatAccessUnit);
         msg->setSize("trackIndex", trackIndex);
-        msg->setObject("accessUnit", accessUnit);
+        msg->setBuffer("accessUnit", accessUnit);
         msg->post();
     }
 
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index 76b507f..d7cec04 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -35,7 +35,7 @@
 #include <binder/ProcessState.h>
 #include <ui/FramebufferNativeWindow.h>
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
@@ -475,7 +475,7 @@
     mr->setVideoFrameRate(fps);
     mr->prepare();
     ALOGV("Starting MediaRecorder...");
-    CHECK_EQ(OK, mr->start());
+    CHECK_EQ((status_t)OK, mr->start());
     return mr;
 }
 
@@ -757,7 +757,7 @@
 
     ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU));
     ALOGV("Stopping MediaRecorder...");
-    CHECK_EQ(OK, mr->stop());
+    CHECK_EQ((status_t)OK, mr->stop());
     mr.clear();
     close(fd);
 }
@@ -886,7 +886,7 @@
     mEglSurface = EGL_NO_SURFACE;
 
     ALOGV("Stopping MediaRecorder...");
-    CHECK_EQ(OK, mr->stop());
+    CHECK_EQ((status_t)OK, mr->stop());
     mr.clear();
     close(fd);
 }
@@ -929,7 +929,7 @@
     mEglSurface = EGL_NO_SURFACE;
 
     ALOGV("Stopping MediaRecorder...");
-    CHECK_EQ(OK, mr->stop());
+    CHECK_EQ((status_t)OK, mr->stop());
     mr.clear();
     close(fd);
 }
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 8b23dee..dde2066 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -4,7 +4,7 @@
 LOCAL_SRC_FILES:=                 \
         TextDescriptions.cpp      \
         TimedTextDriver.cpp       \
-        TimedTextInBandSource.cpp \
+        TimedText3GPPSource.cpp \
         TimedTextSource.cpp       \
         TimedTextSRTSource.cpp    \
         TimedTextPlayer.cpp
@@ -12,8 +12,7 @@
 LOCAL_CFLAGS += -Wno-multichar
 LOCAL_C_INCLUDES:= \
         $(JNI_H_INCLUDE) \
-        $(TOP)/frameworks/base/media/libstagefright \
-        $(TOP)/frameworks/base/include/media/stagefright/openmax
+        $(TOP)/frameworks/base/media/libstagefright
 
 LOCAL_MODULE:= libstagefright_timedtext
 
diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.cpp b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
similarity index 62%
rename from media/libstagefright/timedtext/TimedTextInBandSource.cpp
rename to media/libstagefright/timedtext/TimedText3GPPSource.cpp
index f2c4d54..4a3bfd3 100644
--- a/media/libstagefright/timedtext/TimedTextInBandSource.cpp
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
@@ -15,30 +15,30 @@
  */
 
 //#define LOG_NDEBUG 0
-#define LOG_TAG "TimedTextInBandSource"
+#define LOG_TAG "TimedText3GPPSource"
 #include <utils/Log.h>
 
 #include <binder/Parcel.h>
+#include <media/stagefright/foundation/ADebug.h>  // CHECK_XX macro
 #include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>  // CHECK_XX macro
 #include <media/stagefright/MediaDefs.h>  // for MEDIA_MIMETYPE_xxx
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 
-#include "TimedTextInBandSource.h"
+#include "TimedText3GPPSource.h"
 #include "TextDescriptions.h"
 
 namespace android {
 
-TimedTextInBandSource::TimedTextInBandSource(const sp<MediaSource>& mediaSource)
+TimedText3GPPSource::TimedText3GPPSource(const sp<MediaSource>& mediaSource)
     : mSource(mediaSource) {
 }
 
-TimedTextInBandSource::~TimedTextInBandSource() {
+TimedText3GPPSource::~TimedText3GPPSource() {
 }
 
-status_t TimedTextInBandSource::read(
+status_t TimedText3GPPSource::read(
         int64_t *timeUs, Parcel *parcel, const MediaSource::ReadOptions *options) {
     MediaBuffer *textBuffer = NULL;
     status_t err = mSource->read(&textBuffer, options);
@@ -60,7 +60,7 @@
 // text style for the string of text. These descriptions are present only
 // if they are needed. This method is used to extract the modifier
 // description and append it at the end of the text.
-status_t TimedTextInBandSource::extractAndAppendLocalDescriptions(
+status_t TimedText3GPPSource::extractAndAppendLocalDescriptions(
         int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel) {
     const void *data;
     size_t size = 0;
@@ -68,51 +68,46 @@
 
     const char *mime;
     CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+    CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
 
-    if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
-        data = textBuffer->data();
-        size = textBuffer->size();
+    data = textBuffer->data();
+    size = textBuffer->size();
 
-        if (size > 0) {
-            parcel->freeData();
-            flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
-            return TextDescriptions::getParcelOfDescriptions(
-                    (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
-        }
-        return OK;
+    if (size > 0) {
+      parcel->freeData();
+      flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+      return TextDescriptions::getParcelOfDescriptions(
+          (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
     }
-    return ERROR_UNSUPPORTED;
+    return OK;
 }
 
 // To extract and send the global text descriptions for all the text samples
 // in the text track or text file.
 // TODO: send error message to application via notifyListener()...?
-status_t TimedTextInBandSource::extractGlobalDescriptions(Parcel *parcel) {
+status_t TimedText3GPPSource::extractGlobalDescriptions(Parcel *parcel) {
     const void *data;
     size_t size = 0;
     int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
 
     const char *mime;
     CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+    CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
 
-    // support 3GPP only for now
-    if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
-        uint32_t type;
-        // get the 'tx3g' box content. This box contains the text descriptions
-        // used to render the text track
-        if (!mSource->getFormat()->findData(
-                kKeyTextFormatData, &type, &data, &size)) {
-            return ERROR_MALFORMED;
-        }
-
-        if (size > 0) {
-            flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
-            return TextDescriptions::getParcelOfDescriptions(
-                    (const uint8_t *)data, size, flag, 0, parcel);
-        }
-        return OK;
+    uint32_t type;
+    // get the 'tx3g' box content. This box contains the text descriptions
+    // used to render the text track
+    if (!mSource->getFormat()->findData(
+            kKeyTextFormatData, &type, &data, &size)) {
+        return ERROR_MALFORMED;
     }
-    return ERROR_UNSUPPORTED;
+
+    if (size > 0) {
+        flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+        return TextDescriptions::getParcelOfDescriptions(
+                (const uint8_t *)data, size, flag, 0, parcel);
+    }
+    return OK;
 }
 
 }  // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.h b/media/libstagefright/timedtext/TimedText3GPPSource.h
similarity index 80%
rename from media/libstagefright/timedtext/TimedTextInBandSource.h
rename to media/libstagefright/timedtext/TimedText3GPPSource.h
index 26e5737..cb7e47c 100644
--- a/media/libstagefright/timedtext/TimedTextInBandSource.h
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef TIMED_TEXT_IN_BAND_SOURCE_H_
-#define TIMED_TEXT_IN_BAND_SOURCE_H_
+#ifndef TIMED_TEXT_3GPP_SOURCE_H_
+#define TIMED_TEXT_3GPP_SOURCE_H_
 
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
@@ -27,9 +27,9 @@
 class MediaBuffer;
 class Parcel;
 
-class TimedTextInBandSource : public TimedTextSource {
+class TimedText3GPPSource : public TimedTextSource {
  public:
-  TimedTextInBandSource(const sp<MediaSource>& mediaSource);
+  TimedText3GPPSource(const sp<MediaSource>& mediaSource);
   virtual status_t start() { return mSource->start(); }
   virtual status_t stop() { return mSource->stop(); }
   virtual status_t read(
@@ -39,7 +39,7 @@
   virtual status_t extractGlobalDescriptions(Parcel *parcel);
 
  protected:
-  virtual ~TimedTextInBandSource();
+  virtual ~TimedText3GPPSource();
 
  private:
   sp<MediaSource> mSource;
@@ -47,9 +47,9 @@
   status_t extractAndAppendLocalDescriptions(
         int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel);
 
-  DISALLOW_EVIL_CONSTRUCTORS(TimedTextInBandSource);
+  DISALLOW_EVIL_CONSTRUCTORS(TimedText3GPPSource);
 };
 
 }  // namespace android
 
-#endif  // TIMED_TEXT_IN_BAND_SOURCE_H_
+#endif  // TIMED_TEXT_3GPP_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index 8c2df88..bf7cbf6 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -18,8 +18,8 @@
 #define LOG_TAG "TimedTextPlayer"
 #include <utils/Log.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/MediaPlayerInterface.h>
 
diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp
index 9efe67c..ffbe1c3 100644
--- a/media/libstagefright/timedtext/TimedTextSource.cpp
+++ b/media/libstagefright/timedtext/TimedTextSource.cpp
@@ -18,12 +18,15 @@
 #define LOG_TAG "TimedTextSource"
 #include <utils/Log.h>
 
+#include <media/stagefright/foundation/ADebug.h>  // CHECK_XX macro
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>  // for MEDIA_MIMETYPE_xxx
 #include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
 
 #include "TimedTextSource.h"
 
-#include "TimedTextInBandSource.h"
+#include "TimedText3GPPSource.h"
 #include "TimedTextSRTSource.h"
 
 namespace android {
@@ -31,7 +34,13 @@
 // static
 sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
         const sp<MediaSource>& mediaSource) {
-    return new TimedTextInBandSource(mediaSource);
+    const char *mime;
+    CHECK(mediaSource->getFormat()->findCString(kKeyMIMEType, &mime));
+    if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
+        return new TimedText3GPPSource(mediaSource);
+    }
+    ALOGE("Unsupported mime type for subtitle. : %s", mime);
+    return NULL;
 }
 
 // static
diff --git a/media/libstagefright/yuv/YUVCanvas.cpp b/media/libstagefright/yuv/YUVCanvas.cpp
index 38aa779..4c9fee8 100644
--- a/media/libstagefright/yuv/YUVCanvas.cpp
+++ b/media/libstagefright/yuv/YUVCanvas.cpp
@@ -17,7 +17,7 @@
 #define LOG_NDEBUG 0
 #define LOG_TAG "YUVCanvas"
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/YUVCanvas.h>
 #include <media/stagefright/YUVImage.h>
 #include <ui/Rect.h>
diff --git a/media/libstagefright/yuv/YUVImage.cpp b/media/libstagefright/yuv/YUVImage.cpp
index 0d67c96..7b9000b 100644
--- a/media/libstagefright/yuv/YUVImage.cpp
+++ b/media/libstagefright/yuv/YUVImage.cpp
@@ -17,9 +17,9 @@
 #define LOG_NDEBUG 0
 #define LOG_TAG "YUVImage"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/YUVImage.h>
 #include <ui/Rect.h>
-#include <media/stagefright/MediaDebug.h>
 
 namespace android {
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
index 6f1959c..d15a535 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
@@ -196,7 +196,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_001
     @LargeTest
     public void testPerformanceAddRemoveVideoItem() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH +
@@ -241,7 +240,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_002
     @LargeTest
     public void testPerformanceAddRemoveImageItem() throws Exception {
         final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
@@ -280,7 +278,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_003
     @LargeTest
     public void testPerformanceAddRemoveTransition() throws Exception {
         final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -360,7 +357,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_004
     @LargeTest
     public void testPerformanceExport() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -541,7 +537,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_005
     @LargeTest
     public void testPerformanceThumbnailVideoItem() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -574,7 +569,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_006
     @LargeTest
     public void testPerformanceOverlayVideoItem() throws Exception {
         final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -629,7 +623,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_007
     @LargeTest
     public void testPerformanceVideoItemProperties() throws Exception {
         final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -688,7 +681,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_008
     @LargeTest
     public void testPerformanceGeneratePreviewWithTransitions()
         throws Exception {
@@ -740,7 +732,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_009
     @LargeTest
     public void testPerformanceWithKenBurn() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH +
@@ -795,7 +786,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_010
     @LargeTest
     public void testPerformanceEffectOverlappingTransition() throws Exception {
         final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -864,7 +854,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_011
     @LargeTest
     public void testPerformanceTransitionWithEffectOverlapping() throws Exception {
         final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -994,7 +983,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_014
     @LargeTest
     public void testPerformanceWithAudioTrack() throws Exception {
         final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -1049,7 +1037,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove PRF_015
     @LargeTest
     public void testPerformanceAddRemoveImageItem640x480() throws Exception {
         final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java
index 4d30784..7784c7b 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java
@@ -167,7 +167,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_001
     @LargeTest
     public void testStressAddRemoveVideoItem() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -241,7 +240,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_002
     @LargeTest
     public void testStressAddRemoveImageItem() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -310,7 +308,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_003
     @LargeTest
     public void testStressAddRemoveTransition() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -428,7 +425,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_004
     @LargeTest
     public void testStressAddRemoveOverlay() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -493,7 +489,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_005
     @LargeTest
     public void testStressAddRemoveEffects() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -590,7 +585,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_006
     @LargeTest
     public void testStressThumbnailVideoItem() throws Exception {
         final String videoItemFileName = INPUT_FILE_PATH
@@ -651,7 +645,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_007
     @LargeTest
     public void testStressMediaProperties() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -747,7 +740,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_008
     @LargeTest
     public void testStressInsertMovieItems() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -759,7 +751,7 @@
             "MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp";
         final String[] loggingInfo = new String[1];
         int i = 0;
-        writeTestCaseHeader("testStressInsertMoveItems");
+        writeTestCaseHeader("testStressInsertMovieItems");
 
         final MediaVideoItem mediaItem1 = new MediaVideoItem(mVideoEditor,
             "m1", VideoItemFileName1, renderingMode);
@@ -801,7 +793,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_009
     @LargeTest
     public void testStressLoadAndSave() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -916,7 +907,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_010
     @LargeTest
     public void testStressMultipleExport() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -1007,7 +997,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_011
     @LargeTest
     public void testStressOverlayTransKenBurn() throws Exception {
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -1094,7 +1083,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_012
     @LargeTest
     public void testStressAudioTrackVideo() throws Exception {
         final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -1147,7 +1135,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_013
     @LargeTest
     public void testStressStoryBoard() throws Exception {
         final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -1237,7 +1224,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_014
     @LargeTest
     public void testStressAudioTrackOnly() throws Exception {
 
@@ -1267,7 +1253,6 @@
      *
      * @throws Exception
      */
-    // TODO : remove TC_STR_016  -- New Test Case
     @LargeTest
     public void testStressThumbnailImageItem() throws Exception {
         final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg";
diff --git a/media/tests/omxjpegdecoder/Android.mk b/media/tests/omxjpegdecoder/Android.mk
index 81c6167..025a131 100644
--- a/media/tests/omxjpegdecoder/Android.mk
+++ b/media/tests/omxjpegdecoder/Android.mk
@@ -26,6 +26,7 @@
     libcutils \
     libskia \
     libstagefright \
+    libstagefright_foundation \
     libbinder \
     libutils \
     libjpeg
diff --git a/media/tests/omxjpegdecoder/SkOmxPixelRef.cpp b/media/tests/omxjpegdecoder/SkOmxPixelRef.cpp
index dfdf676..a25e854 100644
--- a/media/tests/omxjpegdecoder/SkOmxPixelRef.cpp
+++ b/media/tests/omxjpegdecoder/SkOmxPixelRef.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <SkBitmap.h>
 
 #include "SkOmxPixelRef.h"
@@ -32,7 +32,7 @@
 
 SkOmxPixelRef::~SkOmxPixelRef() {
     mBuffer->release();
-    CHECK_EQ(mDecoder->stop(), OK);
+    CHECK_EQ(mDecoder->stop(), (status_t)OK);
     SkSafeUnref(mCTable);
 }
 
diff --git a/media/tests/omxjpegdecoder/StreamSource.cpp b/media/tests/omxjpegdecoder/StreamSource.cpp
index 5f44203..f764121a 100644
--- a/media/tests/omxjpegdecoder/StreamSource.cpp
+++ b/media/tests/omxjpegdecoder/StreamSource.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 
 #include "StreamSource.h"
 
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
index 42df66c..6424744 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
@@ -25,7 +25,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <media/IMediaPlayerService.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXClient.h>
@@ -89,7 +89,7 @@
 
 OmxJpegImageDecoder::OmxJpegImageDecoder() {
     status_t err = mClient.connect();
-    CHECK_EQ(err, OK);
+    CHECK_EQ(err, (status_t)OK);
 }
 
 OmxJpegImageDecoder::~OmxJpegImageDecoder() {
@@ -152,7 +152,7 @@
     int64_t duration = getNowUs() - startTime;
 
     if (err != OK) {
-        CHECK_EQ(buffer, NULL);
+        CHECK(buffer == NULL);
     }
     printf("Duration in decoder->read(): %.1f (msecs). \n",
                 duration / 1E3 );
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 9940442f..e2c99ee 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
+    libandroidfw \
     libutils \
     libbinder \
     libui \
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index f5db57c..01db1d3 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -18,9 +18,9 @@
 #include <utils/Log.h>
 
 #include <android/asset_manager_jni.h>
-#include <utils/AssetManager.h>
-#include <utils/AssetDir.h>
-#include <utils/Asset.h>
+#include <androidfw/Asset.h>
+#include <androidfw/AssetDir.h>
+#include <androidfw/AssetManager.h>
 #include <utils/threads.h>
 
 #include "jni.h"
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
index 687924b..7eb51dd 100644
--- a/native/android/configuration.cpp
+++ b/native/android/configuration.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "Configuration"
 #include <utils/Log.h>
 
-#include <utils/AssetManager.h>
+#include <androidfw/AssetManager.h>
 
 #include <android_runtime/android_content_res_Configuration.h>
 
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 91671c3..6eb2990 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -18,8 +18,8 @@
 #include <utils/Log.h>
 
 #include <android/input.h>
-#include <ui/Input.h>
-#include <ui/InputTransport.h>
+#include <androidfw/Input.h>
+#include <androidfw/InputTransport.h>
 #include <utils/Looper.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
diff --git a/native/android/obb.cpp b/native/android/obb.cpp
index e0cb1a6..e990024 100644
--- a/native/android/obb.cpp
+++ b/native/android/obb.cpp
@@ -18,8 +18,8 @@
 
 #include <android/obb.h>
 
+#include <androidfw/ObbFile.h>
 #include <utils/Log.h>
-#include <utils/ObbFile.h>
 
 using namespace android;
 
diff --git a/opengl/java/android/opengl/Group.java b/opengl/java/android/opengl/Group.java
deleted file mode 100644
index 1ef2953..0000000
--- a/opengl/java/android/opengl/Group.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 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 android.opengl;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.ShortBuffer;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Iterator;
-import javax.microedition.khronos.opengles.*;
-
-class MaterialIndices {
-
-    private Material material = null;
-    private ShortBuffer indexBuffer = null;
-
-    public MaterialIndices(Material material, ShortBuffer indexBuffer) {
-        this.material = material;
-        this.indexBuffer = indexBuffer;
-    }
-
-    public Material getMaterial() {
-        return material;
-    }
-
-    public ShortBuffer getIndexBuffer() {
-        return indexBuffer;
-    }
-}
-
-/**
- * {@hide}
- */
-public class Group {
-
-    private Object3D parent;
-    private String name;
-
-    private List<MaterialIndices> materialIndices =
-        new ArrayList<MaterialIndices>();
-
-    public Group(Object3D parent) {
-        this.parent = parent;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void load(DataInputStream dis) throws IOException {
-        dis.readInt(); // name length
-        this.name = dis.readUTF();
-
-        int numMaterials = dis.readInt();
-
-        for (int i = 0; i < numMaterials; i++) {
-            dis.readInt(); // material name length
-            String matName = dis.readUTF();
-            Material material = parent.getMaterial(matName);
-
-            int numIndices = dis.readInt();
-            byte[] indicesBytes = new byte[numIndices * 2];
-            dis.readFully(indicesBytes);
-
-            // Swap bytes from network to native order if necessary
-            if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
-                int idx = 0;
-                for (int j = 0; j < numIndices; j++) {
-                    byte b0 = indicesBytes[idx];
-                    byte b1 = indicesBytes[idx + 1];
-                    indicesBytes[idx] = b1;
-                    indicesBytes[idx + 1] = b0;
-                    idx += 2;
-                }
-            }
-
-            ByteBuffer ibb = ByteBuffer.allocateDirect(2*numIndices);
-            ibb.order(ByteOrder.nativeOrder());
-            ibb.put(indicesBytes);
-            ibb.position(0);
-
-            ShortBuffer sb = ibb.asShortBuffer();
-            materialIndices.add(new MaterialIndices(material, sb));
-        }
-    }
-
-    public int getNumTriangles() {
-        int numTriangles = 0;
-        Iterator<MaterialIndices> iter = materialIndices.iterator();
-        while (iter.hasNext()) {
-            MaterialIndices matIdx = iter.next();
-            ShortBuffer indexBuffer = matIdx.getIndexBuffer();
-            numTriangles += indexBuffer.capacity()/3;
-        }
-        return numTriangles;
-    }
-
-    public void draw(GL10 gl) {
-        gl.glDisableClientState(gl.GL_COLOR_ARRAY);
-
-        gl.glVertexPointer(3, gl.GL_FIXED, 0, parent.getVertexBuffer());
-        gl.glEnableClientState(gl.GL_VERTEX_ARRAY);
-
-        gl.glNormalPointer(gl.GL_FIXED, 0, parent.getNormalBuffer());
-        gl.glEnableClientState(gl.GL_NORMAL_ARRAY);
-
-        if (parent.hasTexcoords()) {
-            gl.glTexCoordPointer(2, gl.GL_FIXED, 0, parent.getTexcoordBuffer());
-            gl.glEnableClientState(gl.GL_TEXTURE_COORD_ARRAY);
-            gl.glEnable(gl.GL_TEXTURE_2D);
-        } else {
-            gl.glDisable(gl.GL_TEXTURE_2D);
-        }
-
-        Iterator<MaterialIndices> iter = materialIndices.iterator();
-        while (iter.hasNext()) {
-            MaterialIndices matIdx = iter.next();
-            ShortBuffer indexBuffer = matIdx.getIndexBuffer();
-            Material mat = matIdx.getMaterial();
-            mat.setMaterialParameters(gl);
-            if (parent.hasTexcoords() && mat.getMap_Kd().length() > 0) {
-                Texture texture = parent.getTexture(mat.getMap_Kd());
-                texture.setTextureParameters(gl);
-            }
-
-            gl.glDrawElements(gl.GL_TRIANGLES,
-                    indexBuffer.capacity(),
-                    gl.GL_UNSIGNED_SHORT,
-                    indexBuffer);
-        }
-    }
-
-    public String toString() {
-        return "Group[" +
-        "name=" + name +
-        "]";
-    }
-}
diff --git a/opengl/java/android/opengl/Material.java b/opengl/java/android/opengl/Material.java
deleted file mode 100644
index 60a3e72..0000000
--- a/opengl/java/android/opengl/Material.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 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 android.opengl;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-import javax.microedition.khronos.opengles.GL10;
-
-/**
- * {@hide}
- */
-public class Material {
-
-    private Object3D parent;
-    private String name;
-    private String map_kd;
-    private float[] ka = new float[4];
-    private float[] kd = new float[4];
-    private float[] ks = new float[4];
-    private float ns;
-    private int illum;
-    private float d;
-
-    private static float[] black = { 0.0f, 0.0f, 0.0f, 1.0f };
-
-    public Material(Object3D parent) {
-        this.parent = parent;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getMap_Kd() {
-        return map_kd;
-    }
-
-    public void setMaterialParameters(GL10 gl) {
-        gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, kd, 0);
-        gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_DIFFUSE, kd, 0);
-        gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_SPECULAR, ks, 0);
-        gl.glMaterialf(gl.GL_FRONT_AND_BACK, gl.GL_SHININESS,
-                Math.min(Math.max(ns, 0), 128));
-
-//      if (illum == 0) {
-//      gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, kd, 0);
-//      } else {
-//      gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, ka, 0);
-//      gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_DIFFUSE, kd, 0);
-//      }
-
-//      if (illum > 1) {
-//      gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_SPECULAR, ks, 0);
-//      gl.glMaterialf(gl.GL_FRONT_AND_BACK, gl.GL_SHININESS,
-//      Math.min(Math.max(ns, 0), 128));
-//      } else {
-//      gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_SPECULAR, black, 0);
-//      }
-    }
-
-    public void load(DataInputStream dis) throws IOException {
-        dis.readInt(); // name length
-        this.name = dis.readUTF();
-
-        dis.readInt(); // map_kdLength
-        this.map_kd = dis.readUTF();
-
-        if (parent.hasTexcoords() && map_kd.length() > 0) {
-            parent.loadTexture(map_kd);
-        }
-
-        this.ka[0] = dis.readFloat();
-        this.ka[1] = dis.readFloat();
-        this.ka[2] = dis.readFloat();
-        this.ka[3] = dis.readFloat();
-
-        this.kd[0] = dis.readFloat();
-        this.kd[1] = dis.readFloat();
-        this.kd[2] = dis.readFloat();
-        this.kd[3] = dis.readFloat();
-
-        this.ks[0] = dis.readFloat();
-        this.ks[1] = dis.readFloat();
-        this.ks[2] = dis.readFloat();
-        this.ks[3] = dis.readFloat();
-
-        this.ns = dis.readFloat();
-        this.illum = dis.readInt();
-        this.d = dis.readFloat();
-    }
-
-    public String toString() {
-        return "Material[" +
-        "name=\"" + name + "\"," +
-        "ka={" + ka[0] + "," + ka[1] + "," + ka[2] + "}," +
-        "kd={" + kd[0] + "," + kd[1] + "," + kd[2] + "}," +
-        "ks={" + ks[0] + "," + ks[1] + "," + ks[2] + "}," +
-        "ns=" + ns + "," +
-        "map_kd=\"" + 
-        (map_kd == null ? "" : map_kd) +
-        "\"," +
-        "illum=" + illum + "," +
-        "d=" + d +
-        "]";
-    }
-}
diff --git a/opengl/java/android/opengl/Object3D.java b/opengl/java/android/opengl/Object3D.java
deleted file mode 100644
index 340c6a7..0000000
--- a/opengl/java/android/opengl/Object3D.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 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 android.opengl;
-
-import java.io.BufferedReader;
-import java.io.DataInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.IntBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import javax.microedition.khronos.opengles.*;
-
-/**
- * {@hide}
- */
-public abstract class Object3D {
-
-    private boolean mHasTexcoords = false;
-
-    private float mBoundsMinX = Float.MAX_VALUE;
-    private float mBoundsMaxX = Float.MIN_VALUE;
-    private float mBoundsMinY = Float.MAX_VALUE;
-    private float mBoundsMaxY = Float.MIN_VALUE;
-    private float mBoundsMinZ = Float.MAX_VALUE;
-    private float mBoundsMaxZ = Float.MIN_VALUE;
-
-    private IntBuffer mVertexBuffer;
-    private IntBuffer mNormalBuffer;
-    private IntBuffer mTexcoordBuffer;
-
-    // All groups, by name
-    private Map<String, Group> mGroups;
-
-    // All materials, by name
-    private Map<String, Material> mMaterials;
-
-    // All texture maps, by name
-    private Map<String, Texture> mTextures;
-
-    public Object3D() {
-        reset();
-    }
-
-    /**
-     * Override this method with an implementation that contructs
-     * and InputStream from the given filename.  For example, if the
-     * source files are to be retrieved using an AssetManager,
-     * the implementation would use AssetManager.load() to
-     * get the input stream.
-     */
-    public abstract InputStream readFile(String filename) throws IOException;
-
-    private void reset() {
-        mVertexBuffer = mNormalBuffer = mTexcoordBuffer = null;
-
-        mGroups = new HashMap<String,Group>();
-        mMaterials = new HashMap<String,Material>();
-        mTextures = new HashMap<String,Texture>();
-    }
-
-    public Material getMaterial(String name) {
-        Material mat = mMaterials.get(name);
-        return mat;
-    }
-
-    public Texture getTexture(String name) {
-        return mTextures.get(name);
-    }
-
-    public IntBuffer getVertexBuffer() {
-        return mVertexBuffer;
-    }
-
-    public IntBuffer getNormalBuffer() {
-        return mNormalBuffer;
-    }
-
-    public IntBuffer getTexcoordBuffer() {
-        return mTexcoordBuffer;
-    }
-
-    public int getNumTriangles() {
-        int numTriangles = 0;
-        Iterator<Group> iter = mGroups.values().iterator();
-        while (iter.hasNext()) {
-            numTriangles += iter.next().getNumTriangles();
-        }
-        return numTriangles;
-    }
-
-    public boolean hasTexcoords() {
-        return mHasTexcoords;
-    }
-
-    public float getBoundsMinX() {
-        return mBoundsMinX;
-    }
-
-    public float getBoundsMaxX() {
-        return mBoundsMaxX;
-    }
-
-    public float getBoundsMinY() {
-        return mBoundsMinY;
-    }
-
-    public float getBoundsMaxY() {
-        return mBoundsMaxY;
-    }
-
-    public float getBoundsMinZ() {
-        return mBoundsMinZ;
-    }
-
-    public float getBoundsMaxZ() {
-        return mBoundsMaxZ;
-    }
-
-    public void loadTexture(String name) throws IOException {
-        InputStream is = readFile(name + ".raw");
-        Texture texture = new Texture(is);
-        mTextures.put(name, texture);
-    }
-
-    private static void verifyByte(DataInputStream dis, int b) 
-    throws IOException {
-        int x = dis.read() & 0xff;
-        if (x != b) {
-            throw new RuntimeException("Bad byte: " +
-                    x +
-                    " (expected " + b + ")");
-        }
-    }
-
-    public void load(String filename) throws IOException {
-        reset();
-
-        DataInputStream dis = new DataInputStream(readFile(filename));
-        verifyByte(dis, 'g' + 128);
-        verifyByte(dis, 'l');
-        verifyByte(dis, 'e');
-        verifyByte(dis, 's');
-
-        int numTuples = dis.readInt();
-
-        this.mBoundsMinX = dis.readFloat();
-        this.mBoundsMaxX = dis.readFloat();
-        this.mBoundsMinY = dis.readFloat();
-        this.mBoundsMaxY = dis.readFloat();
-        this.mBoundsMinZ = dis.readFloat();
-        this.mBoundsMaxZ = dis.readFloat();
-
-        this.mHasTexcoords = dis.readInt() == 1;
-
-        int intsPerTuple = mHasTexcoords ? 8 : 6;
-        int numInts = numTuples*intsPerTuple;
-
-        int len = 4*numTuples*(mHasTexcoords ? 8 : 6);
-
-        byte[] tmp = new byte[len];
-        int tidx = 0;
-        while (tidx < len) {
-            tidx += dis.read(tmp, tidx, len - tidx);
-        }
-        if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
-            for (int i = 0; i < len; i += 4) {
-                byte tmp0 = tmp[i];
-                byte tmp1 = tmp[i + 1];
-                byte tmp2 = tmp[i + 2];
-                byte tmp3 = tmp[i + 3];
-                tmp[i] = tmp3;
-                tmp[i + 1] = tmp2;
-                tmp[i + 2] = tmp1;
-                tmp[i + 3] = tmp0;
-            }
-        }
-
-        ByteBuffer allbb = ByteBuffer.allocateDirect(len);
-        allbb.order(ByteOrder.nativeOrder());
-        allbb.put(tmp);
-
-        allbb.position(0);
-        allbb.limit(4*3*numTuples);
-        ByteBuffer vbb = allbb.slice();
-        this.mVertexBuffer = vbb.asIntBuffer();
-        mVertexBuffer.position(0);
-
-        if (mHasTexcoords) {
-            allbb.position(allbb.limit());
-            allbb.limit(allbb.position() + 4*2*numTuples);
-            ByteBuffer tbb = allbb.slice();
-            this.mTexcoordBuffer = tbb.asIntBuffer();
-            mTexcoordBuffer.position(0);
-        }
-
-        allbb.position(allbb.limit());
-        allbb.limit(allbb.position() + 4*3*numTuples);
-        ByteBuffer nbb = allbb.slice();
-        this.mNormalBuffer = nbb.asIntBuffer();
-        mNormalBuffer.position(0);
-
-        int numMaterials = dis.readInt();
-        for (int i = 0; i < numMaterials; i++) {
-            Material mat = new Material(this);
-            mat.load(dis);
-            mMaterials.put(mat.getName(), mat);
-        }
-
-        int numGroups = dis.readInt();
-        for (int i = 0; i < numGroups; i++) {
-            Group g = new Group(this);
-            g.load(dis);
-            mGroups.put(g.getName(), g);
-        }
-    }
-
-    public void draw(GL10 gl) {
-        Iterator<Group> iter = mGroups.values().iterator();
-        while (iter.hasNext()) {
-            iter.next().draw(gl);
-        }
-    }
-}
-
diff --git a/opengl/java/android/opengl/Texture.java b/opengl/java/android/opengl/Texture.java
deleted file mode 100644
index dcd894d..0000000
--- a/opengl/java/android/opengl/Texture.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 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 android.opengl;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import javax.microedition.khronos.opengles.GL10;
-
-import android.content.res.AssetManager;
-
-/**
- * {@hide}
- */
-public class Texture {
-
-    private int width, height, bpp;
-    private ByteBuffer data;
-    private int name = -1;
-
-    // Texture maps have the following format.  All integers
-    // are 16 bits, high byte first.  Pixels are in 5/6/5
-    // RGB format, low byte first.
-    //
-    // width
-    // height
-    // pixel (0, 0)
-    // pixel (1, 0)
-    // ...
-    // pixel (width - 1, height - 1)
-
-    private int readInt16(InputStream is) throws IOException {
-        return is.read() | (is.read() << 8);
-    }
-
-    public Texture(InputStream is) throws IOException {
-        this.width  = readInt16(is);
-        this.height  = readInt16(is);
-        this.bpp = 2;
-
-        int npixels = width*height;
-        int nbytes = npixels*bpp;
-        byte[] arr = new byte[nbytes];
-
-        int idx = 0;
-        while (idx < nbytes) {
-            int nread = is.read(arr, idx, nbytes - idx);
-            idx += nread;
-        }
-
-        if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
-            // Swap pairs of bytes on big-endian platforms
-            for (int i = 0; i < npixels; i++) {
-                int j = i*2;
-                int k = j + 1;
-
-                byte tmp = arr[j];
-                arr[j] = arr[k];
-                arr[k] = tmp;
-            }
-        }
-
-        this.data = ByteBuffer.allocateDirect(arr.length);
-        this.data.order(ByteOrder.nativeOrder());
-        data.put(arr);
-        data.position(0);
-    }
-
-    private int loadTexture(GL10 gl,
-            int textureUnit,
-            int minFilter, int magFilter,
-            int wrapS, int wrapT,
-            int mode,
-            int width, int height,
-            int dataType,
-            Buffer data) {
-        int[] texture = new int[1];
-        gl.glGenTextures(1, texture, 0);
-
-        gl.glEnable(gl.GL_TEXTURE_2D);
-        gl.glClientActiveTexture(textureUnit);
-        gl.glBindTexture(gl.GL_TEXTURE_2D, texture[0]);
-        gl.glTexParameterf(gl.GL_TEXTURE_2D,
-                gl.GL_TEXTURE_MIN_FILTER,
-                minFilter);
-        gl.glTexParameterf(gl.GL_TEXTURE_2D,
-                gl.GL_TEXTURE_MAG_FILTER,
-                magFilter);
-        gl.glTexParameterf(gl.GL_TEXTURE_2D,
-                gl.GL_TEXTURE_WRAP_S,
-                wrapS);
-        gl.glTexParameterf(gl.GL_TEXTURE_2D,
-                gl.GL_TEXTURE_WRAP_T,
-                wrapT);
-        gl.glTexEnvf(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, mode);
-
-        gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB,
-                width, height,
-                0, gl.GL_RGB, dataType,
-                data);
-
-        return texture[0];
-    }
-
-    public void setTextureParameters(GL10 gl) {
-        if (name < 0) {
-            name = loadTexture(gl,
-                    gl.GL_TEXTURE0,
-                    gl.GL_NEAREST, gl.GL_NEAREST,
-                    gl.GL_REPEAT, gl.GL_REPEAT,
-                    gl.GL_MODULATE,
-                    width, height,
-                    gl.GL_UNSIGNED_SHORT_5_6_5,
-                    data);
-        }
-
-        gl.glBindTexture(gl.GL_TEXTURE_2D, name);
-    }
-}
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 06be2ef..0b1016c 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -118,12 +118,6 @@
 
 // ----------------------------------------------------------------------------
 
-Loader::entry_t::entry_t(int dpy, int impl, const char* tag)
-    : dpy(dpy), impl(impl), tag(tag) {
-}
-
-// ----------------------------------------------------------------------------
-
 Loader::Loader()
 {
     char line[256];
@@ -131,8 +125,9 @@
 
     /* Special case for GLES emulation */
     if (checkGlesEmulationStatus() == 0) {
-        ALOGD("Emulator without GPU support detected. Fallback to software renderer.");
-        gConfig.add( entry_t(0, 0, "android") );
+        ALOGD("Emulator without GPU support detected. "
+              "Fallback to software renderer.");
+        mDriverTag.setTo("android");
         return;
     }
 
@@ -141,14 +136,16 @@
     if (cfg == NULL) {
         // default config
         ALOGD("egl.cfg not found, using default config");
-        gConfig.add( entry_t(0, 0, "android") );
+        mDriverTag.setTo("android");
     } else {
         while (fgets(line, 256, cfg)) {
-            int dpy;
-            int impl;
+            int dpy, impl;
             if (sscanf(line, "%u %u %s", &dpy, &impl, tag) == 3) {
                 //ALOGD(">>> %u %u %s", dpy, impl, tag);
-                gConfig.add( entry_t(dpy, impl, tag) );
+                // We only load the h/w accelerated implementation
+                if (tag != String8("android")) {
+                    mDriverTag = tag;
+                }
             }
         }
         fclose(cfg);
@@ -160,30 +157,12 @@
     GLTrace_stop();
 }
 
-const char* Loader::getTag(int dpy, int impl)
+void* Loader::open(egl_connection_t* cnx)
 {
-    const Vector<entry_t>& cfgs(gConfig);    
-    const size_t c = cfgs.size();
-    for (size_t i=0 ; i<c ; i++) {
-        if (dpy == cfgs[i].dpy)
-            if (impl == cfgs[i].impl)
-                return cfgs[i].tag.string();
-    }
-    return 0;
-}
-
-void* Loader::open(EGLNativeDisplayType display, int impl, egl_connection_t* cnx)
-{
-    /*
-     * TODO: if we don't find display/0, then use 0/0
-     * (0/0 should always work)
-     */
-    
     void* dso;
-    int index = int(display);
     driver_t* hnd = 0;
     
-    char const* tag = getTag(index, impl);
+    char const* tag = mDriverTag.string();
     if (tag) {
         dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2);
         if (dso) {
@@ -193,16 +172,14 @@
             dso = load_driver("EGL", tag, cnx, EGL);
             if (dso) {
                 hnd = new driver_t(dso);
-
                 // TODO: make this more automated
                 hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM );
-
-                hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 );
+                hnd->set( load_driver("GLESv2",    tag, cnx, GLESv2),    GLESv2 );
             }
         }
     }
 
-    LOG_FATAL_IF(!index && !impl && !hnd, 
+    LOG_FATAL_IF(!index && !hnd,
             "couldn't find the default OpenGL ES implementation "
             "for default display");
     
@@ -221,7 +198,7 @@
         __eglMustCastToProperFunctionPointerType* curr, 
         getProcAddressType getProcAddress) 
 {
-    const size_t SIZE = 256;
+    const ssize_t SIZE = 256;
     char scrap[SIZE];
     while (*api) {
         char const * name = *api;
@@ -326,14 +303,14 @@
     if (mask & GLESv1_CM) {
         init_api(dso, gl_names,
             (__eglMustCastToProperFunctionPointerType*)
-                &cnx->hooks[GLESv1_INDEX]->gl,
+                &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,
             getProcAddress);
     }
 
     if (mask & GLESv2) {
       init_api(dso, gl_names,
             (__eglMustCastToProperFunctionPointerType*)
-                &cnx->hooks[GLESv2_INDEX]->gl,
+                &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
             getProcAddress);
     }
     
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 580d6e4..30773cb 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -24,7 +24,6 @@
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 #include <utils/String8.h>
-#include <utils/Vector.h>
 
 #include <EGL/egl.h>
 
@@ -53,23 +52,13 @@
         void* dso[3];
     };
     
-    struct entry_t {
-        entry_t() { }
-        entry_t(int dpy, int impl, const char* tag);
-        int dpy;
-        int impl;
-        String8 tag;
-    };
-
-    Vector<entry_t> gConfig;    
+    String8 mDriverTag;
     getProcAddressType getProcAddress;
     
-    const char* getTag(int dpy, int impl);
-
 public:
     ~Loader();
     
-    void* open(EGLNativeDisplayType display, int impl, egl_connection_t* cnx);
+    void* open(egl_connection_t* cnx);
     status_t close(void* driver);
     
 private:
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 83933e5..eec5ce1 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -48,8 +48,8 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
-gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
+egl_connection_t gEGLImpl;
+gl_hooks_t gHooks[2];
 gl_hooks_t gHooksNoContext;
 pthread_key_t gGLWrapperKey = -1;
 
@@ -187,16 +187,13 @@
     return dp;
 }
 
-egl_connection_t* validate_display_config(EGLDisplay dpy, EGLConfig config,
+egl_connection_t* validate_display_config(EGLDisplay dpy, EGLConfig,
         egl_display_t const*& dp) {
     dp = validate_display(dpy);
     if (!dp)
         return (egl_connection_t*) NULL;
 
-    if (intptr_t(config) >= dp->numTotalConfigs) {
-        return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
-    }
-    egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(config)].impl];
+    egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso == 0) {
         return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
     }
@@ -205,34 +202,6 @@
 
 // ----------------------------------------------------------------------------
 
-EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image)
-{
-    EGLContext context = egl_tls_t::getContext();
-    if (context == EGL_NO_CONTEXT || image == EGL_NO_IMAGE_KHR)
-        return EGL_NO_IMAGE_KHR;
-
-    egl_context_t const * const c = get_context(context);
-    if (c == NULL) // this should never happen, by construction
-        return EGL_NO_IMAGE_KHR;
-
-    egl_display_t* display = egl_display_t::get(c->dpy);
-    if (display == NULL) // this should never happen, by construction
-        return EGL_NO_IMAGE_KHR;
-
-    ImageRef _i(display, image);
-    if (!_i.get())
-        return EGL_NO_IMAGE_KHR;
-
-    // here we don't validate the context because if it's been marked for
-    // termination, this call should still succeed since it's internal to
-    // EGL.
-
-    egl_image_t const * const i = get_image(image);
-    return i->images[c->impl];
-}
-
-// ----------------------------------------------------------------------------
-
 const GLubyte * egl_get_string_for_current_context(GLenum name) {
     // NOTE: returning NULL here will fall-back to the default
     // implementation.
@@ -266,34 +235,17 @@
     // get our driver loader
     Loader& loader(Loader::getInstance());
 
-    // dynamically load all our EGL implementations
-    egl_connection_t* cnx;
-
-    cnx = &gEGLImpl[IMPL_SOFTWARE];
+    // dynamically load our EGL implementation
+    egl_connection_t* cnx = &gEGLImpl;
     if (cnx->dso == 0) {
-        cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_SOFTWARE];
-        cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_SOFTWARE];
-        cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 0, cnx);
+        cnx->hooks[egl_connection_t::GLESv1_INDEX] =
+                &gHooks[egl_connection_t::GLESv1_INDEX];
+        cnx->hooks[egl_connection_t::GLESv2_INDEX] =
+                &gHooks[egl_connection_t::GLESv2_INDEX];
+        cnx->dso = loader.open(cnx);
     }
 
-    cnx = &gEGLImpl[IMPL_HARDWARE];
-    if (cnx->dso == 0) {
-        char value[PROPERTY_VALUE_MAX];
-        property_get("debug.egl.hw", value, "1");
-        if (atoi(value) != 0) {
-            cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_HARDWARE];
-            cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_HARDWARE];
-            cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 1, cnx);
-        } else {
-            ALOGD("3D hardware acceleration is disabled");
-        }
-    }
-
-    if (!gEGLImpl[IMPL_SOFTWARE].dso && !gEGLImpl[IMPL_HARDWARE].dso) {
-        return EGL_FALSE;
-    }
-
-    return EGL_TRUE;
+    return cnx->dso ? EGL_TRUE : EGL_FALSE;
 }
 
 static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 73aab26..a5dc832 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -44,6 +44,7 @@
 #include "egl_impl.h"
 #include "egl_object.h"
 #include "egl_tls.h"
+#include "egldefs.h"
 
 using namespace android;
 
@@ -88,24 +89,6 @@
 
 // ----------------------------------------------------------------------------
 
-template<typename T>
-static __attribute__((noinline))
-int binarySearch(T const sortedArray[], int first, int last, T key) {
-    while (first <= last) {
-        int mid = (first + last) / 2;
-        if (sortedArray[mid] < key) {
-            first = mid + 1;
-        } else if (key < sortedArray[mid]) {
-            last = mid - 1;
-        } else {
-            return mid;
-        }
-    }
-    return -1;
-}
-
-// ----------------------------------------------------------------------------
-
 namespace android {
 extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
 extern EGLBoolean egl_init_drivers();
@@ -183,21 +166,20 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    GLint numConfigs = dp->numTotalConfigs;
-    if (!configs) {
-        *num_config = numConfigs;
-        return EGL_TRUE;
+    if (num_config==0) {
+        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
     }
 
-    GLint n = 0;
-    for (intptr_t i=0 ; i<dp->numTotalConfigs && config_size ; i++) {
-        *configs++ = EGLConfig(i);
-        config_size--;
-        n++;
+    EGLBoolean res = EGL_FALSE;
+    *num_config = 0;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        res = cnx->egl.eglGetConfigs(
+                dp->disp.dpy, configs, config_size, num_config);
     }
-    
-    *num_config = n;
-    return EGL_TRUE;
+
+    return res;
 }
 
 EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
@@ -213,105 +195,13 @@
         return setError(EGL_BAD_PARAMETER, EGL_FALSE);
     }
 
-    EGLint n;
     EGLBoolean res = EGL_FALSE;
     *num_config = 0;
 
-    
-    // It is unfortunate, but we need to remap the EGL_CONFIG_IDs, 
-    // to do this, we have to go through the attrib_list array once
-    // to figure out both its size and if it contains an EGL_CONFIG_ID
-    // key. If so, the full array is copied and patched.
-    // NOTE: we assume that there can be only one occurrence
-    // of EGL_CONFIG_ID.
-    
-    EGLint patch_index = -1;
-    GLint attr;
-    size_t size = 0;
-    if (attrib_list) {
-        while ((attr=attrib_list[size]) != EGL_NONE) {
-            if (attr == EGL_CONFIG_ID)
-                patch_index = size;
-            size += 2;
-        }
-    }
-    if (patch_index >= 0) {
-        size += 2; // we need copy the sentinel as well
-        EGLint* new_list = (EGLint*)malloc(size*sizeof(EGLint));
-        if (new_list == 0)
-            return setError(EGL_BAD_ALLOC, EGL_FALSE);
-        memcpy(new_list, attrib_list, size*sizeof(EGLint));
-
-        // patch the requested EGL_CONFIG_ID
-        bool found = false;
-        EGLConfig ourConfig(0);
-        EGLint& configId(new_list[patch_index+1]);
-        for (intptr_t i=0 ; i<dp->numTotalConfigs ; i++) {
-            if (dp->configs[i].configId == configId) {
-                ourConfig = EGLConfig(i);
-                configId = dp->configs[i].implConfigId;
-                found = true;
-                break;
-            }
-        }
-
-        egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(ourConfig)].impl];
-        if (found && cnx->dso) {
-            // and switch to the new list
-            attrib_list = const_cast<const EGLint *>(new_list);
-
-            // At this point, the only configuration that can match is
-            // dp->configs[i][index], however, we don't know if it would be
-            // rejected because of the other attributes, so we do have to call
-            // cnx->egl.eglChooseConfig() -- but we don't have to loop
-            // through all the EGLimpl[].
-            // We also know we can only get a single config back, and we know
-            // which one.
-
-            res = cnx->egl.eglChooseConfig(
-                    dp->disp[ dp->configs[intptr_t(ourConfig)].impl ].dpy,
-                    attrib_list, configs, config_size, &n);
-            if (res && n>0) {
-                // n has to be 0 or 1, by construction, and we already know
-                // which config it will return (since there can be only one).
-                if (configs) {
-                    configs[0] = ourConfig;
-                }
-                *num_config = 1;
-            }
-        }
-
-        free(const_cast<EGLint *>(attrib_list));
-        return res;
-    }
-
-
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglChooseConfig(
-                    dp->disp[i].dpy, attrib_list, configs, config_size, &n)) {
-                if (configs) {
-                    // now we need to convert these client EGLConfig to our
-                    // internal EGLConfig format.
-                    // This is done in O(n Log(n)) time.
-                    for (int j=0 ; j<n ; j++) {
-                        egl_config_t key(i, configs[j]);
-                        intptr_t index = binarySearch<egl_config_t>(
-                                dp->configs, 0, dp->numTotalConfigs, key);
-                        if (index >= 0) {
-                            configs[j] = EGLConfig(index);
-                        } else {
-                            return setError(EGL_BAD_CONFIG, EGL_FALSE);
-                        }
-                    }
-                    configs += n;
-                    config_size -= n;
-                }
-                *num_config += n;
-                res = EGL_TRUE;
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        res = cnx->egl.eglChooseConfig(
+                dp->disp.dpy, attrib_list, configs, config_size, num_config);
     }
     return res;
 }
@@ -325,13 +215,8 @@
     egl_connection_t* cnx = validate_display_config(dpy, config, dp);
     if (!cnx) return EGL_FALSE;
     
-    if (attribute == EGL_CONFIG_ID) {
-        *value = dp->configs[intptr_t(config)].configId;
-        return EGL_TRUE;
-    }
     return cnx->egl.eglGetConfigAttrib(
-            dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-            dp->configs[intptr_t(config)].config, attribute, value);
+            dp->disp.dpy, config, attribute, value);
 }
 
 // ----------------------------------------------------------------------------
@@ -347,8 +232,7 @@
     egl_display_t const* dp = 0;
     egl_connection_t* cnx = validate_display_config(dpy, config, dp);
     if (cnx) {
-        EGLDisplay iDpy = dp->disp[ dp->configs[intptr_t(config)].impl ].dpy;
-        EGLConfig iConfig = dp->configs[intptr_t(config)].config;
+        EGLDisplay iDpy = dp->disp.dpy;
         EGLint format;
 
         if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) {
@@ -359,7 +243,7 @@
 
         // set the native window's buffers format to match this config
         if (cnx->egl.eglGetConfigAttrib(iDpy,
-                iConfig, EGL_NATIVE_VISUAL_ID, &format)) {
+                config, EGL_NATIVE_VISUAL_ID, &format)) {
             if (format != 0) {
                 int err = native_window_set_buffers_format(window, format);
                 if (err != 0) {
@@ -377,10 +261,9 @@
         anw->setSwapInterval(anw, 1);
 
         EGLSurface surface = cnx->egl.eglCreateWindowSurface(
-                iDpy, iConfig, window, attrib_list);
+                iDpy, config, window, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, window, surface,
-                    dp->configs[intptr_t(config)].impl, cnx);
+            egl_surface_t* s = new egl_surface_t(dpy, config, window, surface, cnx);
             return s;
         }
 
@@ -401,11 +284,9 @@
     egl_connection_t* cnx = validate_display_config(dpy, config, dp);
     if (cnx) {
         EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
-                dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-                dp->configs[intptr_t(config)].config, pixmap, attrib_list);
+                dp->disp.dpy, config, pixmap, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface,
-                    dp->configs[intptr_t(config)].impl, cnx);
+            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx);
             return s;
         }
     }
@@ -421,11 +302,9 @@
     egl_connection_t* cnx = validate_display_config(dpy, config, dp);
     if (cnx) {
         EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
-                dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-                dp->configs[intptr_t(config)].config, attrib_list);
+                dp->disp.dpy, config, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface,
-                    dp->configs[intptr_t(config)].impl, cnx);
+            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx);
             return s;
         }
     }
@@ -444,8 +323,7 @@
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
     egl_surface_t * const s = get_surface(surface);
-    EGLBoolean result = s->cnx->egl.eglDestroySurface(
-            dp->disp[s->impl].dpy, s->surface);
+    EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
     if (result == EGL_TRUE) {
         _s.terminate();
     }
@@ -465,16 +343,8 @@
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
-    EGLBoolean result(EGL_TRUE);
-    if (attribute == EGL_CONFIG_ID) {
-        // We need to remap EGL_CONFIG_IDs
-        *value = dp->configs[intptr_t(s->config)].configId;
-    } else {
-        result = s->cnx->egl.eglQuerySurface(
-                dp->disp[s->impl].dpy, s->surface, attribute, value);
-    }
-
-    return result;
+    return s->cnx->egl.eglQuerySurface(
+            dp->disp.dpy, s->surface, attribute, value);
 }
 
 void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
@@ -514,9 +384,7 @@
             share_list = c->context;
         }
         EGLContext context = cnx->egl.eglCreateContext(
-                dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-                dp->configs[intptr_t(config)].config,
-                share_list, attrib_list);
+                dp->disp.dpy, config, share_list, attrib_list);
         if (context != EGL_NO_CONTEXT) {
             // figure out if it's a GLESv1 or GLESv2
             int version = 0;
@@ -526,15 +394,14 @@
                     GLint value = *attrib_list++;
                     if (attr == EGL_CONTEXT_CLIENT_VERSION) {
                         if (value == 1) {
-                            version = GLESv1_INDEX;
+                            version = egl_connection_t::GLESv1_INDEX;
                         } else if (value == 2) {
-                            version = GLESv2_INDEX;
+                            version = egl_connection_t::GLESv2_INDEX;
                         }
                     }
                 };
             }
-            egl_context_t* c = new egl_context_t(dpy, context, config,
-                    dp->configs[intptr_t(config)].impl, cnx, version);
+            egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
 #if EGL_TRACE
             if (gEGLDebugLevel > 0)
                 GLTrace_eglCreateContext(version, c);
@@ -558,8 +425,7 @@
         return setError(EGL_BAD_CONTEXT, EGL_FALSE);
     
     egl_context_t * const c = get_context(ctx);
-    EGLBoolean result = c->cnx->egl.eglDestroyContext(
-            dp->disp[c->impl].dpy, c->context);
+    EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
     if (result == EGL_TRUE) {
         _c.terminate();
     }
@@ -625,20 +491,12 @@
     // retrieve the underlying implementation's draw EGLSurface
     if (draw != EGL_NO_SURFACE) {
         d = get_surface(draw);
-        // make sure the EGLContext and EGLSurface passed in are for
-        // the same driver
-        if (c && d->impl != c->impl)
-            return setError(EGL_BAD_MATCH, EGL_FALSE);
         impl_draw = d->surface;
     }
 
     // retrieve the underlying implementation's read EGLSurface
     if (read != EGL_NO_SURFACE) {
         r = get_surface(read);
-        // make sure the EGLContext and EGLSurface passed in are for
-        // the same driver
-        if (c && r->impl != c->impl)
-            return setError(EGL_BAD_MATCH, EGL_FALSE);
         impl_read = r->surface;
     }
 
@@ -682,17 +540,9 @@
     if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
 
     egl_context_t * const c = get_context(ctx);
+    return c->cnx->egl.eglQueryContext(
+            dp->disp.dpy, c->context, attribute, value);
 
-    EGLBoolean result(EGL_TRUE);
-    if (attribute == EGL_CONFIG_ID) {
-        *value = dp->configs[intptr_t(c->config)].configId;
-    } else {
-        // We need to remap EGL_CONFIG_IDs
-        result = c->cnx->egl.eglQueryContext(
-                dp->disp[c->impl].dpy, c->context, attribute, value);
-    }
-
-    return result;
 }
 
 EGLContext eglGetCurrentContext(void)
@@ -744,87 +594,37 @@
 
 EGLBoolean eglWaitGL(void)
 {
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would return GL_TRUE, which isn't wrong.
-
     clearError();
 
-    EGLBoolean res = EGL_TRUE;
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        if (uint32_t(c->impl)>=2)
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        egl_connection_t* const cnx = &gEGLImpl[c->impl];
-        if (!cnx->dso) 
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        res = cnx->egl.eglWaitGL();
-    }
-    return res;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+
+    return cnx->egl.eglWaitGL();
 }
 
 EGLBoolean eglWaitNative(EGLint engine)
 {
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would return GL_TRUE, which isn't wrong.
-
     clearError();
 
-    EGLBoolean res = EGL_TRUE;
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        if (uint32_t(c->impl)>=2)
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        egl_connection_t* const cnx = &gEGLImpl[c->impl];
-        if (!cnx->dso) 
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        res = cnx->egl.eglWaitNative(engine);
-    }
-    return res;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+
+    return cnx->egl.eglWaitNative(engine);
 }
 
 EGLint eglGetError(void)
 {
-    EGLint result = EGL_SUCCESS;
-    EGLint err;
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        err = EGL_SUCCESS;
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso)
-            err = cnx->egl.eglGetError();
-        if (err!=EGL_SUCCESS && result==EGL_SUCCESS)
-            result = err;
+    EGLint err = EGL_SUCCESS;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        err = cnx->egl.eglGetError();
     }
-    err = egl_tls_t::getError();
-    if (result == EGL_SUCCESS)
-        result = err;
-    return result;
-}
-
-// Note: Similar implementations of these functions also exist in
-// gl2.cpp and gl.cpp, and are used by applications that call the
-// exported entry points directly.
-typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image);
-typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image);
-
-static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES_impl = NULL;
-static PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES_impl = NULL;
-
-static void glEGLImageTargetTexture2DOES_wrapper(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage =
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    glEGLImageTargetTexture2DOES_impl(target, implImage);
-}
-
-static void glEGLImageTargetRenderbufferStorageOES_wrapper(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage =
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    glEGLImageTargetRenderbufferStorageOES_impl(target, implImage);
+    if (err == EGL_SUCCESS) {
+        err = egl_tls_t::getError();
+    }
+    return err;
 }
 
 __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
@@ -885,32 +685,22 @@
 
         if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
             bool found = false;
-            for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-                egl_connection_t* const cnx = &gEGLImpl[i];
-                if (cnx->dso && cnx->egl.eglGetProcAddress) {
-                    found = true;
-                    // Extensions are independent of the bound context
-                    cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] =
-                    cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] =
+
+            egl_connection_t* const cnx = &gEGLImpl;
+            if (cnx->dso && cnx->egl.eglGetProcAddress) {
+                found = true;
+                // Extensions are independent of the bound context
+                cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] =
 #if EGL_TRACE
-                    debugHooks->ext.extensions[slot] =
-                    gHooksTrace.ext.extensions[slot] =
+                debugHooks->ext.extensions[slot] =
+                gHooksTrace.ext.extensions[slot] =
 #endif
-                            cnx->egl.eglGetProcAddress(procname);
-                }
+                        cnx->egl.eglGetProcAddress(procname);
             }
+
             if (found) {
                 addr = gExtensionForwarders[slot];
-
-                if (!strcmp(procname, "glEGLImageTargetTexture2DOES")) {
-                    glEGLImageTargetTexture2DOES_impl = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)addr;
-                    addr = (__eglMustCastToProperFunctionPointerType)glEGLImageTargetTexture2DOES_wrapper;
-                }
-                if (!strcmp(procname, "glEGLImageTargetRenderbufferStorageOES")) {
-                    glEGLImageTargetRenderbufferStorageOES_impl = (PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC)addr;
-                    addr = (__eglMustCastToProperFunctionPointerType)glEGLImageTargetRenderbufferStorageOES_wrapper;
-                }
-
                 sGLExtentionMap.add(name, addr);
                 sGLExtentionSlot++;
             }
@@ -937,7 +727,7 @@
 #endif
 
     egl_surface_t const * const s = get_surface(draw);
-    return s->cnx->egl.eglSwapBuffers(dp->disp[s->impl].dpy, s->surface);
+    return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
 }
 
 EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
@@ -953,8 +743,7 @@
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
-    return s->cnx->egl.eglCopyBuffers(
-            dp->disp[s->impl].dpy, s->surface, target);
+    return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
 }
 
 const char* eglQueryString(EGLDisplay dpy, EGLint name)
@@ -973,12 +762,8 @@
             return dp->getExtensionString();
         case EGL_CLIENT_APIS:
             return dp->getClientApiString();
-        case EGL_VERSION_HW_ANDROID: {
-            if (gEGLImpl[IMPL_HARDWARE].dso) {
-                return dp->disp[IMPL_HARDWARE].queryString.version;
-            }
-            return dp->disp[IMPL_SOFTWARE].queryString.version;
-        }
+        case EGL_VERSION_HW_ANDROID:
+            return dp->disp.queryString.version;
     }
     return setError(EGL_BAD_PARAMETER, (const char *)0);
 }
@@ -1003,7 +788,7 @@
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglSurfaceAttrib) {
         return s->cnx->egl.eglSurfaceAttrib(
-                dp->disp[s->impl].dpy, s->surface, attribute, value);
+                dp->disp.dpy, s->surface, attribute, value);
     }
     return setError(EGL_BAD_SURFACE, EGL_FALSE);
 }
@@ -1023,7 +808,7 @@
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglBindTexImage) {
         return s->cnx->egl.eglBindTexImage(
-                dp->disp[s->impl].dpy, s->surface, buffer);
+                dp->disp.dpy, s->surface, buffer);
     }
     return setError(EGL_BAD_SURFACE, EGL_FALSE);
 }
@@ -1043,7 +828,7 @@
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglReleaseTexImage) {
         return s->cnx->egl.eglReleaseTexImage(
-                dp->disp[s->impl].dpy, s->surface, buffer);
+                dp->disp.dpy, s->surface, buffer);
     }
     return setError(EGL_BAD_SURFACE, EGL_FALSE);
 }
@@ -1056,17 +841,11 @@
     if (!dp) return EGL_FALSE;
 
     EGLBoolean res = EGL_TRUE;
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglSwapInterval) {
-                if (cnx->egl.eglSwapInterval(
-                        dp->disp[i].dpy, interval) == EGL_FALSE) {
-                    res = EGL_FALSE;
-                }
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglSwapInterval) {
+        res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
     }
+
     return res;
 }
 
@@ -1079,23 +858,15 @@
 {
     clearError();
 
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would return GL_TRUE, which isn't wrong.
-    EGLBoolean res = EGL_TRUE;
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        if (uint32_t(c->impl)>=2)
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        egl_connection_t* const cnx = &gEGLImpl[c->impl];
-        if (!cnx->dso) 
-            return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-        if (cnx->egl.eglWaitClient) {
-            res = cnx->egl.eglWaitClient();
-        } else {
-            res = cnx->egl.eglWaitGL();
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+
+    EGLBoolean res;
+    if (cnx->egl.eglWaitClient) {
+        res = cnx->egl.eglWaitClient();
+    } else {
+        res = cnx->egl.eglWaitGL();
     }
     return res;
 }
@@ -1110,15 +881,9 @@
 
     // bind this API on all EGLs
     EGLBoolean res = EGL_TRUE;
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglBindAPI) {
-                if (cnx->egl.eglBindAPI(api) == EGL_FALSE) {
-                    res = EGL_FALSE;
-                }
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglBindAPI) {
+        res = cnx->egl.eglBindAPI(api);
     }
     return res;
 }
@@ -1131,16 +896,11 @@
         return setError(EGL_BAD_PARAMETER, EGL_FALSE);
     }
 
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglQueryAPI) {
-                // the first one we find is okay, because they all
-                // should be the same
-                return cnx->egl.eglQueryAPI();
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryAPI) {
+        return cnx->egl.eglQueryAPI();
     }
+
     // or, it can only be OpenGL ES
     return EGL_OPENGL_ES_API;
 }
@@ -1152,14 +912,11 @@
     // If there is context bound to the thread, release it
     egl_display_t::loseCurrent(get_context(getContext()));
 
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso) {
-            if (cnx->egl.eglReleaseThread) {
-                cnx->egl.eglReleaseThread();
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglReleaseThread) {
+        cnx->egl.eglReleaseThread();
     }
+
     egl_tls_t::clearTLS();
 #if EGL_TRACE
     if (gEGLDebugLevel > 0)
@@ -1179,9 +936,7 @@
     if (!cnx) return EGL_FALSE;
     if (cnx->egl.eglCreatePbufferFromClientBuffer) {
         return cnx->egl.eglCreatePbufferFromClientBuffer(
-                dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
-                buftype, buffer,
-                dp->configs[intptr_t(config)].config, attrib_list);
+                dp->disp.dpy, buftype, buffer, config, attrib_list);
     }
     return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
 }
@@ -1205,7 +960,7 @@
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglLockSurfaceKHR) {
         return s->cnx->egl.eglLockSurfaceKHR(
-                dp->disp[s->impl].dpy, s->surface, attrib_list);
+                dp->disp.dpy, s->surface, attrib_list);
     }
     return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 }
@@ -1223,8 +978,7 @@
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglUnlockSurfaceKHR) {
-        return s->cnx->egl.eglUnlockSurfaceKHR(
-                dp->disp[s->impl].dpy, s->surface);
+        return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
     }
     return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 }
@@ -1237,67 +991,18 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_NO_IMAGE_KHR;
 
-    if (ctx != EGL_NO_CONTEXT) {
-        ContextRef _c(dp, ctx);
-        if (!_c.get())
-            return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
-        egl_context_t * const c = get_context(ctx);
-        // since we have an EGLContext, we know which implementation to use
-        EGLImageKHR image = c->cnx->egl.eglCreateImageKHR(
-                dp->disp[c->impl].dpy, c->context, target, buffer, attrib_list);
-        if (image == EGL_NO_IMAGE_KHR)
-            return image;
-            
-        egl_image_t* result = new egl_image_t(dpy, ctx);
-        result->images[c->impl] = image;
-        return (EGLImageKHR)result;
-    } else {
-        // EGL_NO_CONTEXT is a valid parameter
+    ContextRef _c(dp, ctx);
+    egl_context_t * const c = _c.get();
 
-        /* Since we don't have a way to know which implementation to call,
-         * we're calling all of them. If at least one of the implementation
-         * succeeded, this is a success.
-         */
-
-        EGLint currentError = eglGetError();
-
-        EGLImageKHR implImages[IMPL_NUM_IMPLEMENTATIONS];
-        bool success = false;
-        for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-            egl_connection_t* const cnx = &gEGLImpl[i];
-            implImages[i] = EGL_NO_IMAGE_KHR;
-            if (cnx->dso) {
-                if (cnx->egl.eglCreateImageKHR) {
-                    implImages[i] = cnx->egl.eglCreateImageKHR(
-                            dp->disp[i].dpy, ctx, target, buffer, attrib_list);
-                    if (implImages[i] != EGL_NO_IMAGE_KHR) {
-                        success = true;
-                    }
-                }
-            }
-        }
-
-        if (!success) {
-            // failure, if there was an error when we entered this function,
-            // the error flag must not be updated.
-            // Otherwise, the error is whatever happened in the implementation
-            // that faulted.
-            if (currentError != EGL_SUCCESS) {
-                setError(currentError, EGL_NO_IMAGE_KHR);
-            }
-            return EGL_NO_IMAGE_KHR;
-        } else {
-            // In case of success, we need to clear all error flags
-            // (especially those caused by the implementation that didn't
-            // succeed). TODO: we could avoid this if we knew this was
-            // a "full" success (all implementation succeeded).
-            eglGetError();
-        }
-
-        egl_image_t* result = new egl_image_t(dpy, ctx);
-        memcpy(result->images, implImages, sizeof(implImages));
-        return (EGLImageKHR)result;
+    EGLImageKHR result = EGL_NO_IMAGE_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateImageKHR) {
+        result = cnx->egl.eglCreateImageKHR(
+                dp->disp.dpy,
+                c ? c->context : EGL_NO_CONTEXT,
+                target, buffer, attrib_list);
     }
+    return result;
 }
 
 EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
@@ -1307,29 +1012,10 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    ImageRef _i(dp, img);
-    if (!_i.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-
-    egl_image_t* image = get_image(img);
-    bool success = false;
-    for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (image->images[i] != EGL_NO_IMAGE_KHR) {
-            if (cnx->dso) {
-                if (cnx->egl.eglDestroyImageKHR) {
-                    if (cnx->egl.eglDestroyImageKHR(
-                            dp->disp[i].dpy, image->images[i])) {
-                        success = true;
-                    }
-                }
-            }
-        }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDestroyImageKHR) {
+        cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img);
     }
-    if (!success)
-        return EGL_FALSE;
-
-    _i.terminate();
-
     return EGL_TRUE;
 }
 
@@ -1345,21 +1031,12 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_NO_SYNC_KHR;
 
-    EGLContext ctx = eglGetCurrentContext();
-    ContextRef _c(dp, ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_NO_SYNC_KHR);
-
-    egl_context_t * const c = get_context(ctx);
     EGLSyncKHR result = EGL_NO_SYNC_KHR;
-    if (c->cnx->egl.eglCreateSyncKHR) {
-        EGLSyncKHR sync = c->cnx->egl.eglCreateSyncKHR(
-                dp->disp[c->impl].dpy, type, attrib_list);
-        if (sync == EGL_NO_SYNC_KHR)
-            return sync;
-        result = (egl_sync_t*)new egl_sync_t(dpy, ctx, sync);
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateSyncKHR) {
+        result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list);
     }
-    return (EGLSyncKHR)result;
+    return result;
 }
 
 EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
@@ -1369,75 +1046,46 @@
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SyncRef _s(dp, sync);
-    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    egl_sync_t* syncObject = get_sync(sync);
-
-    EGLContext ctx = syncObject->context;
-    ContextRef _c(dp, ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-
     EGLBoolean result = EGL_FALSE;
-    egl_context_t * const c = get_context(ctx);
-    if (c->cnx->egl.eglDestroySyncKHR) {
-        result = c->cnx->egl.eglDestroySyncKHR(
-                dp->disp[c->impl].dpy, syncObject->sync);
-        if (result)
-            _s.terminate();
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDestroySyncKHR) {
+        result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync);
     }
     return result;
 }
 
-EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync,
+        EGLint flags, EGLTimeKHR timeout)
 {
     clearError();
 
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SyncRef _s(dp, sync);
-    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    egl_sync_t* syncObject = get_sync(sync);
-
-    EGLContext ctx = syncObject->context;
-    ContextRef _c(dp, ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-
-    egl_context_t * const c = get_context(ctx);
-    if (c->cnx->egl.eglClientWaitSyncKHR) {
-        return c->cnx->egl.eglClientWaitSyncKHR(
-                dp->disp[c->impl].dpy, syncObject->sync, flags, timeout);
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
+        result = cnx->egl.eglClientWaitSyncKHR(
+                dp->disp.dpy, sync, flags, timeout);
     }
-
-    return EGL_FALSE;
+    return result;
 }
 
-EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value)
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
+        EGLint attribute, EGLint *value)
 {
     clearError();
 
     egl_display_t const * const dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SyncRef _s(dp, sync);
-    if (!_s.get())
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-
-    egl_sync_t* syncObject = get_sync(sync);
-    EGLContext ctx = syncObject->context;
-    ContextRef _c(dp, ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
-
-    egl_context_t * const c = get_context(ctx);
-    if (c->cnx->egl.eglGetSyncAttribKHR) {
-        return c->cnx->egl.eglGetSyncAttribKHR(
-                dp->disp[c->impl].dpy, syncObject->sync, attribute, value);
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) {
+        result = cnx->egl.eglGetSyncAttribKHR(
+                dp->disp.dpy, sync, attribute, value);
     }
-
-    return EGL_FALSE;
+    return result;
 }
 
 // ----------------------------------------------------------------------------
@@ -1458,12 +1106,10 @@
     }
 
     EGLuint64NV ret = 0;
-    egl_connection_t* const cnx = &gEGLImpl[IMPL_HARDWARE];
+    egl_connection_t* const cnx = &gEGLImpl;
 
-    if (cnx->dso) {
-        if (cnx->egl.eglGetSystemTimeFrequencyNV) {
-            return cnx->egl.eglGetSystemTimeFrequencyNV();
-        }
+    if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
+        return cnx->egl.eglGetSystemTimeFrequencyNV();
     }
 
     return setErrorQuiet(EGL_BAD_DISPLAY, 0);
@@ -1478,12 +1124,10 @@
     }
 
     EGLuint64NV ret = 0;
-    egl_connection_t* const cnx = &gEGLImpl[IMPL_HARDWARE];
+    egl_connection_t* const cnx = &gEGLImpl;
 
-    if (cnx->dso) {
-        if (cnx->egl.eglGetSystemTimeNV) {
-            return cnx->egl.eglGetSystemTimeNV();
-        }
+    if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
+        return cnx->egl.eglGetSystemTimeNV();
     }
 
     return setErrorQuiet(EGL_BAD_DISPLAY, 0);
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 7fd6519..c79fb5f 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -83,39 +83,39 @@
 
 void egl_cache_t::initialize(egl_display_t *display) {
     Mutex::Autolock lock(mMutex);
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
-            const char* exts = display->disp[i].queryString.extensions;
-            size_t bcExtLen = strlen(BC_EXT_STR);
-            size_t extsLen = strlen(exts);
-            bool equal = !strcmp(BC_EXT_STR, exts);
-            bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
-            bool atEnd = (bcExtLen+1) < extsLen &&
-                    !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
-            bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
-            if (equal || atStart || atEnd || inMiddle) {
-                PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
-                eglSetBlobCacheFuncsANDROID =
-                        reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
+        const char* exts = display->disp.queryString.extensions;
+        size_t bcExtLen = strlen(BC_EXT_STR);
+        size_t extsLen = strlen(exts);
+        bool equal = !strcmp(BC_EXT_STR, exts);
+        bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
+        bool atEnd = (bcExtLen+1) < extsLen &&
+                !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
+        bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
+        if (equal || atStart || atEnd || inMiddle) {
+            PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
+            eglSetBlobCacheFuncsANDROID =
+                    reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
                             cnx->egl.eglGetProcAddress(
                                     "eglSetBlobCacheFuncsANDROID"));
-                if (eglSetBlobCacheFuncsANDROID == NULL) {
-                    ALOGE("EGL_ANDROID_blob_cache advertised by display %d, "
-                            "but unable to get eglSetBlobCacheFuncsANDROID", i);
-                    continue;
-                }
+            if (eglSetBlobCacheFuncsANDROID == NULL) {
+                ALOGE("EGL_ANDROID_blob_cache advertised, "
+                        "but unable to get eglSetBlobCacheFuncsANDROID");
+                return;
+            }
 
-                eglSetBlobCacheFuncsANDROID(display->disp[i].dpy,
-                        android::setBlob, android::getBlob);
-                EGLint err = cnx->egl.eglGetError();
-                if (err != EGL_SUCCESS) {
-                    ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
-                            "%#x", err);
-                }
+            eglSetBlobCacheFuncsANDROID(display->disp.dpy,
+                    android::setBlob, android::getBlob);
+            EGLint err = cnx->egl.eglGetError();
+            if (err != EGL_SUCCESS) {
+                ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
+                        "%#x", err);
             }
         }
     }
+
     mInitialized = true;
 }
 
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 6b2ae51..c85b4ce 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -60,18 +60,12 @@
 extern void initEglTraceLevel();
 extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
 
-static int cmp_configs(const void* a, const void *b) {
-    const egl_config_t& c0 = *(egl_config_t const *)a;
-    const egl_config_t& c1 = *(egl_config_t const *)b;
-    return c0<c1 ? -1 : (c1<c0 ? 1 : 0);
-}
-
 // ----------------------------------------------------------------------------
 
 egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
 
 egl_display_t::egl_display_t() :
-    magic('_dpy'), numTotalConfigs(0), configs(0), refs(0) {
+    magic('_dpy'), refs(0) {
 }
 
 egl_display_t::~egl_display_t() {
@@ -119,15 +113,13 @@
     // get our driver loader
     Loader& loader(Loader::getInstance());
 
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso && disp[i].dpy == EGL_NO_DISPLAY) {
-            EGLDisplay dpy = cnx->egl.eglGetDisplay(display);
-            disp[i].dpy = dpy;
-            if (dpy == EGL_NO_DISPLAY) {
-                loader.close(cnx->dso);
-                cnx->dso = NULL;
-            }
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) {
+        EGLDisplay dpy = cnx->egl.eglGetDisplay(display);
+        disp.dpy = dpy;
+        if (dpy == EGL_NO_DISPLAY) {
+            loader.close(cnx->dso);
+            cnx->dso = NULL;
         }
     }
 
@@ -160,12 +152,11 @@
     // initialize each EGL and
     // build our own extension string first, based on the extension we know
     // and the extension supported by our client implementation
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        cnx->major = -1;
-        cnx->minor = -1;
-        if (!cnx->dso)
-            continue;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    cnx->major = -1;
+    cnx->minor = -1;
+    if (cnx->dso) {
 
 #if defined(ADRENO130)
 #warning "Adreno-130 eglInitialize() workaround"
@@ -177,31 +168,30 @@
          * eglGetDisplay() before calling eglInitialize();
          */
         if (i == IMPL_HARDWARE) {
-            disp[i].dpy =
-            cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+            disp[i].dpy = cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
         }
 #endif
 
-        EGLDisplay idpy = disp[i].dpy;
+        EGLDisplay idpy = disp.dpy;
         if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
-            //ALOGD("initialized %d dpy=%p, ver=%d.%d, cnx=%p",
-            //        i, idpy, cnx->major, cnx->minor, cnx);
+            //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
+            //        idpy, cnx->major, cnx->minor, cnx);
 
             // display is now initialized
-            disp[i].state = egl_display_t::INITIALIZED;
+            disp.state = egl_display_t::INITIALIZED;
 
             // get the query-strings for this display for each implementation
-            disp[i].queryString.vendor = cnx->egl.eglQueryString(idpy,
+            disp.queryString.vendor = cnx->egl.eglQueryString(idpy,
                     EGL_VENDOR);
-            disp[i].queryString.version = cnx->egl.eglQueryString(idpy,
+            disp.queryString.version = cnx->egl.eglQueryString(idpy,
                     EGL_VERSION);
-            disp[i].queryString.extensions = cnx->egl.eglQueryString(idpy,
+            disp.queryString.extensions = cnx->egl.eglQueryString(idpy,
                     EGL_EXTENSIONS);
-            disp[i].queryString.clientApi = cnx->egl.eglQueryString(idpy,
+            disp.queryString.clientApi = cnx->egl.eglQueryString(idpy,
                     EGL_CLIENT_APIS);
 
         } else {
-            ALOGW("%d: eglInitialize(%p) failed (%s)", i, idpy,
+            ALOGW("eglInitialize(%p) failed (%s)", idpy,
                     egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
         }
     }
@@ -211,7 +201,7 @@
     mVersionString.setTo(sVersionString);
     mClientApiString.setTo(sClientApiString);
 
-    // we only add extensions that exist in at least one implementation
+    // we only add extensions that exist in the implementation
     char const* start = sExtensionString;
     char const* end;
     do {
@@ -223,15 +213,13 @@
             if (len) {
                 // NOTE: we could avoid the copy if we had strnstr.
                 const String8 ext(start, len);
-                // now go through all implementations and look for this extension
-                for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-                    if (disp[i].queryString.extensions) {
-                        // if we find it, add this extension string to our list
-                        // (and don't forget the space)
-                        const char* match = strstr(disp[i].queryString.extensions, ext.string());
-                        if (match && (match[len] == ' ' || match[len] == 0)) {
-                            mExtensionString.append(start, len+1);
-                        }
+                // now look for this extension
+                if (disp.queryString.extensions) {
+                    // if we find it, add this extension string to our list
+                    // (and don't forget the space)
+                    const char* match = strstr(disp.queryString.extensions, ext.string());
+                    if (match && (match[len] == ' ' || match[len] == 0)) {
+                        mExtensionString.append(start, len+1);
                     }
                 }
             }
@@ -242,52 +230,12 @@
 
     egl_cache_t::get()->initialize(this);
 
-    EGLBoolean res = EGL_FALSE;
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
-            EGLint n;
-            if (cnx->egl.eglGetConfigs(disp[i].dpy, 0, 0, &n)) {
-                disp[i].config = (EGLConfig*) malloc(sizeof(EGLConfig) * n);
-                if (disp[i].config) {
-                    if (cnx->egl.eglGetConfigs(disp[i].dpy, disp[i].config, n,
-                            &disp[i].numConfigs)) {
-                        numTotalConfigs += n;
-                        res = EGL_TRUE;
-                    }
-                }
-            }
-        }
-    }
-
-    if (res == EGL_TRUE) {
-        configs = new egl_config_t[numTotalConfigs];
-        for (int i = 0, k = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-            egl_connection_t* const cnx = &gEGLImpl[i];
-            if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
-                for (int j = 0; j < disp[i].numConfigs; j++) {
-                    configs[k].impl = i;
-                    configs[k].config = disp[i].config[j];
-                    configs[k].configId = k + 1; // CONFIG_ID start at 1
-                    // store the implementation's CONFIG_ID
-                    cnx->egl.eglGetConfigAttrib(disp[i].dpy, disp[i].config[j],
-                            EGL_CONFIG_ID, &configs[k].implConfigId);
-                    k++;
-                }
-            }
-        }
-
-        // sort our configurations so we can do binary-searches
-        qsort(configs, numTotalConfigs, sizeof(egl_config_t), cmp_configs);
-
-        refs++;
-        if (major != NULL)
-            *major = VERSION_MAJOR;
-        if (minor != NULL)
-            *minor = VERSION_MINOR;
-        return EGL_TRUE;
-    }
-    return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
+    refs++;
+    if (major != NULL)
+        *major = VERSION_MAJOR;
+    if (minor != NULL)
+        *minor = VERSION_MINOR;
+    return EGL_TRUE;
 }
 
 EGLBoolean egl_display_t::terminate() {
@@ -305,22 +253,15 @@
     }
 
     EGLBoolean res = EGL_FALSE;
-    for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
-        egl_connection_t* const cnx = &gEGLImpl[i];
-        if (cnx->dso && disp[i].state == egl_display_t::INITIALIZED) {
-            if (cnx->egl.eglTerminate(disp[i].dpy) == EGL_FALSE) {
-                ALOGW("%d: eglTerminate(%p) failed (%s)", i, disp[i].dpy,
-                        egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
-            }
-            // REVISIT: it's unclear what to do if eglTerminate() fails
-            free(disp[i].config);
-
-            disp[i].numConfigs = 0;
-            disp[i].config = 0;
-            disp[i].state = egl_display_t::TERMINATED;
-
-            res = EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
+        if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
+            ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
+                    egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
         }
+        // REVISIT: it's unclear what to do if eglTerminate() fails
+        disp.state = egl_display_t::TERMINATED;
+        res = EGL_TRUE;
     }
 
     // Mark all objects remaining in the list as terminated, unless
@@ -337,8 +278,6 @@
     objects.clear();
 
     refs--;
-    numTotalConfigs = 0;
-    delete[] configs;
     return res;
 }
 
@@ -390,13 +329,13 @@
         Mutex::Autolock _l(lock);
         if (c) {
             result = c->cnx->egl.eglMakeCurrent(
-                    disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
+                    disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 c->onMakeCurrent(draw, read);
             }
         } else {
             result = cur_c->cnx->egl.eglMakeCurrent(
-                    disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
+                    disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 cur_c->onLooseCurrent();
             }
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index f3c4ddf..6348228 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -44,23 +44,6 @@
 
 // ----------------------------------------------------------------------------
 
-struct egl_config_t {
-    egl_config_t() {}
-    egl_config_t(int impl, EGLConfig config)
-        : impl(impl), config(config), configId(0), implConfigId(0) { }
-    int         impl;           // the implementation this config is for
-    EGLConfig   config;         // the implementation's EGLConfig
-    EGLint      configId;       // our CONFIG_ID
-    EGLint      implConfigId;   // the implementation's CONFIG_ID
-    inline bool operator < (const egl_config_t& rhs) const {
-        if (impl < rhs.impl) return true;
-        if (impl > rhs.impl) return false;
-        return config < rhs.config;
-    }
-};
-
-// ----------------------------------------------------------------------------
-
 class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
     static egl_display_t sDisplay[NUM_DISPLAYS];
     EGLDisplay getDisplay(EGLNativeDisplayType display);
@@ -113,12 +96,9 @@
     };
 
     struct DisplayImpl {
-        DisplayImpl() : dpy(EGL_NO_DISPLAY), config(0),
-                        state(NOT_INITIALIZED), numConfigs(0) { }
+        DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) { }
         EGLDisplay  dpy;
-        EGLConfig*  config;
         EGLint      state;
-        EGLint      numConfigs;
         strings_t   queryString;
     };
 
@@ -126,9 +106,7 @@
     uint32_t        magic;
 
 public:
-    DisplayImpl     disp[IMPL_NUM_IMPLEMENTATIONS];
-    EGLint          numTotalConfigs;
-    egl_config_t*   configs;
+    DisplayImpl     disp;
 
 private:
             uint32_t                    refs;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index b660c53..d0cbb31 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -64,9 +64,9 @@
 // ----------------------------------------------------------------------------
 
 egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-        int impl, egl_connection_t const* cnx, int version) :
+        egl_connection_t const* cnx, int version) :
     egl_object_t(get_display(dpy)), dpy(dpy), context(context),
-            config(config), read(0), draw(0), impl(impl), cnx(cnx),
+            config(config), read(0), draw(0), cnx(cnx),
             version(version)
 {
 }
@@ -87,7 +87,7 @@
 
     if (gl_extensions.isEmpty()) {
         // call the implementation's glGetString(GL_EXTENSIONS)
-        const char* exts = (const char *)gEGLImpl[impl].hooks[version]->gl.glGetString(GL_EXTENSIONS);
+        const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
         gl_extensions.setTo(exts);
         if (gl_extensions.find("GL_EXT_debug_marker") < 0) {
             String8 temp("GL_EXT_debug_marker ");
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index abd4cbb..4d91f54 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -125,7 +125,7 @@
 
 // ----------------------------------------------------------------------------
 
-class egl_surface_t: public egl_object_t {
+class egl_surface_t : public egl_object_t {
 protected:
     ~egl_surface_t() {
         ANativeWindow* const window = win.get();
@@ -140,15 +140,14 @@
     typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
 
     egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win,
-            EGLSurface surface, int impl, egl_connection_t const* cnx) :
+            EGLSurface surface, egl_connection_t const* cnx) :
         egl_object_t(get_display(dpy)), dpy(dpy), surface(surface),
-                config(config), win(win), impl(impl), cnx(cnx) {
+                config(config), win(win), cnx(cnx) {
     }
     EGLDisplay dpy;
     EGLSurface surface;
     EGLConfig config;
     sp<ANativeWindow> win;
-    int impl;
     egl_connection_t const* cnx;
 };
 
@@ -159,7 +158,7 @@
     typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
 
     egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-            int impl, egl_connection_t const* cnx, int version);
+            egl_connection_t const* cnx, int version);
 
     void onLooseCurrent();
     void onMakeCurrent(EGLSurface draw, EGLSurface read);
@@ -169,47 +168,15 @@
     EGLConfig config;
     EGLSurface read;
     EGLSurface draw;
-    int impl;
     egl_connection_t const* cnx;
     int version;
     String8 gl_extensions;
 };
 
-class egl_image_t: public egl_object_t {
-protected:
-    ~egl_image_t() {}
-public:
-    typedef egl_object_t::LocalRef<egl_image_t, EGLImageKHR> Ref;
-
-    egl_image_t(EGLDisplay dpy, EGLContext context) :
-        egl_object_t(get_display(dpy)), dpy(dpy), context(context) {
-        memset(images, 0, sizeof(images));
-    }
-    EGLDisplay dpy;
-    EGLContext context;
-    EGLImageKHR images[IMPL_NUM_IMPLEMENTATIONS];
-};
-
-class egl_sync_t: public egl_object_t {
-protected:
-    ~egl_sync_t() {}
-public:
-    typedef egl_object_t::LocalRef<egl_sync_t, EGLSyncKHR> Ref;
-
-    egl_sync_t(EGLDisplay dpy, EGLContext context, EGLSyncKHR sync) :
-        egl_object_t(get_display(dpy)), dpy(dpy), context(context), sync(sync) {
-    }
-    EGLDisplay dpy;
-    EGLContext context;
-    EGLSyncKHR sync;
-};
-
 // ----------------------------------------------------------------------------
 
 typedef egl_surface_t::Ref  SurfaceRef;
 typedef egl_context_t::Ref  ContextRef;
-typedef egl_image_t::Ref    ImageRef;
-typedef egl_sync_t::Ref     SyncRef;
 
 // ----------------------------------------------------------------------------
 
@@ -228,16 +195,6 @@
     return egl_to_native_cast<egl_context_t>(context);
 }
 
-static inline
-egl_image_t* get_image(EGLImageKHR image) {
-    return egl_to_native_cast<egl_image_t>(image);
-}
-
-static inline
-egl_sync_t* get_sync(EGLSyncKHR sync) {
-    return egl_to_native_cast<egl_sync_t>(sync);
-}
-
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index ff20957..c900c1c 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -19,31 +19,24 @@
 
 #include "hooks.h"
 
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 4
+
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
 
-#define VERSION_MAJOR 1
-#define VERSION_MINOR 4
-
 //  EGLDisplay are global, not attached to a given thread
 const unsigned int NUM_DISPLAYS = 1;
 
-enum {
-    IMPL_HARDWARE = 0,
-    IMPL_SOFTWARE,
-    IMPL_NUM_IMPLEMENTATIONS
-};
-
-enum {
-    GLESv1_INDEX = 0,
-    GLESv2_INDEX = 1,
-};
-
 // ----------------------------------------------------------------------------
 
-struct egl_connection_t
-{
+struct egl_connection_t {
+    enum {
+        GLESv1_INDEX = 0,
+        GLESv2_INDEX = 1
+    };
+
     inline egl_connection_t() : dso(0) { }
     void *              dso;
     gl_hooks_t *        hooks[2];
@@ -54,7 +47,7 @@
 
 // ----------------------------------------------------------------------------
 
-extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
+extern gl_hooks_t gHooks[2];
 extern gl_hooks_t gHooksNoContext;
 extern pthread_key_t gGLWrapperKey;
 extern "C" void gl_unimplemented();
@@ -63,7 +56,7 @@
 extern char const * const gl_names[];
 extern char const * const egl_names[];
 
-extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
+extern egl_connection_t gEGLImpl;
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 79aa3cd..4345c2b 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -124,27 +124,3 @@
     }
     return ret;
 }
-
-/*
- * These GL calls are special because they need to EGL to retrieve some
- * informations before they can execute.
- */
-
-extern "C" void __glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image);
-extern "C" void __glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image);
-
-
-void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage = 
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    __glEGLImageTargetTexture2DOES(target, implImage);
-}
-
-void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage = 
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    __glEGLImageTargetRenderbufferStorageOES(target, implImage);
-}
-
diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in
index a8907fd..c381075 100644
--- a/opengl/libs/GLES2/gl2ext_api.in
+++ b/opengl/libs/GLES2/gl2ext_api.in
@@ -1,7 +1,7 @@
-void API_ENTRY(__glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
+void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
     CALL_GL_API(glEGLImageTargetTexture2DOES, target, image);
 }
-void API_ENTRY(__glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
+void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
     CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image);
 }
 void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary) {
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index adeaa5b..adcb60d 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -179,27 +179,3 @@
     }
     return ret;
 }
-
-/*
- * These GL calls are special because they need to EGL to retrieve some
- * informations before they can execute.
- */
-
-extern "C" void __glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image);
-extern "C" void __glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image);
-
-
-void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage = 
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    __glEGLImageTargetTexture2DOES(target, implImage);
-}
-
-void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
-{
-    GLeglImageOES implImage = 
-        (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
-    __glEGLImageTargetRenderbufferStorageOES(target, implImage);
-}
-
diff --git a/opengl/libs/GLES_CM/glext_api.in b/opengl/libs/GLES_CM/glext_api.in
index 268a535..7cd6cb5 100644
--- a/opengl/libs/GLES_CM/glext_api.in
+++ b/opengl/libs/GLES_CM/glext_api.in
@@ -31,10 +31,10 @@
 void API_ENTRY(glDrawTexfvOES)(const GLfloat *coords) {
     CALL_GL_API(glDrawTexfvOES, coords);
 }
-void API_ENTRY(__glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
+void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
     CALL_GL_API(glEGLImageTargetTexture2DOES, target, image);
 }
-void API_ENTRY(__glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
+void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
     CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image);
 }
 void API_ENTRY(glAlphaFuncxOES)(GLenum func, GLclampx ref) {
diff --git a/opengl/libs/GLES_trace/TODO.txt b/opengl/libs/GLES_trace/TODO.txt
deleted file mode 100644
index f5e6e95..0000000
--- a/opengl/libs/GLES_trace/TODO.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-TODO:
-    - Context - Currently, we don't do anything regarding the contexts that are created.
-    Need to maintain more state regarding contexts, and figure out what happens in the
-    presence of multiple contexts.
-
-    - Transport: Each GLMessage is sent via a socket as soon as the message is received.
-    i.e., there is no buffering of messages. Buffering should improve performance.
-
-    - Initialization: On first connection, send some basic information that includes:
-        1. version of the trace library
-        2. implementation dependent GL state variables such as # of vertex arrays etc.
-
-    - eglSwapBuffers: The images are lzf compressed, but there is no mode that transfers
-    only the differences from the previous images.
diff --git a/opengl/libs/GLES_trace/gltrace.proto b/opengl/libs/GLES_trace/gltrace.proto
index 11cf24f..2893e6e 100644
--- a/opengl/libs/GLES_trace/gltrace.proto
+++ b/opengl/libs/GLES_trace/gltrace.proto
@@ -543,11 +543,13 @@
 
     required int32      context_id = 1;                     // GL context ID
     required int64      start_time = 2;                     // time when call was invoked
-    required int32      duration = 3;                       // duration of the call
+    required int32      duration = 3;                       // duration of the call (MONOTONIC TIME)
 
     required Function   function = 4 [default = invalid];   // GL function called
     repeated DataType   args = 5;                           // GL function's arguments
     optional DataType   returnValue = 6;                    // GL function's return value
 
     optional FrameBuffer fb = 7;                            // contents of the framebuffer
+
+    optional int32      threadtime = 8;                     // duration of the call (THREAD TIME)
 };
diff --git a/opengl/libs/GLES_trace/src/gltrace.pb.cpp b/opengl/libs/GLES_trace/src/gltrace.pb.cpp
index bb9d4a7..d5f8180 100644
--- a/opengl/libs/GLES_trace/src/gltrace.pb.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace.pb.cpp
@@ -1663,6 +1663,7 @@
 const int GLMessage::kArgsFieldNumber;
 const int GLMessage::kReturnValueFieldNumber;
 const int GLMessage::kFbFieldNumber;
+const int GLMessage::kThreadtimeFieldNumber;
 #endif  // !_MSC_VER
 
 GLMessage::GLMessage()
@@ -1689,6 +1690,7 @@
   function_ = 3000;
   returnvalue_ = NULL;
   fb_ = NULL;
+  threadtime_ = 0;
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
 }
 
@@ -1730,6 +1732,7 @@
     if (_has_bit(6)) {
       if (fb_ != NULL) fb_->::android::gltrace::GLMessage_FrameBuffer::Clear();
     }
+    threadtime_ = 0;
   }
   args_.Clear();
   ::memset(_has_bits_, 0, sizeof(_has_bits_));
@@ -1846,6 +1849,22 @@
         } else {
           goto handle_uninterpreted;
         }
+        if (input->ExpectTag(64)) goto parse_threadtime;
+        break;
+      }
+      
+      // optional int32 threadtime = 8;
+      case 8: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_threadtime:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &threadtime_)));
+          _set_bit(7);
+        } else {
+          goto handle_uninterpreted;
+        }
         if (input->ExpectAtEnd()) return true;
         break;
       }
@@ -1906,6 +1925,11 @@
       7, this->fb(), output);
   }
   
+  // optional int32 threadtime = 8;
+  if (_has_bit(7)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(8, this->threadtime(), output);
+  }
+  
 }
 
 int GLMessage::ByteSize() const {
@@ -1953,6 +1977,13 @@
           this->fb());
     }
     
+    // optional int32 threadtime = 8;
+    if (has_threadtime()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->threadtime());
+    }
+    
   }
   // repeated .android.gltrace.GLMessage.DataType args = 5;
   total_size += 1 * this->args_size();
@@ -1995,6 +2026,9 @@
     if (from._has_bit(6)) {
       mutable_fb()->::android::gltrace::GLMessage_FrameBuffer::MergeFrom(from.fb());
     }
+    if (from._has_bit(7)) {
+      set_threadtime(from.threadtime());
+    }
   }
 }
 
@@ -2028,6 +2062,7 @@
     args_.Swap(&other->args_);
     std::swap(returnvalue_, other->returnvalue_);
     std::swap(fb_, other->fb_);
+    std::swap(threadtime_, other->threadtime_);
     std::swap(_has_bits_[0], other->_has_bits_[0]);
     std::swap(_cached_size_, other->_cached_size_);
   }
diff --git a/opengl/libs/GLES_trace/src/gltrace.pb.h b/opengl/libs/GLES_trace/src/gltrace.pb.h
index e3b8990..a4fcbd3 100644
--- a/opengl/libs/GLES_trace/src/gltrace.pb.h
+++ b/opengl/libs/GLES_trace/src/gltrace.pb.h
@@ -1418,6 +1418,13 @@
   inline const ::android::gltrace::GLMessage_FrameBuffer& fb() const;
   inline ::android::gltrace::GLMessage_FrameBuffer* mutable_fb();
   
+  // optional int32 threadtime = 8;
+  inline bool has_threadtime() const;
+  inline void clear_threadtime();
+  static const int kThreadtimeFieldNumber = 8;
+  inline ::google::protobuf::int32 threadtime() const;
+  inline void set_threadtime(::google::protobuf::int32 value);
+  
   // @@protoc_insertion_point(class_scope:android.gltrace.GLMessage)
  private:
   mutable int _cached_size_;
@@ -1429,11 +1436,12 @@
   ::google::protobuf::RepeatedPtrField< ::android::gltrace::GLMessage_DataType > args_;
   ::android::gltrace::GLMessage_DataType* returnvalue_;
   ::android::gltrace::GLMessage_FrameBuffer* fb_;
+  ::google::protobuf::int32 threadtime_;
   friend void  protobuf_AddDesc_gltrace_2eproto();
   friend void protobuf_AssignDesc_gltrace_2eproto();
   friend void protobuf_ShutdownFile_gltrace_2eproto();
   
-  ::google::protobuf::uint32 _has_bits_[(7 + 31) / 32];
+  ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
   inline bool _has_bit(int index) const {
@@ -1860,6 +1868,22 @@
   return fb_;
 }
 
+// optional int32 threadtime = 8;
+inline bool GLMessage::has_threadtime() const {
+  return _has_bit(7);
+}
+inline void GLMessage::clear_threadtime() {
+  threadtime_ = 0;
+  _clear_bit(7);
+}
+inline ::google::protobuf::int32 GLMessage::threadtime() const {
+  return threadtime_;
+}
+inline void GLMessage::set_threadtime(::google::protobuf::int32 value) {
+  _set_bit(7);
+  threadtime_ = value;
+}
+
 
 // @@protoc_insertion_point(namespace_scope)
 
diff --git a/opengl/libs/GLES_trace/src/gltrace_api.cpp b/opengl/libs/GLES_trace/src/gltrace_api.cpp
index a2366ac..358bf54 100644
--- a/opengl/libs/GLES_trace/src/gltrace_api.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace_api.cpp
@@ -43,11 +43,15 @@
     arg_texture->add_intvalue((int)texture);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glActiveTexture(texture);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -70,11 +74,15 @@
     arg_shader->add_intvalue(shader);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glAttachShader(program, shader);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -103,11 +111,15 @@
     arg_name->add_intvalue((int)name);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindAttribLocation(program, index, name);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -130,11 +142,15 @@
     arg_buffer->add_intvalue(buffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindBuffer(target, buffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -157,11 +173,15 @@
     arg_framebuffer->add_intvalue(framebuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindFramebuffer(target, framebuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -184,11 +204,15 @@
     arg_renderbuffer->add_intvalue(renderbuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindRenderbuffer(target, renderbuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -211,11 +235,15 @@
     arg_texture->add_intvalue(texture);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindTexture(target, texture);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -250,11 +278,15 @@
     arg_alpha->add_floatvalue(alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlendColor(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -271,11 +303,15 @@
     arg_mode->add_intvalue((int)mode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlendEquation(mode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -298,11 +334,15 @@
     arg_modeAlpha->add_intvalue((int)modeAlpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlendEquationSeparate(modeRGB, modeAlpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -325,11 +365,15 @@
     arg_dfactor->add_intvalue((int)dfactor);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlendFunc(sfactor, dfactor);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -364,11 +408,15 @@
     arg_dstAlpha->add_intvalue((int)dstAlpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -403,11 +451,15 @@
     arg_usage->add_intvalue((int)usage);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBufferData(target, size, data, usage);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -442,11 +494,15 @@
     arg_data->add_intvalue((int)data);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBufferSubData(target, offset, size, data);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -463,9 +519,11 @@
     arg_target->add_intvalue((int)target);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLenum retValue = glContext->hooks->gl.glCheckFramebufferStatus(target);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -473,7 +531,9 @@
     rt->set_type(GLMessage::DataType::ENUM);
     rt->add_intvalue((int)retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -492,11 +552,15 @@
     arg_mask->add_intvalue(mask);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClear(mask);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -531,11 +595,15 @@
     arg_alpha->add_floatvalue(alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClearColor(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -552,11 +620,15 @@
     arg_depth->add_floatvalue(depth);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClearDepthf(depth);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -573,11 +645,15 @@
     arg_s->add_intvalue(s);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClearStencil(s);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -612,11 +688,15 @@
     arg_alpha->add_boolvalue(alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glColorMask(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -633,11 +713,15 @@
     arg_shader->add_intvalue(shader);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCompileShader(shader);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -696,11 +780,15 @@
     arg_data->add_intvalue((int)data);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -765,11 +853,15 @@
     arg_data->add_intvalue((int)data);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -828,11 +920,15 @@
     arg_border->add_intvalue(border);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -891,11 +987,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -906,9 +1006,11 @@
     glmsg.set_function(GLMessage::glCreateProgram);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLuint retValue = glContext->hooks->gl.glCreateProgram();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -916,7 +1018,9 @@
     rt->set_type(GLMessage::DataType::INT);
     rt->add_intvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -935,9 +1039,11 @@
     arg_type->add_intvalue((int)type);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLuint retValue = glContext->hooks->gl.glCreateShader(type);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -945,7 +1051,9 @@
     rt->set_type(GLMessage::DataType::INT);
     rt->add_intvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -964,11 +1072,15 @@
     arg_mode->add_intvalue((int)mode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCullFace(mode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -991,11 +1103,15 @@
     arg_buffers->add_intvalue((int)buffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteBuffers(n, buffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1018,11 +1134,15 @@
     arg_framebuffers->add_intvalue((int)framebuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteFramebuffers(n, framebuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1039,11 +1159,15 @@
     arg_program->add_intvalue(program);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteProgram(program);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1066,11 +1190,15 @@
     arg_renderbuffers->add_intvalue((int)renderbuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteRenderbuffers(n, renderbuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1087,11 +1215,15 @@
     arg_shader->add_intvalue(shader);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteShader(shader);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1114,11 +1246,15 @@
     arg_textures->add_intvalue((int)textures);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteTextures(n, textures);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1135,11 +1271,15 @@
     arg_func->add_intvalue((int)func);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDepthFunc(func);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1156,11 +1296,15 @@
     arg_flag->add_boolvalue(flag);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDepthMask(flag);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1183,11 +1327,15 @@
     arg_zFar->add_floatvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDepthRangef(zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1210,11 +1358,15 @@
     arg_shader->add_intvalue(shader);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDetachShader(program, shader);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1231,11 +1383,15 @@
     arg_cap->add_intvalue((int)cap);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDisable(cap);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1252,11 +1408,15 @@
     arg_index->add_intvalue(index);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDisableVertexAttribArray(index);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1285,11 +1445,15 @@
     arg_count->add_intvalue(count);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawArrays(mode, first, count);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1324,11 +1488,15 @@
     arg_indices->add_intvalue((int)indices);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawElements(mode, count, type, indices);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1345,11 +1513,15 @@
     arg_cap->add_intvalue((int)cap);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEnable(cap);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1366,11 +1538,15 @@
     arg_index->add_intvalue(index);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEnableVertexAttribArray(index);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1381,11 +1557,15 @@
     glmsg.set_function(GLMessage::glFinish);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFinish();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1396,11 +1576,15 @@
     glmsg.set_function(GLMessage::glFlush);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFlush();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1435,11 +1619,15 @@
     arg_renderbuffer->add_intvalue(renderbuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1480,11 +1668,15 @@
     arg_level->add_intvalue(level);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFramebufferTexture2D(target, attachment, textarget, texture, level);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1501,11 +1693,15 @@
     arg_mode->add_intvalue((int)mode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFrontFace(mode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1528,11 +1724,15 @@
     arg_buffers->add_intvalue((int)buffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenBuffers(n, buffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1549,11 +1749,15 @@
     arg_target->add_intvalue((int)target);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenerateMipmap(target);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1576,11 +1780,15 @@
     arg_framebuffers->add_intvalue((int)framebuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenFramebuffers(n, framebuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1603,11 +1811,15 @@
     arg_renderbuffers->add_intvalue((int)renderbuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenRenderbuffers(n, renderbuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1630,11 +1842,15 @@
     arg_textures->add_intvalue((int)textures);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenTextures(n, textures);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1687,11 +1903,15 @@
     arg_name->add_intvalue((int)name);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetActiveAttrib(program, index, bufsize, length, size, type, name);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1744,11 +1964,15 @@
     arg_name->add_intvalue((int)name);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetActiveUniform(program, index, bufsize, length, size, type, name);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1783,11 +2007,15 @@
     arg_shaders->add_intvalue((int)shaders);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetAttachedShaders(program, maxcount, count, shaders);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1810,9 +2038,11 @@
     arg_name->add_intvalue((int)name);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     int retValue = glContext->hooks->gl.glGetAttribLocation(program, name);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -1820,7 +2050,9 @@
     rt->set_type(GLMessage::DataType::INT);
     rt->add_intvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -1845,11 +2077,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetBooleanv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1878,11 +2114,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetBufferParameteriv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1893,9 +2133,11 @@
     glmsg.set_function(GLMessage::glGetError);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLenum retValue = glContext->hooks->gl.glGetError();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -1903,7 +2145,9 @@
     rt->set_type(GLMessage::DataType::ENUM);
     rt->add_intvalue((int)retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -1928,11 +2172,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetFloatv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1967,11 +2215,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -1994,11 +2246,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetIntegerv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2027,11 +2283,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetProgramiv(program, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2066,11 +2326,15 @@
     arg_infolog->add_intvalue((int)infolog);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetProgramInfoLog(program, bufsize, length, infolog);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2099,11 +2363,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetRenderbufferParameteriv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2132,11 +2400,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetShaderiv(shader, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2171,11 +2443,15 @@
     arg_infolog->add_intvalue((int)infolog);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetShaderInfoLog(shader, bufsize, length, infolog);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2210,11 +2486,15 @@
     arg_precision->add_intvalue((int)precision);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2249,11 +2529,15 @@
     arg_source->add_intvalue((int)source);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetShaderSource(shader, bufsize, length, source);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2270,9 +2554,11 @@
     arg_name->add_intvalue((int)name);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     const GLubyte* retValue = glContext->hooks->gl.glGetString(name);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2280,7 +2566,9 @@
     rt->set_type(GLMessage::DataType::INT);
     rt->add_intvalue((int)retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2311,11 +2599,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexParameterfv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2344,11 +2636,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexParameteriv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2377,11 +2673,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetUniformfv(program, location, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2410,11 +2710,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetUniformiv(program, location, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2437,9 +2741,11 @@
     arg_name->add_intvalue((int)name);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     int retValue = glContext->hooks->gl.glGetUniformLocation(program, name);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2447,7 +2753,9 @@
     rt->set_type(GLMessage::DataType::INT);
     rt->add_intvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2478,11 +2786,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetVertexAttribfv(index, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2511,11 +2823,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetVertexAttribiv(index, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2544,11 +2860,15 @@
     arg_pointer->add_intvalue((int)pointer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetVertexAttribPointerv(index, pname, pointer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2571,11 +2891,15 @@
     arg_mode->add_intvalue((int)mode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glHint(target, mode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2592,9 +2916,11 @@
     arg_buffer->add_intvalue(buffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsBuffer(buffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2602,7 +2928,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2621,9 +2949,11 @@
     arg_cap->add_intvalue((int)cap);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsEnabled(cap);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2631,7 +2961,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2650,9 +2982,11 @@
     arg_framebuffer->add_intvalue(framebuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsFramebuffer(framebuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2660,7 +2994,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2679,9 +3015,11 @@
     arg_program->add_intvalue(program);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsProgram(program);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2689,7 +3027,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2708,9 +3048,11 @@
     arg_renderbuffer->add_intvalue(renderbuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsRenderbuffer(renderbuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2718,7 +3060,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2737,9 +3081,11 @@
     arg_shader->add_intvalue(shader);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsShader(shader);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2747,7 +3093,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2766,9 +3114,11 @@
     arg_texture->add_intvalue(texture);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsTexture(texture);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -2776,7 +3126,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -2795,11 +3147,15 @@
     arg_width->add_floatvalue(width);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLineWidth(width);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2816,11 +3172,15 @@
     arg_program->add_intvalue(program);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLinkProgram(program);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2843,11 +3203,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPixelStorei(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2870,11 +3234,15 @@
     arg_units->add_floatvalue(units);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPolygonOffset(factor, units);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2927,11 +3295,15 @@
     arg_pixels->add_intvalue((int)pixels);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glReadPixels(x, y, width, height, format, type, pixels);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2942,11 +3314,15 @@
     glmsg.set_function(GLMessage::glReleaseShaderCompiler);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glReleaseShaderCompiler();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -2981,11 +3357,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRenderbufferStorage(target, internalformat, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3008,11 +3388,15 @@
     arg_invert->add_boolvalue(invert);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glSampleCoverage(value, invert);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3047,11 +3431,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glScissor(x, y, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3092,11 +3480,15 @@
     arg_length->add_intvalue(length);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glShaderBinary(n, shaders, binaryformat, binary, length);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3131,11 +3523,15 @@
     arg_length->add_intvalue((int)length);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glShaderSource(shader, count, string, length);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3164,11 +3560,15 @@
     arg_mask->add_intvalue(mask);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glStencilFunc(func, ref, mask);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3203,11 +3603,15 @@
     arg_mask->add_intvalue(mask);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glStencilFuncSeparate(face, func, ref, mask);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3224,11 +3628,15 @@
     arg_mask->add_intvalue(mask);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glStencilMask(mask);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3251,11 +3659,15 @@
     arg_mask->add_intvalue(mask);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glStencilMaskSeparate(face, mask);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3284,11 +3696,15 @@
     arg_zpass->add_intvalue((int)zpass);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glStencilOp(fail, zfail, zpass);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3323,11 +3739,15 @@
     arg_zpass->add_intvalue((int)zpass);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glStencilOpSeparate(face, fail, zfail, zpass);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3392,11 +3812,15 @@
     arg_pixels->add_intvalue((int)pixels);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3425,11 +3849,15 @@
     arg_param->add_floatvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexParameterf(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3458,11 +3886,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexParameterfv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3491,11 +3923,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexParameteri(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3524,11 +3960,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexParameteriv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3593,11 +4033,15 @@
     arg_pixels->add_intvalue((int)pixels);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3620,11 +4064,15 @@
     arg_x->add_floatvalue(x);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform1f(location, x);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3653,11 +4101,15 @@
     arg_v->add_intvalue((int)v);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform1fv(location, count, v);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3680,11 +4132,15 @@
     arg_x->add_intvalue(x);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform1i(location, x);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3713,11 +4169,15 @@
     arg_v->add_intvalue((int)v);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform1iv(location, count, v);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3746,11 +4206,15 @@
     arg_y->add_floatvalue(y);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform2f(location, x, y);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3779,11 +4243,15 @@
     arg_v->add_intvalue((int)v);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform2fv(location, count, v);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3812,11 +4280,15 @@
     arg_y->add_intvalue(y);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform2i(location, x, y);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3845,11 +4317,15 @@
     arg_v->add_intvalue((int)v);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform2iv(location, count, v);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3884,11 +4360,15 @@
     arg_z->add_floatvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform3f(location, x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3917,11 +4397,15 @@
     arg_v->add_intvalue((int)v);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform3fv(location, count, v);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3956,11 +4440,15 @@
     arg_z->add_intvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform3i(location, x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -3989,11 +4477,15 @@
     arg_v->add_intvalue((int)v);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform3iv(location, count, v);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4034,11 +4526,15 @@
     arg_w->add_floatvalue(w);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform4f(location, x, y, z, w);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4067,11 +4563,15 @@
     arg_v->add_intvalue((int)v);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform4fv(location, count, v);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4112,11 +4612,15 @@
     arg_w->add_intvalue(w);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform4i(location, x, y, z, w);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4145,11 +4649,15 @@
     arg_v->add_intvalue((int)v);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniform4iv(location, count, v);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4184,11 +4692,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniformMatrix2fv(location, count, transpose, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4223,11 +4735,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniformMatrix3fv(location, count, transpose, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4262,11 +4778,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUniformMatrix4fv(location, count, transpose, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4283,11 +4803,15 @@
     arg_program->add_intvalue(program);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUseProgram(program);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4304,11 +4828,15 @@
     arg_program->add_intvalue(program);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glValidateProgram(program);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4331,11 +4859,15 @@
     arg_x->add_floatvalue(x);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttrib1f(indx, x);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4358,11 +4890,15 @@
     arg_values->add_intvalue((int)values);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttrib1fv(indx, values);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4391,11 +4927,15 @@
     arg_y->add_floatvalue(y);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttrib2f(indx, x, y);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4418,11 +4958,15 @@
     arg_values->add_intvalue((int)values);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttrib2fv(indx, values);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4457,11 +5001,15 @@
     arg_z->add_floatvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttrib3f(indx, x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4484,11 +5032,15 @@
     arg_values->add_intvalue((int)values);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttrib3fv(indx, values);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4529,11 +5081,15 @@
     arg_w->add_floatvalue(w);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttrib4f(indx, x, y, z, w);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4556,11 +5112,15 @@
     arg_values->add_intvalue((int)values);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttrib4fv(indx, values);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4607,11 +5167,15 @@
     arg_ptr->add_intvalue((int)ptr);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexAttribPointer(indx, size, type, normalized, stride, ptr);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4646,11 +5210,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glViewport(x, y, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4676,11 +5244,15 @@
     arg_image->add_intvalue((int)image);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEGLImageTargetTexture2DOES(target, image);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4703,11 +5275,15 @@
     arg_image->add_intvalue((int)image);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEGLImageTargetRenderbufferStorageOES(target, image);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4748,11 +5324,15 @@
     arg_binary->add_intvalue((int)binary);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetProgramBinaryOES(program, bufSize, length, binaryFormat, binary);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4787,11 +5367,15 @@
     arg_length->add_intvalue(length);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramBinaryOES(program, binaryFormat, binary, length);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4814,9 +5398,11 @@
     arg_access->add_intvalue((int)access);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     void* retValue = glContext->hooks->gl.glMapBufferOES(target, access);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -4824,7 +5410,9 @@
     rt->set_type(GLMessage::DataType::INT);
     rt->add_intvalue((int)retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -4843,9 +5431,11 @@
     arg_target->add_intvalue((int)target);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glUnmapBufferOES(target);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -4853,7 +5443,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -4884,11 +5476,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetBufferPointervOES(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -4959,11 +5555,15 @@
     arg_pixels->add_intvalue((int)pixels);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexImage3DOES(target, level, internalformat, width, height, depth, border, format, type, pixels);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5040,11 +5640,15 @@
     arg_pixels->add_intvalue((int)pixels);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexSubImage3DOES(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5109,11 +5713,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCopyTexSubImage3DOES(target, level, xoffset, yoffset, zoffset, x, y, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5178,11 +5786,15 @@
     arg_data->add_intvalue((int)data);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCompressedTexImage3DOES(target, level, internalformat, width, height, depth, border, imageSize, data);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5259,11 +5871,15 @@
     arg_data->add_intvalue((int)data);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCompressedTexSubImage3DOES(target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5310,11 +5926,15 @@
     arg_zoffset->add_intvalue(zoffset);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFramebufferTexture3DOES(target, attachment, textarget, texture, level, zoffset);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5331,11 +5951,15 @@
     arg_array->add_intvalue(array);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindVertexArrayOES(array);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5358,11 +5982,15 @@
     arg_arrays->add_intvalue((int)arrays);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteVertexArraysOES(n, arrays);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5385,11 +6013,15 @@
     arg_arrays->add_intvalue((int)arrays);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenVertexArraysOES(n, arrays);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5406,9 +6038,11 @@
     arg_array->add_intvalue(array);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsVertexArrayOES(array);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -5416,7 +6050,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -5447,11 +6083,15 @@
     arg_groups->add_intvalue((int)groups);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetPerfMonitorGroupsAMD(numGroups, groupsSize, groups);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5492,11 +6132,15 @@
     arg_counters->add_intvalue((int)counters);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetPerfMonitorCountersAMD(group, numCounters, maxActiveCounters, counterSize, counters);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5531,11 +6175,15 @@
     arg_groupString->add_intvalue((int)groupString);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetPerfMonitorGroupStringAMD(group, bufSize, length, groupString);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5576,11 +6224,15 @@
     arg_counterString->add_intvalue((int)counterString);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetPerfMonitorCounterStringAMD(group, counter, bufSize, length, counterString);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5615,11 +6267,15 @@
     arg_data->add_intvalue((int)data);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetPerfMonitorCounterInfoAMD(group, counter, pname, data);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5642,11 +6298,15 @@
     arg_monitors->add_intvalue((int)monitors);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenPerfMonitorsAMD(n, monitors);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5669,11 +6329,15 @@
     arg_monitors->add_intvalue((int)monitors);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeletePerfMonitorsAMD(n, monitors);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5714,11 +6378,15 @@
     arg_countersList->add_intvalue((int)countersList);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glSelectPerfMonitorCountersAMD(monitor, enable, group, numCounters, countersList);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5735,11 +6403,15 @@
     arg_monitor->add_intvalue(monitor);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBeginPerfMonitorAMD(monitor);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5756,11 +6428,15 @@
     arg_monitor->add_intvalue(monitor);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEndPerfMonitorAMD(monitor);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5801,11 +6477,15 @@
     arg_bytesWritten->add_intvalue((int)bytesWritten);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetPerfMonitorCounterDataAMD(monitor, pname, dataSize, data, bytesWritten);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5876,11 +6556,15 @@
     arg_filter->add_intvalue((int)filter);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlitFramebufferANGLE(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5921,11 +6605,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRenderbufferStorageMultisampleANGLE(target, samples, internalformat, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5966,11 +6654,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRenderbufferStorageMultisampleAPPLE(target, samples, internalformat, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -5981,11 +6673,15 @@
     glmsg.set_function(GLMessage::glResolveMultisampleFramebufferAPPLE);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glResolveMultisampleFramebufferAPPLE();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6020,11 +6716,15 @@
     arg_label->add_intvalue((int)label);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLabelObjectEXT(type, object, length, label);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6065,11 +6765,15 @@
     arg_label->add_intvalue((int)label);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetObjectLabelEXT(type, object, bufSize, length, label);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6092,11 +6796,15 @@
     arg_marker->add_intvalue((int)marker);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glInsertEventMarkerEXT(length, marker);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6119,11 +6827,15 @@
     arg_marker->add_intvalue((int)marker);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPushGroupMarkerEXT(length, marker);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6134,11 +6846,15 @@
     glmsg.set_function(GLMessage::glPopGroupMarkerEXT);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPopGroupMarkerEXT();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6167,11 +6883,15 @@
     arg_attachments->add_intvalue((int)attachments);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDiscardFramebufferEXT(target, numAttachments, attachments);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6212,11 +6932,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6263,11 +6987,15 @@
     arg_samples->add_intvalue(samples);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFramebufferTexture2DMultisampleEXT(target, attachment, textarget, texture, level, samples);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6302,11 +7030,15 @@
     arg_primcount->add_intvalue(primcount);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMultiDrawArraysEXT(mode, first, count, primcount);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6347,11 +7079,15 @@
     arg_primcount->add_intvalue(primcount);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMultiDrawElementsEXT(mode, count, type, indices, primcount);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6374,11 +7110,15 @@
     arg_ids->add_intvalue((int)ids);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenQueriesEXT(n, ids);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6401,11 +7141,15 @@
     arg_ids->add_intvalue((int)ids);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteQueriesEXT(n, ids);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6422,9 +7166,11 @@
     arg_id->add_intvalue(id);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsQueryEXT(id);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -6432,7 +7178,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -6457,11 +7205,15 @@
     arg_id->add_intvalue(id);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBeginQueryEXT(target, id);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6478,11 +7230,15 @@
     arg_target->add_intvalue((int)target);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEndQueryEXT(target);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6511,11 +7267,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetQueryivEXT(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6544,11 +7304,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetQueryObjectuivEXT(id, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6559,9 +7323,11 @@
     glmsg.set_function(GLMessage::glGetGraphicsResetStatusEXT);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLenum retValue = glContext->hooks->gl.glGetGraphicsResetStatusEXT();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -6569,7 +7335,9 @@
     rt->set_type(GLMessage::DataType::ENUM);
     rt->add_intvalue((int)retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -6630,11 +7398,15 @@
     arg_data->add_intvalue((int)data);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glReadnPixelsEXT(x, y, width, height, format, type, bufSize, data);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6669,11 +7441,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetnUniformfvEXT(program, location, bufSize, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6708,11 +7484,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetnUniformivEXT(program, location, bufSize, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6741,11 +7521,15 @@
     arg_program->add_intvalue(program);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glUseProgramStagesEXT(pipeline, stages, program);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6768,11 +7552,15 @@
     arg_program->add_intvalue(program);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glActiveShaderProgramEXT(pipeline, program);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6801,9 +7589,11 @@
     arg_strings->add_intvalue((int)strings);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLuint retValue = glContext->hooks->gl.glCreateShaderProgramvEXT(type, count, strings);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -6811,7 +7601,9 @@
     rt->set_type(GLMessage::DataType::INT);
     rt->add_intvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -6830,11 +7622,15 @@
     arg_pipeline->add_intvalue(pipeline);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindProgramPipelineEXT(pipeline);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6857,11 +7653,15 @@
     arg_pipelines->add_intvalue((int)pipelines);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteProgramPipelinesEXT(n, pipelines);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6884,11 +7684,15 @@
     arg_pipelines->add_intvalue((int)pipelines);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenProgramPipelinesEXT(n, pipelines);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6905,9 +7709,11 @@
     arg_pipeline->add_intvalue(pipeline);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsProgramPipelineEXT(pipeline);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -6915,7 +7721,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -6946,11 +7754,15 @@
     arg_value->add_intvalue(value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramParameteriEXT(program, pname, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -6979,11 +7791,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetProgramPipelineivEXT(pipeline, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7012,11 +7828,15 @@
     arg_x->add_intvalue(x);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform1iEXT(program, location, x);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7051,11 +7871,15 @@
     arg_y->add_intvalue(y);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform2iEXT(program, location, x, y);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7096,11 +7920,15 @@
     arg_z->add_intvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform3iEXT(program, location, x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7147,11 +7975,15 @@
     arg_w->add_intvalue(w);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform4iEXT(program, location, x, y, z, w);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7180,11 +8012,15 @@
     arg_x->add_floatvalue(x);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform1fEXT(program, location, x);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7219,11 +8055,15 @@
     arg_y->add_floatvalue(y);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform2fEXT(program, location, x, y);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7264,11 +8104,15 @@
     arg_z->add_floatvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform3fEXT(program, location, x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7315,11 +8159,15 @@
     arg_w->add_floatvalue(w);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform4fEXT(program, location, x, y, z, w);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7354,11 +8202,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform1ivEXT(program, location, count, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7393,11 +8245,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform2ivEXT(program, location, count, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7432,11 +8288,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform3ivEXT(program, location, count, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7471,11 +8331,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform4ivEXT(program, location, count, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7510,11 +8374,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform1fvEXT(program, location, count, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7549,11 +8417,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform2fvEXT(program, location, count, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7588,11 +8460,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform3fvEXT(program, location, count, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7627,11 +8503,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniform4fvEXT(program, location, count, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7672,11 +8552,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniformMatrix2fvEXT(program, location, count, transpose, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7717,11 +8601,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniformMatrix3fvEXT(program, location, count, transpose, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7762,11 +8650,15 @@
     arg_value->add_intvalue((int)value);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glProgramUniformMatrix4fvEXT(program, location, count, transpose, value);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7783,11 +8675,15 @@
     arg_pipeline->add_intvalue(pipeline);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glValidateProgramPipelineEXT(pipeline);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7822,11 +8718,15 @@
     arg_infoLog->add_intvalue((int)infoLog);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetProgramPipelineInfoLogEXT(pipeline, bufSize, length, infoLog);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7861,11 +8761,15 @@
     arg_width->add_intvalue(width);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexStorage1DEXT(target, levels, internalformat, width);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7906,11 +8810,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexStorage2DEXT(target, levels, internalformat, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -7957,11 +8865,15 @@
     arg_depth->add_intvalue(depth);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexStorage3DEXT(target, levels, internalformat, width, height, depth);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8002,11 +8914,15 @@
     arg_width->add_intvalue(width);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTextureStorage1DEXT(texture, target, levels, internalformat, width);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8053,11 +8969,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTextureStorage2DEXT(texture, target, levels, internalformat, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8110,11 +9030,15 @@
     arg_depth->add_intvalue(depth);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTextureStorage3DEXT(texture, target, levels, internalformat, width, height, depth);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8155,11 +9079,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRenderbufferStorageMultisampleIMG(target, samples, internalformat, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8206,11 +9134,15 @@
     arg_samples->add_intvalue(samples);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFramebufferTexture2DMultisampleIMG(target, attachment, textarget, texture, level, samples);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8227,11 +9159,15 @@
     arg_mask->add_boolvalue(mask);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCoverageMaskNV(mask);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8248,11 +9184,15 @@
     arg_operation->add_intvalue((int)operation);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCoverageOperationNV(operation);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8275,11 +9215,15 @@
     arg_bufs->add_intvalue((int)bufs);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawBuffersNV(n, bufs);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8302,11 +9246,15 @@
     arg_fences->add_intvalue((int)fences);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteFencesNV(n, fences);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8329,11 +9277,15 @@
     arg_fences->add_intvalue((int)fences);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenFencesNV(n, fences);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8350,9 +9302,11 @@
     arg_fence->add_intvalue(fence);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsFenceNV(fence);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -8360,7 +9314,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -8379,9 +9335,11 @@
     arg_fence->add_intvalue(fence);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glTestFenceNV(fence);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -8389,7 +9347,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -8420,11 +9380,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetFenceivNV(fence, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8441,11 +9405,15 @@
     arg_fence->add_intvalue(fence);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFinishFenceNV(fence);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8468,11 +9436,15 @@
     arg_condition->add_intvalue((int)condition);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glSetFenceNV(fence, condition);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8489,11 +9461,15 @@
     arg_mode->add_intvalue((int)mode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glReadBufferNV(mode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8516,11 +9492,15 @@
     arg_ref->add_floatvalue(ref);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glAlphaFuncQCOM(func, ref);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8549,11 +9529,15 @@
     arg_driverControls->add_intvalue((int)driverControls);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetDriverControlsQCOM(num, size, driverControls);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8588,11 +9572,15 @@
     arg_driverControlString->add_intvalue((int)driverControlString);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetDriverControlStringQCOM(driverControl, bufSize, length, driverControlString);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8609,11 +9597,15 @@
     arg_driverControl->add_intvalue(driverControl);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEnableDriverControlQCOM(driverControl);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8630,11 +9622,15 @@
     arg_driverControl->add_intvalue(driverControl);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDisableDriverControlQCOM(driverControl);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8663,11 +9659,15 @@
     arg_numTextures->add_intvalue((int)numTextures);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetTexturesQCOM(textures, maxTextures, numTextures);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8696,11 +9696,15 @@
     arg_numBuffers->add_intvalue((int)numBuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetBuffersQCOM(buffers, maxBuffers, numBuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8729,11 +9733,15 @@
     arg_numRenderbuffers->add_intvalue((int)numRenderbuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetRenderbuffersQCOM(renderbuffers, maxRenderbuffers, numRenderbuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8762,11 +9770,15 @@
     arg_numFramebuffers->add_intvalue((int)numFramebuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetFramebuffersQCOM(framebuffers, maxFramebuffers, numFramebuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8807,11 +9819,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetTexLevelParameterivQCOM(texture, face, level, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8840,11 +9856,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtTexObjectStateOverrideiQCOM(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8921,11 +9941,15 @@
     arg_texels->add_intvalue((int)texels);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetTexSubImageQCOM(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texels);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8948,11 +9972,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetBufferPointervQCOM(target, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -8981,11 +10009,15 @@
     arg_numShaders->add_intvalue((int)numShaders);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetShadersQCOM(shaders, maxShaders, numShaders);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9014,11 +10046,15 @@
     arg_numPrograms->add_intvalue((int)numPrograms);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetProgramsQCOM(programs, maxPrograms, numPrograms);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9035,9 +10071,11 @@
     arg_program->add_intvalue(program);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glExtIsProgramBinaryQCOM(program);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -9045,7 +10083,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -9082,11 +10122,15 @@
     arg_length->add_intvalue((int)length);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glExtGetProgramBinarySourceQCOM(program, shadertype, source, length);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9127,11 +10171,15 @@
     arg_preserveMask->add_intvalue(preserveMask);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glStartTilingQCOM(x, y, width, height, preserveMask);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9148,11 +10196,15 @@
     arg_preserveMask->add_intvalue(preserveMask);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEndTilingQCOM(preserveMask);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9178,11 +10230,15 @@
     arg_ref->add_floatvalue(ref);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glAlphaFunc(func, ref);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9205,11 +10261,15 @@
     arg_equation->add_intvalue((int)equation);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClipPlanef(plane, equation);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9244,11 +10304,15 @@
     arg_alpha->add_floatvalue(alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glColor4f(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9271,11 +10335,15 @@
     arg_param->add_floatvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFogf(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9298,11 +10366,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFogfv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9349,11 +10421,15 @@
     arg_zFar->add_floatvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFrustumf(left, right, bottom, top, zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9376,11 +10452,15 @@
     arg_eqn->add_intvalue((int)eqn);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetClipPlanef(pname, eqn);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9409,11 +10489,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetLightfv(light, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9442,11 +10526,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetMaterialfv(face, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9475,11 +10563,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexEnvfv(env, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9502,11 +10594,15 @@
     arg_param->add_floatvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightModelf(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9529,11 +10625,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightModelfv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9562,11 +10662,15 @@
     arg_param->add_floatvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightf(light, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9595,11 +10699,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightfv(light, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9616,11 +10724,15 @@
     arg_m->add_intvalue((int)m);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLoadMatrixf(m);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9649,11 +10761,15 @@
     arg_param->add_floatvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMaterialf(face, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9682,11 +10798,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMaterialfv(face, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9703,11 +10823,15 @@
     arg_m->add_intvalue((int)m);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMultMatrixf(m);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9748,11 +10872,15 @@
     arg_q->add_floatvalue(q);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMultiTexCoord4f(target, s, t, r, q);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9781,11 +10909,15 @@
     arg_nz->add_floatvalue(nz);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glNormal3f(nx, ny, nz);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9832,11 +10964,15 @@
     arg_zFar->add_floatvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glOrthof(left, right, bottom, top, zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9859,11 +10995,15 @@
     arg_param->add_floatvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointParameterf(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9886,11 +11026,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointParameterfv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9907,11 +11051,15 @@
     arg_size->add_floatvalue(size);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointSize(size);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9946,11 +11094,15 @@
     arg_z->add_floatvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRotatef(angle, x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -9979,11 +11131,15 @@
     arg_z->add_floatvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glScalef(x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10012,11 +11168,15 @@
     arg_param->add_floatvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexEnvf(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10045,11 +11205,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexEnvfv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10078,11 +11242,15 @@
     arg_z->add_floatvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTranslatef(x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10105,11 +11273,15 @@
     arg_ref->add_intvalue(ref);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glAlphaFuncx(func, ref);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10144,11 +11316,15 @@
     arg_alpha->add_intvalue(alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClearColorx(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10165,11 +11341,15 @@
     arg_depth->add_intvalue(depth);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClearDepthx(depth);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10186,11 +11366,15 @@
     arg_texture->add_intvalue((int)texture);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClientActiveTexture(texture);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10213,11 +11397,15 @@
     arg_equation->add_intvalue((int)equation);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClipPlanex(plane, equation);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10252,11 +11440,15 @@
     arg_alpha->add_intvalue((int)alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glColor4ub(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10291,11 +11483,15 @@
     arg_alpha->add_intvalue(alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glColor4x(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10330,11 +11526,15 @@
     arg_pointer->add_intvalue((int)pointer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glColorPointer(size, type, stride, pointer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10357,11 +11557,15 @@
     arg_zFar->add_intvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDepthRangex(zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10378,11 +11582,15 @@
     arg_array->add_intvalue((int)array);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDisableClientState(array);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10399,11 +11607,15 @@
     arg_array->add_intvalue((int)array);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glEnableClientState(array);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10426,11 +11638,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFogx(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10453,11 +11669,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFogxv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10504,11 +11724,15 @@
     arg_zFar->add_intvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFrustumx(left, right, bottom, top, zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10531,11 +11755,15 @@
     arg_eqn->add_intvalue((int)eqn);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetClipPlanex(pname, eqn);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10558,11 +11786,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetFixedv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10591,11 +11823,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetLightxv(light, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10624,11 +11860,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetMaterialxv(face, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10651,11 +11891,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetPointerv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10684,11 +11928,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexEnviv(env, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10717,11 +11965,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexEnvxv(env, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10750,11 +12002,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexParameterxv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10777,11 +12033,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightModelx(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10804,11 +12064,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightModelxv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10837,11 +12101,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightx(light, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10870,11 +12138,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightxv(light, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10891,11 +12163,15 @@
     arg_width->add_intvalue(width);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLineWidthx(width);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10906,11 +12182,15 @@
     glmsg.set_function(GLMessage::glLoadIdentity);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLoadIdentity();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10927,11 +12207,15 @@
     arg_m->add_intvalue((int)m);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLoadMatrixx(m);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10948,11 +12232,15 @@
     arg_opcode->add_intvalue((int)opcode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLogicOp(opcode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -10981,11 +12269,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMaterialx(face, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11014,11 +12306,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMaterialxv(face, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11035,11 +12331,15 @@
     arg_mode->add_intvalue((int)mode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMatrixMode(mode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11056,11 +12356,15 @@
     arg_m->add_intvalue((int)m);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMultMatrixx(m);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11101,11 +12405,15 @@
     arg_q->add_intvalue(q);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMultiTexCoord4x(target, s, t, r, q);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11134,11 +12442,15 @@
     arg_nz->add_intvalue(nz);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glNormal3x(nx, ny, nz);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11167,11 +12479,15 @@
     arg_pointer->add_intvalue((int)pointer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glNormalPointer(type, stride, pointer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11218,11 +12534,15 @@
     arg_zFar->add_intvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glOrthox(left, right, bottom, top, zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11245,11 +12565,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointParameterx(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11272,11 +12596,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointParameterxv(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11293,11 +12621,15 @@
     arg_size->add_intvalue(size);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointSizex(size);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11320,11 +12652,15 @@
     arg_units->add_intvalue(units);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPolygonOffsetx(factor, units);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11335,11 +12671,15 @@
     glmsg.set_function(GLMessage::glPopMatrix);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPopMatrix();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11350,11 +12690,15 @@
     glmsg.set_function(GLMessage::glPushMatrix);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPushMatrix();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11389,11 +12733,15 @@
     arg_z->add_intvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRotatex(angle, x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11416,11 +12764,15 @@
     arg_invert->add_boolvalue(invert);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glSampleCoveragex(value, invert);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11449,11 +12801,15 @@
     arg_z->add_intvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glScalex(x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11470,11 +12826,15 @@
     arg_mode->add_intvalue((int)mode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glShadeModel(mode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11509,11 +12869,15 @@
     arg_pointer->add_intvalue((int)pointer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexCoordPointer(size, type, stride, pointer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11542,11 +12906,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexEnvi(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11575,11 +12943,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexEnvx(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11608,11 +12980,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexEnviv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11641,11 +13017,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexEnvxv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11674,11 +13054,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexParameterx(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11707,11 +13091,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexParameterxv(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11740,11 +13128,15 @@
     arg_z->add_intvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTranslatex(x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11779,11 +13171,15 @@
     arg_pointer->add_intvalue((int)pointer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glVertexPointer(size, type, stride, pointer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11812,11 +13208,15 @@
     arg_pointer->add_intvalue((int)pointer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointSizePointerOES(type, stride, pointer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11842,11 +13242,15 @@
     arg_modeAlpha->add_intvalue((int)modeAlpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlendEquationSeparateOES(modeRGB, modeAlpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11881,11 +13285,15 @@
     arg_dstAlpha->add_intvalue((int)dstAlpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlendFuncSeparateOES(srcRGB, dstRGB, srcAlpha, dstAlpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11902,11 +13310,15 @@
     arg_mode->add_intvalue((int)mode);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBlendEquationOES(mode);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11947,11 +13359,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawTexsOES(x, y, z, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -11992,11 +13408,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawTexiOES(x, y, z, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12037,11 +13457,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawTexxOES(x, y, z, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12058,11 +13482,15 @@
     arg_coords->add_intvalue((int)coords);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawTexsvOES(coords);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12079,11 +13507,15 @@
     arg_coords->add_intvalue((int)coords);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawTexivOES(coords);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12100,11 +13532,15 @@
     arg_coords->add_intvalue((int)coords);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawTexxvOES(coords);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12145,11 +13581,15 @@
     arg_height->add_floatvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawTexfOES(x, y, z, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12166,11 +13606,15 @@
     arg_coords->add_intvalue((int)coords);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDrawTexfvOES(coords);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12193,11 +13637,15 @@
     arg_ref->add_intvalue(ref);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glAlphaFuncxOES(func, ref);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12232,11 +13680,15 @@
     arg_alpha->add_intvalue(alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClearColorxOES(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12253,11 +13705,15 @@
     arg_depth->add_intvalue(depth);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClearDepthxOES(depth);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12280,11 +13736,15 @@
     arg_equation->add_intvalue((int)equation);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClipPlanexOES(plane, equation);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12319,11 +13779,15 @@
     arg_alpha->add_intvalue(alpha);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glColor4xOES(red, green, blue, alpha);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12346,11 +13810,15 @@
     arg_zFar->add_intvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDepthRangexOES(zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12373,11 +13841,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFogxOES(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12400,11 +13872,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFogxvOES(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12451,11 +13927,15 @@
     arg_zFar->add_intvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFrustumxOES(left, right, bottom, top, zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12478,11 +13958,15 @@
     arg_eqn->add_intvalue((int)eqn);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetClipPlanexOES(pname, eqn);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12505,11 +13989,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetFixedvOES(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12538,11 +14026,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetLightxvOES(light, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12571,11 +14063,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetMaterialxvOES(face, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12604,11 +14100,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexEnvxvOES(env, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12637,11 +14137,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexParameterxvOES(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12664,11 +14168,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightModelxOES(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12691,11 +14199,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightModelxvOES(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12724,11 +14236,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightxOES(light, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12757,11 +14273,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLightxvOES(light, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12778,11 +14298,15 @@
     arg_width->add_intvalue(width);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLineWidthxOES(width);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12799,11 +14323,15 @@
     arg_m->add_intvalue((int)m);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLoadMatrixxOES(m);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12832,11 +14360,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMaterialxOES(face, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12865,11 +14397,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMaterialxvOES(face, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12886,11 +14422,15 @@
     arg_m->add_intvalue((int)m);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMultMatrixxOES(m);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12931,11 +14471,15 @@
     arg_q->add_intvalue(q);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMultiTexCoord4xOES(target, s, t, r, q);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -12964,11 +14508,15 @@
     arg_nz->add_intvalue(nz);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glNormal3xOES(nx, ny, nz);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13015,11 +14563,15 @@
     arg_zFar->add_intvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glOrthoxOES(left, right, bottom, top, zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13042,11 +14594,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointParameterxOES(pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13069,11 +14625,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointParameterxvOES(pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13090,11 +14650,15 @@
     arg_size->add_intvalue(size);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPointSizexOES(size);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13117,11 +14681,15 @@
     arg_units->add_intvalue(units);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glPolygonOffsetxOES(factor, units);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13156,11 +14724,15 @@
     arg_z->add_intvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRotatexOES(angle, x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13183,11 +14755,15 @@
     arg_invert->add_boolvalue(invert);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glSampleCoveragexOES(value, invert);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13216,11 +14792,15 @@
     arg_z->add_intvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glScalexOES(x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13249,11 +14829,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexEnvxOES(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13282,11 +14866,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexEnvxvOES(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13315,11 +14903,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexParameterxOES(target, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13348,11 +14940,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexParameterxvOES(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13381,11 +14977,15 @@
     arg_z->add_intvalue(z);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTranslatexOES(x, y, z);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13402,9 +15002,11 @@
     arg_renderbuffer->add_intvalue(renderbuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsRenderbufferOES(renderbuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -13412,7 +15014,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -13437,11 +15041,15 @@
     arg_renderbuffer->add_intvalue(renderbuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindRenderbufferOES(target, renderbuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13464,11 +15072,15 @@
     arg_renderbuffers->add_intvalue((int)renderbuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteRenderbuffersOES(n, renderbuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13491,11 +15103,15 @@
     arg_renderbuffers->add_intvalue((int)renderbuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenRenderbuffersOES(n, renderbuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13530,11 +15146,15 @@
     arg_height->add_intvalue(height);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glRenderbufferStorageOES(target, internalformat, width, height);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13563,11 +15183,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetRenderbufferParameterivOES(target, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13584,9 +15208,11 @@
     arg_framebuffer->add_intvalue(framebuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLboolean retValue = glContext->hooks->gl.glIsFramebufferOES(framebuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -13594,7 +15220,9 @@
     rt->set_type(GLMessage::DataType::BOOL);
     rt->add_boolvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -13619,11 +15247,15 @@
     arg_framebuffer->add_intvalue(framebuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glBindFramebufferOES(target, framebuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13646,11 +15278,15 @@
     arg_framebuffers->add_intvalue((int)framebuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDeleteFramebuffersOES(n, framebuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13673,11 +15309,15 @@
     arg_framebuffers->add_intvalue((int)framebuffers);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenFramebuffersOES(n, framebuffers);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13694,9 +15334,11 @@
     arg_target->add_intvalue((int)target);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLenum retValue = glContext->hooks->gl.glCheckFramebufferStatusOES(target);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -13704,7 +15346,9 @@
     rt->set_type(GLMessage::DataType::ENUM);
     rt->add_intvalue((int)retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -13741,11 +15385,15 @@
     arg_renderbuffer->add_intvalue(renderbuffer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFramebufferRenderbufferOES(target, attachment, renderbuffertarget, renderbuffer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13786,11 +15434,15 @@
     arg_level->add_intvalue(level);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFramebufferTexture2DOES(target, attachment, textarget, texture, level);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13825,11 +15477,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetFramebufferAttachmentParameterivOES(target, attachment, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13846,11 +15502,15 @@
     arg_target->add_intvalue((int)target);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGenerateMipmapOES(target);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13867,11 +15527,15 @@
     arg_matrixpaletteindex->add_intvalue(matrixpaletteindex);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glCurrentPaletteMatrixOES(matrixpaletteindex);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13882,11 +15546,15 @@
     glmsg.set_function(GLMessage::glLoadPaletteFromModelViewMatrixOES);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glLoadPaletteFromModelViewMatrixOES();
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13921,11 +15589,15 @@
     arg_pointer->add_intvalue((int)pointer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glMatrixIndexPointerOES(size, type, stride, pointer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13960,11 +15632,15 @@
     arg_pointer->add_intvalue((int)pointer);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glWeightPointerOES(size, type, stride, pointer);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -13987,9 +15663,11 @@
     arg_exponent->add_intvalue((int)exponent);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     GLbitfield retValue = glContext->hooks->gl.glQueryMatrixxOES(mantissa, exponent);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // set return value
     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
@@ -13997,7 +15675,9 @@
     rt->set_type(GLMessage::DataType::INT);
     rt->add_intvalue(retValue);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 
     return retValue;
@@ -14022,11 +15702,15 @@
     arg_zFar->add_floatvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glDepthRangefOES(zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14073,11 +15757,15 @@
     arg_zFar->add_floatvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glFrustumfOES(left, right, bottom, top, zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14124,11 +15812,15 @@
     arg_zFar->add_floatvalue(zFar);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glOrthofOES(left, right, bottom, top, zNear, zFar);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14151,11 +15843,15 @@
     arg_equation->add_intvalue((int)equation);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClipPlanefOES(plane, equation);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14178,11 +15874,15 @@
     arg_eqn->add_intvalue((int)eqn);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetClipPlanefOES(pname, eqn);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14199,11 +15899,15 @@
     arg_depth->add_floatvalue(depth);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClearDepthfOES(depth);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14232,11 +15936,15 @@
     arg_param->add_floatvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexGenfOES(coord, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14265,11 +15973,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexGenfvOES(coord, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14298,11 +16010,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexGeniOES(coord, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14331,11 +16047,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexGenivOES(coord, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14364,11 +16084,15 @@
     arg_param->add_intvalue(param);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexGenxOES(coord, pname, param);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14397,11 +16121,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glTexGenxvOES(coord, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14430,11 +16158,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexGenfvOES(coord, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14463,11 +16195,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexGenivOES(coord, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14496,11 +16232,15 @@
     arg_params->add_intvalue((int)params);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glGetTexGenxvOES(coord, pname, params);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14523,11 +16263,15 @@
     arg_eqn->add_intvalue((int)eqn);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClipPlanefIMG(p, eqn);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
@@ -14550,11 +16294,15 @@
     arg_eqn->add_intvalue((int)eqn);
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
     glContext->hooks->gl.glClipPlanexIMG(p, eqn);
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 }
 
diff --git a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
index 871b5dc..6c4feb5 100644
--- a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
@@ -15,9 +15,13 @@
  */
 
 #include <cutils/log.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
 #include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
 
 #include "gltrace.pb.h"
+#include "gltrace_api.h"
 #include "gltrace_context.h"
 #include "gltrace_fixup.h"
 
@@ -198,6 +202,20 @@
     arg_strpp->add_charvalue(src);
 }
 
+void fixup_glUniformGenericInteger(int argIndex, int nIntegers, GLMessage *glmsg) {
+    /* void glUniform?iv(GLint location, GLsizei count, const GLint *value); */
+    GLMessage_DataType *arg_values = glmsg->mutable_args(argIndex);
+    GLint *src = (GLint*)arg_values->intvalue(0);
+
+    arg_values->set_type(GLMessage::DataType::INT);
+    arg_values->set_isarray(true);
+    arg_values->clear_intvalue();
+
+    for (int i = 0; i < nIntegers; i++) {
+        arg_values->add_intvalue(*src++);
+    }
+}
+
 void fixup_glUniformGeneric(int argIndex, int nFloats, GLMessage *glmsg) {
     GLMessage_DataType *arg_values = glmsg->mutable_args(argIndex);
     GLfloat *src = (GLfloat*)arg_values->intvalue(0);
@@ -223,6 +241,10 @@
     GLMessage_DataType *arg_intarray = glmsg->mutable_args(argIndex);
     GLint *intp = (GLint *)arg_intarray->intvalue(0);
 
+    if (intp == NULL) {
+        return;
+    }
+
     arg_intarray->set_type(GLMessage::DataType::INT);
     arg_intarray->set_isarray(true);
     arg_intarray->clear_intvalue();
@@ -232,6 +254,15 @@
     }
 }
 
+void fixup_GenericEnumArray(int argIndex, int nEnums, GLMessage *glmsg) {
+    // fixup as if they were ints
+    fixup_GenericIntArray(argIndex, nEnums, glmsg);
+
+    // and then set the data type to be enum
+    GLMessage_DataType *arg_enumarray = glmsg->mutable_args(argIndex);
+    arg_enumarray->set_type(GLMessage::DataType::ENUM);
+}
+
 void fixup_glGenGeneric(GLMessage *glmsg) {
     /* void glGen*(GLsizei n, GLuint * buffers); */
     GLMessage_DataType arg_n  = glmsg->args(0);
@@ -270,13 +301,94 @@
     arg_params->add_floatvalue(*src);
 }
 
-void fixupGLMessage(GLTraceContext *context, nsecs_t start, nsecs_t end, GLMessage *glmsg) {
+void fixup_glLinkProgram(GLMessage *glmsg) {
+    /* void glLinkProgram(GLuint program); */
+    GLuint program = glmsg->args(0).intvalue(0);
+
+    /* We don't have to fixup this call, but as soon as a program is linked,
+       we obtain information about all active attributes and uniforms to
+       pass on to the debugger. Note that in order to pass this info to
+       the debugger, all we need to do is call the trace versions of the
+       necessary calls. */
+
+    GLint n, maxNameLength;
+    GLchar *name;
+    GLint size;
+    GLenum type;
+
+    // obtain info regarding active attributes
+    GLTrace_glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &n);
+    GLTrace_glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameLength);
+
+    name = (GLchar *) malloc(maxNameLength);
+    for (int i = 0; i < n; i++) {
+        GLTrace_glGetActiveAttrib(program, i, maxNameLength, NULL, &size, &type, name);
+    }
+    free(name);
+
+    // obtain info regarding active uniforms
+    GLTrace_glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &n);
+    GLTrace_glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength);
+
+    name = (GLchar *) malloc(maxNameLength);
+    for (int i = 0; i < n; i++) {
+        GLTrace_glGetActiveUniform(program, i, maxNameLength, NULL, &size, &type, name);
+    }
+    free(name);
+}
+
+/** Given a glGetActive[Uniform|Attrib] call, obtain the location
+ *  of the variable in the call.
+ */
+int getShaderVariableLocation(GLTraceContext *context, GLMessage *glmsg) {
+    GLMessage_Function func = glmsg->function();
+    if (func != GLMessage::glGetActiveAttrib && func != GLMessage::glGetActiveUniform) {
+        return -1;
+    }
+
+    int program = glmsg->args(0).intvalue(0);
+    GLchar *name = (GLchar*) glmsg->args(6).intvalue(0);
+
+    if (func == GLMessage::glGetActiveAttrib) {
+        return context->hooks->gl.glGetAttribLocation(program, name);
+    } else {
+        return context->hooks->gl.glGetUniformLocation(program, name);
+    }
+}
+
+void fixup_glGetActiveAttribOrUniform(GLMessage *glmsg, int location) {
+    /* void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize,
+                GLsizei* length, GLint* size, GLenum* type, GLchar* name); */
+    /* void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize,
+                GLsizei* length, GLint* size, GLenum* type, GLchar* name) */
+
+    fixup_GenericIntArray(3, 1, glmsg);     // length
+    fixup_GenericIntArray(4, 1, glmsg);     // size
+    fixup_GenericEnumArray(5, 1, glmsg);    // type
+    fixup_CStringPtr(6, glmsg);             // name
+
+    // The index argument in the glGetActive[Attrib|Uniform] functions
+    // does not correspond to the actual location index as used in
+    // glUniform*() or glVertexAttrib*() to actually upload the data.
+    // In order to make things simpler for the debugger, we also pass
+    // a hidden location argument that stores the actual location.
+    // append the location value to the end of the argument list
+    GLMessage_DataType *arg_location = glmsg->add_args();
+    arg_location->set_isarray(false);
+    arg_location->set_type(GLMessage::DataType::INT);
+    arg_location->add_intvalue(location);
+}
+
+void fixupGLMessage(GLTraceContext *context, nsecs_t wallStart, nsecs_t wallEnd,
+                                             nsecs_t threadStart, nsecs_t threadEnd,
+                                             GLMessage *glmsg) {
     // for all messages, set the current context id
     glmsg->set_context_id(context->getId());
 
     // set start time and duration
-    glmsg->set_start_time(start);
-    glmsg->set_duration((unsigned)(end - start));
+    glmsg->set_start_time(wallStart);
+    glmsg->set_duration((unsigned)(wallEnd - wallStart));
+    glmsg->set_threadtime((unsigned)(threadEnd - threadStart));
 
     // do any custom message dependent processing
     switch (glmsg->function()) {
@@ -292,6 +404,19 @@
     case GLMessage::glGenTextures:       /* void glGenTextures(GLsizei n, GLuint *textures); */
         fixup_glGenGeneric(glmsg);
         break;
+    case GLMessage::glLinkProgram:       /* void glLinkProgram(GLuint program); */
+        fixup_glLinkProgram(glmsg);
+        break;
+    case GLMessage::glGetActiveAttrib:
+        fixup_glGetActiveAttribOrUniform(glmsg, getShaderVariableLocation(context, glmsg));
+        break;
+    case GLMessage::glGetActiveUniform:
+        fixup_glGetActiveAttribOrUniform(glmsg, getShaderVariableLocation(context, glmsg));
+        break;
+    case GLMessage::glBindAttribLocation:
+        /* void glBindAttribLocation(GLuint program, GLuint index, const GLchar* name); */
+        fixup_CStringPtr(2, glmsg);
+        break;
     case GLMessage::glGetAttribLocation:  
     case GLMessage::glGetUniformLocation: 
         /* int glGetAttribLocation(GLuint program, const GLchar* name) */
@@ -331,6 +456,38 @@
     case GLMessage::glShaderSource:
         fixup_glShaderSource(glmsg);
         break;
+    case GLMessage::glUniform1iv:
+        /* void glUniform1iv(GLint location, GLsizei count, const GLint *value); */
+        fixup_glUniformGenericInteger(2, 1, glmsg);
+        break;
+    case GLMessage::glUniform2iv:
+        /* void glUniform2iv(GLint location, GLsizei count, const GLint *value); */
+        fixup_glUniformGenericInteger(2, 2, glmsg);
+        break;
+    case GLMessage::glUniform3iv:
+        /* void glUniform3iv(GLint location, GLsizei count, const GLint *value); */
+        fixup_glUniformGenericInteger(2, 3, glmsg);
+        break;
+    case GLMessage::glUniform4iv:
+        /* void glUniform4iv(GLint location, GLsizei count, const GLint *value); */
+        fixup_glUniformGenericInteger(2, 4, glmsg);
+        break;
+    case GLMessage::glUniform1fv:
+        /* void glUniform1fv(GLint location, GLsizei count, const GLfloat *value); */
+        fixup_glUniformGeneric(2, 1, glmsg);
+        break;
+    case GLMessage::glUniform2fv:
+        /* void glUniform2fv(GLint location, GLsizei count, const GLfloat *value); */
+        fixup_glUniformGeneric(2, 2, glmsg);
+        break;
+    case GLMessage::glUniform3fv:
+        /* void glUniform3fv(GLint location, GLsizei count, const GLfloat *value); */
+        fixup_glUniformGeneric(2, 3, glmsg);
+        break;
+    case GLMessage::glUniform4fv:
+        /* void glUniform4fv(GLint location, GLsizei count, const GLfloat *value); */
+        fixup_glUniformGeneric(2, 4, glmsg);
+        break;
     case GLMessage::glUniformMatrix2fv:
         /* void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose,
                                                                     const GLfloat* value) */
diff --git a/opengl/libs/GLES_trace/src/gltrace_fixup.h b/opengl/libs/GLES_trace/src/gltrace_fixup.h
index 64f7545..f63b056 100644
--- a/opengl/libs/GLES_trace/src/gltrace_fixup.h
+++ b/opengl/libs/GLES_trace/src/gltrace_fixup.h
@@ -25,7 +25,9 @@
 namespace android {
 namespace gltrace {
 
-void fixupGLMessage(GLTraceContext *curContext, nsecs_t start, nsecs_t end, GLMessage *message);
+void fixupGLMessage(GLTraceContext *curContext, nsecs_t wallStart, nsecs_t wallEnd,
+                                                nsecs_t threadStart, nsecs_t threadEnd,
+                                                GLMessage *message);
 void fixup_addFBContents(GLTraceContext *curContext, GLMessage *message, FBBinding fbToRead);
 
 };
diff --git a/opengl/libs/GLES_trace/tools/genapi.py b/opengl/libs/GLES_trace/tools/genapi.py
index 557e407..e1660be 100755
--- a/opengl/libs/GLES_trace/tools/genapi.py
+++ b/opengl/libs/GLES_trace/tools/genapi.py
@@ -162,13 +162,15 @@
 <!--(end)-->
 
     // call function
-    nsecs_t start_time = systemTime();
+    nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
 <!--(if retType != "void")-->
     $!retType!$ retValue = glContext->hooks->gl.$!callsite!$;
 <!--(else)-->
     glContext->hooks->gl.$!callsite!$;
 <!--(end)-->
-    nsecs_t end_time = systemTime();
+    nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
+    nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
 <!--(if retType != "void")-->
 
     // set return value
@@ -178,7 +180,9 @@
     rt->$!retDataType.getProtobufCall()!$retValue);
 <!--(end)-->
 
-    fixupGLMessage(glContext, start_time, end_time, &glmsg);
+    fixupGLMessage(glContext, wallStartTime, wallEndTime,
+                              threadStartTime, threadEndTime,
+                              &glmsg);
     glContext->traceGLMessage(&glmsg);
 <!--(if retType != "void")-->
 
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index 8ff51ec..cb0e908 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -30,7 +30,6 @@
 // ----------------------------------------------------------------------------
 
 EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
-EGLAPI EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image);
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen
index 9be40cf..4d8334f 100755
--- a/opengl/libs/tools/glapigen
+++ b/opengl/libs/tools/glapigen
@@ -37,12 +37,6 @@
   #printf("%s", $line);
   
   my $prefix = "";
-  if ($name eq "glEGLImageTargetTexture2DOES") {
-    $prefix = "__";
-  }
-  if ($name eq "glEGLImageTargetRenderbufferStorageOES") {
-    $prefix = "__";
-  }
   if ($name eq "glGetString") {
     $prefix = "__";
   }
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 4ea2c31..3a8e3fc 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -43,6 +43,7 @@
     <bool name="assisted_gps_enabled">true</bool>
     <!--  0 == mobile, 1 == wifi. -->
     <integer name="def_network_preference">1</integer>
+    <bool name="def_netstats_enabled">true</bool>
     <bool name="def_usb_mass_storage_enabled">true</bool>
     <bool name="def_wifi_on">false</bool>
     <bool name="def_networks_available_notification_on">true</bool>
@@ -70,6 +71,8 @@
     <integer name="def_lockscreen_sounds_enabled">1</integer>
     <string name="def_lock_sound" translatable="false">/system/media/audio/ui/Lock.ogg</string>
     <string name="def_unlock_sound" translatable="false">/system/media/audio/ui/Unlock.ogg</string>
+    <bool name="def_lockscreen_disabled">false</bool>
+    <bool name="def_device_provisioned">false</bool>
 
     <!-- Notifications use ringer volume -->
     <bool name="def_notifications_use_ring_volume">true</bool>
@@ -141,4 +144,11 @@
     <bool name="def_dtmf_tones_enabled">true</bool>
     <!-- Default for UI touch sounds enabled -->
     <bool name="def_sound_effects_enabled">true</bool>
+
+    <!-- Development settings -->
+    <bool name="def_stay_on_while_plugged_in">false</bool>
+
+    <!-- Number of retries for connecting to DHCP.
+         Value here is the same as WifiStateMachine.DEFAULT_MAX_DHCP_RETRIES -->
+    <integer name="def_max_dhcp_retries">9</integer>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6e9ea52..330a189 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -63,7 +63,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 75;
+    private static final int DATABASE_VERSION = 76;
 
     private Context mContext;
 
@@ -1006,6 +1006,29 @@
             }
             upgradeVersion = 75;
         }
+        if (upgradeVersion == 75) {
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            Cursor c = null;
+            try {
+                c = db.query("secure", new String[] {"_id", "value"},
+                        "name='lockscreen.disabled'",
+                        null, null, null, null);
+                // only set default if it has not yet been set
+                if (c == null || c.getCount() == 0) {
+                    stmt = db.compileStatement("INSERT INTO system(name,value)"
+                            + " VALUES(?,?);");
+                    loadBooleanSetting(stmt, Settings.System.LOCKSCREEN_DISABLED,
+                            R.bool.def_lockscreen_disabled);
+                }
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (c != null) c.close();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 76;
+        }
 
         // *** Remember to update DATABASE_VERSION above!
 
@@ -1352,7 +1375,9 @@
             loadBooleanSetting(stmt, Settings.System.DIM_SCREEN,
                     R.bool.def_dim_screen);
             loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN,
-                    "1".equals(SystemProperties.get("ro.kernel.qemu")) ? 1 : 0);
+                    ("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
+                        mContext.getResources().getBoolean(R.bool.def_stay_on_while_plugged_in))
+                     ? 1 : 0);
             loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
                     R.integer.def_screen_off_timeout);
     
@@ -1568,6 +1593,18 @@
 
             loadStringSetting(stmt, Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL,
                     R.string.def_accessibility_screen_reader_url);
+
+            loadBooleanSetting(stmt, Settings.System.LOCKSCREEN_DISABLED,
+                    R.bool.def_lockscreen_disabled);
+
+            loadBooleanSetting(stmt, Settings.Secure.DEVICE_PROVISIONED,
+                    R.bool.def_device_provisioned);
+
+            loadBooleanSetting(stmt, Settings.Secure.NETSTATS_ENABLED,
+                    R.bool.def_netstats_enabled);
+
+            loadIntegerSetting(stmt, Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
+                    R.integer.def_max_dhcp_retries);
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index 2d76455..aa27861 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -58,6 +58,7 @@
             android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
             android:scaleType="centerInside"
             android:adjustViewBounds="true"
+            android:visibility="invisible"
         />
 
         <TextView android:id="@+id/app_label"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index b653fcd..bc389f9 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -81,6 +81,7 @@
             android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
             android:scaleType="centerInside"
             android:adjustViewBounds="true"
+            android:visibility="invisible"
         />
 
         <TextView android:id="@+id/app_description"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index cb26db00..333fcda 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -65,6 +65,7 @@
         android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
         android:scaleType="centerInside"
         android:adjustViewBounds="true"
+        android:visibility="invisible"
     />
 
 
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index a5bfb4a..16bb040 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -65,7 +65,7 @@
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Kuvatõmmise salvestamine ..."</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"Kuvatõmmise salvestamine ..."</string>
     <string name="screenshot_saving_text" msgid="2419718443411738818">"Kuvatõmmist salvestatakse."</string>
-    <string name="screenshot_saved_title" msgid="6461865960961414961">"Kuvatõmmis on jäädvustatud."</string>
+    <string name="screenshot_saved_title" msgid="6461865960961414961">"Ekraanipilt on jäädvustatud."</string>
     <string name="screenshot_saved_text" msgid="1152839647677558815">"Puudutage kuvatõmmise vaatamiseks."</string>
     <string name="screenshot_failed_title" msgid="705781116746922771">"Kuvatõmmist ei saanud jäädvustada."</string>
     <string name="screenshot_failed_text" msgid="8134011269572415402">"Kuvatõmmist ei saa salvestada. Mäluseade võib olla kasutuses."</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 39b428a..0249e89 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -36,7 +36,7 @@
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Izaziso"</string>
     <string name="battery_low_title" msgid="2783104807551211639">"Xhuma ishaja."</string>
     <string name="battery_low_subtitle" msgid="1752040062087829196">"Ibhetri iya ngokuphela."</string>
-    <string name="battery_low_percent_format" msgid="1077244949318261761">"okusele okungu-<xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+    <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> okusele"</string>
     <string name="invalid_charger" msgid="4549105996740522523">"Ukushaja i-USB akusekelwe."\n"Sebenzisa kuphela ishaja enikeziwe."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Ukusebenzisa ibhetri"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Izilungiselelo"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index ad38a11..dc2f0be 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -134,6 +134,9 @@
 
     void jumpTo(boolean appearing) {
         mContentView.setTranslationY(appearing ? 0 : mPanelHeight);
+        if (mScrimView.getBackground() != null) {
+            mScrimView.getBackground().setAlpha(appearing ? 255 : 0);
+        }
     }
 
     public void setPanelHeight(int h) {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 4145fc4..92f4ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -16,12 +16,6 @@
 
 package com.android.systemui.recent;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -36,15 +30,17 @@
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Process;
-import android.os.SystemClock;
-import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.LruCache;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.tablet.TabletStatusBar;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
 public class RecentTasksLoader {
     static final String TAG = "RecentTasksLoader";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
@@ -55,11 +51,14 @@
     private Context mContext;
     private RecentsPanelView mRecentsPanel;
 
-    private AsyncTask<Void, Integer, Void> mThumbnailLoader;
+    private AsyncTask<Void, ArrayList<TaskDescription>, Void> mTaskLoader;
+    private AsyncTask<Void, TaskDescription, Void> mThumbnailLoader;
     private final Handler mHandler;
 
     private int mIconDpi;
     private Bitmap mDefaultThumbnailBackground;
+    private Bitmap mDefaultIconBackground;
+    private int mNumTasksInFirstScreenful;
 
     public RecentTasksLoader(Context context) {
         mContext = context;
@@ -76,12 +75,20 @@
             mIconDpi = res.getDisplayMetrics().densityDpi;
         }
 
+        // Render default icon (just a blank image)
+        int defaultIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.app_icon_size);
+        int iconSize = (int) (defaultIconSize * mIconDpi / res.getDisplayMetrics().densityDpi);
+        mDefaultIconBackground = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+
         // Render the default thumbnail background
-        int width = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
-        int height = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+        int thumbnailWidth =
+                (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+        int thumbnailHeight =
+                (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
         int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
 
-        mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        mDefaultThumbnailBackground =
+                Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
         Canvas c = new Canvas(mDefaultThumbnailBackground);
         c.drawColor(color);
 
@@ -95,12 +102,17 @@
 
     public void setRecentsPanel(RecentsPanelView recentsPanel) {
         mRecentsPanel = recentsPanel;
+        mNumTasksInFirstScreenful = mRecentsPanel.numItemsInOneScreenful();
     }
 
     public Bitmap getDefaultThumbnail() {
         return mDefaultThumbnailBackground;
     }
 
+    public Bitmap getDefaultIcon() {
+        return mDefaultIconBackground;
+    }
+
     // Create an TaskDescription, returning null if the title or icon is null, or if it's the
     // home activity
     TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
@@ -114,6 +126,12 @@
             homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
             .resolveActivityInfo(pm, 0);
         }
+        // Don't load the current home activity.
+        if (homeInfo != null
+            && homeInfo.packageName.equals(intent.getComponent().getPackageName())
+            && homeInfo.name.equals(intent.getComponent().getClassName())) {
+            return null;
+        }
 
         intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                 | Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -121,9 +139,8 @@
         if (resolveInfo != null) {
             final ActivityInfo info = resolveInfo.activityInfo;
             final String title = info.loadLabel(pm).toString();
-            Drawable icon = getFullResIcon(resolveInfo, pm);
 
-            if (title != null && title.length() > 0 && icon != null) {
+            if (title != null && title.length() > 0) {
                 if (DEBUG) Log.v(TAG, "creating activity desc for id="
                         + persistentTaskId + ", label=" + title);
 
@@ -131,14 +148,6 @@
                         persistentTaskId, resolveInfo, baseIntent, info.packageName,
                         description);
                 item.setLabel(title);
-                item.setIcon(icon);
-
-                // Don't load the current home activity.
-                if (homeInfo != null
-                        && homeInfo.packageName.equals(intent.getComponent().getPackageName())
-                        && homeInfo.name.equals(intent.getComponent().getClassName())) {
-                    return null;
-                }
 
                 return item;
             } else {
@@ -148,10 +157,12 @@
         return null;
     }
 
-    void loadThumbnail(TaskDescription td) {
+    void loadThumbnailAndIcon(TaskDescription td) {
         final ActivityManager am = (ActivityManager)
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        final PackageManager pm = mContext.getPackageManager();
         ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(td.persistentTaskId);
+        Drawable icon = getFullResIcon(td.resolveInfo, pm);
 
         if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
                 + td + ": " + thumbs.mainThumbnail);
@@ -161,6 +172,10 @@
             } else {
                 td.setThumbnail(mDefaultThumbnailBackground);
             }
+            if (icon != null) {
+                td.setIcon(icon);
+            }
+            td.setLoaded(true);
         }
     }
 
@@ -194,111 +209,149 @@
         return getFullResDefaultActivityIcon();
     }
 
-    public void cancelLoadingThumbnails() {
+    public void cancelLoadingThumbnailsAndIcons() {
+        if (mTaskLoader != null) {
+            mTaskLoader.cancel(false);
+            mTaskLoader = null;
+        }
         if (mThumbnailLoader != null) {
             mThumbnailLoader.cancel(false);
             mThumbnailLoader = null;
         }
     }
 
-    // return a snapshot of the current list of recent apps
-    ArrayList<TaskDescription> getRecentTasks() {
-        cancelLoadingThumbnails();
-
-        ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
-        final PackageManager pm = mContext.getPackageManager();
-        final ActivityManager am = (ActivityManager)
+    public void loadTasksInBackground() {
+        // cancel all previous loading of tasks and thumbnails
+        cancelLoadingThumbnailsAndIcons();
+        final LinkedBlockingQueue<TaskDescription> tasksWaitingForThumbnails =
+                new LinkedBlockingQueue<TaskDescription>();
+        final ArrayList<TaskDescription> taskDescriptionsWaitingToLoad =
+                new ArrayList<TaskDescription>();
+        mTaskLoader = new AsyncTask<Void, ArrayList<TaskDescription>, Void>() {
+            @Override
+            protected void onProgressUpdate(ArrayList<TaskDescription>... values) {
+                if (!isCancelled()) {
+                    ArrayList<TaskDescription> newTasks = values[0];
+                    // do a callback to RecentsPanelView to let it know we have more values
+                    // how do we let it know we're all done? just always call back twice
+                    mRecentsPanel.onTasksLoaded(newTasks);
+                }
+            }
+            @Override
+            protected Void doInBackground(Void... params) {
+                // We load in two stages: first, we update progress with just the first screenful
+                // of items. Then, we update with the rest of the items
+                final int origPri = Process.getThreadPriority(Process.myTid());
+                Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                final PackageManager pm = mContext.getPackageManager();
+                final ActivityManager am = (ActivityManager)
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
-        final List<ActivityManager.RecentTaskInfo> recentTasks =
-                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+                final List<ActivityManager.RecentTaskInfo> recentTasks =
+                        am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+                int numTasks = recentTasks.size();
+                ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
+                        .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
 
-        ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
-                    .resolveActivityInfo(pm, 0);
+                boolean firstScreenful = true;
+                ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
 
-        HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>();
-        int numTasks = recentTasks.size();
-
-        // skip the first task - assume it's either the home screen or the current activity.
-        final int first = 1;
-        recentTasksToKeepInCache.add(recentTasks.get(0).persistentId);
-        for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
-            final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
-
-            TaskDescription item = createTaskDescription(recentInfo.id,
-                    recentInfo.persistentId, recentInfo.baseIntent,
-                    recentInfo.origActivity, recentInfo.description, homeInfo);
-
-            if (item != null) {
-                tasks.add(item);
-                ++index;
-            }
-        }
-
-        // when we're not using the TaskDescription cache, we load the thumbnails in the
-        // background
-        loadThumbnailsInBackground(new ArrayList<TaskDescription>(tasks));
-        return tasks;
-    }
-
-    private void loadThumbnailsInBackground(final ArrayList<TaskDescription> descriptions) {
-        if (descriptions.size() > 0) {
-            if (DEBUG) Log.v(TAG, "Showing " + descriptions.size() + " tasks");
-            loadThumbnail(descriptions.get(0));
-            if (descriptions.size() > 1) {
-                mThumbnailLoader = new AsyncTask<Void, Integer, Void>() {
-                    @Override
-                    protected void onProgressUpdate(Integer... values) {
-                        final TaskDescription td = descriptions.get(values[0]);
-                        if (!isCancelled()) {
-                            mRecentsPanel.onTaskThumbnailLoaded(td);
-                        }
-                        // This is to prevent the loader thread from getting ahead
-                        // of our UI updates.
-                        mHandler.post(new Runnable() {
-                            @Override public void run() {
-                                synchronized (td) {
-                                    td.notifyAll();
-                                }
-                            }
-                        });
+                // skip the first task - assume it's either the home screen or the current activity.
+                final int first = 1;
+                for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+                    if (isCancelled()) {
+                        break;
                     }
+                    final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
+                    TaskDescription item = createTaskDescription(recentInfo.id,
+                            recentInfo.persistentId, recentInfo.baseIntent,
+                            recentInfo.origActivity, recentInfo.description, homeInfo);
 
-                    @Override
-                    protected Void doInBackground(Void... params) {
-                        final int origPri = Process.getThreadPriority(Process.myTid());
-                        Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
-                        long nextTime = SystemClock.uptimeMillis();
-                        for (int i=1; i<descriptions.size(); i++) {
-                            TaskDescription td = descriptions.get(i);
-                            loadThumbnail(td);
-                            long now = SystemClock.uptimeMillis();
-                            nextTime += 0;
-                            if (nextTime > now) {
-                                try {
-                                    Thread.sleep(nextTime-now);
-                                } catch (InterruptedException e) {
-                                }
-                            }
-
-                            if (isCancelled()) {
+                    if (item != null) {
+                        while (true) {
+                            try {
+                                tasksWaitingForThumbnails.put(item);
                                 break;
-                            }
-                            synchronized (td) {
-                                publishProgress(i);
-                                try {
-                                    td.wait(500);
-                                } catch (InterruptedException e) {
-                                }
+                            } catch (InterruptedException e) {
                             }
                         }
-                        Process.setThreadPriority(origPri);
-                        return null;
+                        tasks.add(item);
+                        if (firstScreenful && tasks.size() == mNumTasksInFirstScreenful) {
+                            publishProgress(tasks);
+                            tasks = new ArrayList<TaskDescription>();
+                            firstScreenful = false;
+                            //break;
+                        }
+                        ++index;
                     }
-                };
-                mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+                }
+
+                if (!isCancelled()) {
+                    publishProgress(tasks);
+                    if (firstScreenful) {
+                        // always should publish two updates
+                        publishProgress(new ArrayList<TaskDescription>());
+                    }
+                }
+
+                while (true) {
+                    try {
+                        tasksWaitingForThumbnails.put(new TaskDescription());
+                        break;
+                    } catch (InterruptedException e) {
+                    }
+                }
+
+                Process.setThreadPriority(origPri);
+                return null;
             }
-        }
+        };
+        mTaskLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        loadThumbnailsAndIconsInBackground(tasksWaitingForThumbnails);
     }
 
+    private void loadThumbnailsAndIconsInBackground(
+            final BlockingQueue<TaskDescription> tasksWaitingForThumbnails) {
+        // continually read items from tasksWaitingForThumbnails and load
+        // thumbnails and icons for them. finish thread when cancelled or there
+        // is a null item in tasksWaitingForThumbnails
+        mThumbnailLoader = new AsyncTask<Void, TaskDescription, Void>() {
+            @Override
+            protected void onProgressUpdate(TaskDescription... values) {
+                if (!isCancelled()) {
+                    TaskDescription td = values[0];
+                    mRecentsPanel.onTaskThumbnailLoaded(td);
+                }
+            }
+            @Override
+            protected Void doInBackground(Void... params) {
+                final int origPri = Process.getThreadPriority(Process.myTid());
+                Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
+
+                while (true) {
+                    if (isCancelled()) {
+                        break;
+                    }
+                    TaskDescription td = null;
+                    while (td == null) {
+                        try {
+                            td = tasksWaitingForThumbnails.take();
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                    if (td.isNull()) {
+                        break;
+                    }
+                    loadThumbnailAndIcon(td);
+                    synchronized(td) {
+                        publishProgress(td);
+                    }
+                }
+
+                Process.setThreadPriority(origPri);
+                return null;
+            }
+        };
+        mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index f971d2d..4dc3e33 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -22,11 +22,18 @@
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.FloatMath;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
+import android.view.View.MeasureSpec;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
 import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
 
@@ -34,6 +41,8 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
 
+import java.util.ArrayList;
+
 public class RecentsHorizontalScrollView extends HorizontalScrollView
     implements SwipeHelper.Callback {
     private static final String TAG = RecentsPanelView.TAG;
@@ -44,6 +53,8 @@
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
     private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+    private ArrayList<View> mRecycledViews;
+    private int mNumItemsInOneScreenful;
 
     public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
@@ -51,6 +62,7 @@
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, densityScale, pagingTouchSlop);
         mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, false);
+        mRecycledViews = new ArrayList<View>();
     }
 
     private int scrollPositionOfMostRecent() {
@@ -58,9 +70,23 @@
     }
 
     private void update() {
+        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+            View v = mLinearLayout.getChildAt(i);
+            mRecycledViews.add(v);
+            mAdapter.recycleView(v);
+        }
+        LayoutTransition transitioner = getLayoutTransition();
+        setLayoutTransition(null);
+
         mLinearLayout.removeAllViews();
         for (int i = 0; i < mAdapter.getCount(); i++) {
-            final View view = mAdapter.getView(i, null, mLinearLayout);
+            View old = null;
+            if (mRecycledViews.size() != 0) {
+                old = mRecycledViews.remove(mRecycledViews.size() - 1);
+                old.setVisibility(VISIBLE);
+            }
+
+            final View view = mAdapter.getView(i, old, mLinearLayout);
 
             if (mPerformanceHelper != null) {
                 mPerformanceHelper.addViewCallback(view);
@@ -87,7 +113,8 @@
                 }
             };
 
-            final View thumbnailView = view.findViewById(R.id.app_thumbnail);
+            RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) view.getTag();
+            final View thumbnailView = holder.thumbnailView;
             OnLongClickListener longClickListener = new OnLongClickListener() {
                 public boolean onLongClick(View v) {
                     final View anchorView = view.findViewById(R.id.app_description);
@@ -107,13 +134,21 @@
             appTitle.setOnTouchListener(noOpListener);
             mLinearLayout.addView(view);
         }
+        setLayoutTransition(transitioner);
+
         // Scroll to end after layout.
-        post(new Runnable() {
-            public void run() {
-                mLastScrollPosition = scrollPositionOfMostRecent();
-                scrollTo(mLastScrollPosition, 0);
-            }
-        });
+        final ViewTreeObserver observer = getViewTreeObserver();
+
+        final OnGlobalLayoutListener updateScroll = new OnGlobalLayoutListener() {
+                public void onGlobalLayout() {
+                    mLastScrollPosition = scrollPositionOfMostRecent();
+                    scrollTo(mLastScrollPosition, 0);
+                    if (observer.isAlive()) {
+                        observer.removeOnGlobalLayoutListener(this);
+                    }
+                }
+            };
+        observer.addOnGlobalLayoutListener(updateScroll);
     }
 
     @Override
@@ -142,8 +177,15 @@
     }
 
     public void onChildDismissed(View v) {
+        mRecycledViews.add(v);
         mLinearLayout.removeView(v);
         mCallback.handleSwipe(v);
+        v.setActivated(false);
+        // Restore the alpha/translation parameters to what they were before swiping
+        // (for when these items are recycled)
+        View contentView = getChildContentView(v);
+        contentView.setAlpha(1f);
+        contentView.setTranslationY(0);
     }
 
     public void onBeginDrag(View v) {
@@ -315,6 +357,24 @@
                 update();
             }
         });
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        int childWidthMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.AT_MOST);
+        int childheightMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.AT_MOST);
+        View child = mAdapter.createView(mLinearLayout);
+        child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+        mNumItemsInOneScreenful =
+                (int) FloatMath.ceil(dm.widthPixels / (float) child.getMeasuredWidth());
+        mRecycledViews.add(child);
+
+        for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
+            mRecycledViews.add(mAdapter.createView(mLinearLayout));
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        return mNumItemsInOneScreenful;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index a10e363..7896720 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -27,6 +27,7 @@
 import android.graphics.Matrix;
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -36,18 +37,18 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 import android.widget.PopupMenu;
-import android.widget.RelativeLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
@@ -59,7 +60,7 @@
 
 import java.util.ArrayList;
 
-public class RecentsPanelView extends RelativeLayout implements OnItemClickListener, RecentsCallback,
+public class RecentsPanelView extends FrameLayout implements OnItemClickListener, RecentsCallback,
         StatusBarPanel, Animator.AnimatorListener, View.OnTouchListener {
     static final String TAG = "RecentsPanelView";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
@@ -71,6 +72,10 @@
     private StatusBarTouchProxy mStatusBarTouchProxy;
 
     private boolean mShowing;
+    private boolean mWaitingToShow;
+    private boolean mWaitingToShowAnimated;
+    private boolean mReadyToShow;
+    private int mNumItemsWaitingForThumbnailsAndIcons;
     private Choreographer mChoreo;
     OnRecentsPanelVisibilityChangedListener mVisibilityChangedListener;
 
@@ -104,6 +109,7 @@
         TextView labelView;
         TextView descriptionView;
         TaskDescription taskDescription;
+        boolean loadedThumbnailAndIcon;
     }
 
     /* package */ final class TaskDescriptionAdapter extends BaseAdapter {
@@ -125,42 +131,82 @@
             return position; // we just need something unique for this position
         }
 
-        public View getView(int position, View convertView, ViewGroup parent) {
-            ViewHolder holder;
-            if (convertView == null) {
-                convertView = mInflater.inflate(R.layout.status_bar_recent_item, parent, false);
-                holder = new ViewHolder();
-                holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
-                holder.thumbnailViewImage = (ImageView) convertView.findViewById(
-                        R.id.app_thumbnail_image);
-                // If we set the default thumbnail now, we avoid an onLayout when we update
-                // the thumbnail later (if they both have the same dimensions)
+        public View createView(ViewGroup parent) {
+            View convertView = mInflater.inflate(R.layout.status_bar_recent_item, parent, false);
+            ViewHolder holder = new ViewHolder();
+            holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
+            holder.thumbnailViewImage =
+                    (ImageView) convertView.findViewById(R.id.app_thumbnail_image);
+            // If we set the default thumbnail now, we avoid an onLayout when we update
+            // the thumbnail later (if they both have the same dimensions)
+            if (mRecentTasksLoader != null) {
                 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
-
-                holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
-                holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
-                holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
-
-                convertView.setTag(holder);
-            } else {
-                holder = (ViewHolder) convertView.getTag();
             }
+            holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
+            if (mRecentTasksLoader != null) {
+                holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
+            }
+            holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
+            holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
+
+            convertView.setTag(holder);
+            return convertView;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = createView(parent);
+            }
+            ViewHolder holder = (ViewHolder) convertView.getTag();
 
             // index is reverse since most recent appears at the bottom...
             final int index = mRecentTaskDescriptions.size() - position - 1;
 
             final TaskDescription td = mRecentTaskDescriptions.get(index);
-            holder.iconView.setImageDrawable(td.getIcon());
+
             holder.labelView.setText(td.getLabel());
             holder.thumbnailView.setContentDescription(td.getLabel());
-            updateThumbnail(holder, td.getThumbnail(), true, false);
+            holder.loadedThumbnailAndIcon = td.isLoaded();
+            if (td.isLoaded()) {
+                updateThumbnail(holder, td.getThumbnail(), true, false);
+                updateIcon(holder, td.getIcon(), true, false);
+                mNumItemsWaitingForThumbnailsAndIcons--;
+            }
 
             holder.thumbnailView.setTag(td);
             holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
             holder.taskDescription = td;
-
             return convertView;
         }
+
+        public void recycleView(View v) {
+            ViewHolder holder = (ViewHolder) v.getTag();
+            updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
+            holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
+            holder.iconView.setVisibility(INVISIBLE);
+            holder.labelView.setText(null);
+            holder.thumbnailView.setContentDescription(null);
+            holder.thumbnailView.setTag(null);
+            holder.thumbnailView.setOnLongClickListener(null);
+            holder.thumbnailView.setVisibility(INVISIBLE);
+            holder.taskDescription = null;
+            holder.loadedThumbnailAndIcon = false;
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        if (mRecentsContainer instanceof RecentsHorizontalScrollView){
+            RecentsHorizontalScrollView scrollView
+                    = (RecentsHorizontalScrollView) mRecentsContainer;
+            return scrollView.numItemsInOneScreenful();
+        } else if (mRecentsContainer instanceof RecentsVerticalScrollView){
+            RecentsVerticalScrollView scrollView
+                    = (RecentsVerticalScrollView) mRecentsContainer;
+            return scrollView.numItemsInOneScreenful();
+        }
+        else {
+            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
+        }
     }
 
     @Override
@@ -192,15 +238,32 @@
     }
 
     public void show(boolean show, boolean animate) {
-        show(show, animate, null);
+        if (show) {
+            refreshRecentTasksList(null, true);
+            mWaitingToShow = true;
+            mWaitingToShowAnimated = animate;
+            showIfReady();
+        } else {
+            show(show, animate, null, false);
+        }
+    }
+
+    private void showIfReady() {
+        // mWaitingToShow = there was a touch up on the recents button
+        // mReadyToShow = we've created views for the first screenful of items
+        if (mWaitingToShow && mReadyToShow) { // && mNumItemsWaitingForThumbnailsAndIcons <= 0
+            show(true, mWaitingToShowAnimated, null, false);
+        }
     }
 
     public void show(boolean show, boolean animate,
-            ArrayList<TaskDescription> recentTaskDescriptions) {
+            ArrayList<TaskDescription> recentTaskDescriptions, boolean firstScreenful) {
+        // For now, disable animations. We may want to re-enable in the future
+        animate = false;
         if (show) {
             // Need to update list of recent apps before we set visibility so this view's
             // content description is updated before it gets focus for TalkBack mode
-            refreshRecentTasksList(recentTaskDescriptions);
+            refreshRecentTasksList(recentTaskDescriptions, firstScreenful);
 
             // if there are no apps, either bring up a "No recent apps" message, or just
             // quit early
@@ -209,19 +272,24 @@
                 mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
             } else {
                 if (noApps) {
-                    if (DEBUG) Log.v(TAG, "Nothing to show");
+                   if (DEBUG) Log.v(TAG, "Nothing to show");
                     // Need to set recent tasks to dirty so that next time we load, we
                     // refresh the list of tasks
-                    mRecentTasksLoader.cancelLoadingThumbnails();
+                    mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
                     mRecentTasksDirty = true;
+
+                    mWaitingToShow = false;
+                    mReadyToShow = false;
                     return;
                 }
             }
         } else {
             // Need to set recent tasks to dirty so that next time we load, we
             // refresh the list of tasks
-            mRecentTasksLoader.cancelLoadingThumbnails();
+            mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
             mRecentTasksDirty = true;
+            mWaitingToShow = false;
+            mReadyToShow = false;
         }
         if (animate) {
             if (mShowing != show) {
@@ -385,7 +453,6 @@
             throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
         }
 
-
         mRecentsScrim = findViewById(R.id.recents_bg_protect);
         mRecentsNoApps = findViewById(R.id.recents_no_apps);
         mChoreo = new Choreographer(this, mRecentsScrim, mRecentsContainer, mRecentsNoApps, this);
@@ -425,6 +492,20 @@
         }
     }
 
+
+    private void updateIcon(ViewHolder h, Drawable icon, boolean show, boolean anim) {
+        if (icon != null) {
+            h.iconView.setImageDrawable(icon);
+            if (show && h.iconView.getVisibility() != View.VISIBLE) {
+                if (anim) {
+                    h.iconView.setAnimation(
+                            AnimationUtils.loadAnimation(mContext, R.anim.recent_appear));
+                }
+                h.iconView.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
     private void updateThumbnail(ViewHolder h, Bitmap thumbnail, boolean show, boolean anim) {
         if (thumbnail != null) {
             // Should remove the default image in the frame
@@ -458,31 +539,36 @@
         }
     }
 
-    void onTaskThumbnailLoaded(TaskDescription ad) {
-        synchronized (ad) {
+    void onTaskThumbnailLoaded(TaskDescription td) {
+        synchronized (td) {
             if (mRecentsContainer != null) {
                 ViewGroup container = mRecentsContainer;
                 if (container instanceof HorizontalScrollView
                         || container instanceof ScrollView) {
-                    container = (ViewGroup)container.findViewById(
+                    container = (ViewGroup) container.findViewById(
                             R.id.recents_linear_layout);
                 }
                 // Look for a view showing this thumbnail, to update.
-                for (int i=0; i<container.getChildCount(); i++) {
+                for (int i=0; i < container.getChildCount(); i++) {
                     View v = container.getChildAt(i);
                     if (v.getTag() instanceof ViewHolder) {
                         ViewHolder h = (ViewHolder)v.getTag();
-                        if (h.taskDescription == ad) {
+                        if (!h.loadedThumbnailAndIcon && h.taskDescription == td) {
                             // only fade in the thumbnail if recents is already visible-- we
                             // show it immediately otherwise
-                            boolean animateShow = mShowing &&
-                                mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
-                            updateThumbnail(h, ad.getThumbnail(), true, animateShow);
+                            //boolean animateShow = mShowing &&
+                            //    mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
+                            boolean animateShow = false;
+                            updateIcon(h, td.getIcon(), true, animateShow);
+                            updateThumbnail(h, td.getThumbnail(), true, animateShow);
+                            h.loadedThumbnailAndIcon = true;
+                            mNumItemsWaitingForThumbnailsAndIcons--;
                         }
                     }
                 }
             }
-        }
+            }
+        showIfReady();
     }
 
     // additional optimization when we have sofware system buttons - start loading the recent
@@ -516,7 +602,7 @@
     public void clearRecentTasksList() {
         // Clear memory used by screenshots
         if (mRecentTaskDescriptions != null) {
-            mRecentTasksLoader.cancelLoadingThumbnails();
+            mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
             mRecentTaskDescriptions.clear();
             mListAdapter.notifyDataSetInvalidated();
             mRecentTasksDirty = true;
@@ -524,26 +610,50 @@
     }
 
     public void refreshRecentTasksList() {
-        refreshRecentTasksList(null);
+        refreshRecentTasksList(null, false);
     }
 
-    private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
+    private void refreshRecentTasksList(
+            ArrayList<TaskDescription> recentTasksList, boolean firstScreenful) {
         if (mRecentTasksDirty) {
             if (recentTasksList != null) {
-                mRecentTaskDescriptions = recentTasksList;
+                mFirstScreenful = true;
+                onTasksLoaded(recentTasksList);
             } else {
-                mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
+                mFirstScreenful = true;
+                mRecentTasksLoader.loadTasksInBackground();
             }
-            mListAdapter.notifyDataSetInvalidated();
-            updateUiElements(getResources().getConfiguration());
             mRecentTasksDirty = false;
         }
     }
 
+    boolean mFirstScreenful;
+    public void onTasksLoaded(ArrayList<TaskDescription> tasks) {
+        if (!mFirstScreenful && tasks.size() == 0) {
+            return;
+        }
+        mNumItemsWaitingForThumbnailsAndIcons =
+                mFirstScreenful ? tasks.size() : mRecentTaskDescriptions.size();
+        if (mRecentTaskDescriptions == null) {
+            mRecentTaskDescriptions = new ArrayList(tasks);
+        } else {
+            mRecentTaskDescriptions.addAll(tasks);
+        }
+        mListAdapter.notifyDataSetInvalidated();
+        updateUiElements(getResources().getConfiguration());
+        mReadyToShow = true;
+        mFirstScreenful = false;
+        showIfReady();
+    }
+
     public ArrayList<TaskDescription> getRecentTasksList() {
         return mRecentTaskDescriptions;
     }
 
+    public boolean getFirstScreenful() {
+        return mFirstScreenful;
+    }
+
     private void updateUiElements(Configuration config) {
         final int items = mRecentTaskDescriptions.size();
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index dc13092..19fce37 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -22,10 +22,18 @@
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.FloatMath;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.View.OnTouchListener;
 import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
@@ -33,6 +41,8 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
 
+import java.util.ArrayList;
+
 public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper.Callback {
     private static final String TAG = RecentsPanelView.TAG;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
@@ -42,6 +52,8 @@
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
     private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+    private ArrayList<View> mRecycledViews;
+    private int mNumItemsInOneScreenful;
 
     public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
@@ -50,6 +62,7 @@
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
 
         mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, true);
+        mRecycledViews = new ArrayList<View>();
     }
 
     private int scrollPositionOfMostRecent() {
@@ -57,77 +70,91 @@
     }
 
     private void update() {
+        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+            View v = mLinearLayout.getChildAt(i);
+            mRecycledViews.add(v);
+            mAdapter.recycleView(v);
+        }
+        LayoutTransition transitioner = getLayoutTransition();
+        setLayoutTransition(null);
+
         mLinearLayout.removeAllViews();
         // Once we can clear the data associated with individual item views,
         // we can get rid of the removeAllViews() and the code below will
         // recycle them.
         for (int i = 0; i < mAdapter.getCount(); i++) {
             View old = null;
-            if (i < mLinearLayout.getChildCount()) {
-                old = mLinearLayout.getChildAt(i);
-                old.setVisibility(View.VISIBLE);
+            if (mRecycledViews.size() != 0) {
+                old = mRecycledViews.remove(mRecycledViews.size() - 1);
+                old.setVisibility(VISIBLE);
             }
+
             final View view = mAdapter.getView(i, old, mLinearLayout);
 
             if (mPerformanceHelper != null) {
                 mPerformanceHelper.addViewCallback(view);
             }
 
-            if (old == null) {
-                OnTouchListener noOpListener = new OnTouchListener() {
-                    @Override
-                    public boolean onTouch(View v, MotionEvent event) {
-                        return true;
-                    }
-                };
+            OnTouchListener noOpListener = new OnTouchListener() {
+                @Override
+                public boolean onTouch(View v, MotionEvent event) {
+                    return true;
+                }
+            };
 
-                view.setOnClickListener(new OnClickListener() {
-                    public void onClick(View v) {
-                        mCallback.dismiss();
-                    }
-                });
-                // We don't want a click sound when we dimiss recents
-                view.setSoundEffectsEnabled(false);
+            view.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    mCallback.dismiss();
+                }
+            });
+            // We don't want a click sound when we dimiss recents
+            view.setSoundEffectsEnabled(false);
 
-                OnClickListener launchAppListener = new OnClickListener() {
-                    public void onClick(View v) {
-                        mCallback.handleOnClick(view);
-                    }
-                };
+            OnClickListener launchAppListener = new OnClickListener() {
+                public void onClick(View v) {
+                    mCallback.handleOnClick(view);
+                }
+            };
 
-                final View thumbnailView = view.findViewById(R.id.app_thumbnail);
-                OnLongClickListener longClickListener = new OnLongClickListener() {
-                    public boolean onLongClick(View v) {
-                        final View anchorView = view.findViewById(R.id.app_description);
-                        mCallback.handleLongPress(view, anchorView, thumbnailView);
-                        return true;
-                    }
-                };
-                thumbnailView.setClickable(true);
-                thumbnailView.setOnClickListener(launchAppListener);
-                thumbnailView.setOnLongClickListener(longClickListener);
+            RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) view.getTag();
+            final View thumbnailView = holder.thumbnailView;
+            OnLongClickListener longClickListener = new OnLongClickListener() {
+                public boolean onLongClick(View v) {
+                    final View anchorView = view.findViewById(R.id.app_description);
+                    mCallback.handleLongPress(view, anchorView, thumbnailView);
+                    return true;
+                }
+            };
+            thumbnailView.setClickable(true);
+            thumbnailView.setOnClickListener(launchAppListener);
+            thumbnailView.setOnLongClickListener(longClickListener);
 
-                // We don't want to dismiss recents if a user clicks on the app title
-                // (we also don't want to launch the app either, though, because the
-                // app title is a small target and doesn't have great click feedback)
-                final View appTitle = view.findViewById(R.id.app_label);
-                appTitle.setContentDescription(" ");
-                appTitle.setOnTouchListener(noOpListener);
-                final View calloutLine = view.findViewById(R.id.recents_callout_line);
-                calloutLine.setOnTouchListener(noOpListener);
-                mLinearLayout.addView(view);
-            }
+            // We don't want to dismiss recents if a user clicks on the app title
+            // (we also don't want to launch the app either, though, because the
+            // app title is a small target and doesn't have great click feedback)
+            final View appTitle = view.findViewById(R.id.app_label);
+            appTitle.setContentDescription(" ");
+            appTitle.setOnTouchListener(noOpListener);
+            final View calloutLine = view.findViewById(R.id.recents_callout_line);
+            calloutLine.setOnTouchListener(noOpListener);
+
+            mLinearLayout.addView(view);
         }
-        for (int i = mAdapter.getCount(); i < mLinearLayout.getChildCount(); i++) {
-            mLinearLayout.getChildAt(i).setVisibility(View.GONE);
-        }
+        setLayoutTransition(transitioner);
+
         // Scroll to end after layout.
-        post(new Runnable() {
-            public void run() {
-                mLastScrollPosition = scrollPositionOfMostRecent();
-                scrollTo(0, mLastScrollPosition);
-            }
-        });
+        final ViewTreeObserver observer = getViewTreeObserver();
+
+        final OnGlobalLayoutListener updateScroll = new OnGlobalLayoutListener() {
+                public void onGlobalLayout() {
+                    mLastScrollPosition = scrollPositionOfMostRecent();
+                    scrollTo(0, mLastScrollPosition);
+                    if (observer.isAlive()) {
+                        observer.removeOnGlobalLayoutListener(this);
+                    }
+                }
+            };
+        observer.addOnGlobalLayoutListener(updateScroll);
     }
 
     @Override
@@ -156,8 +183,15 @@
     }
 
     public void onChildDismissed(View v) {
+        mRecycledViews.add(v);
         mLinearLayout.removeView(v);
         mCallback.handleSwipe(v);
+        v.setActivated(false);
+        // Restore the alpha/translation parameters to what they were before swiping
+        // (for when these items are recycled)
+        View contentView = getChildContentView(v);
+        contentView.setAlpha(1f);
+        contentView.setTranslationX(0);
     }
 
     public void onBeginDrag(View v) {
@@ -330,6 +364,25 @@
                 update();
             }
         });
+
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        int childWidthMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.AT_MOST);
+        int childheightMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.AT_MOST);
+        View child = mAdapter.createView(mLinearLayout);
+        child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+        mNumItemsInOneScreenful =
+                (int) FloatMath.ceil(dm.heightPixels / (float) child.getMeasuredHeight());
+        mRecycledViews.add(child);
+
+        for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
+            mRecycledViews.add(mAdapter.createView(mLinearLayout));
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        return mNumItemsInOneScreenful;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
index dcfd6d8..7e979b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
@@ -32,6 +32,7 @@
     private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
     private Drawable mIcon; // application package icon
     private CharSequence mLabel; // application package label
+    private boolean mLoaded;
 
     public TaskDescription(int _taskId, int _persistentTaskId,
             ResolveInfo _resolveInfo, Intent _intent,
@@ -45,6 +46,28 @@
         packageName = _packageName;
     }
 
+    public TaskDescription() {
+        resolveInfo = null;
+        intent = null;
+        taskId = -1;
+        persistentTaskId = -1;
+
+        description = null;
+        packageName = null;
+    }
+
+    public void setLoaded(boolean loaded) {
+        mLoaded = loaded;
+    }
+
+    public boolean isLoaded() {
+        return mLoaded;
+    }
+
+    public boolean isNull() {
+        return resolveInfo == null;
+    }
+
     // mark all these as locked?
     public CharSequence getLabel() {
         return mLabel;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 3d904ee..6fbcd64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -47,12 +47,13 @@
     }
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
     private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
+        // sort first by score, then by when
         public int compare(Entry a, Entry b) {
             final StatusBarNotification na = a.notification;
             final StatusBarNotification nb = b.notification;
-            int priDiff = na.priority - nb.priority;
-            return (priDiff != 0)
-                ? priDiff
+            int d = na.score - nb.score;
+            return (d != 0)
+                ? d
                 : (int)(na.notification.when - nb.notification.when);
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 5a1e3f4..401553f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -232,6 +232,11 @@
 
     private int mNavigationIconHints = 0;
 
+    // TODO(dsandler): codify this stuff in NotificationManager's header somewhere
+    private int mDisplayMinScore             = Notification.PRIORITY_LOW * 10;
+    private int mIntruderMinScore            = Notification.PRIORITY_HIGH * 10;
+    private int mIntruderInImmersiveMinScore = Notification.PRIORITY_HIGH * 10 + 5;
+    
     private class ExpandedDialog extends Dialog {
         ExpandedDialog(Context context) {
             super(context, com.android.internal.R.style.Theme_Translucent_NoTitleBar);
@@ -393,7 +398,7 @@
         }
         lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
         lp.setTitle("RecentsPanel");
-        lp.windowAnimations = R.style.Animation_RecentPanel;
+        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
         lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
         | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
         return lp;
@@ -403,11 +408,13 @@
         // Recents Panel
         boolean visible = false;
         ArrayList<TaskDescription> recentTasksList = null;
+        boolean firstScreenful = false;
         if (mRecentsPanel != null) {
             visible = mRecentsPanel.isShowing();
             WindowManagerImpl.getDefault().removeView(mRecentsPanel);
             if (visible) {
                 recentTasksList = mRecentsPanel.getRecentTasksList();
+                firstScreenful = mRecentsPanel.getFirstScreenful();
             }
         }
 
@@ -425,7 +432,7 @@
         WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
         mRecentsPanel.setBar(this);
         if (visible) {
-            mRecentsPanel.show(true, false, recentTasksList);
+            mRecentsPanel.show(true, false, recentTasksList, firstScreenful);
         }
 
     }
@@ -538,6 +545,7 @@
     }
 
     public void addNotification(IBinder key, StatusBarNotification notification) {
+        /* if (DEBUG) */ Slog.d(TAG, "addNotification score=" + notification.score);
         StatusBarIconView iconView = addNotificationViews(key, notification);
         if (iconView == null) return;
 
@@ -549,31 +557,30 @@
             }
         } catch (RemoteException ex) {
         }
-        if (immersive) {
-            if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
-                Slog.d(TAG, "Presenting high-priority notification in immersive activity");
-                // special new transient ticker mode
-                // 1. Populate mIntruderAlertView
+        if ((notification.score >= mIntruderInImmersiveMinScore) 
+                || (!immersive && (notification.score > mIntruderMinScore))) {
+            Slog.d(TAG, "Presenting high-priority notification");
+            // special new transient ticker mode
+            // 1. Populate mIntruderAlertView
 
-                ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
-                TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
-                alertIcon.setImageDrawable(StatusBarIconView.getIcon(
-                    alertIcon.getContext(),
-                    iconView.getStatusBarIcon()));
-                alertText.setText(notification.notification.tickerText);
+            ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
+            TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
+            alertIcon.setImageDrawable(StatusBarIconView.getIcon(
+                alertIcon.getContext(),
+                iconView.getStatusBarIcon()));
+            alertText.setText(notification.notification.tickerText);
 
-                View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
-                button.setOnClickListener(
-                    new NotificationClicker(notification.notification.contentIntent,
-                        notification.pkg, notification.tag, notification.id));
+            View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
+            button.setOnClickListener(
+                new NotificationClicker(notification.notification.contentIntent,
+                    notification.pkg, notification.tag, notification.id));
 
-                // 2. Animate mIntruderAlertView in
-                mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
+            // 2. Animate mIntruderAlertView in
+            mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
 
-                // 3. Set alarm to age the notification off (TODO)
-                mHandler.removeMessages(MSG_HIDE_INTRUDER);
-                mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
-            }
+            // 3. Set alarm to age the notification off (TODO)
+            mHandler.removeMessages(MSG_HIDE_INTRUDER);
+            mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
         } else if (notification.notification.fullScreenIntent != null) {
             // not immersive & a full-screen alert should be shown
             Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
@@ -630,8 +637,8 @@
                 && oldContentView.getLayoutId() == contentView.getLayoutId();
         ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
         boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
-                && notification.priority == oldNotification.priority;
-                // priority now encompasses isOngoing()
+                && notification.score == oldNotification.score;
+                // score now encompasses/supersedes isOngoing()
 
         boolean updateTicker = notification.notification.tickerText != null
                 && !TextUtils.equals(notification.notification.tickerText,
@@ -723,69 +730,6 @@
     }
 
 
-    View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
-        Notification n = notification.notification;
-        RemoteViews remoteViews = n.contentView;
-        if (remoteViews == null) {
-            return null;
-        }
-
-        // create the row view
-        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-        View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
-
-        // wire up the veto button
-        View vetoButton = updateNotificationVetoButton(row, notification);
-        vetoButton.setContentDescription(mContext.getString(
-                R.string.accessibility_remove_notification));
-
-        // the large icon
-        ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
-        if (notification.notification.largeIcon != null) {
-            largeIcon.setImageBitmap(notification.notification.largeIcon);
-        } else {
-            largeIcon.getLayoutParams().width = 0;
-            largeIcon.setVisibility(View.INVISIBLE);
-        }
-
-        // bind the click event to the content area
-        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
-        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-        content.setOnFocusChangeListener(mFocusChangeListener);
-        PendingIntent contentIntent = n.contentIntent;
-        if (contentIntent != null) {
-            final View.OnClickListener listener = new NotificationClicker(contentIntent,
-                    notification.pkg, notification.tag, notification.id);
-            largeIcon.setOnClickListener(listener);
-            content.setOnClickListener(listener);
-        } else {
-            largeIcon.setOnClickListener(null);
-            content.setOnClickListener(null);
-        }
-
-        View expanded = null;
-        Exception exception = null;
-        try {
-            expanded = remoteViews.apply(mContext, content);
-        }
-        catch (RuntimeException e) {
-            exception = e;
-        }
-        if (expanded == null) {
-            String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
-            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
-            return null;
-        } else {
-            content.addView(expanded);
-            row.setDrawingCacheEnabled(true);
-        }
-
-        applyLegacyRowBackground(notification, content);
-
-        return new View[] { row, content, expanded };
-    }
-
     StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
         if (DEBUG) {
             Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
@@ -848,7 +792,7 @@
         for (int i=0; i<toShow.size(); i++) {
             View v = toShow.get(i);
             if (v.getParent() == null) {
-                mPile.addView(v, 0); // the notification shade has newest at the top
+                mPile.addView(v, i);
             }
         }
     }
@@ -1805,7 +1749,7 @@
                     NotificationData.Entry e = mNotificationData.get(i);
                     pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
                     StatusBarNotification n = e.notification;
-                    pw.println("         pkg=" + n.pkg + " id=" + n.id + " priority=" + n.priority);
+                    pw.println("         pkg=" + n.pkg + " id=" + n.id + " score=" + n.score);
                     pw.println("         notification=" + n.notification);
                     pw.println("         tickerText=\"" + n.notification.tickerText + "\"");
                 }
@@ -2315,6 +2259,30 @@
         vib.vibrate(250);
     }
 
+    public int getScoreThreshold() {
+        return mDisplayMinScore;
+    }
+
+    public void setScoreThreshold(int score) {
+        // XXX HAX
+        if (mDisplayMinScore != score) {
+            this.mDisplayMinScore = score;
+            applyScoreThreshold();
+        }
+    }
+    
+    private void applyScoreThreshold() {
+        int N = mNotificationData.size();
+        for (int i=0; i<N; i++) {
+            NotificationData.Entry entry = mNotificationData.get(i);
+            int vis = (entry.notification.score < mDisplayMinScore)
+                ? View.GONE
+                : View.VISIBLE;
+            entry.row.setVisibility(vis);
+            entry.icon.setVisibility(vis);
+        }
+    }
+
     Runnable mStartTracing = new Runnable() {
         public void run() {
             vibrate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index bb326fe..a60bba7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -99,13 +99,14 @@
                 // Notification.Builder will helpfully fill these out for you no matter what you do
                 n.tickerView = null;
                 n.tickerText = null;
+                
+                n.priority = Notification.PRIORITY_HIGH;
 
                 int[] idOut = new int[1];
-                mNotificationService.enqueueNotificationWithTagPriority(
+                mNotificationService.enqueueNotificationWithTag(
                         mContext.getPackageName(),
                         null, 
                         GPS_NOTIFICATION_ID, 
-                        StatusBarNotification.PRIORITY_SYSTEM, // !!!1!one!!!
                         n,
                         idOut);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index d787e10..c59290c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -88,6 +88,7 @@
     int mLastSignalLevel;
     boolean mShowPhoneRSSIForData = false;
     boolean mShowAtLeastThreeGees = false;
+    boolean mAlwaysShowCdmaRssi = false;
 
     String mContentDescriptionPhoneSignal;
     String mContentDescriptionWifi;
@@ -156,7 +157,7 @@
     IBatteryStats mBatteryStats;
 
     public interface SignalCluster {
-        void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon, 
+        void setWifiIndicators(boolean visible, int strengthIcon, int activityIcon,
                 String contentDescription);
         void setMobileDataIndicators(boolean visible, int strengthIcon, int activityIcon,
                 int typeIcon, String contentDescription, String typeContentDescription);
@@ -176,6 +177,8 @@
 
         mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData);
         mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G);
+        mAlwaysShowCdmaRssi = res.getBoolean(
+                com.android.internal.R.bool.config_alwaysUseCdmaRssi);
 
         // set up the default wifi icon, used when no radios have ever appeared
         updateWifiIcons();
@@ -287,7 +290,7 @@
             // wimax is special
             cluster.setMobileDataIndicators(
                     true,
-                    mWimaxIconId,
+                    mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId,
                     mMobileActivityIconId,
                     mDataTypeIconId,
                     mContentDescriptionWimax,
@@ -351,7 +354,7 @@
         @Override
         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
             if (DEBUG) {
-                Slog.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength + 
+                Slog.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
                     ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
             }
             mSignalStrength = signalStrength;
@@ -469,7 +472,15 @@
             } else {
                 int iconLevel;
                 int[] iconList;
-                mLastSignalLevel = iconLevel = mSignalStrength.getLevel();
+                if (isCdma() && mAlwaysShowCdmaRssi) {
+                    mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel();
+                    if(DEBUG) Slog.d(TAG, "mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi
+                            + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel()
+                            + " instead of level=" + mSignalStrength.getLevel());
+                } else {
+                    mLastSignalLevel = iconLevel = mSignalStrength.getLevel();
+                }
+
                 if (isCdma()) {
                     if (isCdmaEri()) {
                         iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
@@ -487,7 +498,6 @@
                 mPhoneSignalIconId = iconList[iconLevel];
                 mContentDescriptionPhoneSignal = mContext.getString(
                         AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]);
-
                 mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
             }
         }
@@ -914,7 +924,7 @@
                     mobileLabel = "";
                 }
             } else {
-                mobileLabel 
+                mobileLabel
                     = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
             }
 
@@ -1190,7 +1200,7 @@
                 v.setText(wifiLabel);
             }
         }
-        
+
         // mobile label
         N = mMobileLabelViews.size();
         for (int i=0; i<N; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 6287408..6e56cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -456,8 +456,7 @@
             // Sanity-check that someone hasn't set up the config wrong and asked for a navigation
             // bar on a tablet that has only the system bar
             if (mWindowManager.hasNavigationBar()) {
-                throw new RuntimeException(
-                        "Tablet device cannot show navigation bar and system bar");
+                Slog.e(TAG, "Tablet device cannot show navigation bar and system bar");
             }
         } catch (RemoteException ex) {
         }
@@ -860,8 +859,8 @@
                 && oldContentView.getLayoutId() == contentView.getLayoutId();
         ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
         boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
-                && notification.priority == oldNotification.priority;
-                // priority now encompasses isOngoing()
+                && notification.score == oldNotification.score;
+                // score now encompasses/supersedes isOngoing()
         boolean updateTicker = notification.notification.tickerText != null
                 && !TextUtils.equals(notification.notification.tickerText,
                         oldEntry.notification.notification.tickerText);
@@ -1689,7 +1688,7 @@
 
                 mNotificationDNDDummyEntry = new NotificationData.Entry(
                         null,
-                        new StatusBarNotification("", 0, "", 0, 0, dndNotification),
+                        new StatusBarNotification("", 0, "", 0, 0, Notification.PRIORITY_MAX, dndNotification),
                         iconView);
 
                 mIconLayout.addView(iconView, params);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
index abed18f..83f7788 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
@@ -52,8 +52,7 @@
     }
 
     public void preDispatchKeyEvent(KeyEvent event) {
-        getAudioManager().preDispatchKeyEvent(event.getKeyCode(),
-                AudioManager.USE_DEFAULT_STREAM_TYPE);
+        getAudioManager().preDispatchKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE);
     }
 
     public boolean dispatchKeyEvent(KeyEvent event) {
@@ -79,7 +78,7 @@
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                getAudioManager().handleKeyDown(keyCode, AudioManager.USE_DEFAULT_STREAM_TYPE);
+                getAudioManager().handleKeyDown(event, AudioManager.USE_DEFAULT_STREAM_TYPE);
                 return true;
             }
 
@@ -197,8 +196,7 @@
                     AudioManager audioManager = (AudioManager)mContext.getSystemService(
                             Context.AUDIO_SERVICE);
                     if (audioManager != null) {
-                        getAudioManager().handleKeyUp(keyCode,
-                                AudioManager.USE_DEFAULT_STREAM_TYPE);
+                        getAudioManager().handleKeyUp(event, AudioManager.USE_DEFAULT_STREAM_TYPE);
                     }
                 }
                 return true;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index f1fe43b..301dbf5 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -115,6 +115,10 @@
 
     final TypedValue mMinWidthMajor = new TypedValue();
     final TypedValue mMinWidthMinor = new TypedValue();
+    TypedValue mFixedWidthMajor;
+    TypedValue mFixedWidthMinor;
+    TypedValue mFixedHeightMajor;
+    TypedValue mFixedHeightMinor;
 
     // This is the top-level view of the window, containing the window decor.
     private DecorView mDecor;
@@ -1416,7 +1420,7 @@
                 // doesn't have one of these.  In this case, we execute it here and
                 // eat the event instead, because we have mVolumeControlStreamType
                 // and they don't.
-                getAudioManager().handleKeyDown(keyCode, mVolumeControlStreamType);
+                getAudioManager().handleKeyDown(event, mVolumeControlStreamType);
                 return true;
             }
 
@@ -1478,7 +1482,7 @@
                 // doesn't have one of these.  In this case, we execute it here and
                 // eat the event instead, because we have mVolumeControlStreamType
                 // and they don't.
-                getAudioManager().handleKeyUp(keyCode, mVolumeControlStreamType);
+                getAudioManager().handleKeyUp(event, mVolumeControlStreamType);
                 return true;
             }
 
@@ -2088,6 +2092,44 @@
             final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
 
             final int widthMode = getMode(widthMeasureSpec);
+            final int heightMode = getMode(heightMeasureSpec);
+
+            boolean fixedWidth = false;
+            if (widthMode == AT_MOST) {
+                final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
+                if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
+                    fixedWidth = true;
+                    final int w;
+                    if (tvw.type == TypedValue.TYPE_DIMENSION) {
+                        w = (int) tvw.getDimension(metrics);
+                    } else if (tvw.type == TypedValue.TYPE_FRACTION) {
+                        w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
+                    } else {
+                        w = 0;
+                    }
+
+                    final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(w, widthSize), EXACTLY);
+                }
+            }
+
+            if (heightMode == AT_MOST) {
+                final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
+                if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
+                    final int h;
+                    if (tvh.type == TypedValue.TYPE_DIMENSION) {
+                        h = (int) tvh.getDimension(metrics);
+                    } else if (tvh.type == TypedValue.TYPE_FRACTION) {
+                        h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
+                    } else {
+                        h = 0;
+                    }
+
+                    final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+                    heightMeasureSpec =
+                            MeasureSpec.makeMeasureSpec(Math.min(h, heightSize), EXACTLY);
+                }
+            }
 
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
@@ -2096,21 +2138,22 @@
 
             widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
 
-            final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
+            if (!fixedWidth && widthMode == AT_MOST) {
+                final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
+                if (tv.type != TypedValue.TYPE_NULL) {
+                    final int min;
+                    if (tv.type == TypedValue.TYPE_DIMENSION) {
+                        min = (int)tv.getDimension(metrics);
+                    } else if (tv.type == TypedValue.TYPE_FRACTION) {
+                        min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
+                    } else {
+                        min = 0;
+                    }
 
-            if (widthMode == AT_MOST && tv.type != TypedValue.TYPE_NULL) {
-                final int min;
-                if (tv.type == TypedValue.TYPE_DIMENSION) {
-                    min = (int)tv.getDimension(metrics);
-                } else if (tv.type == TypedValue.TYPE_FRACTION) {
-                    min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
-                } else {
-                    min = 0;
-                }
-
-                if (width < min) {
-                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
-                    measure = true;
+                    if (width < min) {
+                        widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
+                        measure = true;
+                    }
                 }
             }
 
@@ -2571,6 +2614,26 @@
 
         a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
         a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
+        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) {
+            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
+            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor,
+                    mFixedWidthMajor);
+        }
+        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) {
+            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
+            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor,
+                    mFixedWidthMinor);
+        }
+        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) {
+            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
+            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor,
+                    mFixedHeightMajor);
+        }
+        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) {
+            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
+            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor,
+                    mFixedHeightMinor);
+        }
 
         final Context context = getContext();
         final int targetSdk = context.getApplicationInfo().targetSdkVersion;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index d34ed177..8f35afb 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -286,7 +286,8 @@
 
     /** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
     boolean mEnableShiftMenuBugReports = false;
-    
+
+    boolean mHeadless;
     boolean mSafeMode;
     WindowState mStatusBar = null;
     boolean mStatusBarCanHide;
@@ -686,7 +687,7 @@
         if (mGlobalActions == null) {
             mGlobalActions = new GlobalActions(mContext);
         }
-        final boolean keyguardShowing = mKeyguardMediator.isShowingAndNotHidden();
+        final boolean keyguardShowing = keyguardIsShowingTq();
         mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
         if (keyguardShowing) {
             // since it took two seconds of long press to bring this up,
@@ -783,7 +784,11 @@
         mWindowManager = windowManager;
         mWindowManagerFuncs = windowManagerFuncs;
         mPowerManager = powerManager;
-        mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
+        mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
+        if (!mHeadless) {
+            // don't create KeyguardViewMediator if headless
+            mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
+        }
         mHandler = new Handler();
         mOrientationListener = new MyOrientationListener(mContext);
         try {
@@ -1511,12 +1516,8 @@
     }
     
     static ITelephony getTelephonyService() {
-        ITelephony telephonyService = ITelephony.Stub.asInterface(
+        return ITelephony.Stub.asInterface(
                 ServiceManager.checkService(Context.TELEPHONY_SERVICE));
-        if (telephonyService == null) {
-            Log.w(TAG, "Unable to find ITelephony interface.");
-        }
-        return telephonyService;
     }
 
     static IAudioService getAudioService() {
@@ -1839,7 +1840,7 @@
      * given the situation with the keyguard.
      */
     void launchHomeFromHotKey() {
-        if (mKeyguardMediator.isShowingAndNotHidden()) {
+        if (mKeyguardMediator != null && mKeyguardMediator.isShowingAndNotHidden()) {
             // don't launch home if keyguard showing
         } else if (!mHideLockScreen && mKeyguardMediator.isInputRestricted()) {
             // when in keyguard restricted mode, must first verify unlock
@@ -2284,13 +2285,21 @@
                                     "Laying out navigation bar window: (%d,%d - %d,%d)",
                                     pf.left, pf.top, pf.right, pf.bottom));
                     }
-                } else if (attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
+                } else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
+                                || attrs.type == TYPE_BOOT_PROGRESS)
                         && ((fl & FLAG_FULLSCREEN) != 0)) {
                     // Fullscreen secure system overlays get what they ask for.
                     pf.left = df.left = mUnrestrictedScreenLeft;
                     pf.top = df.top = mUnrestrictedScreenTop;
                     pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
                     pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                } else if (attrs.type == TYPE_BOOT_PROGRESS) {
+                    // Boot progress screen always covers entire display.
+                    pf.left = df.left = cf.left = mUnrestrictedScreenLeft;
+                    pf.top = df.top = cf.top = mUnrestrictedScreenTop;
+                    pf.right = df.right = cf.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+                    pf.bottom = df.bottom = cf.bottom
+                            = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
                 } else {
                     pf.left = df.left = cf.left = mRestrictedScreenLeft;
                     pf.top = df.top = cf.top = mRestrictedScreenTop;
@@ -2580,6 +2589,9 @@
 
     /** {@inheritDoc} */
     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+        // do nothing if headless
+        if (mHeadless) return;
+
         // lid changed state
         mLidOpen = lidOpen ? LID_OPEN : LID_CLOSED;
         updateKeyboardVisibility();
@@ -2776,9 +2788,10 @@
         // the same as if it were open and in front.
         // This will prevent any keys other than the power button from waking the screen
         // when the keyguard is hidden by another activity.
-        final boolean keyguardActive = (isScreenOn ?
-                                        mKeyguardMediator.isShowingAndNotHidden() :
-                                        mKeyguardMediator.isShowing());
+        final boolean keyguardActive = (mKeyguardMediator == null ? false :
+                                            (isScreenOn ?
+                                                mKeyguardMediator.isShowingAndNotHidden() :
+                                                mKeyguardMediator.isShowing()));
 
         if (!mSystemBooted) {
             // If we have not yet booted, don't let key events do anything.
@@ -2810,7 +2823,7 @@
         //        the device some other way (which is why we have an exemption here for injected
         //        events).
         int result;
-        if (isScreenOn || (isInjected && !isWakeKey)) {
+        if ((isScreenOn && !mHeadless) || (isInjected && !isWakeKey)) {
             // When the screen is on or if the key is injected pass the key to the application.
             result = ACTION_PASS_TO_USER;
         } else {
@@ -3046,7 +3059,7 @@
         final boolean isWakeMotion = (policyFlags
                 & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
         if (isWakeMotion) {
-            if (mKeyguardMediator.isShowing()) {
+            if (mKeyguardMediator != null && mKeyguardMediator.isShowing()) {
                 // If the keyguard is showing, let it decide what to do with the wake motion.
                 mKeyguardMediator.onWakeMotionWhenKeyguardShowingTq();
             } else {
@@ -3114,7 +3127,9 @@
             mScreenOnEarly = false;
             mScreenOnFully = false;
         }
-        mKeyguardMediator.onScreenTurnedOff(why);
+        if (mKeyguardMediator != null) {
+            mKeyguardMediator.onScreenTurnedOff(why);
+        }
         synchronized (mLock) {
             updateOrientationListenerLp();
             updateLockScreenTimeout();
@@ -3131,31 +3146,33 @@
             Slog.i(TAG, "Screen turning on...", here);
         }
         if (screenOnListener != null) {
-            mKeyguardMediator.onScreenTurnedOn(new KeyguardViewManager.ShowListener() {
-                @Override public void onShown(IBinder windowToken) {
-                    if (windowToken != null) {
-                        try {
-                            mWindowManager.waitForWindowDrawn(windowToken,
-                                    new IRemoteCallback.Stub() {
-                                @Override public void sendResult(Bundle data) {
-                                    Slog.i(TAG, "Lock screen displayed!");
-                                    screenOnListener.onScreenOn();
-                                    synchronized (mLock) {
-                                        mScreenOnFully = true;
+            if (mKeyguardMediator != null) {
+                mKeyguardMediator.onScreenTurnedOn(new KeyguardViewManager.ShowListener() {
+                    @Override public void onShown(IBinder windowToken) {
+                        if (windowToken != null) {
+                            try {
+                                mWindowManager.waitForWindowDrawn(windowToken,
+                                        new IRemoteCallback.Stub() {
+                                    @Override public void sendResult(Bundle data) {
+                                        Slog.i(TAG, "Lock screen displayed!");
+                                        screenOnListener.onScreenOn();
+                                        synchronized (mLock) {
+                                            mScreenOnFully = true;
+                                        }
                                     }
-                                }
-                            });
-                        } catch (RemoteException e) {
-                        }
-                    } else {
-                        Slog.i(TAG, "No lock screen!");
-                        screenOnListener.onScreenOn();
-                        synchronized (mLock) {
-                            mScreenOnFully = true;
+                                });
+                            } catch (RemoteException e) {
+                            }
+                        } else {
+                            Slog.i(TAG, "No lock screen!");
+                            screenOnListener.onScreenOn();
+                            synchronized (mLock) {
+                                mScreenOnFully = true;
+                            }
                         }
                     }
-                }
-            });
+                });
+            }
         } else {
             synchronized (mLock) {
                 mScreenOnFully = true;
@@ -3181,15 +3198,20 @@
     
     /** {@inheritDoc} */
     public void enableKeyguard(boolean enabled) {
-        mKeyguardMediator.setKeyguardEnabled(enabled);
+        if (mKeyguardMediator != null) {
+            mKeyguardMediator.setKeyguardEnabled(enabled);
+        }
     }
 
     /** {@inheritDoc} */
     public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
-        mKeyguardMediator.verifyUnlock(callback);
+        if (mKeyguardMediator != null) {
+            mKeyguardMediator.verifyUnlock(callback);
+        }
     }
 
     private boolean keyguardIsShowingTq() {
+        if (mKeyguardMediator == null) return false;
         return mKeyguardMediator.isShowingAndNotHidden();
     }
 
@@ -3201,11 +3223,13 @@
 
     /** {@inheritDoc} */
     public boolean isKeyguardSecure() {
+        if (mKeyguardMediator == null) return false;
         return mKeyguardMediator.isSecure();
     }
 
     /** {@inheritDoc} */
     public boolean inKeyguardRestrictedKeyInputMode() {
+        if (mKeyguardMediator == null) return false;
         return mKeyguardMediator.isInputRestricted();
     }
 
@@ -3461,8 +3485,10 @@
     
     /** {@inheritDoc} */
     public void systemReady() {
-        // tell the keyguard
-        mKeyguardMediator.onSystemReady();
+        if (mKeyguardMediator != null) {
+            // tell the keyguard
+            mKeyguardMediator.onSystemReady();
+        }
         synchronized (mLock) {
             updateOrientationListenerLp();
             mSystemReady = true;
@@ -3485,6 +3511,7 @@
 
     /** {@inheritDoc} */
     public void showBootMessage(final CharSequence msg, final boolean always) {
+        if (mHeadless) return;
         mHandler.post(new Runnable() {
             @Override public void run() {
                 if (mBootMsgDialog == null) {
@@ -3648,7 +3675,9 @@
         public void run() {
             synchronized (this) {
                 if (localLOGV) Log.v(TAG, "mScreenLockTimeout activating keyguard");
-                mKeyguardMediator.doKeyguardTimeout();
+                if (mKeyguardMediator != null) {
+                    mKeyguardMediator.doKeyguardTimeout();
+                }
                 mLockScreenTimerActive = false;
             }
         }
@@ -3662,7 +3691,8 @@
 
     private void updateLockScreenTimeout() {
         synchronized (mScreenLockTimeout) {
-            boolean enable = (mAllowLockscreenWhenOn && mScreenOnEarly && mKeyguardMediator.isSecure());
+            boolean enable = (mAllowLockscreenWhenOn && mScreenOnEarly &&
+                    mKeyguardMediator != null && mKeyguardMediator.isSecure());
             if (mLockScreenTimerActive != enable) {
                 if (enable) {
                     if (localLOGV) Log.v(TAG, "setting lockscreen timer");
@@ -3862,7 +3892,7 @@
 
     public void screenOnStoppedLw() {
         if (mPowerManager.isScreenOn()) {
-            if (!mKeyguardMediator.isShowingAndNotHidden()) {
+            if (mKeyguardMediator != null && !mKeyguardMediator.isShowingAndNotHidden()) {
                 long curTime = SystemClock.uptimeMillis();
                 mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT);
             }
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 52834db..22fa752 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -6,9 +6,11 @@
     AudioFlinger.cpp            \
     AudioMixer.cpp.arm          \
     AudioResampler.cpp.arm      \
-    AudioResamplerSinc.cpp.arm  \
-    AudioResamplerCubic.cpp.arm \
-    AudioPolicyService.cpp
+    AudioPolicyService.cpp      \
+    AudioBufferProvider.cpp     \
+    ServiceUtilities.cpp
+#   AudioResamplerSinc.cpp.arm
+#   AudioResamplerCubic.cpp.arm
 
 LOCAL_C_INCLUDES := \
     system/media/audio_effects/include \
@@ -16,6 +18,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libaudioutils \
+    libcommon_time_client \
     libcutils \
     libutils \
     libbinder \
diff --git a/libs/rs/rsFifo.cpp b/services/audioflinger/AudioBufferProvider.cpp
similarity index 72%
rename from libs/rs/rsFifo.cpp
rename to services/audioflinger/AudioBufferProvider.cpp
index 3d5d8c4..678fd58 100644
--- a/libs/rs/rsFifo.cpp
+++ b/services/audioflinger/AudioBufferProvider.cpp
@@ -14,18 +14,15 @@
  * limitations under the License.
  */
 
-#include "rsFifoSocket.h"
-#include "utils/Timers.h"
-#include "utils/StopWatch.h"
+#undef __STRICT_ANSI__
+#define __STDINT_LIMITS
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
 
-using namespace android;
-using namespace android::renderscript;
+#include "AudioBufferProvider.h"
 
-Fifo::Fifo() {
+namespace android {
 
-}
+const int64_t AudioBufferProvider::kInvalidPTS = INT64_MAX;
 
-Fifo::~Fifo() {
-
-}
-
+}; // namespace android
diff --git a/services/audioflinger/AudioBufferProvider.h b/services/audioflinger/AudioBufferProvider.h
index 81c5c39..62ad6bd 100644
--- a/services/audioflinger/AudioBufferProvider.h
+++ b/services/audioflinger/AudioBufferProvider.h
@@ -38,8 +38,15 @@
     };
 
     virtual ~AudioBufferProvider() {}
-    
-    virtual status_t getNextBuffer(Buffer* buffer) = 0;
+
+    // value representing an invalid presentation timestamp
+    static const int64_t kInvalidPTS;
+
+    // pts is the local time when the next sample yielded by getNextBuffer
+    // will be rendered.
+    // Pass kInvalidPTS if the PTS is unknown or not applicable.
+    virtual status_t getNextBuffer(Buffer* buffer, int64_t pts) = 0;
+
     virtual void releaseBuffer(Buffer* buffer) = 0;
 };
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f71ba0a..2c3329e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioFlinger.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -48,6 +48,7 @@
 
 #include "AudioMixer.h"
 #include "AudioFlinger.h"
+#include "ServiceUtilities.h"
 
 #include <media/EffectsFactoryApi.h>
 #include <audio_effects/effect_visualizer.h>
@@ -60,6 +61,9 @@
 #include <powermanager/PowerManager.h>
 // #define DEBUG_CPU_USAGE 10  // log statistics every n wall clock seconds
 
+#include <common_time/cc_helper.h>
+#include <common_time/local_clock.h>
+
 // ----------------------------------------------------------------------------
 
 
@@ -68,7 +72,6 @@
 static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
 static const char kHardwareLockedString[] = "Hardware lock is taken\n";
 
-//static const nsecs_t kStandbyTimeInNsecs = seconds(3);
 static const float MAX_GAIN = 4096.0f;
 static const uint32_t MAX_GAIN_INT = 0x1000;
 
@@ -98,23 +101,10 @@
 // maximum divider applied to the active sleep time in the mixer thread loop
 static const uint32_t kMaxThreadSleepTimeShift = 2;
 
+nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
 
 // ----------------------------------------------------------------------------
 
-static bool recordingAllowed() {
-    if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
-    bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO"));
-    if (!ok) ALOGE("Request requires android.permission.RECORD_AUDIO");
-    return ok;
-}
-
-static bool settingsAllowed() {
-    if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
-    bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
-    if (!ok) ALOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
-    return ok;
-}
-
 // To collect the amplifier usage
 static void addBatteryData(uint32_t params) {
     sp<IMediaPlayerService> service = IMediaDeathNotifier::getMediaPlayerService();
@@ -160,11 +150,14 @@
 
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(),
-        mPrimaryHardwareDev(NULL),
-        mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef()
-        mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
-        mMode(AUDIO_MODE_INVALID),
-        mBtNrecIsOff(false)
+      mPrimaryHardwareDev(NULL),
+      mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef()
+      mMasterVolume(1.0f),
+      mMasterVolumeSupportLvl(MVS_NONE),
+      mMasterMute(false),
+      mNextUniqueId(1),
+      mMode(AUDIO_MODE_INVALID),
+      mBtNrecIsOff(false)
 {
 }
 
@@ -175,6 +168,18 @@
     Mutex::Autolock _l(mLock);
 
     /* TODO: move all this work into an Init() function */
+    char val_str[PROPERTY_VALUE_MAX] = { 0 };
+    if (property_get("ro.audio.flinger_standbytime_ms", val_str, NULL) >= 0) {
+        uint32_t int_val;
+        if (1 == sscanf(val_str, "%u", &int_val)) {
+            mStandbyTimeInNsecs = milliseconds(int_val);
+            ALOGI("Using %u mSec as standby time.", int_val);
+        } else {
+            mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
+            ALOGI("Using default %u mSec as standby time.",
+                    (uint32_t)(mStandbyTimeInNsecs / 1000000));
+        }
+    }
 
     for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
         const hw_module_t *mod;
@@ -188,49 +193,80 @@
              mod->name, mod->id);
         mAudioHwDevs.push(dev);
 
-        if (!mPrimaryHardwareDev) {
+        if (mPrimaryHardwareDev == NULL) {
             mPrimaryHardwareDev = dev;
             ALOGI("Using '%s' (%s.%s) as the primary audio interface",
                  mod->name, mod->id, audio_interfaces[i]);
         }
     }
 
-    mHardwareStatus = AUDIO_HW_INIT;
-
-    if (!mPrimaryHardwareDev || mAudioHwDevs.size() == 0) {
+    if (mPrimaryHardwareDev == NULL) {
         ALOGE("Primary audio interface not found");
-        return;
+        // proceed, all later accesses to mPrimaryHardwareDev verify it's safe with initCheck()
     }
 
+    // Currently (mPrimaryHardwareDev == NULL) == (mAudioHwDevs.size() == 0), but the way the
+    // primary HW dev is selected can change so these conditions might not always be equivalent.
+    // When that happens, re-visit all the code that assumes this.
+
+    AutoMutex lock(mHardwareLock);
+
+    // Determine the level of master volume support the primary audio HAL has,
+    // and set the initial master volume at the same time.
+    float initialVolume = 1.0;
+    mMasterVolumeSupportLvl = MVS_NONE;
+    if (0 == mPrimaryHardwareDev->init_check(mPrimaryHardwareDev)) {
+        audio_hw_device_t *dev = mPrimaryHardwareDev;
+
+        mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
+        if ((NULL != dev->get_master_volume) &&
+            (NO_ERROR == dev->get_master_volume(dev, &initialVolume))) {
+            mMasterVolumeSupportLvl = MVS_FULL;
+        } else {
+            mMasterVolumeSupportLvl = MVS_SETONLY;
+            initialVolume = 1.0;
+        }
+
+        mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+        if ((NULL == dev->set_master_volume) ||
+            (NO_ERROR != dev->set_master_volume(dev, initialVolume))) {
+            mMasterVolumeSupportLvl = MVS_NONE;
+        }
+        mHardwareStatus = AUDIO_HW_INIT;
+    }
+
+    // Set the mode for each audio HAL, and try to set the initial volume (if
+    // supported) for all of the non-primary audio HALs.
     for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
         audio_hw_device_t *dev = mAudioHwDevs[i];
 
         mHardwareStatus = AUDIO_HW_INIT;
         rc = dev->init_check(dev);
+        mHardwareStatus = AUDIO_HW_IDLE;
         if (rc == 0) {
-            AutoMutex lock(mHardwareLock);
-
-            mMode = AUDIO_MODE_NORMAL;
+            mMode = AUDIO_MODE_NORMAL;  // assigned multiple times with same value
             mHardwareStatus = AUDIO_HW_SET_MODE;
             dev->set_mode(dev, mMode);
-            mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
-            dev->set_master_volume(dev, 1.0f);
-            mHardwareStatus = AUDIO_HW_IDLE;
+
+            if ((dev != mPrimaryHardwareDev) &&
+                (NULL != dev->set_master_volume)) {
+                mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+                dev->set_master_volume(dev, initialVolume);
+            }
+
+            mHardwareStatus = AUDIO_HW_INIT;
         }
     }
-}
 
-status_t AudioFlinger::initCheck() const
-{
-    Mutex::Autolock _l(mLock);
-    if (mPrimaryHardwareDev == NULL || mAudioHwDevs.size() == 0)
-        return NO_INIT;
-    return NO_ERROR;
+    mMasterVolumeSW = (MVS_NONE == mMasterVolumeSupportLvl)
+                    ? initialVolume
+                    : 1.0;
+    mMasterVolume   = initialVolume;
+    mHardwareStatus = AUDIO_HW_IDLE;
 }
 
 AudioFlinger::~AudioFlinger()
 {
-    int num_devs = mAudioHwDevs.size();
 
     while (!mRecordThreads.isEmpty()) {
         // closeInput() will remove first entry from mRecordThreads
@@ -241,11 +277,10 @@
         closeOutput(mPlaybackThreads.keyAt(0));
     }
 
-    for (int i = 0; i < num_devs; i++) {
-        audio_hw_device_t *dev = mAudioHwDevs[i];
-        audio_hw_device_close(dev);
+    for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+        // no mHardwareLock needed, as there are no other references to this
+        audio_hw_device_close(mAudioHwDevs[i]);
     }
-    mAudioHwDevs.clear();
 }
 
 audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(uint32_t devices)
@@ -293,7 +328,10 @@
     String8 result;
     hardware_call_state hardwareStatus = mHardwareStatus;
 
-    snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus);
+    snprintf(buffer, SIZE, "Hardware status: %d\n"
+                           "Standby Time mSec: %u\n",
+                            hardwareStatus,
+                            (uint32_t)(mStandbyTimeInNsecs / 1000000));
     result.append(buffer);
     write(fd, result.string(), result.size());
     return NO_ERROR;
@@ -328,7 +366,7 @@
 
 status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
 {
-    if (!checkCallingPermission(String16("android.permission.DUMP"))) {
+    if (!dumpAllowed()) {
         dumpPermissionDenial(fd, args);
     } else {
         // get state of hardware lock
@@ -371,6 +409,18 @@
     return NO_ERROR;
 }
 
+sp<AudioFlinger::Client> AudioFlinger::registerPid_l(pid_t pid)
+{
+    // If pid is already in the mClients wp<> map, then use that entry
+    // (for which promote() is always != 0), otherwise create a new entry and Client.
+    sp<Client> client = mClients.valueFor(pid).promote();
+    if (client == 0) {
+        client = new Client(this, pid);
+        mClients.add(pid, client);
+    }
+
+    return client;
+}
 
 // IAudioFlinger interface
 
@@ -382,16 +432,17 @@
         audio_format_t format,
         uint32_t channelMask,
         int frameCount,
+        // FIXME dead, remove from IAudioFlinger
         uint32_t flags,
         const sp<IMemory>& sharedBuffer,
-        int output,
+        audio_io_handle_t output,
+        bool isTimed,
         int *sessionId,
         status_t *status)
 {
     sp<PlaybackThread::Track> track;
     sp<TrackHandle> trackHandle;
     sp<Client> client;
-    wp<Client> wclient;
     status_t lStatus;
     int lSessionId;
 
@@ -413,14 +464,7 @@
             goto Exit;
         }
 
-        wclient = mClients.valueFor(pid);
-
-        if (wclient != NULL) {
-            client = wclient.promote();
-        } else {
-            client = new Client(this, pid);
-            mClients.add(pid, client);
-        }
+        client = registerPid_l(pid);
 
         ALOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
         if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
@@ -451,7 +495,7 @@
         ALOGV("createTrack() lSessionId: %d", lSessionId);
 
         track = thread->createTrack_l(client, streamType, sampleRate, format,
-                channelMask, frameCount, sharedBuffer, lSessionId, &lStatus);
+                channelMask, frameCount, sharedBuffer, lSessionId, isTimed, &lStatus);
 
         // move effect chain to this output thread if an effect on same session was waiting
         // for a track to be created
@@ -477,7 +521,7 @@
     return trackHandle;
 }
 
-uint32_t AudioFlinger::sampleRate(int output) const
+uint32_t AudioFlinger::sampleRate(audio_io_handle_t output) const
 {
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -488,7 +532,7 @@
     return thread->sampleRate();
 }
 
-int AudioFlinger::channelCount(int output) const
+int AudioFlinger::channelCount(audio_io_handle_t output) const
 {
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -499,7 +543,7 @@
     return thread->channelCount();
 }
 
-audio_format_t AudioFlinger::format(int output) const
+audio_format_t AudioFlinger::format(audio_io_handle_t output) const
 {
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -510,7 +554,7 @@
     return thread->format();
 }
 
-size_t AudioFlinger::frameCount(int output) const
+size_t AudioFlinger::frameCount(audio_io_handle_t output) const
 {
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -521,7 +565,7 @@
     return thread->frameCount();
 }
 
-uint32_t AudioFlinger::latency(int output) const
+uint32_t AudioFlinger::latency(audio_io_handle_t output) const
 {
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -544,20 +588,29 @@
         return PERMISSION_DENIED;
     }
 
+    float swmv = value;
+
     // when hw supports master volume, don't scale in sw mixer
-    { // scope for the lock
-        AutoMutex lock(mHardwareLock);
-        mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
-        if (mPrimaryHardwareDev->set_master_volume(mPrimaryHardwareDev, value) == NO_ERROR) {
-            value = 1.0f;
+    if (MVS_NONE != mMasterVolumeSupportLvl) {
+        for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+            AutoMutex lock(mHardwareLock);
+            audio_hw_device_t *dev = mAudioHwDevs[i];
+
+            mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+            if (NULL != dev->set_master_volume) {
+                dev->set_master_volume(dev, value);
+            }
+            mHardwareStatus = AUDIO_HW_IDLE;
         }
-        mHardwareStatus = AUDIO_HW_IDLE;
+
+        swmv = 1.0;
     }
 
     Mutex::Autolock _l(mLock);
-    mMasterVolume = value;
-    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
-       mPlaybackThreads.valueAt(i)->setMasterVolume(value);
+    mMasterVolume   = value;
+    mMasterVolumeSW = swmv;
+    for (size_t i = 0; i < mPlaybackThreads.size(); i++)
+       mPlaybackThreads.valueAt(i)->setMasterVolume(swmv);
 
     return NO_ERROR;
 }
@@ -588,7 +641,7 @@
     if (NO_ERROR == ret) {
         Mutex::Autolock _l(mLock);
         mMode = mode;
-        for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+        for (size_t i = 0; i < mPlaybackThreads.size(); i++)
            mPlaybackThreads.valueAt(i)->setMode(mode);
     }
 
@@ -622,6 +675,7 @@
     }
 
     bool state = AUDIO_MODE_INVALID;
+    AutoMutex lock(mHardwareLock);
     mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
     mPrimaryHardwareDev->get_mic_mute(mPrimaryHardwareDev, &state);
     mHardwareStatus = AUDIO_HW_IDLE;
@@ -636,8 +690,9 @@
     }
 
     Mutex::Autolock _l(mLock);
+    // This is an optimization, so PlaybackThread doesn't have to look at the one from AudioFlinger
     mMasterMute = muted;
-    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+    for (size_t i = 0; i < mPlaybackThreads.size(); i++)
        mPlaybackThreads.valueAt(i)->setMasterMute(muted);
 
     return NO_ERROR;
@@ -649,13 +704,38 @@
     return masterVolume_l();
 }
 
+float AudioFlinger::masterVolumeSW() const
+{
+    Mutex::Autolock _l(mLock);
+    return masterVolumeSW_l();
+}
+
 bool AudioFlinger::masterMute() const
 {
     Mutex::Autolock _l(mLock);
     return masterMute_l();
 }
 
-status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, int output)
+float AudioFlinger::masterVolume_l() const
+{
+    if (MVS_FULL == mMasterVolumeSupportLvl) {
+        float ret_val;
+        AutoMutex lock(mHardwareLock);
+
+        mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
+        assert(NULL != mPrimaryHardwareDev);
+        assert(NULL != mPrimaryHardwareDev->get_master_volume);
+
+        mPrimaryHardwareDev->get_master_volume(mPrimaryHardwareDev, &ret_val);
+        mHardwareStatus = AUDIO_HW_IDLE;
+        return ret_val;
+    }
+
+    return mMasterVolume;
+}
+
+status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
+        audio_io_handle_t output)
 {
     // check calling permissions
     if (!settingsAllowed()) {
@@ -679,7 +759,7 @@
     mStreamTypes[stream].volume = value;
 
     if (thread == NULL) {
-        for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
+        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
            mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
         }
     } else {
@@ -710,7 +790,7 @@
     return NO_ERROR;
 }
 
-float AudioFlinger::streamVolume(audio_stream_type_t stream, int output) const
+float AudioFlinger::streamVolume(audio_stream_type_t stream, audio_io_handle_t output) const
 {
     if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
         return 0.0f;
@@ -725,7 +805,7 @@
         }
         volume = thread->streamVolume(stream);
     } else {
-        volume = mStreamTypes[stream].volume;
+        volume = streamVolume_l(stream);
     }
 
     return volume;
@@ -737,14 +817,15 @@
         return true;
     }
 
-    return mStreamTypes[stream].mute;
+    AutoMutex lock(mLock);
+    return streamMute_l(stream);
 }
 
-status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
+status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
 {
     status_t result;
 
-    ALOGV("setParameters(): io %d, keyvalue %s, tid %d, calling tid %d",
+    ALOGV("setParameters(): io %d, keyvalue %s, tid %d, calling pid %d",
             ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid());
     // check calling permissions
     if (!settingsAllowed()) {
@@ -809,16 +890,15 @@
             }
         }
     }
-    if (thread != NULL) {
-        result = thread->setParameters(keyValuePairs);
-        return result;
+    if (thread != 0) {
+        return thread->setParameters(keyValuePairs);
     }
     return BAD_VALUE;
 }
 
-String8 AudioFlinger::getParameters(int ioHandle, const String8& keys)
+String8 AudioFlinger::getParameters(audio_io_handle_t ioHandle, const String8& keys) const
 {
-//    ALOGV("getParameters() io %d, keys %s, tid %d, calling tid %d",
+//    ALOGV("getParameters() io %d, keys %s, tid %d, calling pid %d",
 //            ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid());
 
     if (ioHandle == 0) {
@@ -827,7 +907,7 @@
         for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
             audio_hw_device_t *dev = mAudioHwDevs[i];
             char *s = dev->get_parameters(dev, keys.string());
-            out_s8 += String8(s);
+            out_s8 += String8(s ? s : "");
             free(s);
         }
         return out_s8;
@@ -846,17 +926,21 @@
     return String8("");
 }
 
-size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount)
+size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const
 {
     status_t ret = initCheck();
     if (ret != NO_ERROR) {
         return 0;
     }
 
-    return mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, sampleRate, format, channelCount);
+    AutoMutex lock(mHardwareLock);
+    mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
+    size_t size = mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, sampleRate, format, channelCount);
+    mHardwareStatus = AUDIO_HW_IDLE;
+    return size;
 }
 
-unsigned int AudioFlinger::getInputFramesLost(int ioHandle)
+unsigned int AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
 {
     if (ioHandle == 0) {
         return 0;
@@ -891,7 +975,8 @@
     return ret;
 }
 
-status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output)
+status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
+        audio_io_handle_t output) const
 {
     status_t status;
 
@@ -910,7 +995,7 @@
 
     Mutex::Autolock _l(mLock);
 
-    int pid = IPCThreadState::self()->getCallingPid();
+    pid_t pid = IPCThreadState::self()->getCallingPid();
     if (mNotificationClients.indexOfKey(pid) < 0) {
         sp<NotificationClient> notificationClient = new NotificationClient(this,
                                                                             client,
@@ -938,7 +1023,7 @@
 {
     Mutex::Autolock _l(mLock);
 
-    int index = mNotificationClients.indexOfKey(pid);
+    ssize_t index = mNotificationClients.indexOfKey(pid);
     if (index >= 0) {
         sp <NotificationClient> client = mNotificationClients.valueFor(pid);
         ALOGV("removeNotificationClient() %p, pid %d", client.get(), pid);
@@ -946,9 +1031,9 @@
     }
 
     ALOGV("%d died, releasing its sessions", pid);
-    int num = mAudioSessionRefs.size();
+    size_t num = mAudioSessionRefs.size();
     bool removed = false;
-    for (int i = 0; i< num; i++) {
+    for (size_t i = 0; i< num; ) {
         AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
         ALOGV(" pid %d @ %d", ref->pid, i);
         if (ref->pid == pid) {
@@ -956,8 +1041,9 @@
             mAudioSessionRefs.removeAt(i);
             delete ref;
             removed = true;
-            i--;
             num--;
+        } else {
+            i++;
         }
     }
     if (removed) {
@@ -966,7 +1052,7 @@
 }
 
 // audioConfigChanged_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2)
+void AudioFlinger::audioConfigChanged_l(int event, audio_io_handle_t ioHandle, void *param2)
 {
     size_t size = mNotificationClients.size();
     for (size_t i = 0; i < size; i++) {
@@ -985,8 +1071,8 @@
 
 // ----------------------------------------------------------------------------
 
-AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device,
-        type_t type)
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
+        uint32_t device, type_t type)
     :   Thread(false),
         mType(type),
         mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0),
@@ -994,7 +1080,7 @@
         mChannelCount(0),
         mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
         mParamStatus(NO_ERROR),
-        mStandby(false), mId(id), mExiting(false),
+        mStandby(false), mId(id),
         mDevice(device),
         mDeathRecipient(new PMDeathRecipient(this))
 {
@@ -1013,40 +1099,26 @@
 
 void AudioFlinger::ThreadBase::exit()
 {
-    // keep a strong ref on ourself so that we won't get
-    // destroyed in the middle of requestExitAndWait()
-    sp <ThreadBase> strongMe = this;
-
     ALOGV("ThreadBase::exit");
     {
+        // This lock prevents the following race in thread (uniprocessor for illustration):
+        //  if (!exitPending()) {
+        //      // context switch from here to exit()
+        //      // exit() calls requestExit(), what exitPending() observes
+        //      // exit() calls signal(), which is dropped since no waiters
+        //      // context switch back from exit() to here
+        //      mWaitWorkCV.wait(...);
+        //      // now thread is hung
+        //  }
         AutoMutex lock(mLock);
-        mExiting = true;
         requestExit();
         mWaitWorkCV.signal();
     }
+    // When Thread::requestExitAndWait is made virtual and this method is renamed to
+    // "virtual status_t requestExitAndWait()", replace by "return Thread::requestExitAndWait();"
     requestExitAndWait();
 }
 
-uint32_t AudioFlinger::ThreadBase::sampleRate() const
-{
-    return mSampleRate;
-}
-
-int AudioFlinger::ThreadBase::channelCount() const
-{
-    return (int)mChannelCount;
-}
-
-audio_format_t AudioFlinger::ThreadBase::format() const
-{
-    return mFormat;
-}
-
-size_t AudioFlinger::ThreadBase::frameCount() const
-{
-    return mFrameCount;
-}
-
 status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
 {
     status_t status;
@@ -1248,8 +1320,7 @@
 void AudioFlinger::ThreadBase::setEffectSuspended_l(
         const effect_uuid_t *type, bool suspend, int sessionId)
 {
-    sp<EffectChain> chain;
-    chain = getEffectChain_l(sessionId);
+    sp<EffectChain> chain = getEffectChain_l(sessionId);
     if (chain != 0) {
         if (type != NULL) {
             chain->setEffectSuspended_l(type, suspend);
@@ -1263,7 +1334,7 @@
 
 void AudioFlinger::ThreadBase::checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain)
 {
-    int index = mSuspendedSessions.indexOfKey(chain->sessionId());
+    ssize_t index = mSuspendedSessions.indexOfKey(chain->sessionId());
     if (index < 0) {
         return;
     }
@@ -1289,7 +1360,7 @@
                                                          bool suspend,
                                                          int sessionId)
 {
-    int index = mSuspendedSessions.indexOfKey(sessionId);
+    ssize_t index = mSuspendedSessions.indexOfKey(sessionId);
 
     KeyedVector <int, sp<SuspendedSessionDesc> > sessionEffects;
 
@@ -1377,7 +1448,7 @@
 
 AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
                                              AudioStreamOut* output,
-                                             int id,
+                                             audio_io_handle_t id,
                                              uint32_t device,
                                              type_t type)
     :   ThreadBase(audioFlinger, id, device, type),
@@ -1389,7 +1460,7 @@
         mOutput(output),
         // Assumes constructor is called by AudioFlinger with it's mLock held,
         // but it would be safer to explicitly pass initial masterVolume as parameter
-        mMasterVolume(audioFlinger->masterVolume_l()),
+        mMasterVolume(audioFlinger->masterVolumeSW_l()),
         mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
 {
     snprintf(mName, kNameLength, "AudioOut_%d", id);
@@ -1400,11 +1471,13 @@
     // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
     for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT;
             stream = (audio_stream_type_t) (stream + 1)) {
-        mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
-        mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
+        mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
+        mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
         // initialized by stream_type_t default constructor
         // mStreamTypes[stream].valid = true;
     }
+    // mStreamTypes[AUDIO_STREAM_CNT] exists but isn't explicitly initialized here,
+    // because mAudioFlinger doesn't have one to copy from
 }
 
 AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -1505,6 +1578,7 @@
         int frameCount,
         const sp<IMemory>& sharedBuffer,
         int sessionId,
+        bool isTimed,
         status_t *status)
 {
     sp<Track> track;
@@ -1541,12 +1615,11 @@
         // all tracks in same audio session must share the same routing strategy otherwise
         // conflicts will happen when tracks are moved from one output to another by audio policy
         // manager
-        uint32_t strategy =
-                AudioSystem::getStrategyForStream((audio_stream_type_t)streamType);
+        uint32_t strategy = AudioSystem::getStrategyForStream(streamType);
         for (size_t i = 0; i < mTracks.size(); ++i) {
             sp<Track> t = mTracks[i];
             if (t != 0) {
-                uint32_t actual = AudioSystem::getStrategyForStream((audio_stream_type_t)t->type());
+                uint32_t actual = AudioSystem::getStrategyForStream(t->streamType());
                 if (sessionId == t->sessionId() && strategy != actual) {
                     ALOGE("createTrack_l() mismatched strategy; expected %u but found %u",
                             strategy, actual);
@@ -1556,9 +1629,14 @@
             }
         }
 
-        track = new Track(this, client, streamType, sampleRate, format,
-                channelMask, frameCount, sharedBuffer, sessionId);
-        if (track->getCblk() == NULL || track->name() < 0) {
+        if (!isTimed) {
+            track = new Track(this, client, streamType, sampleRate, format,
+                    channelMask, frameCount, sharedBuffer, sessionId);
+        } else {
+            track = TimedTrack::create(this, client, streamType, sampleRate, format,
+                    channelMask, frameCount, sharedBuffer, sessionId);
+        }
+        if (track == NULL || track->getCblk() == NULL || track->name() < 0) {
             lStatus = NO_MEMORY;
             goto Exit;
         }
@@ -1568,7 +1646,7 @@
         if (chain != 0) {
             ALOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
             track->setMainBuffer(chain->inBuffer());
-            chain->setStrategy(AudioSystem::getStrategyForStream((audio_stream_type_t)track->type()));
+            chain->setStrategy(AudioSystem::getStrategyForStream(track->streamType()));
             chain->incTrackCnt();
         }
 
@@ -1599,50 +1677,36 @@
     }
 }
 
-status_t AudioFlinger::PlaybackThread::setMasterVolume(float value)
+void AudioFlinger::PlaybackThread::setMasterVolume(float value)
 {
+    Mutex::Autolock _l(mLock);
     mMasterVolume = value;
-    return NO_ERROR;
 }
 
-status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted)
+void AudioFlinger::PlaybackThread::setMasterMute(bool muted)
 {
-    mMasterMute = muted;
-    return NO_ERROR;
+    Mutex::Autolock _l(mLock);
+    setMasterMute_l(muted);
 }
 
-float AudioFlinger::PlaybackThread::masterVolume() const
+void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
 {
-    return mMasterVolume;
-}
-
-bool AudioFlinger::PlaybackThread::masterMute() const
-{
-    return mMasterMute;
-}
-
-status_t AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
-{
+    Mutex::Autolock _l(mLock);
     mStreamTypes[stream].volume = value;
-    return NO_ERROR;
 }
 
-status_t AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
+void AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
 {
+    Mutex::Autolock _l(mLock);
     mStreamTypes[stream].mute = muted;
-    return NO_ERROR;
 }
 
 float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) const
 {
+    Mutex::Autolock _l(mLock);
     return mStreamTypes[stream].volume;
 }
 
-bool AudioFlinger::PlaybackThread::streamMute(audio_stream_type_t stream) const
-{
-    return mStreamTypes[stream].mute;
-}
-
 // addTrack_l() must be called with ThreadBase::mLock held
 status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
 {
@@ -1808,7 +1872,7 @@
         sp<Track> track = mTracks[i];
         if (sessionId == track->sessionId() &&
                 !(track->mCblk->flags & CBLK_INVALID_MSK)) {
-            return AudioSystem::getStrategyForStream((audio_stream_type_t) track->type());
+            return AudioSystem::getStrategyForStream(track->streamType());
         }
     }
     return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
@@ -1853,7 +1917,7 @@
 // ----------------------------------------------------------------------------
 
 AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
-        int id, uint32_t device, type_t type)
+        audio_io_handle_t id, uint32_t device, type_t type)
     :   PlaybackThread(audioFlinger, output, id, device, type),
         mAudioMixer(new AudioMixer(mFrameCount, mSampleRate)),
         mPrevMixerStatus(MIXER_IDLE)
@@ -1869,6 +1933,46 @@
     delete mAudioMixer;
 }
 
+class CpuStats {
+public:
+    void sample();
+#ifdef DEBUG_CPU_USAGE
+private:
+    ThreadCpuUsage mCpu;
+#endif
+};
+
+void CpuStats::sample() {
+#ifdef DEBUG_CPU_USAGE
+    const CentralTendencyStatistics& stats = mCpu.statistics();
+    mCpu.sampleAndEnable();
+    unsigned n = stats.n();
+    // mCpu.elapsed() is expensive, so don't call it every loop
+    if ((n & 127) == 1) {
+        long long elapsed = mCpu.elapsed();
+        if (elapsed >= DEBUG_CPU_USAGE * 1000000000LL) {
+            double perLoop = elapsed / (double) n;
+            double perLoop100 = perLoop * 0.01;
+            double mean = stats.mean();
+            double stddev = stats.stddev();
+            double minimum = stats.minimum();
+            double maximum = stats.maximum();
+            mCpu.resetStatistics();
+            ALOGI("CPU usage over past %.1f secs (%u mixer loops at %.1f mean ms per loop):\n  us per mix loop: mean=%.0f stddev=%.0f min=%.0f max=%.0f\n  %% of wall: mean=%.1f stddev=%.1f min=%.1f max=%.1f",
+                    elapsed * .000000001, n, perLoop * .000001,
+                    mean * .001,
+                    stddev * .001,
+                    minimum * .001,
+                    maximum * .001,
+                    mean / perLoop100,
+                    stddev / perLoop100,
+                    minimum / perLoop100,
+                    maximum / perLoop100);
+        }
+    }
+#endif
+};
+
 bool AudioFlinger::MixerThread::threadLoop()
 {
     Vector< sp<Track> > tracksToRemove;
@@ -1887,42 +1991,13 @@
     uint32_t sleepTime = idleSleepTime;
     uint32_t sleepTimeShift = 0;
     Vector< sp<EffectChain> > effectChains;
-#ifdef DEBUG_CPU_USAGE
-    ThreadCpuUsage cpu;
-    const CentralTendencyStatistics& stats = cpu.statistics();
-#endif
+    CpuStats cpuStats;
 
     acquireWakeLock();
 
     while (!exitPending())
     {
-#ifdef DEBUG_CPU_USAGE
-        cpu.sampleAndEnable();
-        unsigned n = stats.n();
-        // cpu.elapsed() is expensive, so don't call it every loop
-        if ((n & 127) == 1) {
-            long long elapsed = cpu.elapsed();
-            if (elapsed >= DEBUG_CPU_USAGE * 1000000000LL) {
-                double perLoop = elapsed / (double) n;
-                double perLoop100 = perLoop * 0.01;
-                double mean = stats.mean();
-                double stddev = stats.stddev();
-                double minimum = stats.minimum();
-                double maximum = stats.maximum();
-                cpu.resetStatistics();
-                ALOGI("CPU usage over past %.1f secs (%u mixer loops at %.1f mean ms per loop):\n  us per mix loop: mean=%.0f stddev=%.0f min=%.0f max=%.0f\n  %% of wall: mean=%.1f stddev=%.1f min=%.1f max=%.1f",
-                        elapsed * .000000001, n, perLoop * .000001,
-                        mean * .001,
-                        stddev * .001,
-                        minimum * .001,
-                        maximum * .001,
-                        mean / perLoop100,
-                        stddev / perLoop100,
-                        minimum / perLoop100,
-                        maximum / perLoop100);
-            }
-        }
-#endif
+        cpuStats.sample();
         processConfigEvents();
 
         mixerStatus = MIXER_IDLE;
@@ -1947,7 +2022,7 @@
             if (CC_UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
                         mSuspended)) {
                 if (!mStandby) {
-                    ALOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended);
+                    ALOGV("Audio hardware entering standby, mixer %p, mSuspended %d", this, mSuspended);
                     mOutput->stream->common.standby(&mOutput->stream->common);
                     mStandby = true;
                     mBytesWritten = 0;
@@ -1961,9 +2036,9 @@
 
                     releaseWakeLock_l();
                     // wait until we have something to do...
-                    ALOGV("MixerThread %p TID %d going to sleep\n", this, gettid());
+                    ALOGV("MixerThread %p TID %d going to sleep", this, gettid());
                     mWaitWorkCV.wait(mLock);
-                    ALOGV("MixerThread %p TID %d waking up\n", this, gettid());
+                    ALOGV("MixerThread %p TID %d waking up", this, gettid());
                     acquireWakeLock_l();
 
                     mPrevMixerStatus = MIXER_IDLE;
@@ -1972,11 +2047,11 @@
                         property_get("ro.audio.silent", value, "0");
                         if (atoi(value)) {
                             ALOGD("Silence is golden");
-                            setMasterMute(true);
+                            setMasterMute_l(true);
                         }
                     }
 
-                    standbyTime = systemTime() + kStandbyTimeInNsecs;
+                    standbyTime = systemTime() + mStandbyTimeInNsecs;
                     sleepTime = idleSleepTime;
                     sleepTimeShift = 0;
                     continue;
@@ -1992,8 +2067,21 @@
         }
 
         if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
+            // obtain the presentation timestamp of the next output buffer
+            int64_t pts;
+            status_t status = INVALID_OPERATION;
+
+            if (NULL != mOutput->stream->get_next_write_timestamp) {
+                status = mOutput->stream->get_next_write_timestamp(
+                        mOutput->stream, &pts);
+            }
+
+            if (status != NO_ERROR) {
+                pts = AudioBufferProvider::kInvalidPTS;
+            }
+
             // mix buffers...
-            mAudioMixer->process();
+            mAudioMixer->process(pts);
             // increase sleep time progressively when application underrun condition clears.
             // Only increase sleep time if the mixer is ready for two consecutive times to avoid
             // that a steady state of alternating ready/not ready conditions keeps the sleep time
@@ -2002,7 +2090,7 @@
                 sleepTimeShift--;
             }
             sleepTime = 0;
-            standbyTime = systemTime() + kStandbyTimeInNsecs;
+            standbyTime = systemTime() + mStandbyTimeInNsecs;
             //TODO: delay standby when effects have a tail
         } else {
             // If no tracks are ready, sleep once for the duration of an output
@@ -2149,7 +2237,7 @@
                 ALOG_ASSERT(minFrames <= cblk->frameCount);
             }
         }
-        if ((cblk->framesReady() >= minFrames) && track->isReady() &&
+        if ((track->framesReady() >= minFrames) && track->isReady() &&
                 !track->isPaused() && !track->isTerminated())
         {
             //ALOGV("track %d u=%08x, s=%08x [OK] on thread %p", name, cblk->user, cblk->server, this);
@@ -2189,7 +2277,7 @@
             // compute volume for this track
             uint32_t vl, vr, va;
             if (track->isMuted() || track->isPausing() ||
-                mStreamTypes[track->type()].mute) {
+                mStreamTypes[track->streamType()].mute) {
                 vl = vr = va = 0;
                 if (track->isPausing()) {
                     track->setPaused();
@@ -2197,7 +2285,7 @@
             } else {
 
                 // read original volumes with volume control
-                float typeVolume = mStreamTypes[track->type()].volume;
+                float typeVolume = mStreamTypes[track->streamType()].volume;
                 float v = masterVolume * typeVolume;
                 uint32_t vlr = cblk->getVolumeLR();
                 vl = vlr & 0xFFFF;
@@ -2219,7 +2307,7 @@
 
                 uint16_t sendLevel = cblk->getSendLevel_U4_12();
                 // send level comes from shared memory and so may be corrupt
-                if (sendLevel >= MAX_GAIN_INT) {
+                if (sendLevel > MAX_GAIN_INT) {
                     ALOGV("Track send level out of range: %04X", sendLevel);
                     sendLevel = MAX_GAIN_INT;
                 }
@@ -2240,25 +2328,21 @@
             }
 
             // Convert volumes from 8.24 to 4.12 format
-            int16_t left, right, aux;
             // This additional clamping is needed in case chain->setVolume_l() overshot
-            uint32_t v_clamped = (vl + (1 << 11)) >> 12;
-            if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
-            left = int16_t(v_clamped);
-            v_clamped = (vr + (1 << 11)) >> 12;
-            if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
-            right = int16_t(v_clamped);
+            vl = (vl + (1 << 11)) >> 12;
+            if (vl > MAX_GAIN_INT) vl = MAX_GAIN_INT;
+            vr = (vr + (1 << 11)) >> 12;
+            if (vr > MAX_GAIN_INT) vr = MAX_GAIN_INT;
 
-            if (va > MAX_GAIN_INT) va = MAX_GAIN_INT;
-            aux = int16_t(va);
+            if (va > MAX_GAIN_INT) va = MAX_GAIN_INT;   // va is uint32_t, so no need to check for -
 
             // XXX: these things DON'T need to be done each time
             mAudioMixer->setBufferProvider(name, track);
             mAudioMixer->enable(name);
 
-            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)left);
-            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)right);
-            mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)aux);
+            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)vl);
+            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)vr);
+            mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)va);
             mAudioMixer->setParameter(
                 name,
                 AudioMixer::TRACK,
@@ -2358,7 +2442,7 @@
     size_t size = mTracks.size();
     for (size_t i = 0; i < size; i++) {
         sp<Track> t = mTracks[i];
-        if (t->type() == streamType) {
+        if (t->streamType() == streamType) {
             android_atomic_or(CBLK_INVALID_ON, &t->mCblk->flags);
             t->mCblk->cv.signal();
         }
@@ -2520,7 +2604,8 @@
 }
 
 // ----------------------------------------------------------------------------
-AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
+        AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
     :   PlaybackThread(audioFlinger, output, id, device, DIRECT)
         // mLeftVolFloat, mRightVolFloat
         // mLeftVolShort, mRightVolShort
@@ -2531,21 +2616,6 @@
 {
 }
 
-static inline
-int32_t mul(int16_t in, int16_t v)
-{
-#if defined(__arm__) && !defined(__thumb__)
-    int32_t out;
-    asm( "smulbb %[out], %[in], %[v] \n"
-         : [out]"=r"(out)
-         : [in]"%r"(in), [v]"r"(v)
-         : );
-    return out;
-#else
-    return in * int32_t(v);
-#endif
-}
-
 void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp)
 {
     // Do not apply volume on compressed audio
@@ -2626,7 +2696,6 @@
     sp<Track> trackToRemove;
     sp<Track> activeTrack;
     nsecs_t standbyTime = systemTime();
-    int8_t *curBuf;
     size_t mixBufferSize = mFrameCount*mFrameSize;
     uint32_t activeSleepTime = activeSleepTimeUs();
     uint32_t idleSleepTime = idleSleepTimeUs();
@@ -2664,7 +2733,7 @@
                         mSuspended)) {
                 // wait until we have something to do...
                 if (!mStandby) {
-                    ALOGV("Audio hardware entering standby, mixer %p\n", this);
+                    ALOGV("Audio hardware entering standby, mixer %p", this);
                     mOutput->stream->common.standby(&mOutput->stream->common);
                     mStandby = true;
                     mBytesWritten = 0;
@@ -2677,9 +2746,9 @@
                     if (exitPending()) break;
 
                     releaseWakeLock_l();
-                    ALOGV("DirectOutputThread %p TID %d going to sleep\n", this, gettid());
+                    ALOGV("DirectOutputThread %p TID %d going to sleep", this, gettid());
                     mWaitWorkCV.wait(mLock);
-                    ALOGV("DirectOutputThread %p TID %d waking up in active mode\n", this, gettid());
+                    ALOGV("DirectOutputThread %p TID %d waking up in active mode", this, gettid());
                     acquireWakeLock_l();
 
                     if (!mMasterMute) {
@@ -2687,7 +2756,7 @@
                         property_get("ro.audio.silent", value, "0");
                         if (atoi(value)) {
                             ALOGD("Silence is golden");
-                            setMasterMute(true);
+                            setMasterMute_l(true);
                         }
                     }
 
@@ -2730,13 +2799,13 @@
                     // compute volume for this track
                     float left, right;
                     if (track->isMuted() || mMasterMute || track->isPausing() ||
-                        mStreamTypes[track->type()].mute) {
+                        mStreamTypes[track->streamType()].mute) {
                         left = right = 0;
                         if (track->isPausing()) {
                             track->setPaused();
                         }
                     } else {
-                        float typeVolume = mStreamTypes[track->type()].volume;
+                        float typeVolume = mStreamTypes[track->streamType()].volume;
                         float v = mMasterVolume * typeVolume;
                         uint32_t vlr = cblk->getVolumeLR();
                         float v_clamped = v * (vlr & 0xFFFF);
@@ -2830,11 +2899,12 @@
         if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
             AudioBufferProvider::Buffer buffer;
             size_t frameCount = mFrameCount;
-            curBuf = (int8_t *)mMixBuffer;
+            int8_t *curBuf = (int8_t *)mMixBuffer;
             // output audio to hardware
             while (frameCount) {
                 buffer.frameCount = frameCount;
-                activeTrack->getNextBuffer(&buffer);
+                activeTrack->getNextBuffer(&buffer,
+                                           AudioBufferProvider::kInvalidPTS);
                 if (CC_UNLIKELY(buffer.raw == NULL)) {
                     memset(curBuf, 0, frameCount * mFrameSize);
                     break;
@@ -3002,7 +3072,7 @@
 // ----------------------------------------------------------------------------
 
 AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
-        AudioFlinger::MixerThread* mainThread, int id)
+        AudioFlinger::MixerThread* mainThread, audio_io_handle_t id)
     :   MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device(), DUPLICATING),
         mWaitTimeMs(UINT_MAX)
 {
@@ -3014,7 +3084,6 @@
     for (size_t i = 0; i < mOutputTracks.size(); i++) {
         mOutputTracks[i]->destroy();
     }
-    mOutputTracks.clear();
 }
 
 bool AudioFlinger::DuplicatingThread::threadLoop()
@@ -3073,9 +3142,9 @@
                     if (exitPending()) break;
 
                     releaseWakeLock_l();
-                    ALOGV("DuplicatingThread %p TID %d going to sleep\n", this, gettid());
+                    ALOGV("DuplicatingThread %p TID %d going to sleep", this, gettid());
                     mWaitWorkCV.wait(mLock);
-                    ALOGV("DuplicatingThread %p TID %d waking up\n", this, gettid());
+                    ALOGV("DuplicatingThread %p TID %d waking up", this, gettid());
                     acquireWakeLock_l();
 
                     mPrevMixerStatus = MIXER_IDLE;
@@ -3084,11 +3153,11 @@
                         property_get("ro.audio.silent", value, "0");
                         if (atoi(value)) {
                             ALOGD("Silence is golden");
-                            setMasterMute(true);
+                            setMasterMute_l(true);
                         }
                     }
 
-                    standbyTime = systemTime() + kStandbyTimeInNsecs;
+                    standbyTime = systemTime() + mStandbyTimeInNsecs;
                     sleepTime = idleSleepTime;
                     continue;
                 }
@@ -3105,7 +3174,7 @@
         if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
             // mix buffers...
             if (outputsReady(outputTracks)) {
-                mAudioMixer->process();
+                mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
             } else {
                 memset(mMixBuffer, 0, mixBufferSize);
             }
@@ -3142,7 +3211,7 @@
             // enable changes in effect chain
             unlockEffectChains(effectChains);
 
-            standbyTime = systemTime() + kStandbyTimeInNsecs;
+            standbyTime = systemTime() + mStandbyTimeInNsecs;
             for (size_t i = 0; i < outputTracks.size(); i++) {
                 outputTracks[i]->write(mMixBuffer, writeFrames);
             }
@@ -3172,8 +3241,9 @@
 
 void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
 {
+    // FIXME explain this formula
     int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate();
-    OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread,
+    OutputTrack *outputTrack = new OutputTrack(thread,
                                             this,
                                             mSampleRate,
                                             mFormat,
@@ -3191,7 +3261,7 @@
 {
     Mutex::Autolock _l(mLock);
     for (size_t i = 0; i < mOutputTracks.size(); i++) {
-        if (mOutputTracks[i]->thread() == (ThreadBase *)thread) {
+        if (mOutputTracks[i]->thread() == thread) {
             mOutputTracks[i]->destroy();
             mOutputTracks.removeAt(i);
             updateWaitTime();
@@ -3206,7 +3276,7 @@
     mWaitTimeMs = UINT_MAX;
     for (size_t i = 0; i < mOutputTracks.size(); i++) {
         sp<ThreadBase> strong = mOutputTracks[i]->thread().promote();
-        if (strong != NULL) {
+        if (strong != 0) {
             uint32_t waitTimeMs = (strong->frameCount() * 2 * 1000) / strong->sampleRate();
             if (waitTimeMs < mWaitTimeMs) {
                 mWaitTimeMs = waitTimeMs;
@@ -3242,13 +3312,12 @@
 
 // TrackBase constructor must be called with AudioFlinger::mLock held
 AudioFlinger::ThreadBase::TrackBase::TrackBase(
-            const wp<ThreadBase>& thread,
+            ThreadBase *thread,
             const sp<Client>& client,
             uint32_t sampleRate,
             audio_format_t format,
             uint32_t channelMask,
             int frameCount,
-            uint32_t flags,
             const sp<IMemory>& sharedBuffer,
             int sessionId)
     :   RefBase(),
@@ -3259,9 +3328,8 @@
         // mBufferEnd
         mFrameCount(0),
         mState(IDLE),
-        mClientTid(-1),
         mFormat(format),
-        mFlags(flags & ~SYSTEM_FLAGS_MASK),
+        mStepServerFailed(false),
         mSessionId(sessionId)
         // mChannelCount
         // mChannelMask
@@ -3324,15 +3392,19 @@
 AudioFlinger::ThreadBase::TrackBase::~TrackBase()
 {
     if (mCblk != NULL) {
-        mCblk->~audio_track_cblk_t();   // destroy our shared-structure.
-        if (mClient == NULL) {
+        if (mClient == 0) {
             delete mCblk;
+        } else {
+            mCblk->~audio_track_cblk_t();   // destroy our shared-structure.
         }
     }
-    mCblkMemory.clear();            // and free the shared memory
-    if (mClient != NULL) {
+    mCblkMemory.clear();    // free the shared memory before releasing the heap it belongs to
+    if (mClient != 0) {
         // Client destructor must run with AudioFlinger mutex locked
         Mutex::Autolock _l(mClient->audioFlinger()->mLock);
+        // If the client's reference count drops to zero, the associated destructor
+        // must run with AudioFlinger lock held. Thus the explicit clear() rather than
+        // relying on the automatic clear() at end of scope.
         mClient.clear();
     }
 }
@@ -3352,7 +3424,7 @@
     result = cblk->stepServer(mFrameCount);
     if (!result) {
         ALOGV("stepServer failed acquiring cblk mutex");
-        mFlags |= STEPSERVER_FAILED;
+        mStepServerFailed = true;
     }
     return result;
 }
@@ -3364,27 +3436,14 @@
     cblk->server = 0;
     cblk->userBase = 0;
     cblk->serverBase = 0;
-    mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK);
+    mStepServerFailed = false;
     ALOGV("TrackBase::reset");
 }
 
-sp<IMemory> AudioFlinger::ThreadBase::TrackBase::getCblk() const
-{
-    return mCblkMemory;
-}
-
 int AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
     return (int)mCblk->sampleRate;
 }
 
-int AudioFlinger::ThreadBase::TrackBase::channelCount() const {
-    return (const int)mChannelCount;
-}
-
-uint32_t AudioFlinger::ThreadBase::TrackBase::channelMask() const {
-    return mChannelMask;
-}
-
 void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
     audio_track_cblk_t* cblk = this->cblk();
     size_t frameSize = cblk->frameSize;
@@ -3408,7 +3467,7 @@
 
 // Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
 AudioFlinger::PlaybackThread::Track::Track(
-            const wp<ThreadBase>& thread,
+            PlaybackThread *thread,
             const sp<Client>& client,
             audio_stream_type_t streamType,
             uint32_t sampleRate,
@@ -3417,18 +3476,16 @@
             int frameCount,
             const sp<IMemory>& sharedBuffer,
             int sessionId)
-    :   TrackBase(thread, client, sampleRate, format, channelMask, frameCount, 0, sharedBuffer, sessionId),
+    :   TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId),
     mMute(false), mSharedBuffer(sharedBuffer), mName(-1), mMainBuffer(NULL), mAuxBuffer(NULL),
     mAuxEffectId(0), mHasVolumeController(false)
 {
     if (mCblk != NULL) {
-        sp<ThreadBase> baseThread = thread.promote();
-        if (baseThread != 0) {
-            PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get();
-            mName = playbackThread->getTrackName_l();
-            mMainBuffer = playbackThread->mixBuffer();
+        if (thread != NULL) {
+            mName = thread->getTrackName_l();
+            mMainBuffer = thread->mixBuffer();
         }
-        ALOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+        ALOGV("Track constructor name %d, calling pid %d", mName, IPCThreadState::self()->getCallingPid());
         if (mName < 0) {
             ALOGE("no more track names available");
         }
@@ -3453,7 +3510,7 @@
 {
     // NOTE: destroyTrack_l() can remove a strong reference to this Track
     // by removing it from mTracks vector, so there is a risk that this Tracks's
-    // desctructor is called. As the destructor needs to lock mLock,
+    // destructor is called. As the destructor needs to lock mLock,
     // we must acquire a strong reference on this Track before locking mLock
     // here so that the destructor is called only when exiting this function.
     // On the other hand, as long as Track::destroy() is only called by
@@ -3465,9 +3522,7 @@
         if (thread != 0) {
             if (!isOutputTrack()) {
                 if (mState == ACTIVE || mState == RESUMING) {
-                    AudioSystem::stopOutput(thread->id(),
-                                            (audio_stream_type_t)mStreamType,
-                                            mSessionId);
+                    AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
 
                     // to track the speaker usage
                     addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
@@ -3486,7 +3541,7 @@
     uint32_t vlr = mCblk->getVolumeLR();
     snprintf(buffer, size, "   %05d %05d %03u %03u 0x%08x %05u   %04u %1d %1d %1d %05u %05u %05u  0x%08x 0x%08x 0x%08x 0x%08x\n",
             mName - AudioMixer::TRACK0,
-            (mClient == NULL) ? getpid() : mClient->pid(),
+            (mClient == 0) ? getpid_cached : mClient->pid(),
             mStreamType,
             mFormat,
             mChannelMask,
@@ -3504,17 +3559,18 @@
             (int)mAuxBuffer);
 }
 
-status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
+    AudioBufferProvider::Buffer* buffer, int64_t pts)
 {
      audio_track_cblk_t* cblk = this->cblk();
      uint32_t framesReady;
      uint32_t framesReq = buffer->frameCount;
 
      // Check if last stepServer failed, try to step now
-     if (mFlags & TrackBase::STEPSERVER_FAILED) {
+     if (mStepServerFailed) {
          if (!step())  goto getNextBuffer_exit;
          ALOGV("stepServer recovered");
-         mFlags &= ~TrackBase::STEPSERVER_FAILED;
+         mStepServerFailed = false;
      }
 
      framesReady = cblk->framesReady();
@@ -3545,10 +3601,14 @@
      return NOT_ENOUGH_DATA;
 }
 
+uint32_t AudioFlinger::PlaybackThread::Track::framesReady() const{
+    return mCblk->framesReady();
+}
+
 bool AudioFlinger::PlaybackThread::Track::isReady() const {
     if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing()) return true;
 
-    if (mCblk->framesReady() >= mCblk->frameCount ||
+    if (framesReady() >= mCblk->frameCount ||
             (mCblk->flags & CBLK_FORCEREADY_MSK)) {
         mFillingUpStatus = FS_FILLED;
         android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
@@ -3557,11 +3617,11 @@
     return false;
 }
 
-status_t AudioFlinger::PlaybackThread::Track::start()
+status_t AudioFlinger::PlaybackThread::Track::start(pid_t tid)
 {
     status_t status = NO_ERROR;
-    ALOGV("start(%d), calling thread %d session %d",
-            mName, IPCThreadState::self()->getCallingPid(), mSessionId);
+    ALOGV("start(%d), calling pid %d session %d tid %d",
+            mName, IPCThreadState::self()->getCallingPid(), mSessionId, tid);
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0) {
         Mutex::Autolock _l(thread->mLock);
@@ -3578,9 +3638,7 @@
 
         if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
             thread->mLock.unlock();
-            status = AudioSystem::startOutput(thread->id(),
-                                              (audio_stream_type_t)mStreamType,
-                                              mSessionId);
+            status = AudioSystem::startOutput(thread->id(), mStreamType, mSessionId);
             thread->mLock.lock();
 
             // to track the speaker usage
@@ -3602,7 +3660,7 @@
 
 void AudioFlinger::PlaybackThread::Track::stop()
 {
-    ALOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+    ALOGV("stop(%d), calling pid %d", mName, IPCThreadState::self()->getCallingPid());
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0) {
         Mutex::Autolock _l(thread->mLock);
@@ -3618,9 +3676,7 @@
         }
         if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
             thread->mLock.unlock();
-            AudioSystem::stopOutput(thread->id(),
-                                    (audio_stream_type_t)mStreamType,
-                                    mSessionId);
+            AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
             thread->mLock.lock();
 
             // to track the speaker usage
@@ -3631,7 +3687,7 @@
 
 void AudioFlinger::PlaybackThread::Track::pause()
 {
-    ALOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+    ALOGV("pause(%d), calling pid %d", mName, IPCThreadState::self()->getCallingPid());
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0) {
         Mutex::Autolock _l(thread->mLock);
@@ -3640,9 +3696,7 @@
             ALOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
             if (!isOutputTrack()) {
                 thread->mLock.unlock();
-                AudioSystem::stopOutput(thread->id(),
-                                        (audio_stream_type_t)mStreamType,
-                                        mSessionId);
+                AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
                 thread->mLock.lock();
 
                 // to track the speaker usage
@@ -3711,20 +3765,410 @@
     mAuxBuffer = buffer;
 }
 
+// timed audio tracks
+
+sp<AudioFlinger::PlaybackThread::TimedTrack>
+AudioFlinger::PlaybackThread::TimedTrack::create(
+            PlaybackThread *thread,
+            const sp<Client>& client,
+            audio_stream_type_t streamType,
+            uint32_t sampleRate,
+            audio_format_t format,
+            uint32_t channelMask,
+            int frameCount,
+            const sp<IMemory>& sharedBuffer,
+            int sessionId) {
+    if (!client->reserveTimedTrack())
+        return NULL;
+
+    sp<TimedTrack> track = new TimedTrack(
+        thread, client, streamType, sampleRate, format, channelMask, frameCount,
+        sharedBuffer, sessionId);
+
+    if (track == NULL) {
+        client->releaseTimedTrack();
+        return NULL;
+    }
+
+    return track;
+}
+
+AudioFlinger::PlaybackThread::TimedTrack::TimedTrack(
+            PlaybackThread *thread,
+            const sp<Client>& client,
+            audio_stream_type_t streamType,
+            uint32_t sampleRate,
+            audio_format_t format,
+            uint32_t channelMask,
+            int frameCount,
+            const sp<IMemory>& sharedBuffer,
+            int sessionId)
+    : Track(thread, client, streamType, sampleRate, format, channelMask,
+            frameCount, sharedBuffer, sessionId),
+      mTimedSilenceBuffer(NULL),
+      mTimedSilenceBufferSize(0),
+      mTimedAudioOutputOnTime(false),
+      mMediaTimeTransformValid(false)
+{
+    LocalClock lc;
+    mLocalTimeFreq = lc.getLocalFreq();
+
+    mLocalTimeToSampleTransform.a_zero = 0;
+    mLocalTimeToSampleTransform.b_zero = 0;
+    mLocalTimeToSampleTransform.a_to_b_numer = sampleRate;
+    mLocalTimeToSampleTransform.a_to_b_denom = mLocalTimeFreq;
+    LinearTransform::reduce(&mLocalTimeToSampleTransform.a_to_b_numer,
+                            &mLocalTimeToSampleTransform.a_to_b_denom);
+}
+
+AudioFlinger::PlaybackThread::TimedTrack::~TimedTrack() {
+    mClient->releaseTimedTrack();
+    delete [] mTimedSilenceBuffer;
+}
+
+status_t AudioFlinger::PlaybackThread::TimedTrack::allocateTimedBuffer(
+    size_t size, sp<IMemory>* buffer) {
+
+    Mutex::Autolock _l(mTimedBufferQueueLock);
+
+    trimTimedBufferQueue_l();
+
+    // lazily initialize the shared memory heap for timed buffers
+    if (mTimedMemoryDealer == NULL) {
+        const int kTimedBufferHeapSize = 512 << 10;
+
+        mTimedMemoryDealer = new MemoryDealer(kTimedBufferHeapSize,
+                                              "AudioFlingerTimed");
+        if (mTimedMemoryDealer == NULL)
+            return NO_MEMORY;
+    }
+
+    sp<IMemory> newBuffer = mTimedMemoryDealer->allocate(size);
+    if (newBuffer == NULL) {
+        newBuffer = mTimedMemoryDealer->allocate(size);
+        if (newBuffer == NULL)
+            return NO_MEMORY;
+    }
+
+    *buffer = newBuffer;
+    return NO_ERROR;
+}
+
+// caller must hold mTimedBufferQueueLock
+void AudioFlinger::PlaybackThread::TimedTrack::trimTimedBufferQueue_l() {
+    int64_t mediaTimeNow;
+    {
+        Mutex::Autolock mttLock(mMediaTimeTransformLock);
+        if (!mMediaTimeTransformValid)
+            return;
+
+        int64_t targetTimeNow;
+        status_t res = (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME)
+            ? mCCHelper.getCommonTime(&targetTimeNow)
+            : mCCHelper.getLocalTime(&targetTimeNow);
+
+        if (OK != res)
+            return;
+
+        if (!mMediaTimeTransform.doReverseTransform(targetTimeNow,
+                                                    &mediaTimeNow)) {
+            return;
+        }
+    }
+
+    size_t trimIndex;
+    for (trimIndex = 0; trimIndex < mTimedBufferQueue.size(); trimIndex++) {
+        if (mTimedBufferQueue[trimIndex].pts() > mediaTimeNow)
+            break;
+    }
+
+    if (trimIndex) {
+        mTimedBufferQueue.removeItemsAt(0, trimIndex);
+    }
+}
+
+status_t AudioFlinger::PlaybackThread::TimedTrack::queueTimedBuffer(
+    const sp<IMemory>& buffer, int64_t pts) {
+
+    {
+        Mutex::Autolock mttLock(mMediaTimeTransformLock);
+        if (!mMediaTimeTransformValid)
+            return INVALID_OPERATION;
+    }
+
+    Mutex::Autolock _l(mTimedBufferQueueLock);
+
+    mTimedBufferQueue.add(TimedBuffer(buffer, pts));
+
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::TimedTrack::setMediaTimeTransform(
+    const LinearTransform& xform, TimedAudioTrack::TargetTimeline target) {
+
+    ALOGV("%s az=%lld bz=%lld n=%d d=%u tgt=%d", __PRETTY_FUNCTION__,
+         xform.a_zero, xform.b_zero, xform.a_to_b_numer, xform.a_to_b_denom,
+         target);
+
+    if (!(target == TimedAudioTrack::LOCAL_TIME ||
+          target == TimedAudioTrack::COMMON_TIME)) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mMediaTimeTransformLock);
+    mMediaTimeTransform = xform;
+    mMediaTimeTransformTarget = target;
+    mMediaTimeTransformValid = true;
+
+    return NO_ERROR;
+}
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+// implementation of getNextBuffer for tracks whose buffers have timestamps
+status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
+    AudioBufferProvider::Buffer* buffer, int64_t pts)
+{
+    if (pts == AudioBufferProvider::kInvalidPTS) {
+        buffer->raw = 0;
+        buffer->frameCount = 0;
+        return INVALID_OPERATION;
+    }
+
+    Mutex::Autolock _l(mTimedBufferQueueLock);
+
+    while (true) {
+
+        // if we have no timed buffers, then fail
+        if (mTimedBufferQueue.isEmpty()) {
+            buffer->raw = 0;
+            buffer->frameCount = 0;
+            return NOT_ENOUGH_DATA;
+        }
+
+        TimedBuffer& head = mTimedBufferQueue.editItemAt(0);
+
+        // calculate the PTS of the head of the timed buffer queue expressed in
+        // local time
+        int64_t headLocalPTS;
+        {
+            Mutex::Autolock mttLock(mMediaTimeTransformLock);
+
+            assert(mMediaTimeTransformValid);
+
+            if (mMediaTimeTransform.a_to_b_denom == 0) {
+                // the transform represents a pause, so yield silence
+                timedYieldSilence(buffer->frameCount, buffer);
+                return NO_ERROR;
+            }
+
+            int64_t transformedPTS;
+            if (!mMediaTimeTransform.doForwardTransform(head.pts(),
+                                                        &transformedPTS)) {
+                // the transform failed.  this shouldn't happen, but if it does
+                // then just drop this buffer
+                ALOGW("timedGetNextBuffer transform failed");
+                buffer->raw = 0;
+                buffer->frameCount = 0;
+                mTimedBufferQueue.removeAt(0);
+                return NO_ERROR;
+            }
+
+            if (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME) {
+                if (OK != mCCHelper.commonTimeToLocalTime(transformedPTS,
+                                                          &headLocalPTS)) {
+                    buffer->raw = 0;
+                    buffer->frameCount = 0;
+                    return INVALID_OPERATION;
+                }
+            } else {
+                headLocalPTS = transformedPTS;
+            }
+        }
+
+        // adjust the head buffer's PTS to reflect the portion of the head buffer
+        // that has already been consumed
+        int64_t effectivePTS = headLocalPTS +
+                ((head.position() / mCblk->frameSize) * mLocalTimeFreq / sampleRate());
+
+        // Calculate the delta in samples between the head of the input buffer
+        // queue and the start of the next output buffer that will be written.
+        // If the transformation fails because of over or underflow, it means
+        // that the sample's position in the output stream is so far out of
+        // whack that it should just be dropped.
+        int64_t sampleDelta;
+        if (llabs(effectivePTS - pts) >= (static_cast<int64_t>(1) << 31)) {
+            ALOGV("*** head buffer is too far from PTS: dropped buffer");
+            mTimedBufferQueue.removeAt(0);
+            continue;
+        }
+        if (!mLocalTimeToSampleTransform.doForwardTransform(
+                (effectivePTS - pts) << 32, &sampleDelta)) {
+            ALOGV("*** too late during sample rate transform: dropped buffer");
+            mTimedBufferQueue.removeAt(0);
+            continue;
+        }
+
+        ALOGV("*** %s head.pts=%lld head.pos=%d pts=%lld sampleDelta=[%d.%08x]",
+             __PRETTY_FUNCTION__, head.pts(), head.position(), pts,
+             static_cast<int32_t>((sampleDelta >= 0 ? 0 : 1) + (sampleDelta >> 32)),
+             static_cast<uint32_t>(sampleDelta & 0xFFFFFFFF));
+
+        // if the delta between the ideal placement for the next input sample and
+        // the current output position is within this threshold, then we will
+        // concatenate the next input samples to the previous output
+        const int64_t kSampleContinuityThreshold =
+                (static_cast<int64_t>(sampleRate()) << 32) / 10;
+
+        // if this is the first buffer of audio that we're emitting from this track
+        // then it should be almost exactly on time.
+        const int64_t kSampleStartupThreshold = 1LL << 32;
+
+        if ((mTimedAudioOutputOnTime && llabs(sampleDelta) <= kSampleContinuityThreshold) ||
+            (!mTimedAudioOutputOnTime && llabs(sampleDelta) <= kSampleStartupThreshold)) {
+            // the next input is close enough to being on time, so concatenate it
+            // with the last output
+            timedYieldSamples(buffer);
+
+            ALOGV("*** on time: head.pos=%d frameCount=%u", head.position(), buffer->frameCount);
+            return NO_ERROR;
+        } else if (sampleDelta > 0) {
+            // the gap between the current output position and the proper start of
+            // the next input sample is too big, so fill it with silence
+            uint32_t framesUntilNextInput = (sampleDelta + 0x80000000) >> 32;
+
+            timedYieldSilence(framesUntilNextInput, buffer);
+            ALOGV("*** silence: frameCount=%u", buffer->frameCount);
+            return NO_ERROR;
+        } else {
+            // the next input sample is late
+            uint32_t lateFrames = static_cast<uint32_t>(-((sampleDelta + 0x80000000) >> 32));
+            size_t onTimeSamplePosition =
+                    head.position() + lateFrames * mCblk->frameSize;
+
+            if (onTimeSamplePosition > head.buffer()->size()) {
+                // all the remaining samples in the head are too late, so
+                // drop it and move on
+                ALOGV("*** too late: dropped buffer");
+                mTimedBufferQueue.removeAt(0);
+                continue;
+            } else {
+                // skip over the late samples
+                head.setPosition(onTimeSamplePosition);
+
+                // yield the available samples
+                timedYieldSamples(buffer);
+
+                ALOGV("*** late: head.pos=%d frameCount=%u", head.position(), buffer->frameCount);
+                return NO_ERROR;
+            }
+        }
+    }
+}
+
+// Yield samples from the timed buffer queue head up to the given output
+// buffer's capacity.
+//
+// Caller must hold mTimedBufferQueueLock
+void AudioFlinger::PlaybackThread::TimedTrack::timedYieldSamples(
+    AudioBufferProvider::Buffer* buffer) {
+
+    const TimedBuffer& head = mTimedBufferQueue[0];
+
+    buffer->raw = (static_cast<uint8_t*>(head.buffer()->pointer()) +
+                   head.position());
+
+    uint32_t framesLeftInHead = ((head.buffer()->size() - head.position()) /
+                                 mCblk->frameSize);
+    size_t framesRequested = buffer->frameCount;
+    buffer->frameCount = min(framesLeftInHead, framesRequested);
+
+    mTimedAudioOutputOnTime = true;
+}
+
+// Yield samples of silence up to the given output buffer's capacity
+//
+// Caller must hold mTimedBufferQueueLock
+void AudioFlinger::PlaybackThread::TimedTrack::timedYieldSilence(
+    uint32_t numFrames, AudioBufferProvider::Buffer* buffer) {
+
+    // lazily allocate a buffer filled with silence
+    if (mTimedSilenceBufferSize < numFrames * mCblk->frameSize) {
+        delete [] mTimedSilenceBuffer;
+        mTimedSilenceBufferSize = numFrames * mCblk->frameSize;
+        mTimedSilenceBuffer = new uint8_t[mTimedSilenceBufferSize];
+        memset(mTimedSilenceBuffer, 0, mTimedSilenceBufferSize);
+    }
+
+    buffer->raw = mTimedSilenceBuffer;
+    size_t framesRequested = buffer->frameCount;
+    buffer->frameCount = min(numFrames, framesRequested);
+
+    mTimedAudioOutputOnTime = false;
+}
+
+void AudioFlinger::PlaybackThread::TimedTrack::releaseBuffer(
+    AudioBufferProvider::Buffer* buffer) {
+
+    Mutex::Autolock _l(mTimedBufferQueueLock);
+
+    // If the buffer which was just released is part of the buffer at the head
+    // of the queue, be sure to update the amt of the buffer which has been
+    // consumed.  If the buffer being returned is not part of the head of the
+    // queue, its either because the buffer is part of the silence buffer, or
+    // because the head of the timed queue was trimmed after the mixer called
+    // getNextBuffer but before the mixer called releaseBuffer.
+    if ((buffer->raw != mTimedSilenceBuffer) && mTimedBufferQueue.size()) {
+        TimedBuffer& head = mTimedBufferQueue.editItemAt(0);
+
+        void* start = head.buffer()->pointer();
+        void* end   = (char *) head.buffer()->pointer() + head.buffer()->size();
+
+        if ((buffer->raw >= start) && (buffer->raw <= end)) {
+            head.setPosition(head.position() +
+                    (buffer->frameCount * mCblk->frameSize));
+            if (static_cast<size_t>(head.position()) >= head.buffer()->size()) {
+                mTimedBufferQueue.removeAt(0);
+            }
+        }
+    }
+
+    buffer->raw = 0;
+    buffer->frameCount = 0;
+}
+
+uint32_t AudioFlinger::PlaybackThread::TimedTrack::framesReady() const {
+    Mutex::Autolock _l(mTimedBufferQueueLock);
+
+    uint32_t frames = 0;
+    for (size_t i = 0; i < mTimedBufferQueue.size(); i++) {
+        const TimedBuffer& tb = mTimedBufferQueue[i];
+        frames += (tb.buffer()->size() - tb.position())  / mCblk->frameSize;
+    }
+
+    return frames;
+}
+
+AudioFlinger::PlaybackThread::TimedTrack::TimedBuffer::TimedBuffer()
+        : mPTS(0), mPosition(0) {}
+
+AudioFlinger::PlaybackThread::TimedTrack::TimedBuffer::TimedBuffer(
+    const sp<IMemory>& buffer, int64_t pts)
+        : mBuffer(buffer), mPTS(pts), mPosition(0) {}
+
 // ----------------------------------------------------------------------------
 
 // RecordTrack constructor must be called with AudioFlinger::mLock held
 AudioFlinger::RecordThread::RecordTrack::RecordTrack(
-            const wp<ThreadBase>& thread,
+            RecordThread *thread,
             const sp<Client>& client,
             uint32_t sampleRate,
             audio_format_t format,
             uint32_t channelMask,
             int frameCount,
-            uint32_t flags,
             int sessionId)
     :   TrackBase(thread, client, sampleRate, format,
-                  channelMask, frameCount, flags, 0, sessionId),
+                  channelMask, frameCount, 0 /*sharedBuffer*/, sessionId),
         mOverflow(false)
 {
     if (mCblk != NULL) {
@@ -3747,17 +4191,17 @@
     }
 }
 
-status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts)
 {
     audio_track_cblk_t* cblk = this->cblk();
     uint32_t framesAvail;
     uint32_t framesReq = buffer->frameCount;
 
      // Check if last stepServer failed, try to step now
-    if (mFlags & TrackBase::STEPSERVER_FAILED) {
+    if (mStepServerFailed) {
         if (!step()) goto getNextBuffer_exit;
         ALOGV("stepServer recovered");
-        mFlags &= ~TrackBase::STEPSERVER_FAILED;
+        mStepServerFailed = false;
     }
 
     framesAvail = cblk->framesAvailable_l();
@@ -3786,12 +4230,12 @@
     return NOT_ENOUGH_DATA;
 }
 
-status_t AudioFlinger::RecordThread::RecordTrack::start()
+status_t AudioFlinger::RecordThread::RecordTrack::start(pid_t tid)
 {
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0) {
         RecordThread *recordThread = (RecordThread *)thread.get();
-        return recordThread->start(this);
+        return recordThread->start(this, tid);
     } else {
         return BAD_VALUE;
     }
@@ -3813,7 +4257,7 @@
 void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
 {
     snprintf(buffer, size, "   %05d %03u 0x%08x %05d   %04u %01d %05u  %08x %08x\n",
-            (mClient == NULL) ? getpid() : mClient->pid(),
+            (mClient == 0) ? getpid_cached : mClient->pid(),
             mFormat,
             mChannelMask,
             mSessionId,
@@ -3828,17 +4272,16 @@
 // ----------------------------------------------------------------------------
 
 AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
-            const wp<ThreadBase>& thread,
+            PlaybackThread *playbackThread,
             DuplicatingThread *sourceThread,
             uint32_t sampleRate,
             audio_format_t format,
             uint32_t channelMask,
             int frameCount)
-    :   Track(thread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, NULL, 0),
+    :   Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, NULL, 0),
     mActive(false), mSourceThread(sourceThread)
 {
 
-    PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get();
     if (mCblk != NULL) {
         mCblk->flags |= CBLK_DIRECTION_OUT;
         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
@@ -3858,9 +4301,9 @@
     clearBufferQueue();
 }
 
-status_t AudioFlinger::PlaybackThread::OutputTrack::start()
+status_t AudioFlinger::PlaybackThread::OutputTrack::start(pid_t tid)
 {
-    status_t status = Track::start();
+    status_t status = Track::start(tid);
     if (status != NO_ERROR) {
         return status;
     }
@@ -3890,7 +4333,7 @@
     uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
 
     if (!mActive && frames != 0) {
-        start();
+        start(0);
         sp<ThreadBase> thread = mThread.promote();
         if (thread != 0) {
             MixerThread *mixerThread = (MixerThread *)thread.get();
@@ -4052,10 +4495,9 @@
 void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
 {
     size_t size = mBufferQueue.size();
-    Buffer *pBuffer;
 
     for (size_t i = 0; i < size; i++) {
-        pBuffer = mBufferQueue.itemAt(i);
+        Buffer *pBuffer = mBufferQueue.itemAt(i);
         delete [] pBuffer->mBuffer;
         delete pBuffer;
     }
@@ -4067,8 +4509,10 @@
 AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid)
     :   RefBase(),
         mAudioFlinger(audioFlinger),
+        // FIXME should be a "k" constant not hard-coded, in .h or ro. property, see 4 lines below
         mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")),
-        mPid(pid)
+        mPid(pid),
+        mTimedTrackCount(0)
 {
     // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer
 }
@@ -4084,6 +4528,31 @@
     return mMemoryDealer;
 }
 
+// Reserve one of the limited slots for a timed audio track associated
+// with this client
+bool AudioFlinger::Client::reserveTimedTrack()
+{
+    const int kMaxTimedTracksPerClient = 4;
+
+    Mutex::Autolock _l(mTimedTrackLock);
+
+    if (mTimedTrackCount >= kMaxTimedTracksPerClient) {
+        ALOGW("can not create timed track - pid %d has exceeded the limit",
+             mPid);
+        return false;
+    }
+
+    mTimedTrackCount++;
+    return true;
+}
+
+// Release a slot for a timed audio track
+void AudioFlinger::Client::releaseTimedTrack()
+{
+    Mutex::Autolock _l(mTimedTrackLock);
+    mTimedTrackCount--;
+}
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,
@@ -4100,9 +4569,7 @@
 void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
 {
     sp<NotificationClient> keep(this);
-    {
-        mAudioFlinger->removeNotificationClient(mPid);
-    }
+    mAudioFlinger->removeNotificationClient(mPid);
 }
 
 // ----------------------------------------------------------------------------
@@ -4125,8 +4592,8 @@
     return mTrack->getCblk();
 }
 
-status_t AudioFlinger::TrackHandle::start() {
-    return mTrack->start();
+status_t AudioFlinger::TrackHandle::start(pid_t tid) {
+    return mTrack->start(tid);
 }
 
 void AudioFlinger::TrackHandle::stop() {
@@ -4150,6 +4617,38 @@
     return mTrack->attachAuxEffect(EffectId);
 }
 
+status_t AudioFlinger::TrackHandle::allocateTimedBuffer(size_t size,
+                                                         sp<IMemory>* buffer) {
+    if (!mTrack->isTimedTrack())
+        return INVALID_OPERATION;
+
+    PlaybackThread::TimedTrack* tt =
+            reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
+    return tt->allocateTimedBuffer(size, buffer);
+}
+
+status_t AudioFlinger::TrackHandle::queueTimedBuffer(const sp<IMemory>& buffer,
+                                                     int64_t pts) {
+    if (!mTrack->isTimedTrack())
+        return INVALID_OPERATION;
+
+    PlaybackThread::TimedTrack* tt =
+            reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
+    return tt->queueTimedBuffer(buffer, pts);
+}
+
+status_t AudioFlinger::TrackHandle::setMediaTimeTransform(
+    const LinearTransform& xform, int target) {
+
+    if (!mTrack->isTimedTrack())
+        return INVALID_OPERATION;
+
+    PlaybackThread::TimedTrack* tt =
+            reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
+    return tt->setMediaTimeTransform(
+        xform, static_cast<TimedAudioTrack::TargetTimeline>(target));
+}
+
 status_t AudioFlinger::TrackHandle::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
@@ -4160,11 +4659,12 @@
 
 sp<IAudioRecord> AudioFlinger::openRecord(
         pid_t pid,
-        int input,
+        audio_io_handle_t input,
         uint32_t sampleRate,
         audio_format_t format,
         uint32_t channelMask,
         int frameCount,
+        // FIXME dead, remove from IAudioFlinger
         uint32_t flags,
         int *sessionId,
         status_t *status)
@@ -4172,7 +4672,6 @@
     sp<RecordThread::RecordTrack> recordTrack;
     sp<RecordHandle> recordHandle;
     sp<Client> client;
-    wp<Client> wclient;
     status_t lStatus;
     RecordThread *thread;
     size_t inFrameCount;
@@ -4193,13 +4692,7 @@
             goto Exit;
         }
 
-        wclient = mClients.valueFor(pid);
-        if (wclient != NULL) {
-            client = wclient.promote();
-        } else {
-            client = new Client(this, pid);
-            mClients.add(pid, client);
-        }
+        client = registerPid_l(pid);
 
         // If no audio session id is provided, create one here
         if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
@@ -4216,7 +4709,6 @@
                                                 format,
                                                 channelMask,
                                                 frameCount,
-                                                flags,
                                                 lSessionId,
                                                 &lStatus);
     }
@@ -4255,9 +4747,9 @@
     return mRecordTrack->getCblk();
 }
 
-status_t AudioFlinger::RecordHandle::start() {
+status_t AudioFlinger::RecordHandle::start(pid_t tid) {
     ALOGV("RecordHandle::start()");
-    return mRecordTrack->start();
+    return mRecordTrack->start(tid);
 }
 
 void AudioFlinger::RecordHandle::stop() {
@@ -4277,7 +4769,7 @@
                                          AudioStreamIn *input,
                                          uint32_t sampleRate,
                                          uint32_t channels,
-                                         int id,
+                                         audio_io_handle_t id,
                                          uint32_t device) :
     ThreadBase(audioFlinger, id, device, RECORD),
     mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
@@ -4386,7 +4878,8 @@
             }
 
             buffer.frameCount = mFrameCount;
-            if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+            if (CC_LIKELY(mActiveTrack->getNextBuffer(
+                    &buffer, AudioBufferProvider::kInvalidPTS) == NO_ERROR)) {
                 size_t framesOut = buffer.frameCount;
                 if (mResampler == NULL) {
                     // no resampling
@@ -4509,7 +5002,6 @@
         audio_format_t format,
         int channelMask,
         int frameCount,
-        uint32_t flags,
         int sessionId,
         status_t *status)
 {
@@ -4526,9 +5018,9 @@
         Mutex::Autolock _l(mLock);
 
         track = new RecordTrack(this, client, sampleRate,
-                      format, channelMask, frameCount, flags, sessionId);
+                      format, channelMask, frameCount, sessionId);
 
-        if (track->getCblk() == NULL) {
+        if (track->getCblk() == 0) {
             lStatus = NO_MEMORY;
             goto Exit;
         }
@@ -4549,9 +5041,9 @@
     return track;
 }
 
-status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack)
+status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack, pid_t tid)
 {
-    ALOGV("RecordThread::start");
+    ALOGV("RecordThread::start tid=%d", tid);
     sp <ThreadBase> strongMe = this;
     status_t status = NO_ERROR;
     {
@@ -4584,7 +5076,7 @@
         ALOGV("Signal record thread");
         mWaitWorkCV.signal();
         // do not wait for mStartStopCond if exiting
-        if (mExiting) {
+        if (exitPending()) {
             mActiveTrack.clear();
             status = INVALID_OPERATION;
             goto startError;
@@ -4611,7 +5103,7 @@
         if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) {
             mActiveTrack->mState = TrackBase::PAUSING;
             // do not wait for mStartStopCond if exiting
-            if (mExiting) {
+            if (exitPending()) {
                 return;
             }
             mStartStopCond.wait(mLock);
@@ -4631,7 +5123,6 @@
     const size_t SIZE = 256;
     char buffer[SIZE];
     String8 result;
-    pid_t pid = 0;
 
     snprintf(buffer, SIZE, "\nInput thread %p internals\n", this);
     result.append(buffer);
@@ -4665,7 +5156,7 @@
     return NO_ERROR;
 }
 
-status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts)
 {
     size_t framesReq = buffer->frameCount;
     size_t framesReady = mFrameCount - mRsmpInIndex;
@@ -4736,7 +5227,7 @@
         }
         if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
             // do not accept frame count changes if tracks are open as the track buffer
-            // size depends on frame count and correct behavior would not be garantied
+            // size depends on frame count and correct behavior would not be guaranteed
             // if frame count is changed after track creation
             if (mActiveTrack != 0) {
                 status = INVALID_OPERATION;
@@ -4938,7 +5429,7 @@
 
 // ----------------------------------------------------------------------------
 
-int AudioFlinger::openOutput(uint32_t *pDevices,
+audio_io_handle_t AudioFlinger::openOutput(uint32_t *pDevices,
                                 uint32_t *pSamplingRate,
                                 audio_format_t *pFormat,
                                 uint32_t *pChannels,
@@ -4984,7 +5475,7 @@
     mHardwareStatus = AUDIO_HW_IDLE;
     if (outStream != NULL) {
         AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);
-        int id = nextUniqueId();
+        audio_io_handle_t id = nextUniqueId();
 
         if ((flags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT) ||
             (format != AUDIO_FORMAT_PCM_16_BIT) ||
@@ -5010,7 +5501,8 @@
     return 0;
 }
 
-int AudioFlinger::openDuplicateOutput(int output1, int output2)
+audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1,
+        audio_io_handle_t output2)
 {
     Mutex::Autolock _l(mLock);
     MixerThread *thread1 = checkMixerThread_l(output1);
@@ -5021,7 +5513,7 @@
         return 0;
     }
 
-    int id = nextUniqueId();
+    audio_io_handle_t id = nextUniqueId();
     DuplicatingThread *thread = new DuplicatingThread(this, thread1, id);
     thread->addOutputTrack(thread2);
     mPlaybackThreads.add(id, thread);
@@ -5030,7 +5522,7 @@
     return id;
 }
 
-status_t AudioFlinger::closeOutput(int output)
+status_t AudioFlinger::closeOutput(audio_io_handle_t output)
 {
     // keep strong reference on the playback thread so that
     // it is not destroyed while exit() is executed
@@ -5052,11 +5544,12 @@
                 }
             }
         }
-        void *param2 = NULL;
-        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
+        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
         mPlaybackThreads.removeItem(output);
     }
     thread->exit();
+    // The thread entity (active unit of execution) is no longer running here,
+    // but the ThreadBase container still exists.
 
     if (thread->type() != ThreadBase::DUPLICATING) {
         AudioStreamOut *out = thread->clearOutput();
@@ -5068,7 +5561,7 @@
     return NO_ERROR;
 }
 
-status_t AudioFlinger::suspendOutput(int output)
+status_t AudioFlinger::suspendOutput(audio_io_handle_t output)
 {
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -5083,7 +5576,7 @@
     return NO_ERROR;
 }
 
-status_t AudioFlinger::restoreOutput(int output)
+status_t AudioFlinger::restoreOutput(audio_io_handle_t output)
 {
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -5099,7 +5592,7 @@
     return NO_ERROR;
 }
 
-int AudioFlinger::openInput(uint32_t *pDevices,
+audio_io_handle_t AudioFlinger::openInput(uint32_t *pDevices,
                                 uint32_t *pSamplingRate,
                                 audio_format_t *pFormat,
                                 uint32_t *pChannels,
@@ -5155,7 +5648,7 @@
     if (inStream != NULL) {
         AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
 
-        int id = nextUniqueId();
+        audio_io_handle_t id = nextUniqueId();
         // Start record thread
         // RecorThread require both input and output device indication to forward to audio
         // pre processing modules
@@ -5182,7 +5675,7 @@
     return 0;
 }
 
-status_t AudioFlinger::closeInput(int input)
+status_t AudioFlinger::closeInput(audio_io_handle_t input)
 {
     // keep strong reference on the record thread so that
     // it is not destroyed while exit() is executed
@@ -5195,11 +5688,12 @@
         }
 
         ALOGV("closeInput() %d", input);
-        void *param2 = NULL;
-        audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2);
+        audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, NULL);
         mRecordThreads.removeItem(input);
     }
     thread->exit();
+    // The thread entity (active unit of execution) is no longer running here,
+    // but the ThreadBase container still exists.
 
     AudioStreamIn *in = thread->clearInput();
     assert(in != NULL);
@@ -5210,7 +5704,7 @@
     return NO_ERROR;
 }
 
-status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, int output)
+status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output)
 {
     Mutex::Autolock _l(mLock);
     MixerThread *dstThread = checkMixerThread_l(output);
@@ -5226,8 +5720,7 @@
 
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
         PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
-        if (thread != dstThread &&
-            thread->type() != ThreadBase::DIRECT) {
+        if (thread != dstThread && thread->type() != ThreadBase::DIRECT) {
             MixerThread *srcThread = (MixerThread *)thread;
             srcThread->setStreamValid(stream, false);
             srcThread->invalidateTracks(stream);
@@ -5246,10 +5739,10 @@
 void AudioFlinger::acquireAudioSessionId(int audioSession)
 {
     Mutex::Autolock _l(mLock);
-    int caller = IPCThreadState::self()->getCallingPid();
+    pid_t caller = IPCThreadState::self()->getCallingPid();
     ALOGV("acquiring %d from %d", audioSession, caller);
-    int num = mAudioSessionRefs.size();
-    for (int i = 0; i< num; i++) {
+    size_t num = mAudioSessionRefs.size();
+    for (size_t i = 0; i< num; i++) {
         AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
         if (ref->sessionid == audioSession && ref->pid == caller) {
             ref->cnt++;
@@ -5264,10 +5757,10 @@
 void AudioFlinger::releaseAudioSessionId(int audioSession)
 {
     Mutex::Autolock _l(mLock);
-    int caller = IPCThreadState::self()->getCallingPid();
+    pid_t caller = IPCThreadState::self()->getCallingPid();
     ALOGV("releasing %d from %d", audioSession, caller);
-    int num = mAudioSessionRefs.size();
-    for (int i = 0; i< num; i++) {
+    size_t num = mAudioSessionRefs.size();
+    for (size_t i = 0; i< num; i++) {
         AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
         if (ref->sessionid == audioSession && ref->pid == caller) {
             ref->cnt--;
@@ -5348,35 +5841,22 @@
 }
 
 // checkPlaybackThread_l() must be called with AudioFlinger::mLock held
-AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
+AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const
 {
-    PlaybackThread *thread = NULL;
-    if (mPlaybackThreads.indexOfKey(output) >= 0) {
-        thread = (PlaybackThread *)mPlaybackThreads.valueFor(output).get();
-    }
-    return thread;
+    return mPlaybackThreads.valueFor(output).get();
 }
 
 // checkMixerThread_l() must be called with AudioFlinger::mLock held
-AudioFlinger::MixerThread *AudioFlinger::checkMixerThread_l(int output) const
+AudioFlinger::MixerThread *AudioFlinger::checkMixerThread_l(audio_io_handle_t output) const
 {
     PlaybackThread *thread = checkPlaybackThread_l(output);
-    if (thread != NULL) {
-        if (thread->type() == ThreadBase::DIRECT) {
-            thread = NULL;
-        }
-    }
-    return (MixerThread *)thread;
+    return thread != NULL && thread->type() != ThreadBase::DIRECT ? (MixerThread *) thread : NULL;
 }
 
 // checkRecordThread_l() must be called with AudioFlinger::mLock held
-AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(int input) const
+AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(audio_io_handle_t input) const
 {
-    RecordThread *thread = NULL;
-    if (mRecordThreads.indexOfKey(input) >= 0) {
-        thread = (RecordThread *)mRecordThreads.valueFor(input).get();
-    }
-    return thread;
+    return mRecordThreads.valueFor(input).get();
 }
 
 uint32_t AudioFlinger::nextUniqueId()
@@ -5413,19 +5893,20 @@
 // ----------------------------------------------------------------------------
 
 
-status_t AudioFlinger::queryNumberEffects(uint32_t *numEffects)
+status_t AudioFlinger::queryNumberEffects(uint32_t *numEffects) const
 {
     Mutex::Autolock _l(mLock);
     return EffectQueryNumberEffects(numEffects);
 }
 
-status_t AudioFlinger::queryEffect(uint32_t index, effect_descriptor_t *descriptor)
+status_t AudioFlinger::queryEffect(uint32_t index, effect_descriptor_t *descriptor) const
 {
     Mutex::Autolock _l(mLock);
     return EffectQueryEffect(index, descriptor);
 }
 
-status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor)
+status_t AudioFlinger::getEffectDescriptor(const effect_uuid_t *pUuid,
+        effect_descriptor_t *descriptor) const
 {
     Mutex::Autolock _l(mLock);
     return EffectGetDescriptor(pUuid, descriptor);
@@ -5436,7 +5917,7 @@
         effect_descriptor_t *pDesc,
         const sp<IEffectClient>& effectClient,
         int32_t priority,
-        int io,
+        audio_io_handle_t io,
         int sessionId,
         status_t *status,
         int *id,
@@ -5445,10 +5926,8 @@
     status_t lStatus = NO_ERROR;
     sp<EffectHandle> handle;
     effect_descriptor_t desc;
-    sp<Client> client;
-    wp<Client> wclient;
 
-    ALOGV("createEffect pid %d, client %p, priority %d, sessionId %d, io %d",
+    ALOGV("createEffect pid %d, effectClient %p, priority %d, sessionId %d, io %d",
             pid, effectClient.get(), priority, sessionId, io);
 
     if (pDesc == NULL) {
@@ -5464,7 +5943,7 @@
 
     // Session AUDIO_SESSION_OUTPUT_STAGE is reserved for output stage effects
     // that can only be created by audio policy manager (running in same process)
-    if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && getpid() != pid) {
+    if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && getpid_cached != pid) {
         lStatus = PERMISSION_DENIED;
         goto Exit;
     }
@@ -5599,14 +6078,7 @@
             }
         }
 
-        wclient = mClients.valueFor(pid);
-
-        if (wclient != NULL) {
-            client = wclient.promote();
-        } else {
-            client = new Client(this, pid);
-            mClients.add(pid, client);
-        }
+        sp<Client> client = registerPid_l(pid);
 
         // create effect on selected output thread
         handle = thread->createEffect_l(client, effectClient, priority, sessionId,
@@ -5623,7 +6095,8 @@
     return handle;
 }
 
-status_t AudioFlinger::moveEffects(int sessionId, int srcOutput, int dstOutput)
+status_t AudioFlinger::moveEffects(int sessionId, audio_io_handle_t srcOutput,
+        audio_io_handle_t dstOutput)
 {
     ALOGV("moveEffects() session %d, srcOutput %d, dstOutput %d",
             sessionId, srcOutput, dstOutput);
@@ -5674,7 +6147,7 @@
 
     // transfer all effects one by one so that new effect chain is created on new thread with
     // correct buffer sizes and audio parameters and effect engines reconfigured accordingly
-    int dstOutput = dstThread->id();
+    audio_io_handle_t dstOutput = dstThread->id();
     sp<EffectChain> dstChain;
     uint32_t strategy = 0; // prevent compiler warning
     sp<EffectModule> effect = chain->getEffectFromId_l(0);
@@ -5746,10 +6219,7 @@
         goto Exit;
     }
     // Only Pre processor effects are allowed on input threads and only on input threads
-    if ((mType == RECORD &&
-            (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC) ||
-            (mType != RECORD &&
-                    (desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) {
+    if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) {
         ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d",
                 desc->name, desc->flags, mType);
         lStatus = BAD_VALUE;
@@ -5830,13 +6300,8 @@
 
 sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect_l(int sessionId, int effectId)
 {
-    sp<EffectModule> effect;
-
     sp<EffectChain> chain = getEffectChain_l(sessionId);
-    if (chain != 0) {
-        effect = chain->getEffectFromId_l(effectId);
-    }
-    return effect;
+    return chain != 0 ? chain->getEffectFromId_l(effectId) : 0;
 }
 
 // PlaybackThread::addEffect_l() must be called with AudioFlinger::mLock and
@@ -5921,16 +6386,13 @@
 
 sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain_l(int sessionId)
 {
-    sp<EffectChain> chain;
-
     size_t size = mEffectChains.size();
     for (size_t i = 0; i < size; i++) {
         if (mEffectChains[i]->sessionId() == sessionId) {
-            chain = mEffectChains[i];
-            break;
+            return mEffectChains[i];
         }
     }
-    return chain;
+    return 0;
 }
 
 void AudioFlinger::ThreadBase::setMode(audio_mode_t mode)
@@ -5944,13 +6406,13 @@
 
 void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect,
                                                     const wp<EffectHandle>& handle,
-                                                    bool unpiniflast) {
+                                                    bool unpinIfLast) {
 
     Mutex::Autolock _l(mLock);
     ALOGV("disconnectEffect() %p effect %p", this, effect.get());
     // delete the effect module if removing last handle on it
     if (effect->removeHandle(handle) == 0) {
-        if (!effect->isPinned() || unpiniflast) {
+        if (!effect->isPinned() || unpinIfLast) {
             removeEffect_l(effect);
             AudioSystem::unregisterEffect(effect->id());
         }
@@ -6130,18 +6592,17 @@
 #undef LOG_TAG
 #define LOG_TAG "AudioFlinger::EffectModule"
 
-AudioFlinger::EffectModule::EffectModule(const wp<ThreadBase>& wThread,
+AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
                                         const wp<AudioFlinger::EffectChain>& chain,
                                         effect_descriptor_t *desc,
                                         int id,
                                         int sessionId)
-    : mThread(wThread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL),
+    : mThread(thread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL),
       mStatus(NO_INIT), mState(IDLE), mSuspended(false)
 {
     ALOGV("Constructor %p", this);
     int lStatus;
-    sp<ThreadBase> thread = mThread.promote();
-    if (thread == 0) {
+    if (thread == NULL) {
         return;
     }
 
@@ -6194,7 +6655,6 @@
     status_t status;
 
     Mutex::Autolock _l(mLock);
-    // First handle in mHandles has highest priority and controls the effect module
     int priority = handle->priority();
     size_t size = mHandles.size();
     sp<EffectHandle> h;
@@ -6263,23 +6723,19 @@
 sp<AudioFlinger::EffectHandle> AudioFlinger::EffectModule::controlHandle()
 {
     Mutex::Autolock _l(mLock);
-    sp<EffectHandle> handle;
-    if (mHandles.size() != 0) {
-        handle = mHandles[0].promote();
-    }
-    return handle;
+    return mHandles.size() != 0 ? mHandles[0].promote() : 0;
 }
 
-void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool unpiniflast)
+void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool unpinIfLast)
 {
-    ALOGV("disconnect() %p handle %p ", this, handle.unsafe_get());
+    ALOGV("disconnect() %p handle %p", this, handle.unsafe_get());
     // keep a strong reference on this EffectModule to avoid calling the
     // destructor before we exit
     sp<EffectModule> keep(this);
     {
         sp<ThreadBase> thread = mThread.promote();
         if (thread != 0) {
-            thread->disconnectEffect(keep, handle, unpiniflast);
+            thread->disconnectEffect(keep, handle, unpinIfLast);
         }
     }
 }
@@ -6622,7 +7078,7 @@
     return NO_ERROR;
 }
 
-bool AudioFlinger::EffectModule::isEnabled()
+bool AudioFlinger::EffectModule::isEnabled() const
 {
     switch (mState) {
     case RESTART:
@@ -6638,7 +7094,7 @@
     }
 }
 
-bool AudioFlinger::EffectModule::isProcessEnabled()
+bool AudioFlinger::EffectModule::isProcessEnabled() const
 {
     switch (mState) {
     case RESTART:
@@ -6951,13 +7407,13 @@
     disconnect(true);
 }
 
-void AudioFlinger::EffectHandle::disconnect(bool unpiniflast)
+void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast)
 {
-    ALOGV("disconnect(%s)", unpiniflast ? "true" : "false");
+    ALOGV("disconnect(%s)", unpinIfLast ? "true" : "false");
     if (mEffect == 0) {
         return;
     }
-    mEffect->disconnect(this, unpiniflast);
+    mEffect->disconnect(this, unpinIfLast);
 
     if (mHasControl && mEnabled) {
         sp<ThreadBase> thread = mEffect->thread().promote();
@@ -6970,9 +7426,11 @@
     mEffect.clear();
     if (mClient != 0) {
         if (mCblk != NULL) {
+            // unlike ~TrackBase(), mCblk is never a local new, so don't delete
             mCblk->~effect_param_cblk_t();   // destroy our shared-structure.
         }
-        mCblkMemory.clear();            // and free the shared memory
+        mCblkMemory.clear();    // free the shared memory before releasing the heap it belongs to
+        // Client destructor must run with AudioFlinger mutex locked
         Mutex::Autolock _l(mClient->audioFlinger()->mLock);
         mClient.clear();
     }
@@ -7054,10 +7512,6 @@
     return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
 }
 
-sp<IMemory> AudioFlinger::EffectHandle::getCblk() const {
-    return mCblkMemory;
-}
-
 void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal, bool enabled)
 {
     ALOGV("setControl %p control %d", this, hasControl);
@@ -7102,7 +7556,7 @@
     bool locked = mCblk != NULL && tryLock(mCblk->lock);
 
     snprintf(buffer, size, "\t\t\t%05d %05d    %01u    %01u      %05u  %05u\n",
-            (mClient == NULL) ? getpid() : mClient->pid(),
+            (mClient == 0) ? getpid_cached : mClient->pid(),
             mPriority,
             mHasControl,
             !locked,
@@ -7118,15 +7572,14 @@
 #undef LOG_TAG
 #define LOG_TAG "AudioFlinger::EffectChain"
 
-AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
+AudioFlinger::EffectChain::EffectChain(ThreadBase *thread,
                                         int sessionId)
-    : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
+    : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
       mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
       mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
 {
     mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
-    sp<ThreadBase> thread = mThread.promote();
-    if (thread == 0) {
+    if (thread == NULL) {
         return;
     }
     mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) /
@@ -7144,48 +7597,42 @@
 // getEffectFromDesc_l() must be called with ThreadBase::mLock held
 sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc_l(effect_descriptor_t *descriptor)
 {
-    sp<EffectModule> effect;
     size_t size = mEffects.size();
 
     for (size_t i = 0; i < size; i++) {
         if (memcmp(&mEffects[i]->desc().uuid, &descriptor->uuid, sizeof(effect_uuid_t)) == 0) {
-            effect = mEffects[i];
-            break;
+            return mEffects[i];
         }
     }
-    return effect;
+    return 0;
 }
 
 // getEffectFromId_l() must be called with ThreadBase::mLock held
 sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId_l(int id)
 {
-    sp<EffectModule> effect;
     size_t size = mEffects.size();
 
     for (size_t i = 0; i < size; i++) {
         // by convention, return first effect if id provided is 0 (0 is never a valid id)
         if (id == 0 || mEffects[i]->id() == id) {
-            effect = mEffects[i];
-            break;
+            return mEffects[i];
         }
     }
-    return effect;
+    return 0;
 }
 
 // getEffectFromType_l() must be called with ThreadBase::mLock held
 sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromType_l(
         const effect_uuid_t *type)
 {
-    sp<EffectModule> effect;
     size_t size = mEffects.size();
 
     for (size_t i = 0; i < size; i++) {
         if (memcmp(&mEffects[i]->desc().type, type, sizeof(effect_uuid_t)) == 0) {
-            effect = mEffects[i];
-            break;
+            return mEffects[i];
         }
     }
-    return effect;
+    return 0;
 }
 
 // Must be called with EffectChain::mLock locked
@@ -7276,12 +7723,12 @@
         // Reject insertion if an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is
         // already present
 
-        int size = (int)mEffects.size();
-        int idx_insert = size;
-        int idx_insert_first = -1;
-        int idx_insert_last = -1;
+        size_t size = mEffects.size();
+        size_t idx_insert = size;
+        ssize_t idx_insert_first = -1;
+        ssize_t idx_insert_last = -1;
 
-        for (int i = 0; i < size; i++) {
+        for (size_t i = 0; i < size; i++) {
             effect_descriptor_t d = mEffects[i]->desc();
             uint32_t iMode = d.flags & EFFECT_FLAG_TYPE_MASK;
             uint32_t iPref = d.flags & EFFECT_FLAG_INSERT_MASK;
@@ -7350,11 +7797,10 @@
 size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
 {
     Mutex::Autolock _l(mLock);
-    int size = (int)mEffects.size();
-    int i;
+    size_t size = mEffects.size();
     uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;
 
-    for (i = 0; i < size; i++) {
+    for (size_t i = 0; i < size; i++) {
         if (effect == mEffects[i]) {
             // calling stop here will remove pre-processing effect from the audio HAL.
             // This is safe as we hold the EffectChain mutex which guarantees that we are not in
@@ -7501,7 +7947,7 @@
     sp<SuspendedEffectDesc> desc;
     // use effect type UUID timelow as key as there is no real risk of identical
     // timeLow fields among effect type UUIDs.
-    int index = mSuspendedEffects.indexOfKey(type->timeLow);
+    ssize_t index = mSuspendedEffects.indexOfKey(type->timeLow);
     if (suspend) {
         if (index >= 0) {
             desc = mSuspendedEffects.valueAt(index);
@@ -7551,7 +7997,7 @@
 {
     sp<SuspendedEffectDesc> desc;
 
-    int index = mSuspendedEffects.indexOfKey((int)kKeyForSuspendAll);
+    ssize_t index = mSuspendedEffects.indexOfKey((int)kKeyForSuspendAll);
     if (suspend) {
         if (index >= 0) {
             desc = mSuspendedEffects.valueAt(index);
@@ -7626,18 +8072,14 @@
 sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectIfEnabled(
                                                             const effect_uuid_t *type)
 {
-    sp<EffectModule> effect;
-    effect = getEffectFromType_l(type);
-    if (effect != 0 && !effect->isEnabled()) {
-        effect.clear();
-    }
-    return effect;
+    sp<EffectModule> effect = getEffectFromType_l(type);
+    return effect != 0 && effect->isEnabled() ? effect : 0;
 }
 
 void AudioFlinger::EffectChain::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
                                                             bool enabled)
 {
-    int index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
+    ssize_t index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
     if (enabled) {
         if (index < 0) {
             // if the effect is not suspend check if all effects are suspended
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 3f3188c..a2ab680 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioFlinger.h
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -22,11 +22,14 @@
 #include <sys/types.h>
 #include <limits.h>
 
+#include <common_time/cc_helper.h>
+
 #include <media/IAudioFlinger.h>
 #include <media/IAudioFlingerClient.h>
 #include <media/IAudioTrack.h>
 #include <media/IAudioRecord.h>
 #include <media/AudioSystem.h>
+#include <media/AudioTrack.h>
 
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
@@ -55,7 +58,7 @@
 
 // ----------------------------------------------------------------------------
 
-static const nsecs_t kStandbyTimeInNsecs = seconds(3);
+static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3);
 
 class AudioFlinger :
     public BinderService<AudioFlinger>,
@@ -67,7 +70,7 @@
 
     virtual     status_t    dump(int fd, const Vector<String16>& args);
 
-    // IAudioFlinger interface
+    // IAudioFlinger interface, in binder opcode order
     virtual sp<IAudioTrack> createTrack(
                                 pid_t pid,
                                 audio_stream_type_t streamType,
@@ -77,117 +80,14 @@
                                 int frameCount,
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
-                                int output,
+                                audio_io_handle_t output,
+                                bool isTimed,
                                 int *sessionId,
                                 status_t *status);
 
-    virtual     uint32_t    sampleRate(int output) const;
-    virtual     int         channelCount(int output) const;
-    virtual     audio_format_t format(int output) const;
-    virtual     size_t      frameCount(int output) const;
-    virtual     uint32_t    latency(int output) const;
-
-    virtual     status_t    setMasterVolume(float value);
-    virtual     status_t    setMasterMute(bool muted);
-
-    virtual     float       masterVolume() const;
-    virtual     bool        masterMute() const;
-
-    virtual     status_t    setStreamVolume(audio_stream_type_t stream, float value, int output);
-    virtual     status_t    setStreamMute(audio_stream_type_t stream, bool muted);
-
-    virtual     float       streamVolume(audio_stream_type_t stream, int output) const;
-    virtual     bool        streamMute(audio_stream_type_t stream) const;
-
-    virtual     status_t    setMode(audio_mode_t mode);
-
-    virtual     status_t    setMicMute(bool state);
-    virtual     bool        getMicMute() const;
-
-    virtual     status_t    setParameters(int ioHandle, const String8& keyValuePairs);
-    virtual     String8     getParameters(int ioHandle, const String8& keys);
-
-    virtual     void        registerClient(const sp<IAudioFlingerClient>& client);
-
-    virtual     size_t      getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount);
-    virtual     unsigned int  getInputFramesLost(int ioHandle);
-
-    virtual int openOutput(uint32_t *pDevices,
-                                    uint32_t *pSamplingRate,
-                                    audio_format_t *pFormat,
-                                    uint32_t *pChannels,
-                                    uint32_t *pLatencyMs,
-                                    uint32_t flags);
-
-    virtual int openDuplicateOutput(int output1, int output2);
-
-    virtual status_t closeOutput(int output);
-
-    virtual status_t suspendOutput(int output);
-
-    virtual status_t restoreOutput(int output);
-
-    virtual int openInput(uint32_t *pDevices,
-                            uint32_t *pSamplingRate,
-                            audio_format_t *pFormat,
-                            uint32_t *pChannels,
-                            audio_in_acoustics_t acoustics);
-
-    virtual status_t closeInput(int input);
-
-    virtual status_t setStreamOutput(audio_stream_type_t stream, int output);
-
-    virtual status_t setVoiceVolume(float volume);
-
-    virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output);
-
-    virtual int newAudioSessionId();
-
-    virtual void acquireAudioSessionId(int audioSession);
-
-    virtual void releaseAudioSessionId(int audioSession);
-
-    virtual status_t queryNumberEffects(uint32_t *numEffects);
-
-    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor);
-
-    virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor);
-
-    virtual sp<IEffect> createEffect(pid_t pid,
-                        effect_descriptor_t *pDesc,
-                        const sp<IEffectClient>& effectClient,
-                        int32_t priority,
-                        int io,
-                        int sessionId,
-                        status_t *status,
-                        int *id,
-                        int *enabled);
-
-    virtual status_t moveEffects(int sessionId, int srcOutput, int dstOutput);
-
-    enum hardware_call_state {
-        AUDIO_HW_IDLE = 0,
-        AUDIO_HW_INIT,
-        AUDIO_HW_OUTPUT_OPEN,
-        AUDIO_HW_OUTPUT_CLOSE,
-        AUDIO_HW_INPUT_OPEN,
-        AUDIO_HW_INPUT_CLOSE,
-        AUDIO_HW_STANDBY,
-        AUDIO_HW_SET_MASTER_VOLUME,
-        AUDIO_HW_GET_ROUTING,
-        AUDIO_HW_SET_ROUTING,
-        AUDIO_HW_GET_MODE,
-        AUDIO_HW_SET_MODE,
-        AUDIO_HW_GET_MIC_MUTE,
-        AUDIO_HW_SET_MIC_MUTE,
-        AUDIO_SET_VOICE_VOLUME,
-        AUDIO_SET_PARAMETER,
-    };
-
-    // record interface
     virtual sp<IAudioRecord> openRecord(
                                 pid_t pid,
-                                int input,
+                                audio_io_handle_t input,
                                 uint32_t sampleRate,
                                 audio_format_t format,
                                 uint32_t channelMask,
@@ -196,26 +96,123 @@
                                 int *sessionId,
                                 status_t *status);
 
+    virtual     uint32_t    sampleRate(audio_io_handle_t output) const;
+    virtual     int         channelCount(audio_io_handle_t output) const;
+    virtual     audio_format_t format(audio_io_handle_t output) const;
+    virtual     size_t      frameCount(audio_io_handle_t output) const;
+    virtual     uint32_t    latency(audio_io_handle_t output) const;
+
+    virtual     status_t    setMasterVolume(float value);
+    virtual     status_t    setMasterMute(bool muted);
+
+    virtual     float       masterVolume() const;
+    virtual     float       masterVolumeSW() const;
+    virtual     bool        masterMute() const;
+
+    virtual     status_t    setStreamVolume(audio_stream_type_t stream, float value,
+                                            audio_io_handle_t output);
+    virtual     status_t    setStreamMute(audio_stream_type_t stream, bool muted);
+
+    virtual     float       streamVolume(audio_stream_type_t stream,
+                                         audio_io_handle_t output) const;
+    virtual     bool        streamMute(audio_stream_type_t stream) const;
+
+    virtual     status_t    setMode(audio_mode_t mode);
+
+    virtual     status_t    setMicMute(bool state);
+    virtual     bool        getMicMute() const;
+
+    virtual     status_t    setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
+    virtual     String8     getParameters(audio_io_handle_t ioHandle, const String8& keys) const;
+
+    virtual     void        registerClient(const sp<IAudioFlingerClient>& client);
+
+    virtual     size_t      getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const;
+
+    virtual audio_io_handle_t openOutput(uint32_t *pDevices,
+                                    uint32_t *pSamplingRate,
+                                    audio_format_t *pFormat,
+                                    uint32_t *pChannels,
+                                    uint32_t *pLatencyMs,
+                                    uint32_t flags);
+
+    virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
+                                                  audio_io_handle_t output2);
+
+    virtual status_t closeOutput(audio_io_handle_t output);
+
+    virtual status_t suspendOutput(audio_io_handle_t output);
+
+    virtual status_t restoreOutput(audio_io_handle_t output);
+
+    virtual audio_io_handle_t openInput(uint32_t *pDevices,
+                            uint32_t *pSamplingRate,
+                            audio_format_t *pFormat,
+                            uint32_t *pChannels,
+                            audio_in_acoustics_t acoustics);
+
+    virtual status_t closeInput(audio_io_handle_t input);
+
+    virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output);
+
+    virtual status_t setVoiceVolume(float volume);
+
+    virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
+                                       audio_io_handle_t output) const;
+
+    virtual     unsigned int  getInputFramesLost(audio_io_handle_t ioHandle) const;
+
+    virtual int newAudioSessionId();
+
+    virtual void acquireAudioSessionId(int audioSession);
+
+    virtual void releaseAudioSessionId(int audioSession);
+
+    virtual status_t queryNumberEffects(uint32_t *numEffects) const;
+
+    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor) const;
+
+    virtual status_t getEffectDescriptor(const effect_uuid_t *pUuid,
+                                         effect_descriptor_t *descriptor) const;
+
+    virtual sp<IEffect> createEffect(pid_t pid,
+                        effect_descriptor_t *pDesc,
+                        const sp<IEffectClient>& effectClient,
+                        int32_t priority,
+                        audio_io_handle_t io,
+                        int sessionId,
+                        status_t *status,
+                        int *id,
+                        int *enabled);
+
+    virtual status_t moveEffects(int sessionId, audio_io_handle_t srcOutput,
+                        audio_io_handle_t dstOutput);
+
     virtual     status_t    onTransact(
                                 uint32_t code,
                                 const Parcel& data,
                                 Parcel* reply,
                                 uint32_t flags);
 
-               audio_mode_t getMode() const { return mMode; }
-
-                bool        btNrecIsOff() { return mBtNrecIsOff; }
+    // end of IAudioFlinger interface
 
 private:
+               audio_mode_t getMode() const { return mMode; }
+
+                bool        btNrecIsOff() const { return mBtNrecIsOff; }
 
                             AudioFlinger();
     virtual                 ~AudioFlinger();
 
-    status_t                initCheck() const;
+    // call in any IAudioFlinger method that accesses mPrimaryHardwareDev
+    status_t                initCheck() const { return mPrimaryHardwareDev == NULL ? NO_INIT : NO_ERROR; }
+
     virtual     void        onFirstRef();
     audio_hw_device_t*      findSuitableHwDev_l(uint32_t devices);
     void                    purgeStaleEffects_l();
 
+    static nsecs_t          mStandbyTimeInNsecs;
+
     // Internal dump utilites.
     status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
     status_t dumpClients(int fd, const Vector<String16>& args);
@@ -228,7 +225,10 @@
         virtual             ~Client();
         sp<MemoryDealer>    heap() const;
         pid_t               pid() const { return mPid; }
-        sp<AudioFlinger>    audioFlinger() { return mAudioFlinger; }
+        sp<AudioFlinger>    audioFlinger() const { return mAudioFlinger; }
+
+        bool reserveTimedTrack();
+        void releaseTimedTrack();
 
     private:
                             Client(const Client&);
@@ -236,6 +236,9 @@
         const sp<AudioFlinger> mAudioFlinger;
         const sp<MemoryDealer> mMemoryDealer;
         const pid_t         mPid;
+
+        Mutex               mTimedTrackLock;
+        int                 mTimedTrackCount;
     };
 
     // --- Notification Client ---
@@ -285,7 +288,7 @@
             RECORD              // Thread class is RecordThread
         };
 
-        ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device, type_t type);
+        ThreadBase (const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, uint32_t device, type_t type);
         virtual             ~ThreadBase();
 
         status_t dumpBase(int fd, const Vector<String16>& args);
@@ -300,6 +303,8 @@
             enum track_state {
                 IDLE,
                 TERMINATED,
+                // These are order-sensitive; do not change order without reviewing the impact.
+                // In particular there are assumptions about > STOPPED.
                 STOPPED,
                 RESUMING,
                 ACTIVE,
@@ -307,28 +312,21 @@
                 PAUSED
             };
 
-            enum track_flags {
-                STEPSERVER_FAILED = 0x01, //  StepServer could not acquire cblk->lock mutex
-                SYSTEM_FLAGS_MASK = 0x0000ffffUL,
-                // The upper 16 bits are used for track-specific flags.
-            };
-
-                                TrackBase(const wp<ThreadBase>& thread,
+                                TrackBase(ThreadBase *thread,
                                         const sp<Client>& client,
                                         uint32_t sampleRate,
                                         audio_format_t format,
                                         uint32_t channelMask,
                                         int frameCount,
-                                        uint32_t flags,
                                         const sp<IMemory>& sharedBuffer,
                                         int sessionId);
-                                ~TrackBase();
+            virtual             ~TrackBase();
 
-            virtual status_t    start() = 0;
+            virtual status_t    start(pid_t tid) = 0;
             virtual void        stop() = 0;
-                    sp<IMemory> getCblk() const;
+                    sp<IMemory> getCblk() const { return mCblkMemory; }
                     audio_track_cblk_t* cblk() const { return mCblk; }
-                    int         sessionId() { return mSessionId; }
+                    int         sessionId() const { return mSessionId; }
 
         protected:
             friend class ThreadBase;
@@ -341,18 +339,20 @@
                                 TrackBase(const TrackBase&);
                                 TrackBase& operator = (const TrackBase&);
 
-            virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
+            virtual status_t getNextBuffer(
+                AudioBufferProvider::Buffer* buffer,
+                int64_t pts) = 0;
             virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
 
             audio_format_t format() const {
                 return mFormat;
             }
 
-            int channelCount() const ;
+            int channelCount() const { return mChannelCount; }
 
-            uint32_t channelMask() const;
+            uint32_t channelMask() const { return mChannelMask; }
 
-            int sampleRate() const;
+            int sampleRate() const; // FIXME inline after cblk sr moved
 
             void* getBuffer(uint32_t offset, uint32_t frames) const;
 
@@ -376,9 +376,8 @@
             uint32_t            mFrameCount;
             // we don't really need a lock for these
             track_state         mState;
-            int                 mClientTid;
             const audio_format_t mFormat;
-            uint32_t            mFlags;
+            bool                mStepServerFailed;
             const int           mSessionId;
             uint8_t             mChannelCount;
             uint32_t            mChannelMask;
@@ -409,11 +408,13 @@
 
         virtual     status_t    initCheck() const = 0;
                     type_t      type() const { return mType; }
-                    uint32_t    sampleRate() const;
-                    int         channelCount() const;
-                    audio_format_t format() const;
-                    size_t      frameCount() const;
+                    uint32_t    sampleRate() const { return mSampleRate; }
+                    int         channelCount() const { return mChannelCount; }
+                    audio_format_t format() const { return mFormat; }
+                    size_t      frameCount() const { return mFrameCount; }
                     void        wakeUp()    { mWaitWorkCV.broadcast(); }
+        // Should be "virtual status_t requestExitAndWait()" and override same
+        // method in Thread, but Thread::requestExitAndWait() is not yet virtual.
                     void        exit();
         virtual     bool        checkForNewParameters_l() = 0;
         virtual     status_t    setParameters(const String8& keyValuePairs);
@@ -422,7 +423,7 @@
                     void        sendConfigEvent(int event, int param = 0);
                     void        sendConfigEvent_l(int event, int param = 0);
                     void        processConfigEvents();
-                    int         id() const { return mId;}
+                    audio_io_handle_t id() const { return mId;}
                     bool        standby() { return mStandby; }
                     uint32_t    device() { return mDevice; }
         virtual     audio_stream_t* stream() = 0;
@@ -437,7 +438,7 @@
                                         status_t *status);
                     void disconnectEffect(const sp< EffectModule>& effect,
                                           const wp<EffectHandle>& handle,
-                                          bool unpiniflast);
+                                          bool unpinIfLast);
 
                     // return values for hasAudioSession (bit field)
                     enum effect_state {
@@ -544,8 +545,7 @@
                     status_t                mParamStatus;
                     Vector<ConfigEvent>     mConfigEvents;
                     bool                    mStandby;
-                    int                     mId;
-                    bool                    mExiting;
+                    const audio_io_handle_t mId;
                     Vector< sp<EffectChain> > mEffectChains;
                     uint32_t                mDevice;    // output device for PlaybackThread
                                                         // input + output devices for RecordThread
@@ -559,6 +559,18 @@
                     KeyedVector< int, KeyedVector< int, sp<SuspendedSessionDesc> > >  mSuspendedSessions;
     };
 
+    struct  stream_type_t {
+        stream_type_t()
+            :   volume(1.0f),
+                mute(false),
+                valid(true)
+        {
+        }
+        float       volume;
+        bool        mute;
+        bool        valid;
+    };
+
     // --- PlaybackThread ---
     class PlaybackThread : public ThreadBase {
     public:
@@ -572,7 +584,7 @@
         // playback track
         class Track : public TrackBase {
         public:
-                                Track(  const wp<ThreadBase>& thread,
+                                Track(  PlaybackThread *thread,
                                         const sp<Client>& client,
                                         audio_stream_type_t streamType,
                                         uint32_t sampleRate,
@@ -581,10 +593,10 @@
                                         int frameCount,
                                         const sp<IMemory>& sharedBuffer,
                                         int sessionId);
-                                ~Track();
+            virtual             ~Track();
 
                     void        dump(char* buffer, size_t size);
-            virtual status_t    start();
+            virtual status_t    start(pid_t tid);
             virtual void        stop();
                     void        pause();
 
@@ -595,16 +607,15 @@
                         return mName;
                     }
 
-                    audio_stream_type_t type() const {
+                    audio_stream_type_t streamType() const {
                         return mStreamType;
                     }
                     status_t    attachAuxEffect(int EffectId);
                     void        setAuxBuffer(int EffectId, int32_t *buffer);
-                    int32_t     *auxBuffer() { return mAuxBuffer; }
+                    int32_t     *auxBuffer() const { return mAuxBuffer; }
                     void        setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
-                    int16_t     *mainBuffer() { return mMainBuffer; }
-                    int         auxEffectId() { return mAuxEffectId; }
-
+                    int16_t     *mainBuffer() const { return mMainBuffer; }
+                    int         auxEffectId() const { return mAuxEffectId; }
 
         protected:
             friend class ThreadBase;
@@ -616,8 +627,12 @@
                                 Track(const Track&);
                                 Track& operator = (const Track&);
 
-            virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
-            bool isMuted() { return mMute; }
+            virtual status_t getNextBuffer(
+                AudioBufferProvider::Buffer* buffer,
+                int64_t pts);
+            virtual uint32_t framesReady() const;
+
+            bool isMuted() const { return mMute; }
             bool isPausing() const {
                 return mState == PAUSING;
             }
@@ -632,6 +647,8 @@
                 return (mStreamType == AUDIO_STREAM_CNT);
             }
 
+            virtual bool isTimedTrack() const { return false; }
+
             // we don't really need a lock for these
             volatile bool       mMute;
             // FILLED state is used for suppressing volume ramp at begin of playing
@@ -648,6 +665,79 @@
             bool                mHasVolumeController;
         };  // end of Track
 
+        class TimedTrack : public Track {
+          public:
+            static sp<TimedTrack> create(PlaybackThread *thread,
+                                         const sp<Client>& client,
+                                         audio_stream_type_t streamType,
+                                         uint32_t sampleRate,
+                                         audio_format_t format,
+                                         uint32_t channelMask,
+                                         int frameCount,
+                                         const sp<IMemory>& sharedBuffer,
+                                         int sessionId);
+            ~TimedTrack();
+
+            class TimedBuffer {
+              public:
+                TimedBuffer();
+                TimedBuffer(const sp<IMemory>& buffer, int64_t pts);
+                const sp<IMemory>& buffer() const { return mBuffer; }
+                int64_t pts() const { return mPTS; }
+                int position() const { return mPosition; }
+                void setPosition(int pos) { mPosition = pos; }
+              private:
+                sp<IMemory> mBuffer;
+                int64_t mPTS;
+                int mPosition;
+            };
+
+            virtual bool isTimedTrack() const { return true; }
+
+            virtual uint32_t framesReady() const;
+
+            virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer,
+                                           int64_t pts);
+            virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+            void timedYieldSamples(AudioBufferProvider::Buffer* buffer);
+            void timedYieldSilence(uint32_t numFrames,
+                                   AudioBufferProvider::Buffer* buffer);
+
+            status_t    allocateTimedBuffer(size_t size,
+                                            sp<IMemory>* buffer);
+            status_t    queueTimedBuffer(const sp<IMemory>& buffer,
+                                         int64_t pts);
+            status_t    setMediaTimeTransform(const LinearTransform& xform,
+                                              TimedAudioTrack::TargetTimeline target);
+            void        trimTimedBufferQueue_l();
+
+          private:
+            TimedTrack(PlaybackThread *thread,
+                       const sp<Client>& client,
+                       audio_stream_type_t streamType,
+                       uint32_t sampleRate,
+                       audio_format_t format,
+                       uint32_t channelMask,
+                       int frameCount,
+                       const sp<IMemory>& sharedBuffer,
+                       int sessionId);
+
+            uint64_t            mLocalTimeFreq;
+            LinearTransform     mLocalTimeToSampleTransform;
+            sp<MemoryDealer>    mTimedMemoryDealer;
+            Vector<TimedBuffer> mTimedBufferQueue;
+            uint8_t*            mTimedSilenceBuffer;
+            uint32_t            mTimedSilenceBufferSize;
+            mutable Mutex       mTimedBufferQueueLock;
+            bool                mTimedAudioOutputOnTime;
+            CCHelper            mCCHelper;
+
+            Mutex               mMediaTimeTransformLock;
+            LinearTransform     mMediaTimeTransform;
+            bool                mMediaTimeTransformValid;
+            TimedAudioTrack::TargetTimeline mMediaTimeTransformTarget;
+        };
+
 
         // playback track
         class OutputTrack : public Track {
@@ -658,20 +748,20 @@
                 int16_t *mBuffer;
             };
 
-                                OutputTrack(  const wp<ThreadBase>& thread,
+                                OutputTrack(PlaybackThread *thread,
                                         DuplicatingThread *sourceThread,
                                         uint32_t sampleRate,
                                         audio_format_t format,
                                         uint32_t channelMask,
                                         int frameCount);
-                                ~OutputTrack();
+            virtual             ~OutputTrack();
 
-            virtual status_t    start();
+            virtual status_t    start(pid_t tid);
             virtual void        stop();
                     bool        write(int16_t* data, uint32_t frames);
-                    bool        bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; }
-                    bool        isActive() { return mActive; }
-            const wp<ThreadBase>& thread() { return mThread; }
+                    bool        bufferQueueEmpty() const { return mBufferQueue.size() == 0; }
+                    bool        isActive() const { return mActive; }
+            const wp<ThreadBase>& thread() const { return mThread; }
 
         private:
 
@@ -691,8 +781,8 @@
             DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
         };  // end of OutputTrack
 
-        PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id,
-                        uint32_t device, type_t type);
+        PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+                        audio_io_handle_t id, uint32_t device, type_t type);
         virtual             ~PlaybackThread();
 
         virtual     status_t    dump(int fd, const Vector<String16>& args);
@@ -705,17 +795,13 @@
 
         virtual     uint32_t    latency() const;
 
-        virtual     status_t    setMasterVolume(float value);
-        virtual     status_t    setMasterMute(bool muted);
+                    void        setMasterVolume(float value);
+                    void        setMasterMute(bool muted);
 
-        virtual     float       masterVolume() const;
-        virtual     bool        masterMute() const;
+                    void        setStreamVolume(audio_stream_type_t stream, float value);
+                    void        setStreamMute(audio_stream_type_t stream, bool muted);
 
-        virtual     status_t    setStreamVolume(audio_stream_type_t stream, float value);
-        virtual     status_t    setStreamMute(audio_stream_type_t stream, bool muted);
-
-        virtual     float       streamVolume(audio_stream_type_t stream) const;
-        virtual     bool        streamMute(audio_stream_type_t stream) const;
+                    float       streamVolume(audio_stream_type_t stream) const;
 
                     sp<Track>   createTrack_l(
                                     const sp<AudioFlinger::Client>& client,
@@ -726,6 +812,7 @@
                                     int frameCount,
                                     const sp<IMemory>& sharedBuffer,
                                     int sessionId,
+                                    bool isTimed,
                                     status_t *status);
 
                     AudioStreamOut* getOutput() const;
@@ -738,7 +825,7 @@
         virtual     String8     getParameters(const String8& keys);
         virtual     void        audioConfigChanged_l(int event, int param = 0);
         virtual     status_t    getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
-                    int16_t     *mixBuffer() { return mMixBuffer; };
+                    int16_t     *mixBuffer() const { return mMixBuffer; };
 
         virtual     void detachAuxEffect_l(int effectId);
                     status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track,
@@ -753,24 +840,16 @@
 
                             void setStreamValid(audio_stream_type_t streamType, bool valid);
 
-        struct  stream_type_t {
-            stream_type_t()
-                :   volume(1.0f),
-                    mute(false),
-                    valid(true)
-            {
-            }
-            float       volume;
-            bool        mute;
-            bool        valid;
-        };
-
     protected:
         int16_t*                        mMixBuffer;
         int                             mSuspended;
         int                             mBytesWritten;
     private:
+        // mMasterMute is in both PlaybackThread and in AudioFlinger.  When a
+        // PlaybackThread needs to find out if master-muted, it checks it's local
+        // copy rather than the one in AudioFlinger.  This optimization saves a lock.
         bool                            mMasterMute;
+                    void        setMasterMute_l(bool muted) { mMasterMute = muted; }
     protected:
         SortedVector< wp<Track> >       mActiveTracks;
 
@@ -817,7 +896,7 @@
     public:
         MixerThread (const sp<AudioFlinger>& audioFlinger,
                      AudioStreamOut* output,
-                     int id,
+                     audio_io_handle_t id,
                      uint32_t device,
                      type_t type = MIXER);
         virtual             ~MixerThread();
@@ -844,8 +923,9 @@
     class DirectOutputThread : public PlaybackThread {
     public:
 
-        DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
-        ~DirectOutputThread();
+        DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+                            audio_io_handle_t id, uint32_t device);
+        virtual                 ~DirectOutputThread();
 
         // Thread virtuals
         virtual     bool        threadLoop();
@@ -862,6 +942,8 @@
     private:
         void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
 
+        // volumes last sent to audio HAL with stream->set_volume()
+        // FIXME use standard representation and names
         float mLeftVolFloat;
         float mRightVolFloat;
         uint16_t mLeftVolShort;
@@ -870,8 +952,9 @@
 
     class DuplicatingThread : public MixerThread {
     public:
-        DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread, int id);
-        ~DuplicatingThread();
+        DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread,
+                           audio_io_handle_t id);
+        virtual                 ~DuplicatingThread();
 
         // Thread virtuals
         virtual     bool        threadLoop();
@@ -889,33 +972,47 @@
                     uint32_t    mWaitTimeMs;
     };
 
-              PlaybackThread *checkPlaybackThread_l(int output) const;
-              MixerThread *checkMixerThread_l(int output) const;
-              RecordThread *checkRecordThread_l(int input) const;
-              float streamVolumeInternal(audio_stream_type_t stream) const { return mStreamTypes[stream].volume; }
-              void audioConfigChanged_l(int event, int ioHandle, void *param2);
+              PlaybackThread *checkPlaybackThread_l(audio_io_handle_t output) const;
+              MixerThread *checkMixerThread_l(audio_io_handle_t output) const;
+              RecordThread *checkRecordThread_l(audio_io_handle_t input) const;
+              // no range check, AudioFlinger::mLock held
+              bool streamMute_l(audio_stream_type_t stream) const
+                                { return mStreamTypes[stream].mute; }
+              // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
+              float streamVolume_l(audio_stream_type_t stream) const
+                                { return mStreamTypes[stream].volume; }
+              void audioConfigChanged_l(int event, audio_io_handle_t ioHandle, void *param2);
 
+              // allocate an audio_io_handle_t, session ID, or effect ID
               uint32_t nextUniqueId();
+
               status_t moveEffectChain_l(int sessionId,
-                                     AudioFlinger::PlaybackThread *srcThread,
-                                     AudioFlinger::PlaybackThread *dstThread,
+                                     PlaybackThread *srcThread,
+                                     PlaybackThread *dstThread,
                                      bool reRegister);
               PlaybackThread *primaryPlaybackThread_l();
               uint32_t primaryOutputDevice_l();
 
     friend class AudioBuffer;
 
+    // server side of the client's IAudioTrack
     class TrackHandle : public android::BnAudioTrack {
     public:
                             TrackHandle(const sp<PlaybackThread::Track>& track);
         virtual             ~TrackHandle();
         virtual sp<IMemory> getCblk() const;
-        virtual status_t    start();
+        virtual status_t    start(pid_t tid);
         virtual void        stop();
         virtual void        flush();
         virtual void        mute(bool);
         virtual void        pause();
         virtual status_t    attachAuxEffect(int effectId);
+        virtual status_t    allocateTimedBuffer(size_t size,
+                                                sp<IMemory>* buffer);
+        virtual status_t    queueTimedBuffer(const sp<IMemory>& buffer,
+                                             int64_t pts);
+        virtual status_t    setMediaTimeTransform(const LinearTransform& xform,
+                                                  int target);
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
     private:
@@ -938,17 +1035,16 @@
         // record track
         class RecordTrack : public TrackBase {
         public:
-                                RecordTrack(const wp<ThreadBase>& thread,
+                                RecordTrack(RecordThread *thread,
                                         const sp<Client>& client,
                                         uint32_t sampleRate,
                                         audio_format_t format,
                                         uint32_t channelMask,
                                         int frameCount,
-                                        uint32_t flags,
                                         int sessionId);
-                                ~RecordTrack();
+            virtual             ~RecordTrack();
 
-            virtual status_t    start();
+            virtual status_t    start(pid_t tid);
             virtual void        stop();
 
                     bool        overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; }
@@ -963,7 +1059,9 @@
                                 RecordTrack(const RecordTrack&);
                                 RecordTrack& operator = (const RecordTrack&);
 
-            virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
+            virtual status_t getNextBuffer(
+                AudioBufferProvider::Buffer* buffer,
+                int64_t pts);
 
             bool                mOverflow;
         };
@@ -973,9 +1071,9 @@
                         AudioStreamIn *input,
                         uint32_t sampleRate,
                         uint32_t channels,
-                        int id,
+                        audio_io_handle_t id,
                         uint32_t device);
-                ~RecordThread();
+                virtual     ~RecordThread();
 
         virtual bool        threadLoop();
         virtual status_t    readyToRun();
@@ -988,18 +1086,19 @@
                         audio_format_t format,
                         int channelMask,
                         int frameCount,
-                        uint32_t flags,
                         int sessionId,
                         status_t *status);
 
                 status_t    start(RecordTrack* recordTrack);
+                status_t    start(RecordTrack* recordTrack, pid_t tid);
                 void        stop(RecordTrack* recordTrack);
                 status_t    dump(int fd, const Vector<String16>& args);
                 AudioStreamIn* getInput() const;
                 AudioStreamIn* clearInput();
                 virtual audio_stream_t* stream();
 
-        virtual status_t    getNextBuffer(AudioBufferProvider::Buffer* buffer);
+        virtual status_t    getNextBuffer(AudioBufferProvider::Buffer* buffer,
+                                          int64_t pts);
         virtual void        releaseBuffer(AudioBufferProvider::Buffer* buffer);
         virtual bool        checkForNewParameters_l();
         virtual String8     getParameters(const String8& keys);
@@ -1028,12 +1127,13 @@
                 ssize_t                             mBytesRead;
     };
 
+    // server side of the client's IAudioRecord
     class RecordHandle : public android::BnAudioRecord {
     public:
         RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack);
         virtual             ~RecordHandle();
         virtual sp<IMemory> getCblk() const;
-        virtual status_t    start();
+        virtual status_t    start(pid_t tid);
         virtual void        stop();
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
@@ -1059,12 +1159,12 @@
     // the attached track(s) to accumulate their auxiliary channel.
     class EffectModule: public RefBase {
     public:
-        EffectModule(const wp<ThreadBase>& wThread,
+        EffectModule(ThreadBase *thread,
                         const wp<AudioFlinger::EffectChain>& chain,
                         effect_descriptor_t *desc,
                         int id,
                         int sessionId);
-        ~EffectModule();
+        virtual ~EffectModule();
 
         enum effect_state {
             IDLE,
@@ -1076,7 +1176,7 @@
             DESTROYED
         };
 
-        int         id() { return mId; }
+        int         id() const { return mId; }
         void process();
         void updateState();
         status_t command(uint32_t cmdCode,
@@ -1094,12 +1194,12 @@
         uint32_t status() {
             return mStatus;
         }
-        int sessionId() {
+        int sessionId() const {
             return mSessionId;
         }
         status_t    setEnabled(bool enabled);
-        bool isEnabled();
-        bool isProcessEnabled();
+        bool isEnabled() const;
+        bool isProcessEnabled() const;
 
         void        setInBuffer(int16_t *buffer) { mConfig.inputCfg.buffer.s16 = buffer; }
         int16_t     *inBuffer() { return mConfig.inputCfg.buffer.s16; }
@@ -1110,7 +1210,7 @@
         const wp<ThreadBase>& thread() { return mThread; }
 
         status_t addHandle(const sp<EffectHandle>& handle);
-        void disconnect(const wp<EffectHandle>& handle, bool unpiniflast);
+        void disconnect(const wp<EffectHandle>& handle, bool unpinIfLast);
         size_t removeHandle (const wp<EffectHandle>& handle);
 
         effect_descriptor_t& desc() { return mDescriptor; }
@@ -1126,7 +1226,7 @@
 
         sp<EffectHandle> controlHandle();
 
-        bool             isPinned() { return mPinned; }
+        bool             isPinned() const { return mPinned; }
         void             unPin() { mPinned = false; }
 
         status_t         dump(int fd, const Vector<String16>& args);
@@ -1156,6 +1256,7 @@
         status_t            mStatus;    // initialization status
         effect_state        mState;     // current activation state
         Vector< wp<EffectHandle> > mHandles;    // list of client handles
+                    // First handle in mHandles has highest priority and controls the effect module
         uint32_t mMaxDisableWaitCnt;    // maximum grace period before forcing an effect off after
                                         // sending disable command.
         uint32_t mDisableWaitCnt;       // current process() calls count during disable period.
@@ -1186,8 +1287,10 @@
                                  uint32_t *replySize,
                                  void *pReplyData);
         virtual void disconnect();
-        virtual void disconnect(bool unpiniflast);
-        virtual sp<IMemory> getCblk() const;
+    private:
+                void disconnect(bool unpinIfLast);
+    public:
+        virtual sp<IMemory> getCblk() const { return mCblkMemory; }
         virtual status_t onTransact(uint32_t code, const Parcel& data,
                 Parcel* reply, uint32_t flags);
 
@@ -1203,13 +1306,13 @@
                              uint32_t replySize,
                              void *pReplyData);
         void setEnabled(bool enabled);
-        bool enabled() { return mEnabled; }
+        bool enabled() const { return mEnabled; }
 
         // Getters
-        int id() { return mEffect->id(); }
-        int priority() { return mPriority; }
-        bool hasControl() { return mHasControl; }
-        sp<EffectModule> effect() { return mEffect; }
+        int id() const { return mEffect->id(); }
+        int priority() const { return mPriority; }
+        bool hasControl() const { return mHasControl; }
+        sp<EffectModule> effect() const { return mEffect; }
 
         void dump(char* buffer, size_t size);
 
@@ -1221,7 +1324,7 @@
 
         sp<EffectModule> mEffect;           // pointer to controlled EffectModule
         sp<IEffectClient> mEffectClient;    // callback interface for client notifications
-        sp<Client>          mClient;        // client for shared memory allocation
+        /*const*/ sp<Client> mClient;       // client for shared memory allocation, see disconnect()
         sp<IMemory>         mCblkMemory;    // shared memory for control block
         effect_param_cblk_t* mCblk;         // control block for deferred parameter setting via shared memory
         uint8_t*            mBuffer;        // pointer to parameter area in shared memory
@@ -1241,7 +1344,8 @@
     class EffectChain: public RefBase {
     public:
         EffectChain(const wp<ThreadBase>& wThread, int sessionId);
-        ~EffectChain();
+        EffectChain(ThreadBase *thread, int sessionId);
+        virtual ~EffectChain();
 
         // special key used for an entry in mSuspendedEffects keyed vector
         // corresponding to a suspend all request.
@@ -1263,7 +1367,7 @@
         status_t addEffect_l(const sp<EffectModule>& handle);
         size_t removeEffect_l(const sp<EffectModule>& handle);
 
-        int sessionId() { return mSessionId; }
+        int sessionId() const { return mSessionId; }
         void setSessionId(int sessionId) { mSessionId = sessionId; }
 
         sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor);
@@ -1277,26 +1381,26 @@
             mInBuffer = buffer;
             mOwnInBuffer = ownsBuffer;
         }
-        int16_t *inBuffer() {
+        int16_t *inBuffer() const {
             return mInBuffer;
         }
         void setOutBuffer(int16_t *buffer) {
             mOutBuffer = buffer;
         }
-        int16_t *outBuffer() {
+        int16_t *outBuffer() const {
             return mOutBuffer;
         }
 
         void incTrackCnt() { android_atomic_inc(&mTrackCnt); }
         void decTrackCnt() { android_atomic_dec(&mTrackCnt); }
-        int32_t trackCnt() { return mTrackCnt;}
+        int32_t trackCnt() const { return mTrackCnt;}
 
         void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt);
                                    mTailBufferCount = mMaxTailBuffers; }
         void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); }
-        int32_t activeTrackCnt() { return mActiveTrackCnt;}
+        int32_t activeTrackCnt() const { return mActiveTrackCnt;}
 
-        uint32_t strategy() { return mStrategy; }
+        uint32_t strategy() const { return mStrategy; }
         void setStrategy(uint32_t strategy)
                  { mStrategy = strategy; }
 
@@ -1379,6 +1483,7 @@
             hwDev(dev), stream(in) {}
     };
 
+    // for mAudioSessionRefs only
     struct AudioSessionRef {
         // FIXME rename parameter names when fields get "m" prefix
         AudioSessionRef(int sessionid_, pid_t pid_) :
@@ -1391,34 +1496,88 @@
     friend class RecordThread;
     friend class PlaybackThread;
 
+    enum master_volume_support {
+        // MVS_NONE:
+        // Audio HAL has no support for master volume, either setting or
+        // getting.  All master volume control must be implemented in SW by the
+        // AudioFlinger mixing core.
+        MVS_NONE,
+
+        // MVS_SETONLY:
+        // Audio HAL has support for setting master volume, but not for getting
+        // master volume (original HAL design did not include a getter).
+        // AudioFlinger needs to keep track of the last set master volume in
+        // addition to needing to set an initial, default, master volume at HAL
+        // load time.
+        MVS_SETONLY,
+
+        // MVS_FULL:
+        // Audio HAL has support both for setting and getting master volume.
+        // AudioFlinger should send all set and get master volume requests
+        // directly to the HAL.
+        MVS_FULL,
+    };
+
     mutable     Mutex                               mLock;
 
-                DefaultKeyedVector< pid_t, wp<Client> >     mClients;
+                DefaultKeyedVector< pid_t, wp<Client> >     mClients;   // see ~Client()
 
                 mutable     Mutex                   mHardwareLock;
-                audio_hw_device_t*                  mPrimaryHardwareDev;
+
+                // These two fields are immutable after onFirstRef(), so no lock needed to access
+                audio_hw_device_t*                  mPrimaryHardwareDev; // mAudioHwDevs[0] or NULL
                 Vector<audio_hw_device_t*>          mAudioHwDevs;
+
+    enum hardware_call_state {
+        AUDIO_HW_IDLE = 0,
+        AUDIO_HW_INIT,
+        AUDIO_HW_OUTPUT_OPEN,
+        AUDIO_HW_OUTPUT_CLOSE,
+        AUDIO_HW_INPUT_OPEN,
+        AUDIO_HW_INPUT_CLOSE,
+        AUDIO_HW_STANDBY,
+        AUDIO_HW_SET_MASTER_VOLUME,
+        AUDIO_HW_GET_ROUTING,
+        AUDIO_HW_SET_ROUTING,
+        AUDIO_HW_GET_MODE,
+        AUDIO_HW_SET_MODE,
+        AUDIO_HW_GET_MIC_MUTE,
+        AUDIO_HW_SET_MIC_MUTE,
+        AUDIO_SET_VOICE_VOLUME,
+        AUDIO_SET_PARAMETER,
+        AUDIO_HW_GET_INPUT_BUFFER_SIZE,
+        AUDIO_HW_GET_MASTER_VOLUME,
+    };
+
     mutable     hardware_call_state                 mHardwareStatus;    // for dump only
 
 
-                DefaultKeyedVector< int, sp<PlaybackThread> >  mPlaybackThreads;
-                PlaybackThread::stream_type_t       mStreamTypes[AUDIO_STREAM_CNT];
+                DefaultKeyedVector< audio_io_handle_t, sp<PlaybackThread> >  mPlaybackThreads;
+                stream_type_t                       mStreamTypes[AUDIO_STREAM_CNT];
 
                 // both are protected by mLock
                 float                               mMasterVolume;
+                float                               mMasterVolumeSW;
+                master_volume_support               mMasterVolumeSupportLvl;
                 bool                                mMasterMute;
 
-                DefaultKeyedVector< int, sp<RecordThread> >    mRecordThreads;
+                DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> >    mRecordThreads;
 
                 DefaultKeyedVector< pid_t, sp<NotificationClient> >    mNotificationClients;
-                volatile int32_t                    mNextUniqueId;
+                volatile int32_t                    mNextUniqueId;  // updated by android_atomic_inc
                 audio_mode_t                        mMode;
                 bool                                mBtNrecIsOff;
 
+                // protected by mLock
                 Vector<AudioSessionRef*> mAudioSessionRefs;
 
-                float       masterVolume_l() const  { return mMasterVolume; }
+                float       masterVolume_l() const;
+                float       masterVolumeSW_l() const  { return mMasterVolumeSW; }
                 bool        masterMute_l() const    { return mMasterMute; }
+
+private:
+    sp<Client>  registerPid_l(pid_t pid);    // always returns non-0
+
 };
 
 
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 0b9f8ba..020d62a 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioMixer.cpp
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -33,6 +33,8 @@
 #include <system/audio.h>
 
 #include <audio_utils/primitives.h>
+#include <common_time/local_clock.h>
+#include <common_time/cc_helper.h>
 
 #include "AudioMixer.h"
 
@@ -45,6 +47,9 @@
 {
     // AudioMixer is not yet capable of multi-channel beyond stereo
     assert(2 == MAX_NUM_CHANNELS);
+    
+    LocalClock lc;
+
     mState.enabledTracks= 0;
     mState.needsChanged = 0;
     mState.frameCount   = frameCount;
@@ -68,7 +73,7 @@
         // t->prevAuxLevel
         // t->frameCount
         t->channelCount = 2;
-        t->enabled = 0;
+        t->enabled = false;
         t->format = 16;
         t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
         t->bufferProvider = NULL;
@@ -80,6 +85,7 @@
         t->sampleRate = mSampleRate;
         t->mainBuffer = NULL;
         t->auxBuffer = NULL;
+        t->localTimeFreq = lc.getLocalFreq();
         t++;
     }
 }
@@ -121,8 +127,8 @@
     assert(uint32_t(name) < MAX_NUM_TRACKS);
     ALOGV("deleteTrackName(%d)", name);
     track_t& track(mState.tracks[ name ]);
-    if (track.enabled != 0) {
-        track.enabled = 0;
+    if (track.enabled) {
+        track.enabled = false;
         invalidateState(1<<name);
     }
     if (track.resampler != NULL) {
@@ -143,8 +149,8 @@
     assert(uint32_t(name) < MAX_NUM_TRACKS);
     track_t& track = mState.tracks[name];
 
-    if (track.enabled != 1) {
-        track.enabled = 1;
+    if (!track.enabled) {
+        track.enabled = true;
         ALOGV("enable(%d)", name);
         invalidateState(1 << name);
     }
@@ -156,8 +162,8 @@
     assert(uint32_t(name) < MAX_NUM_TRACKS);
     track_t& track = mState.tracks[name];
 
-    if (track.enabled != 0) {
-        track.enabled = 0;
+    if (track.enabled) {
+        track.enabled = false;
         ALOGV("disable(%d)", name);
         invalidateState(1 << name);
     }
@@ -251,6 +257,7 @@
             }
             break;
         case AUXLEVEL:
+            //assert(0 <= valueInt && valueInt <= MAX_GAIN_INT);
             if (track.auxLevel != valueInt) {
                 ALOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
                 track.prevAuxLevel = track.auxLevel << 16;
@@ -289,6 +296,7 @@
             if (resampler == NULL) {
                 resampler = AudioResampler::create(
                         format, channelCount, devSampleRate);
+                resampler->setLocalTimeFreq(localTimeFreq);
             }
             return true;
         }
@@ -296,18 +304,6 @@
     return false;
 }
 
-bool AudioMixer::track_t::doesResample() const
-{
-    return resampler != NULL;
-}
-
-void AudioMixer::track_t::resetResampler()
-{
-    if (resampler != NULL) {
-        resampler->reset();
-    }
-}
-
 inline
 void AudioMixer::track_t::adjustVolumeRamp(bool aux)
 {
@@ -327,20 +323,11 @@
     }
 }
 
-size_t AudioMixer::track_t::getUnreleasedFrames()
-{
-    if (resampler != NULL) {
-        return resampler->getUnreleasedFrames();
-    }
-    return 0;
-}
-
-size_t AudioMixer::getUnreleasedFrames(int name)
+size_t AudioMixer::getUnreleasedFrames(int name) const
 {
     name -= TRACK0;
     if (uint32_t(name) < MAX_NUM_TRACKS) {
-        track_t& track(mState.tracks[name]);
-        return track.getUnreleasedFrames();
+        return mState.tracks[name].getUnreleasedFrames();
     }
     return 0;
 }
@@ -354,13 +341,13 @@
 
 
 
-void AudioMixer::process()
+void AudioMixer::process(int64_t pts)
 {
-    mState.hook(&mState);
+    mState.hook(&mState, pts);
 }
 
 
-void AudioMixer::process__validate(state_t* state)
+void AudioMixer::process__validate(state_t* state, int64_t pts)
 {
     ALOGW_IF(!state->needsChanged,
         "in process__validate() but nothing's invalid");
@@ -383,9 +370,9 @@
 
     // compute everything we need...
     int countActiveTracks = 0;
-    int all16BitsStereoNoResample = 1;
-    int resampling = 0;
-    int volumeRamp = 0;
+    bool all16BitsStereoNoResample = true;
+    bool resampling = false;
+    bool volumeRamp = false;
     uint32_t en = state->enabledTracks;
     while (en) {
         const int i = 31 - __builtin_clz(en);
@@ -402,7 +389,7 @@
         }
 
         if (t.volumeInc[0]|t.volumeInc[1]) {
-            volumeRamp = 1;
+            volumeRamp = true;
         } else if (!t.doesResample() && t.volumeRL == 0) {
             n |= NEEDS_MUTE_ENABLED;
         }
@@ -412,16 +399,16 @@
             t.hook = track__nop;
         } else {
             if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
-                all16BitsStereoNoResample = 0;
+                all16BitsStereoNoResample = false;
             }
             if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
-                all16BitsStereoNoResample = 0;
-                resampling = 1;
+                all16BitsStereoNoResample = false;
+                resampling = true;
                 t.hook = track__genericResample;
             } else {
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
                     t.hook = track__16BitsMono;
-                    all16BitsStereoNoResample = 0;
+                    all16BitsStereoNoResample = false;
                 }
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_2){
                     t.hook = track__16BitsStereo;
@@ -464,12 +451,12 @@
         countActiveTracks, state->enabledTracks,
         all16BitsStereoNoResample, resampling, volumeRamp);
 
-    state->hook(state);
+   state->hook(state, pts);
 
     // Now that the volume ramp has been done, set optimal state and
     // track hooks for subsequent mixer process
     if (countActiveTracks) {
-        int allMuted = 1;
+        bool allMuted = true;
         uint32_t en = state->enabledTracks;
         while (en) {
             const int i = 31 - __builtin_clz(en);
@@ -480,7 +467,7 @@
                 t.needs |= NEEDS_MUTE_ENABLED;
                 t.hook = track__nop;
             } else {
-                allMuted = 0;
+                allMuted = false;
             }
         }
         if (allMuted) {
@@ -570,7 +557,7 @@
     }
     t->prevVolume[0] = vl;
     t->prevVolume[1] = vr;
-    t->adjustVolumeRamp((aux != NULL));
+    t->adjustVolumeRamp(aux != NULL);
 }
 
 void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
@@ -579,7 +566,7 @@
     const int16_t vr = t->volume[1];
 
     if (CC_UNLIKELY(aux != NULL)) {
-        const int16_t va = (int16_t)t->auxLevel;
+        const int16_t va = t->auxLevel;
         do {
             int16_t l = (int16_t)(*temp++ >> 12);
             int16_t r = (int16_t)(*temp++ >> 12);
@@ -778,7 +765,7 @@
 }
 
 // no-op case
-void AudioMixer::process__nop(state_t* state)
+void AudioMixer::process__nop(state_t* state, int64_t pts)
 {
     uint32_t e0 = state->enabledTracks;
     size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS;
@@ -808,7 +795,9 @@
             size_t outFrames = state->frameCount;
             while (outFrames) {
                 t1.buffer.frameCount = outFrames;
-                t1.bufferProvider->getNextBuffer(&t1.buffer);
+                int64_t outputPTS = calculateOutputPTS(
+                    t1, pts, state->frameCount - outFrames);
+                t1.bufferProvider->getNextBuffer(&t1.buffer, outputPTS);
                 if (t1.buffer.raw == NULL) break;
                 outFrames -= t1.buffer.frameCount;
                 t1.bufferProvider->releaseBuffer(&t1.buffer);
@@ -818,7 +807,7 @@
 }
 
 // generic code without resampling
-void AudioMixer::process__genericNoResampling(state_t* state)
+void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
 {
     int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
 
@@ -830,7 +819,7 @@
         e0 &= ~(1<<i);
         track_t& t = state->tracks[i];
         t.buffer.frameCount = state->frameCount;
-        t.bufferProvider->getNextBuffer(&t.buffer);
+        t.bufferProvider->getNextBuffer(&t.buffer, pts);
         t.frameCount = t.buffer.frameCount;
         t.in = t.buffer.raw;
         // t.in == NULL can happen if the track was flushed just after having
@@ -874,7 +863,7 @@
                 while (outFrames) {
                     size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
                     if (inFrames) {
-                        (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux);
+                        t.hook(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux);
                         t.frameCount -= inFrames;
                         outFrames -= inFrames;
                         if (CC_UNLIKELY(aux != NULL)) {
@@ -884,7 +873,9 @@
                     if (t.frameCount == 0 && outFrames) {
                         t.bufferProvider->releaseBuffer(&t.buffer);
                         t.buffer.frameCount = (state->frameCount - numFrames) - (BLOCKSIZE - outFrames);
-                        t.bufferProvider->getNextBuffer(&t.buffer);
+                        int64_t outputPTS = calculateOutputPTS(
+                            t, pts, numFrames + (BLOCKSIZE - outFrames));
+                        t.bufferProvider->getNextBuffer(&t.buffer, outputPTS);
                         t.in = t.buffer.raw;
                         if (t.in == NULL) {
                             enabledTracks &= ~(1<<i);
@@ -913,7 +904,7 @@
 
 
 // generic code with resampling
-void AudioMixer::process__genericResampling(state_t* state)
+void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
 {
     // this const just means that local variable outTemp doesn't change
     int32_t* const outTemp = state->outputTemp;
@@ -953,14 +944,16 @@
             // acquire/release the buffers because it's done by
             // the resampler.
             if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
-                (t.hook)(&t, outTemp, numFrames, state->resampleTemp, aux);
+                t.resampler->setPTS(pts);
+                t.hook(&t, outTemp, numFrames, state->resampleTemp, aux);
             } else {
 
                 size_t outFrames = 0;
 
                 while (outFrames < numFrames) {
                     t.buffer.frameCount = numFrames - outFrames;
-                    t.bufferProvider->getNextBuffer(&t.buffer);
+                    int64_t outputPTS = calculateOutputPTS(t, pts, outFrames);
+                    t.bufferProvider->getNextBuffer(&t.buffer, outputPTS);
                     t.in = t.buffer.raw;
                     // t.in == NULL can happen if the track was flushed just after having
                     // been enabled for mixing.
@@ -969,7 +962,7 @@
                     if (CC_UNLIKELY(aux != NULL)) {
                         aux += outFrames;
                     }
-                    (t.hook)(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux);
+                    t.hook(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux);
                     outFrames += t.buffer.frameCount;
                     t.bufferProvider->releaseBuffer(&t.buffer);
                 }
@@ -980,9 +973,15 @@
 }
 
 // one track, 16 bits stereo without resampling is the most common case
-void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
+void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state,
+                                                           int64_t pts)
 {
+    // This method is only called when state->enabledTracks has exactly
+    // one bit set.  The asserts below would verify this, but are commented out
+    // since the whole point of this method is to optimize performance.
+    //assert(0 != state->enabledTracks);
     const int i = 31 - __builtin_clz(state->enabledTracks);
+    //assert((1 << i) == state->enabledTracks);
     const track_t& t = state->tracks[i];
 
     AudioBufferProvider::Buffer& b(t.buffer);
@@ -995,7 +994,8 @@
     const uint32_t vrl = t.volumeRL;
     while (numFrames) {
         b.frameCount = numFrames;
-        t.bufferProvider->getNextBuffer(&b);
+        int64_t outputPTS = calculateOutputPTS(t, pts, out - t.mainBuffer);
+        t.bufferProvider->getNextBuffer(&b, outputPTS);
         const int16_t *in = b.i16;
 
         // in == NULL can happen if the track was flushed just after having
@@ -1039,7 +1039,8 @@
 // 2 tracks is also a common case
 // NEVER used in current implementation of process__validate()
 // only use if the 2 tracks have the same output buffer
-void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
+void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state,
+                                                            int64_t pts)
 {
     int i;
     uint32_t en = state->enabledTracks;
@@ -1073,7 +1074,9 @@
 
         if (frameCount0 == 0) {
             b0.frameCount = numFrames;
-            t0.bufferProvider->getNextBuffer(&b0);
+            int64_t outputPTS = calculateOutputPTS(t0, pts,
+                                                   out - t0.mainBuffer);
+            t0.bufferProvider->getNextBuffer(&b0, outputPTS);
             if (b0.i16 == NULL) {
                 if (buff == NULL) {
                     buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount];
@@ -1087,7 +1090,9 @@
         }
         if (frameCount1 == 0) {
             b1.frameCount = numFrames;
-            t1.bufferProvider->getNextBuffer(&b1);
+            int64_t outputPTS = calculateOutputPTS(t1, pts,
+                                                   out - t0.mainBuffer);
+            t1.bufferProvider->getNextBuffer(&b1, outputPTS);
             if (b1.i16 == NULL) {
                 if (buff == NULL) {
                     buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount];
@@ -1133,5 +1138,14 @@
 }
 #endif
 
+int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS,
+                                       int outputFrameIndex)
+{
+    if (AudioBufferProvider::kInvalidPTS == basePTS)
+        return AudioBufferProvider::kInvalidPTS;
+
+    return basePTS + ((outputFrameIndex * t.localTimeFreq) / t.sampleRate);
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 84f6330..b210212 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioMixer.h
+/*
 **
 ** Copyright 2007, The Android Open Source Project
 **
@@ -33,7 +33,7 @@
 public:
                             AudioMixer(size_t frameCount, uint32_t sampleRate);
 
-                            ~AudioMixer();
+    /*virtual*/             ~AudioMixer();  // non-virtual saves a v-table, restore if sub-classed
 
     static const uint32_t MAX_NUM_TRACKS = 32;
     static const uint32_t MAX_NUM_CHANNELS = 2;
@@ -79,11 +79,11 @@
     void        setParameter(int name, int target, int param, void *value);
 
     void        setBufferProvider(int name, AudioBufferProvider* bufferProvider);
-    void        process();
+    void        process(int64_t pts);
 
     uint32_t    trackNames() const { return mTrackNames; }
 
-    size_t      getUnreleasedFrames(int name);
+    size_t      getUnreleasedFrames(int name) const;
 
 private:
 
@@ -114,7 +114,6 @@
     struct state_t;
     struct track_t;
 
-    typedef void (*mix_t)(state_t* state);
     typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
     static const int BLOCKSIZE = 16; // 4 cache lines
 
@@ -128,35 +127,52 @@
 
         int32_t     prevVolume[MAX_NUM_CHANNELS];
 
+        // 16-byte boundary
+
         int32_t     volumeInc[MAX_NUM_CHANNELS];
-        int32_t     auxLevel;
         int32_t     auxInc;
         int32_t     prevAuxLevel;
 
+        // 16-byte boundary
+
+        int16_t     auxLevel;       // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
         uint16_t    frameCount;
 
-        uint8_t     channelCount : 4;
-        uint8_t     enabled      : 1;
-        uint8_t     reserved0    : 3;
-        uint8_t     format;
-        uint32_t    channelMask;
+        uint8_t     channelCount;   // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
+        uint8_t     format;         // always 16
+        uint16_t    enabled;        // actually bool
+        uint32_t    channelMask;    // currently under-used
 
         AudioBufferProvider*                bufferProvider;
-        mutable AudioBufferProvider::Buffer buffer;
+
+        // 16-byte boundary
+
+        mutable AudioBufferProvider::Buffer buffer; // 8 bytes
 
         hook_t      hook;
         const void* in;             // current location in buffer
 
+        // 16-byte boundary
+
         AudioResampler*     resampler;
         uint32_t            sampleRate;
         int32_t*           mainBuffer;
         int32_t*           auxBuffer;
 
+        // 16-byte boundary
+
+        uint64_t    localTimeFreq;
+
+        int64_t     padding;
+
+        // 16-byte boundary
+
         bool        setResampler(uint32_t sampleRate, uint32_t devSampleRate);
-        bool        doesResample() const;
-        void        resetResampler();
+        bool        doesResample() const { return resampler != NULL; }
+        void        resetResampler() { if (resampler != NULL) resampler->reset(); }
         void        adjustVolumeRamp(bool aux);
-        size_t      getUnreleasedFrames();
+        size_t      getUnreleasedFrames() const { return resampler != NULL ?
+                                                    resampler->getUnreleasedFrames() : 0; };
     };
 
     // pad to 32-bytes to fill cache line
@@ -164,7 +180,7 @@
         uint32_t        enabledTracks;
         uint32_t        needsChanged;
         size_t          frameCount;
-        mix_t           hook;
+        void            (*hook)(state_t* state, int64_t pts);   // one of process__*, never NULL
         int32_t         *outputTemp;
         int32_t         *resampleTemp;
         int32_t         reserved[2];
@@ -186,14 +202,19 @@
     static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
     static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
 
-    static void process__validate(state_t* state);
-    static void process__nop(state_t* state);
-    static void process__genericNoResampling(state_t* state);
-    static void process__genericResampling(state_t* state);
-    static void process__OneTrack16BitsStereoNoResampling(state_t* state);
+    static void process__validate(state_t* state, int64_t pts);
+    static void process__nop(state_t* state, int64_t pts);
+    static void process__genericNoResampling(state_t* state, int64_t pts);
+    static void process__genericResampling(state_t* state, int64_t pts);
+    static void process__OneTrack16BitsStereoNoResampling(state_t* state,
+                                                          int64_t pts);
 #if 0
-    static void process__TwoTracks16BitsStereoNoResampling(state_t* state);
+    static void process__TwoTracks16BitsStereoNoResampling(state_t* state,
+                                                           int64_t pts);
 #endif
+
+    static int64_t calculateOutputPTS(const track_t& t, int64_t basePTS,
+                                      int outputFrameIndex);
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 1dddbb3..987b039 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -30,6 +30,7 @@
 #include <utils/String16.h>
 #include <utils/threads.h>
 #include "AudioPolicyService.h"
+#include "ServiceUtilities.h"
 #include <cutils/properties.h>
 #include <hardware_legacy/power.h>
 #include <media/AudioEffect.h>
@@ -49,13 +50,6 @@
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleepUs = 20000;
 
-static bool checkPermission() {
-    if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
-    bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
-    if (!ok) ALOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
-    return ok;
-}
-
 namespace {
     extern struct audio_policy_service_ops aps_ops;
 };
@@ -122,19 +116,7 @@
 
     // release audio pre processing resources
     for (size_t i = 0; i < mInputSources.size(); i++) {
-        InputSourceDesc *source = mInputSources.valueAt(i);
-        Vector <EffectDesc *> effects = source->mEffects;
-        for (size_t j = 0; j < effects.size(); j++) {
-            delete effects[j]->mName;
-            Vector <effect_param_t *> params = effects[j]->mParams;
-            for (size_t k = 0; k < params.size(); k++) {
-                delete params[k];
-            }
-            params.clear();
-            delete effects[j];
-        }
-        effects.clear();
-        delete source;
+        delete mInputSources.valueAt(i);
     }
     mInputSources.clear();
 
@@ -157,7 +139,7 @@
     if (mpAudioPolicy == NULL) {
         return NO_INIT;
     }
-    if (!checkPermission()) {
+    if (!settingsAllowed()) {
         return PERMISSION_DENIED;
     }
     if (!audio_is_output_device(device) && !audio_is_input_device(device)) {
@@ -190,7 +172,7 @@
     if (mpAudioPolicy == NULL) {
         return NO_INIT;
     }
-    if (!checkPermission()) {
+    if (!settingsAllowed()) {
         return PERMISSION_DENIED;
     }
     if (uint32_t(state) >= AUDIO_MODE_CNT) {
@@ -213,7 +195,7 @@
     if (mpAudioPolicy == NULL) {
         return NO_INIT;
     }
-    if (!checkPermission()) {
+    if (!settingsAllowed()) {
         return PERMISSION_DENIED;
     }
     if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
@@ -388,7 +370,7 @@
     if (mpAudioPolicy == NULL) {
         return NO_INIT;
     }
-    if (!checkPermission()) {
+    if (!settingsAllowed()) {
         return PERMISSION_DENIED;
     }
     if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
@@ -405,7 +387,7 @@
     if (mpAudioPolicy == NULL) {
         return NO_INIT;
     }
-    if (!checkPermission()) {
+    if (!settingsAllowed()) {
         return PERMISSION_DENIED;
     }
     if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
@@ -542,7 +524,7 @@
 }
 
 void AudioPolicyService::binderDied(const wp<IBinder>& who) {
-    ALOGW("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(),
+    ALOGW("binderDied() %p, tid %d, calling pid %d", who.unsafe_get(), gettid(),
             IPCThreadState::self()->getCallingPid());
 }
 
@@ -578,7 +560,7 @@
 
 status_t AudioPolicyService::dump(int fd, const Vector<String16>& args)
 {
-    if (!checkCallingPermission(String16("android.permission.DUMP"))) {
+    if (!dumpAllowed()) {
         dumpPermissionDenial(fd);
     } else {
         bool locked = tryLock(mLock);
@@ -588,10 +570,10 @@
         }
 
         dumpInternals(fd);
-        if (mAudioCommandThread != NULL) {
+        if (mAudioCommandThread != 0) {
             mAudioCommandThread->dump(fd);
         }
-        if (mTonePlaybackThread != NULL) {
+        if (mTonePlaybackThread != 0) {
             mTonePlaybackThread->dump(fd);
         }
 
@@ -622,8 +604,7 @@
 {
     Vector<sp<AudioEffect> > fxVector = inputDesc->mEffects;
     for (size_t i = 0; i < fxVector.size(); i++) {
-        sp<AudioEffect> fx = fxVector.itemAt(i);
-        fx->setEnabled(enabled);
+        fxVector.itemAt(i)->setEnabled(enabled);
     }
 }
 
@@ -774,7 +755,7 @@
     snprintf(buffer, SIZE, "- Commands:\n");
     result = String8(buffer);
     result.append("   Command Time        Wait pParam\n");
-    for (int i = 0; i < (int)mAudioCommands.size(); i++) {
+    for (size_t i = 0; i < mAudioCommands.size(); i++) {
         mAudioCommands[i]->dump(buffer, SIZE);
         result.append(buffer);
     }
@@ -819,7 +800,7 @@
 
 status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream,
                                                                float volume,
-                                                               int output,
+                                                               audio_io_handle_t output,
                                                                int delayMs)
 {
     status_t status = NO_ERROR;
@@ -849,7 +830,7 @@
     return status;
 }
 
-status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
+status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_handle_t ioHandle,
                                                                    const char *keyValuePairs,
                                                                    int delayMs)
 {
@@ -908,7 +889,7 @@
 // insertCommand_l() must be called with mLock held
 void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
 {
-    ssize_t i;
+    ssize_t i;  // not size_t because i will count down to -1
     Vector <AudioCommand *> removedCommands;
 
     command->mTime = systemTime() + milliseconds(delayMs);
@@ -1019,7 +1000,7 @@
                                        const char *keyValuePairs,
                                        int delayMs)
 {
-    mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs,
+    mAudioCommandThread->parametersCommand(ioHandle, keyValuePairs,
                                            delayMs);
 }
 
@@ -1029,7 +1010,7 @@
                                         int delayMs)
 {
     return (int)mAudioCommandThread->volumeCommand(stream, volume,
-                                                   (int)output, delayMs);
+                                                   output, delayMs);
 }
 
 int AudioPolicyService::startTone(audio_policy_tone_t tone,
@@ -1249,7 +1230,7 @@
             node = node->next;
             continue;
         }
-        EffectDesc *effect = new EffectDesc(*effects[i]);
+        EffectDesc *effect = new EffectDesc(*effects[i]);   // deep copy
         loadEffectParameters(node, effect->mParams);
         ALOGV("loadInputSource() adding effect %s uuid %08x", effect->mName, effect->mUuid.timeLow);
         source->mEffects.add(effect);
@@ -1300,11 +1281,7 @@
         ALOGW("loadEffect() invalid uuid %s", node->value);
         return NULL;
     }
-    EffectDesc *effect = new EffectDesc();
-    effect->mName = strdup(root->name);
-    memcpy(&effect->mUuid, &uuid, sizeof(effect_uuid_t));
-
-    return effect;
+    return new EffectDesc(root->name, uuid);
 }
 
 status_t AudioPolicyService::loadEffects(cnode *root, Vector <EffectDesc *>& effects)
@@ -1362,7 +1339,7 @@
                                              audio_policy_output_flags_t flags)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL) {
+    if (af == 0) {
         ALOGW("%s: could not get AudioFlinger", __func__);
         return 0;
     }
@@ -1376,7 +1353,7 @@
                                                  audio_io_handle_t output2)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL) {
+    if (af == 0) {
         ALOGW("%s: could not get AudioFlinger", __func__);
         return 0;
     }
@@ -1386,7 +1363,7 @@
 static int aps_close_output(void *service, audio_io_handle_t output)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL)
+    if (af == 0)
         return PERMISSION_DENIED;
 
     return af->closeOutput(output);
@@ -1395,7 +1372,7 @@
 static int aps_suspend_output(void *service, audio_io_handle_t output)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL) {
+    if (af == 0) {
         ALOGW("%s: could not get AudioFlinger", __func__);
         return PERMISSION_DENIED;
     }
@@ -1406,7 +1383,7 @@
 static int aps_restore_output(void *service, audio_io_handle_t output)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL) {
+    if (af == 0) {
         ALOGW("%s: could not get AudioFlinger", __func__);
         return PERMISSION_DENIED;
     }
@@ -1422,7 +1399,7 @@
                                             audio_in_acoustics_t acoustics)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL) {
+    if (af == 0) {
         ALOGW("%s: could not get AudioFlinger", __func__);
         return 0;
     }
@@ -1434,7 +1411,7 @@
 static int aps_close_input(void *service, audio_io_handle_t input)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL)
+    if (af == 0)
         return PERMISSION_DENIED;
 
     return af->closeInput(input);
@@ -1444,7 +1421,7 @@
                                      audio_io_handle_t output)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL)
+    if (af == 0)
         return PERMISSION_DENIED;
 
     return af->setStreamOutput(stream, output);
@@ -1455,10 +1432,10 @@
                                 audio_io_handle_t dst_output)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
-    if (af == NULL)
+    if (af == 0)
         return PERMISSION_DENIED;
 
-    return af->moveEffects(session, (int)src_output, (int)dst_output);
+    return af->moveEffects(session, src_output, dst_output);
 }
 
 static char * aps_get_parameters(void *service, audio_io_handle_t io_handle,
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 62219e5..679fd30 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -174,8 +174,10 @@
                     void        startToneCommand(ToneGenerator::tone_type type,
                                                  audio_stream_type_t stream);
                     void        stopToneCommand();
-                    status_t    volumeCommand(audio_stream_type_t stream, float volume, int output, int delayMs = 0);
-                    status_t    parametersCommand(int ioHandle, const char *keyValuePairs, int delayMs = 0);
+                    status_t    volumeCommand(audio_stream_type_t stream, float volume,
+                                            audio_io_handle_t output, int delayMs = 0);
+                    status_t    parametersCommand(audio_io_handle_t ioHandle,
+                                            const char *keyValuePairs, int delayMs = 0);
                     status_t    voiceVolumeCommand(float volume, int delayMs = 0);
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
 
@@ -207,12 +209,12 @@
         public:
             audio_stream_type_t mStream;
             float mVolume;
-            int mIO;
+            audio_io_handle_t mIO;
         };
 
         class ParametersData {
         public:
-            int mIO;
+            audio_io_handle_t mIO;
             String8 mKeyValuePairs;
         };
 
@@ -231,8 +233,33 @@
 
     class EffectDesc {
     public:
-        EffectDesc() {}
-        virtual ~EffectDesc() {}
+        EffectDesc(const char *name, const effect_uuid_t& uuid) :
+                        mName(strdup(name)),
+                        mUuid(uuid) { }
+        EffectDesc(const EffectDesc& orig) :
+                        mName(strdup(orig.mName)),
+                        mUuid(orig.mUuid) {
+                            // deep copy mParams
+                            for (size_t k = 0; k < orig.mParams.size(); k++) {
+                                effect_param_t *origParam = orig.mParams[k];
+                                // psize and vsize are rounded up to an int boundary for allocation
+                                size_t origSize = sizeof(effect_param_t) +
+                                                  ((origParam->psize + 3) & ~3) +
+                                                  ((origParam->vsize + 3) & ~3);
+                                effect_param_t *dupParam = (effect_param_t *) malloc(origSize);
+                                memcpy(dupParam, origParam, origSize);
+                                // This works because the param buffer allocation is also done by
+                                // multiples of 4 bytes originally. In theory we should memcpy only
+                                // the actual param size, that is without rounding vsize.
+                                mParams.add(dupParam);
+                            }
+                        }
+        /*virtual*/ ~EffectDesc() {
+            free(mName);
+            for (size_t k = 0; k < mParams.size(); k++) {
+                free(mParams[k]);
+            }
+        }
         char *mName;
         effect_uuid_t mUuid;
         Vector <effect_param_t *> mParams;
@@ -241,7 +268,11 @@
     class InputSourceDesc {
     public:
         InputSourceDesc() {}
-        virtual ~InputSourceDesc() {}
+        /*virtual*/ ~InputSourceDesc() {
+            for (size_t j = 0; j < mEffects.size(); j++) {
+                delete mEffects[j];
+            }
+        }
         Vector <EffectDesc *> mEffects;
     };
 
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index feacd96..398ba0b 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -23,8 +23,10 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 #include "AudioResampler.h"
+#if 0
 #include "AudioResamplerSinc.h"
 #include "AudioResamplerCubic.h"
+#endif
 
 #ifdef __arm__
 #include <machine/cpu-features.h>
@@ -99,6 +101,7 @@
         ALOGV("Create linear Resampler");
         resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
         break;
+#if 0
     case MED_QUALITY:
         ALOGV("Create cubic Resampler");
         resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
@@ -107,6 +110,7 @@
         ALOGV("Create sinc Resampler");
         resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
         break;
+#endif
     }
 
     // initialize resampler
@@ -118,7 +122,8 @@
         int32_t sampleRate) :
     mBitDepth(bitDepth), mChannelCount(inChannelCount),
             mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
-            mPhaseFraction(0) {
+            mPhaseFraction(0), mLocalTimeFreq(0),
+            mPTS(AudioBufferProvider::kInvalidPTS) {
     // sanity check on format
     if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) {
         ALOGE("Unsupported sample format, %d bits, %d channels", bitDepth,
@@ -130,12 +135,6 @@
     mVolume[0] = mVolume[1] = 0;
     mBuffer.frameCount = 0;
 
-    // save format for quick lookup
-    if (inChannelCount == 1) {
-        mFormat = MONO_16_BIT;
-    } else {
-        mFormat = STEREO_16_BIT;
-    }
 }
 
 AudioResampler::~AudioResampler() {
@@ -152,6 +151,23 @@
     mVolume[1] = right;
 }
 
+void AudioResampler::setLocalTimeFreq(uint64_t freq) {
+    mLocalTimeFreq = freq;
+}
+
+void AudioResampler::setPTS(int64_t pts) {
+    mPTS = pts;
+}
+
+int64_t AudioResampler::calculateOutputPTS(int outputFrameIndex) {
+
+    if (mPTS == AudioBufferProvider::kInvalidPTS) {
+        return AudioBufferProvider::kInvalidPTS;
+    } else {
+        return mPTS + ((outputFrameIndex * mLocalTimeFreq) / mSampleRate);
+    }
+}
+
 void AudioResampler::reset() {
     mInputIndex = 0;
     mPhaseFraction = 0;
@@ -190,7 +206,7 @@
     size_t outputSampleCount = outFrameCount * 2;
     size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
 
-    // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n",
+    // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
     //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
 
     while (outputIndex < outputSampleCount) {
@@ -198,12 +214,13 @@
         // buffer is empty, fetch a new one
         while (mBuffer.frameCount == 0) {
             mBuffer.frameCount = inFrameCount;
-            provider->getNextBuffer(&mBuffer);
+            provider->getNextBuffer(&mBuffer,
+                                    calculateOutputPTS(outputIndex / 2));
             if (mBuffer.raw == NULL) {
                 goto resampleStereo16_exit;
             }
 
-            // ALOGE("New buffer fetched: %d frames\n", mBuffer.frameCount);
+            // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
             if (mBuffer.frameCount > inputIndex) break;
 
             inputIndex -= mBuffer.frameCount;
@@ -217,7 +234,7 @@
 
         // handle boundary case
         while (inputIndex == 0) {
-            // ALOGE("boundary case\n");
+            // ALOGE("boundary case");
             out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction);
             out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction);
             Advance(&inputIndex, &phaseFraction, phaseIncrement);
@@ -226,7 +243,7 @@
         }
 
         // process input samples
-        // ALOGE("general case\n");
+        // ALOGE("general case");
 
 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
         if (inputIndex + 2 < mBuffer.frameCount) {
@@ -248,7 +265,7 @@
             Advance(&inputIndex, &phaseFraction, phaseIncrement);
         }
 
-        // ALOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
+        // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
 
         // if done with buffer, save samples
         if (inputIndex >= mBuffer.frameCount) {
@@ -265,7 +282,7 @@
         }
     }
 
-    // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
+    // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
 
 resampleStereo16_exit:
     // save state
@@ -286,19 +303,20 @@
     size_t outputSampleCount = outFrameCount * 2;
     size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
 
-    // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n",
+    // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d",
     //      outFrameCount, inputIndex, phaseFraction, phaseIncrement);
     while (outputIndex < outputSampleCount) {
         // buffer is empty, fetch a new one
         while (mBuffer.frameCount == 0) {
             mBuffer.frameCount = inFrameCount;
-            provider->getNextBuffer(&mBuffer);
+            provider->getNextBuffer(&mBuffer,
+                                    calculateOutputPTS(outputIndex / 2));
             if (mBuffer.raw == NULL) {
                 mInputIndex = inputIndex;
                 mPhaseFraction = phaseFraction;
                 goto resampleMono16_exit;
             }
-            // ALOGE("New buffer fetched: %d frames\n", mBuffer.frameCount);
+            // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount);
             if (mBuffer.frameCount >  inputIndex) break;
 
             inputIndex -= mBuffer.frameCount;
@@ -310,7 +328,7 @@
 
         // handle boundary case
         while (inputIndex == 0) {
-            // ALOGE("boundary case\n");
+            // ALOGE("boundary case");
             int32_t sample = Interp(mX0L, in[0], phaseFraction);
             out[outputIndex++] += vl * sample;
             out[outputIndex++] += vr * sample;
@@ -320,7 +338,7 @@
         }
 
         // process input samples
-        // ALOGE("general case\n");
+        // ALOGE("general case");
 
 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
         if (inputIndex + 2 < mBuffer.frameCount) {
@@ -343,7 +361,7 @@
         }
 
 
-        // ALOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
+        // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
 
         // if done with buffer, save samples
         if (inputIndex >= mBuffer.frameCount) {
@@ -359,7 +377,7 @@
         }
     }
 
-    // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
+    // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex);
 
 resampleMono16_exit:
     // save state
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index ffa690a..9deb796 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -49,12 +49,16 @@
     virtual void init() = 0;
     virtual void setSampleRate(int32_t inSampleRate);
     virtual void setVolume(int16_t left, int16_t right);
+    virtual void setLocalTimeFreq(uint64_t freq);
+
+    // set the PTS of the next buffer output by the resampler
+    virtual void setPTS(int64_t pts);
 
     virtual void resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider) = 0;
 
     virtual void reset();
-    virtual size_t getUnreleasedFrames() { return mInputIndex; }
+    virtual size_t getUnreleasedFrames() const { return mInputIndex; }
 
 protected:
     // number of bits for phase fraction - 30 bits allows nearly 2x downsampling
@@ -66,16 +70,17 @@
     // multiplier to calculate fixed point phase increment
     static const double kPhaseMultiplier = 1L << kNumPhaseBits;
 
-    enum format {MONO_16_BIT, STEREO_16_BIT};
     AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate);
 
     // prevent copying
     AudioResampler(const AudioResampler&);
     AudioResampler& operator=(const AudioResampler&);
 
-    int32_t mBitDepth;
-    int32_t mChannelCount;
-    int32_t mSampleRate;
+    int64_t calculateOutputPTS(int outputFrameIndex);
+
+    const int32_t mBitDepth;
+    const int32_t mChannelCount;
+    const int32_t mSampleRate;
     int32_t mInSampleRate;
     AudioBufferProvider::Buffer mBuffer;
     union {
@@ -83,10 +88,11 @@
         uint32_t mVolumeRL;
     };
     int16_t mTargetVolume[2];
-    format mFormat;
     size_t mInputIndex;
     int32_t mPhaseIncrement;
     uint32_t mPhaseFraction;
+    uint64_t mLocalTimeFreq;
+    int64_t mPTS;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp
index 47205ba..18e59e9 100644
--- a/services/audioflinger/AudioResamplerCubic.cpp
+++ b/services/audioflinger/AudioResamplerCubic.cpp
@@ -65,7 +65,7 @@
     // fetch first buffer
     if (mBuffer.frameCount == 0) {
         mBuffer.frameCount = inFrameCount;
-        provider->getNextBuffer(&mBuffer);
+        provider->getNextBuffer(&mBuffer, mPTS);
         if (mBuffer.raw == NULL)
             return;
         // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount);
@@ -95,11 +95,12 @@
                 inputIndex = 0;
                 provider->releaseBuffer(&mBuffer);
                 mBuffer.frameCount = inFrameCount;
-                provider->getNextBuffer(&mBuffer);
+                provider->getNextBuffer(&mBuffer,
+                                        calculateOutputPTS(outputIndex / 2));
                 if (mBuffer.raw == NULL)
                     goto save_state;  // ugly, but efficient
                 in = mBuffer.i16;
-                // ALOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount);
+                // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount);
             }
 
             // advance sample state
@@ -130,10 +131,10 @@
     // fetch first buffer
     if (mBuffer.frameCount == 0) {
         mBuffer.frameCount = inFrameCount;
-        provider->getNextBuffer(&mBuffer);
+        provider->getNextBuffer(&mBuffer, mPTS);
         if (mBuffer.raw == NULL)
             return;
-        // ALOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount);
+        // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount);
     }
     int16_t *in = mBuffer.i16;
 
@@ -160,7 +161,8 @@
                 inputIndex = 0;
                 provider->releaseBuffer(&mBuffer);
                 mBuffer.frameCount = inFrameCount;
-                provider->getNextBuffer(&mBuffer);
+                provider->getNextBuffer(&mBuffer,
+                                        calculateOutputPTS(outputIndex / 2));
                 if (mBuffer.raw == NULL)
                     goto save_state;  // ugly, but efficient
                 // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount);
@@ -181,4 +183,3 @@
 // ----------------------------------------------------------------------------
 }
 ; // namespace android
-
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index d012433..d373c08 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -199,33 +199,33 @@
     size_t outputSampleCount = outFrameCount * 2;
     size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
 
-    AudioBufferProvider::Buffer& buffer(mBuffer);
     while (outputIndex < outputSampleCount) {
         // buffer is empty, fetch a new one
-        while (buffer.frameCount == 0) {
-            buffer.frameCount = inFrameCount;
-            provider->getNextBuffer(&buffer);
-            if (buffer.raw == NULL) {
+        while (mBuffer.frameCount == 0) {
+            mBuffer.frameCount = inFrameCount;
+            provider->getNextBuffer(&mBuffer,
+                                    calculateOutputPTS(outputIndex / 2));
+            if (mBuffer.raw == NULL) {
                 goto resample_exit;
             }
             const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
             if (phaseIndex == 1) {
                 // read one frame
-                read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex);
+                read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
             } else if (phaseIndex == 2) {
                 // read 2 frames
-                read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex);
+                read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
                 inputIndex++;
                 if (inputIndex >= mBuffer.frameCount) {
                     inputIndex -= mBuffer.frameCount;
-                    provider->releaseBuffer(&buffer);
+                    provider->releaseBuffer(&mBuffer);
                 } else {
-                    read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex);
+                    read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
                 }
            }
         }
-        int16_t *in = buffer.i16;
-        const size_t frameCount = buffer.frameCount;
+        int16_t *in = mBuffer.i16;
+        const size_t frameCount = mBuffer.frameCount;
 
         // Always read-in the first samples from the input buffer
         int16_t* head = impulse + halfNumCoefs*CHANNELS;
@@ -264,7 +264,7 @@
         // if done with buffer, save samples
         if (inputIndex >= frameCount) {
             inputIndex -= frameCount;
-            provider->releaseBuffer(&buffer);
+            provider->releaseBuffer(&mBuffer);
         }
     }
 
@@ -355,4 +355,3 @@
 
 // ----------------------------------------------------------------------------
 }; // namespace android
-
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index 0e1bc44..f0a07b8 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -31,7 +31,7 @@
 public:
     AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate);
 
-    ~AudioResamplerSinc();
+    virtual ~AudioResamplerSinc();
 
     virtual void resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
new file mode 100644
index 0000000..6a58852
--- /dev/null
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include "ServiceUtilities.h"
+
+namespace android {
+
+// This optimization assumes mediaserver process doesn't fork, which it doesn't
+const pid_t getpid_cached = getpid();
+
+bool recordingAllowed() {
+    if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+    static const String16 sRecordAudio("android.permission.RECORD_AUDIO");
+    // don't use PermissionCache; this is not a system permission
+    bool ok = checkCallingPermission(sRecordAudio);
+    if (!ok) ALOGE("Request requires android.permission.RECORD_AUDIO");
+    return ok;
+}
+
+bool settingsAllowed() {
+    if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+    static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
+    // don't use PermissionCache; this is not a system permission
+    bool ok = checkCallingPermission(sAudioSettings);
+    if (!ok) ALOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
+    return ok;
+}
+
+bool dumpAllowed() {
+    // don't optimize for same pid, since mediaserver never dumps itself
+    static const String16 sDump("android.permission.DUMP");
+    // OK to use PermissionCache; this is a system permission
+    bool ok = PermissionCache::checkCallingPermission(sDump);
+    // convention is for caller to dump an error message to fd instead of logging here
+    //if (!ok) ALOGE("Request requires android.permission.DUMP");
+    return ok;
+}
+
+} // namespace android
diff --git a/libs/rs/rsFifo.cpp b/services/audioflinger/ServiceUtilities.h
similarity index 70%
copy from libs/rs/rsFifo.cpp
copy to services/audioflinger/ServiceUtilities.h
index 3d5d8c4..f77ec5bc 100644
--- a/libs/rs/rsFifo.cpp
+++ b/services/audioflinger/ServiceUtilities.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012 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.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-#include "rsFifoSocket.h"
-#include "utils/Timers.h"
-#include "utils/StopWatch.h"
+#include <unistd.h>
 
-using namespace android;
-using namespace android::renderscript;
+namespace android {
 
-Fifo::Fifo() {
+extern const pid_t getpid_cached;
+
+bool recordingAllowed();
+bool settingsAllowed();
+bool dumpAllowed();
 
 }
-
-Fifo::~Fifo() {
-
-}
-
diff --git a/services/common_time/Android.mk b/services/common_time/Android.mk
new file mode 100644
index 0000000..e534d49
--- /dev/null
+++ b/services/common_time/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH:= $(call my-dir)
+
+#
+# common_time_service
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    common_clock_service.cpp \
+    common_time_config_service.cpp \
+    common_time_server.cpp \
+    common_time_server_api.cpp \
+    common_time_server_packets.cpp \
+    clock_recovery.cpp \
+    common_clock.cpp \
+    main.cpp
+
+# Uncomment to enable vesbose logging and debug service.
+#TIME_SERVICE_DEBUG=true
+ifeq ($(TIME_SERVICE_DEBUG), true)
+LOCAL_SRC_FILES += diag_thread.cpp
+LOCAL_CFLAGS += -DTIME_SERVICE_DEBUG
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+    libbinder \
+    libcommon_time_client \
+    libutils
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := common_time
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/common_time/clock_recovery.cpp b/services/common_time/clock_recovery.cpp
new file mode 100644
index 0000000..6c98d32
--- /dev/null
+++ b/services/common_time/clock_recovery.cpp
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+
+/*
+ * A service that exchanges time synchronization information between
+ * a master that defines a timeline and clients that follow the timeline.
+ */
+
+#define __STDC_LIMIT_MACROS
+#define LOG_TAG "common_time"
+#include <utils/Log.h>
+#include <stdint.h>
+
+#include <common_time/local_clock.h>
+#include <assert.h>
+
+#include "clock_recovery.h"
+#include "common_clock.h"
+#ifdef TIME_SERVICE_DEBUG
+#include "diag_thread.h"
+#endif
+
+// Define log macro so we can make LOGV into LOGE when we are exclusively
+// debugging this code.
+#ifdef TIME_SERVICE_DEBUG
+#define LOG_TS ALOGE
+#else
+#define LOG_TS ALOGV
+#endif
+
+namespace android {
+
+ClockRecoveryLoop::ClockRecoveryLoop(LocalClock* local_clock,
+                                     CommonClock* common_clock) {
+    assert(NULL != local_clock);
+    assert(NULL != common_clock);
+
+    local_clock_  = local_clock;
+    common_clock_ = common_clock;
+
+    local_clock_can_slew_ = local_clock_->initCheck() &&
+                           (local_clock_->setLocalSlew(0) == OK);
+
+    reset(true, true);
+
+#ifdef TIME_SERVICE_DEBUG
+    diag_thread_ = new DiagThread(common_clock_, local_clock_);
+    if (diag_thread_ != NULL) {
+        status_t res = diag_thread_->startWorkThread();
+        if (res != OK)
+            ALOGW("Failed to start A@H clock recovery diagnostic thread.");
+    } else
+        ALOGW("Failed to allocate diagnostic thread.");
+#endif
+}
+
+ClockRecoveryLoop::~ClockRecoveryLoop() {
+#ifdef TIME_SERVICE_DEBUG
+    diag_thread_->stopWorkThread();
+#endif
+}
+
+// Constants.
+const float ClockRecoveryLoop::dT = 1.0;
+const float ClockRecoveryLoop::Kc = 1.0f;
+const float ClockRecoveryLoop::Ti = 15.0f;
+const float ClockRecoveryLoop::Tf = 0.05;
+const float ClockRecoveryLoop::bias_Fc = 0.01;
+const float ClockRecoveryLoop::bias_RC = (dT / (2 * 3.14159f * bias_Fc));
+const float ClockRecoveryLoop::bias_Alpha = (dT / (bias_RC + dT));
+const int64_t ClockRecoveryLoop::panic_thresh_ = 50000;
+const int64_t ClockRecoveryLoop::control_thresh_ = 10000;
+const float ClockRecoveryLoop::COmin = -100.0f;
+const float ClockRecoveryLoop::COmax = 100.0f;
+
+void ClockRecoveryLoop::reset(bool position, bool frequency) {
+    Mutex::Autolock lock(&lock_);
+    reset_l(position, frequency);
+}
+
+uint32_t ClockRecoveryLoop::findMinRTTNdx(DisciplineDataPoint* data,
+                                          uint32_t count) {
+    uint32_t min_rtt = 0;
+    for (uint32_t i = 1; i < count; ++i)
+        if (data[min_rtt].rtt > data[i].rtt)
+            min_rtt = i;
+
+    return min_rtt;
+}
+
+bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time,
+                                            int64_t nominal_common_time,
+                                            int64_t rtt) {
+    Mutex::Autolock lock(&lock_);
+
+    int64_t local_common_time = 0;
+    common_clock_->localToCommon(local_time, &local_common_time);
+    int64_t raw_delta = nominal_common_time - local_common_time;
+
+#ifdef TIME_SERVICE_DEBUG
+    ALOGE("local=%lld, common=%lld, delta=%lld, rtt=%lld\n",
+         local_common_time, nominal_common_time,
+         raw_delta, rtt);
+#endif
+
+    // If we have not defined a basis for common time, then we need to use these
+    // initial points to do so.  In order to avoid significant initial error
+    // from a particularly bad startup data point, we collect the first N data
+    // points and choose the best of them before moving on.
+    if (!common_clock_->isValid()) {
+        if (startup_filter_wr_ < kStartupFilterSize) {
+            DisciplineDataPoint& d =  startup_filter_data_[startup_filter_wr_];
+            d.local_time = local_time;
+            d.nominal_common_time = nominal_common_time;
+            d.rtt = rtt;
+            startup_filter_wr_++;
+        }
+
+        if (startup_filter_wr_ == kStartupFilterSize) {
+            uint32_t min_rtt = findMinRTTNdx(startup_filter_data_,
+                    kStartupFilterSize);
+
+            common_clock_->setBasis(
+                    startup_filter_data_[min_rtt].local_time,
+                    startup_filter_data_[min_rtt].nominal_common_time);
+        }
+
+        return true;
+    }
+
+    int64_t observed_common;
+    int64_t delta;
+    float delta_f, dCO;
+    int32_t correction_cur;
+
+    if (OK != common_clock_->localToCommon(local_time, &observed_common)) {
+        // Since we just checked to make certain that this conversion was valid,
+        // and no one else in the system should be messing with it, if this
+        // conversion is suddenly invalid, it is a good reason to panic.
+        ALOGE("Failed to convert local time to common time in %s:%d",
+                __PRETTY_FUNCTION__, __LINE__);
+        return false;
+    }
+
+    // Implement a filter which should match NTP filtering behavior when a
+    // client is associated with only one peer of lower stratum.  Basically,
+    // always use the best of the N last data points, where best is defined as
+    // lowest round trip time.  NTP uses an N of 8; we use a value of 6.
+    //
+    // TODO(johngro) : experiment with other filter strategies.  The goal here
+    // is to mitigate the effects of high RTT data points which typically have
+    // large asymmetries in the TX/RX legs.  Downside of the existing NTP
+    // approach (particularly because of the PID controller we are using to
+    // produce the control signal from the filtered data) are that the rate at
+    // which discipline events are actually acted upon becomes irregular and can
+    // become drawn out (the time between actionable event can go way up).  If
+    // the system receives a strong high quality data point, the proportional
+    // component of the controller can produce a strong correction which is left
+    // in place for too long causing overshoot.  In addition, the integral
+    // component of the system currently is an approximation based on the
+    // assumption of a more or less homogeneous sampling of the error.  Its
+    // unclear what the effect of undermining this assumption would be right
+    // now.
+
+    // Two ideas which come to mind immediately would be to...
+    // 1) Keep a history of more data points (32 or so) and ignore data points
+    //    whose RTT is more than a certain number of standard deviations outside
+    //    of the norm.
+    // 2) Eliminate the PID controller portion of this system entirely.
+    //    Instead, move to a system which uses a very wide filter (128 data
+    //    points or more) with a sum-of-least-squares line fitting approach to
+    //    tracking the long term drift.  This would take the place of the I
+    //    component in the current PID controller.  Also use a much more narrow
+    //    outlier-rejector filter (as described in #1) to drive a short term
+    //    correction factor similar to the P component of the PID controller.
+    assert(filter_wr_ < kFilterSize);
+    filter_data_[filter_wr_].local_time           = local_time;
+    filter_data_[filter_wr_].observed_common_time = observed_common;
+    filter_data_[filter_wr_].nominal_common_time  = nominal_common_time;
+    filter_data_[filter_wr_].rtt                  = rtt;
+    filter_data_[filter_wr_].point_used           = false;
+    uint32_t current_point = filter_wr_;
+    filter_wr_ = (filter_wr_ + 1) % kFilterSize;
+    if (!filter_wr_)
+        filter_full_ = true;
+
+    uint32_t scan_end = filter_full_ ? kFilterSize : filter_wr_;
+    uint32_t min_rtt = findMinRTTNdx(filter_data_, scan_end);
+    // We only use packets with low RTTs for control. If the packet RTT
+    // is less than the panic threshold, we can probably eat the jitter with the
+    // control loop. Otherwise, take the packet only if it better than all
+    // of the packets we have in the history. That way we try to track
+    // something, even if it is noisy.
+    if (current_point == min_rtt || rtt < control_thresh_) {
+        delta_f = delta = nominal_common_time - observed_common;
+
+        // Compute the error then clamp to the panic threshold.  If we ever
+        // exceed this amt of error, its time to panic and reset the system.
+        // Given that the error in the measurement of the error could be as
+        // high as the RTT of the data point, we don't actually panic until
+        // the implied error (delta) is greater than the absolute panic
+        // threashold plus the RTT.  IOW - we don't panic until we are
+        // absoluely sure that our best case sync is worse than the absolute
+        // panic threshold.
+        int64_t effective_panic_thresh = panic_thresh_ + rtt;
+        if ((delta > effective_panic_thresh) ||
+            (delta < -effective_panic_thresh)) {
+            // PANIC!!!
+            reset_l(false, true);
+            return false;
+        }
+
+    } else {
+        // We do not have a good packet to look at, but we also do not want to
+        // free-run the clock at some crazy slew rate. So we guess the
+        // trajectory of the clock based on the last controller output and the
+        // estimated bias of our clock against the master.
+        // The net effect of this is that CO == CObias after some extended
+        // period of no feedback.
+        delta_f = last_delta_f_ - dT*(CO - CObias);
+        delta = delta_f;
+    }
+
+    // Velocity form PI control equation.
+    dCO = Kc * (1.0f + dT/Ti) * delta_f - Kc * last_delta_f_;
+    CO += dCO * Tf; // Filter CO by applying gain <1 here.
+
+    // Save error terms for later.
+    last_delta_f_ = delta_f;
+    last_delta_ = delta;
+
+    // Clamp CO to +/- 100ppm.
+    if (CO < COmin)
+        CO = COmin;
+    else if (CO > COmax)
+        CO = COmax;
+
+    // Update the controller bias.
+    CObias = bias_Alpha * CO + (1.0f - bias_Alpha) * lastCObias;
+    lastCObias = CObias;
+
+    // Convert PPM to 16-bit int range. Add some guard band (-0.01) so we
+    // don't get fp weirdness.
+    correction_cur = CO * 327.66;
+
+    // If there was a change in the amt of correction to use, update the
+    // system.
+    if (correction_cur_ != correction_cur) {
+        correction_cur_ = correction_cur;
+        applySlew();
+    }
+
+    LOG_TS("clock_loop %lld %f %f %f %d\n", raw_delta, delta_f, CO, CObias, correction_cur);
+
+#ifdef TIME_SERVICE_DEBUG
+    diag_thread_->pushDisciplineEvent(
+            local_time,
+            observed_common,
+            nominal_common_time,
+            correction_cur,
+            rtt);
+#endif
+
+    return true;
+}
+
+int32_t ClockRecoveryLoop::getLastErrorEstimate() {
+    Mutex::Autolock lock(&lock_);
+
+    if (last_delta_valid_)
+        return last_delta_;
+    else
+        return ICommonClock::kErrorEstimateUnknown;
+}
+
+void ClockRecoveryLoop::reset_l(bool position, bool frequency) {
+    assert(NULL != common_clock_);
+
+    if (position) {
+        common_clock_->resetBasis();
+        startup_filter_wr_ = 0;
+    }
+
+    if (frequency) {
+        last_delta_valid_ = false;
+        last_delta_ = 0;
+        last_delta_f_ = 0.0;
+        correction_cur_ = 0x0;
+        CO = 0.0f;
+        lastCObias = CObias = 0.0f;
+        applySlew();
+    }
+
+    filter_wr_   = 0;
+    filter_full_ = false;
+}
+
+void ClockRecoveryLoop::applySlew() {
+    if (local_clock_can_slew_) {
+        local_clock_->setLocalSlew(correction_cur_);
+    } else {
+        // The SW clock recovery implemented by the common clock class expects
+        // values expressed in PPM. CO is in ppm.
+        common_clock_->setSlew(local_clock_->getLocalTime(), CO);
+    }
+}
+
+}  // namespace android
diff --git a/services/common_time/clock_recovery.h b/services/common_time/clock_recovery.h
new file mode 100644
index 0000000..b7362be
--- /dev/null
+++ b/services/common_time/clock_recovery.h
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#ifndef __CLOCK_RECOVERY_H__
+#define __CLOCK_RECOVERY_H__
+
+#include <stdint.h>
+#include <common_time/ICommonClock.h>
+#include <utils/LinearTransform.h>
+#include <utils/threads.h>
+
+#ifdef TIME_SERVICE_DEBUG
+#include "diag_thread.h"
+#endif
+
+namespace android {
+
+class CommonClock;
+class LocalClock;
+
+class ClockRecoveryLoop {
+  public:
+     ClockRecoveryLoop(LocalClock* local_clock, CommonClock* common_clock);
+    ~ClockRecoveryLoop();
+
+    void reset(bool position, bool frequency);
+    bool pushDisciplineEvent(int64_t local_time,
+                             int64_t nominal_common_time,
+                             int64_t data_point_rtt);
+    int32_t getLastErrorEstimate();
+
+  private:
+
+    // Tuned using the "Good Gain" method.
+    // See:
+    // http://techteach.no/publications/books/dynamics_and_control/tuning_pid_controller.pdf
+
+    // Controller period (1Hz for now).
+    static const float dT;
+
+    // Controller gain, positive and unitless. Larger values converge faster,
+    // but can cause instability.
+    static const float Kc;
+
+    // Integral reset time. Smaller values cause loop to track faster, but can
+    // also cause instability.
+    static const float Ti;
+
+    // Controller output filter time constant. Range (0-1). Smaller values make
+    // output smoother, but slow convergence.
+    static const float Tf;
+
+    // Low-pass filter for bias tracker.
+    static const float bias_Fc; // HZ
+    static const float bias_RC; // Computed in constructor.
+    static const float bias_Alpha; // Computed inconstructor.
+
+    // The maximum allowed error (as indicated by a  pushDisciplineEvent) before
+    // we panic.
+    static const int64_t panic_thresh_;
+
+    // The maximum allowed error rtt time for packets to be used for control
+    // feedback, unless the packet is the best in recent memory.
+    static const int64_t control_thresh_;
+
+    typedef struct {
+        int64_t local_time;
+        int64_t observed_common_time;
+        int64_t nominal_common_time;
+        int64_t rtt;
+        bool point_used;
+    } DisciplineDataPoint;
+
+    static uint32_t findMinRTTNdx(DisciplineDataPoint* data, uint32_t count);
+
+    void reset_l(bool position, bool frequency);
+    void applySlew();
+
+    // The local clock HW abstraction we use as the basis for common time.
+    LocalClock* local_clock_;
+    bool local_clock_can_slew_;
+
+    // The common clock we end up controlling along with the lock used to
+    // serialize operations.
+    CommonClock* common_clock_;
+    Mutex lock_;
+
+    // parameters maintained while running and reset during a reset
+    // of the frequency correction.
+    bool    last_delta_valid_;
+    int32_t last_delta_;
+    float last_delta_f_;
+    int32_t integrated_error_;
+    int32_t correction_cur_;
+
+    // Contoller Output.
+    float CO;
+
+    // Bias tracking for trajectory estimation.
+    float CObias;
+    float lastCObias;
+
+    // Controller output bounds. The controller will not try to
+    // slew faster that +/-100ppm offset from center per interation.
+    static const float COmin;
+    static const float COmax;
+
+    // State kept for filtering the discipline data.
+    static const uint32_t kFilterSize = 16;
+    DisciplineDataPoint filter_data_[kFilterSize];
+    uint32_t filter_wr_;
+    bool filter_full_;
+
+    static const uint32_t kStartupFilterSize = 4;
+    DisciplineDataPoint startup_filter_data_[kStartupFilterSize];
+    uint32_t startup_filter_wr_;
+
+#ifdef TIME_SERVICE_DEBUG
+    sp<DiagThread> diag_thread_;
+#endif
+};
+
+}  // namespace android
+
+#endif  // __CLOCK_RECOVERY_H__
diff --git a/services/common_time/common_clock.cpp b/services/common_time/common_clock.cpp
new file mode 100644
index 0000000..c9eb388
--- /dev/null
+++ b/services/common_time/common_clock.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define __STDC_LIMIT_MACROS
+
+#define LOG_TAG "common_time"
+#include <utils/Log.h>
+
+#include <stdint.h>
+
+#include <utils/Errors.h>
+#include <utils/LinearTransform.h>
+
+#include "common_clock.h"
+
+namespace android {
+
+CommonClock::CommonClock() {
+    cur_slew_        = 0;
+    cur_trans_valid_ = false;
+
+    cur_trans_.a_zero = 0;
+    cur_trans_.b_zero = 0;
+    cur_trans_.a_to_b_numer = local_to_common_freq_numer_ = 1;
+    cur_trans_.a_to_b_denom = local_to_common_freq_denom_ = 1;
+    duration_trans_ = cur_trans_;
+}
+
+bool CommonClock::init(uint64_t local_freq) {
+    Mutex::Autolock lock(&lock_);
+
+    if (!local_freq)
+        return false;
+
+    uint64_t numer = kCommonFreq;
+    uint64_t denom = local_freq;
+
+    LinearTransform::reduce(&numer, &denom);
+    if ((numer > UINT32_MAX) || (denom > UINT32_MAX)) {
+        ALOGE("Overflow in CommonClock::init while trying to reduce %lld/%lld",
+             kCommonFreq, local_freq);
+        return false;
+    }
+
+    cur_trans_.a_to_b_numer = local_to_common_freq_numer_ =
+        static_cast<uint32_t>(numer);
+    cur_trans_.a_to_b_denom = local_to_common_freq_denom_ =
+        static_cast<uint32_t>(denom);
+    duration_trans_ = cur_trans_;
+
+    return true;
+}
+
+status_t CommonClock::localToCommon(int64_t local, int64_t *common_out) const {
+    Mutex::Autolock lock(&lock_);
+
+    if (!cur_trans_valid_)
+        return INVALID_OPERATION;
+
+    if (!cur_trans_.doForwardTransform(local, common_out))
+        return INVALID_OPERATION;
+
+    return OK;
+}
+
+status_t CommonClock::commonToLocal(int64_t common, int64_t *local_out) const {
+    Mutex::Autolock lock(&lock_);
+
+    if (!cur_trans_valid_)
+        return INVALID_OPERATION;
+
+    if (!cur_trans_.doReverseTransform(common, local_out))
+        return INVALID_OPERATION;
+
+    return OK;
+}
+
+int64_t CommonClock::localDurationToCommonDuration(int64_t localDur) const {
+    int64_t ret;
+    duration_trans_.doForwardTransform(localDur, &ret);
+    return ret;
+}
+
+void CommonClock::setBasis(int64_t local, int64_t common) {
+    Mutex::Autolock lock(&lock_);
+
+    cur_trans_.a_zero = local;
+    cur_trans_.b_zero = common;
+    cur_trans_valid_ = true;
+}
+
+void CommonClock::resetBasis() {
+    Mutex::Autolock lock(&lock_);
+
+    cur_trans_.a_zero = 0;
+    cur_trans_.b_zero = 0;
+    cur_trans_valid_ = false;
+}
+
+status_t CommonClock::setSlew(int64_t change_time, int32_t ppm) {
+    Mutex::Autolock lock(&lock_);
+
+    int64_t new_local_basis;
+    int64_t new_common_basis;
+
+    if (cur_trans_valid_) {
+        new_local_basis = change_time;
+        if (!cur_trans_.doForwardTransform(change_time, &new_common_basis)) {
+            ALOGE("Overflow when attempting to set slew rate to %d", ppm);
+            return INVALID_OPERATION;
+        }
+    } else {
+        new_local_basis = 0;
+        new_common_basis = 0;
+    }
+
+    cur_slew_ = ppm;
+    uint32_t n1 = local_to_common_freq_numer_;
+    uint32_t n2 = 1000000 + cur_slew_;
+
+    uint32_t d1 = local_to_common_freq_denom_;
+    uint32_t d2 = 1000000;
+
+    // n1/d1 has already been reduced, no need to do so here.
+    LinearTransform::reduce(&n1, &d2);
+    LinearTransform::reduce(&n2, &d1);
+    LinearTransform::reduce(&n2, &d2);
+
+    cur_trans_.a_zero = new_local_basis;
+    cur_trans_.b_zero = new_common_basis;
+    cur_trans_.a_to_b_numer = n1 * n2;
+    cur_trans_.a_to_b_denom = d1 * d2;
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/services/common_time/common_clock.h b/services/common_time/common_clock.h
new file mode 100644
index 0000000..b786fdc
--- /dev/null
+++ b/services/common_time/common_clock.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef __COMMON_CLOCK_H__
+#define __COMMON_CLOCK_H__
+
+#include <stdint.h>
+
+#include <utils/Errors.h>
+#include <utils/LinearTransform.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class CommonClock {
+  public:
+    CommonClock();
+
+    bool      init(uint64_t local_freq);
+
+    status_t  localToCommon(int64_t local, int64_t *common_out) const;
+    status_t  commonToLocal(int64_t common, int64_t *local_out) const;
+    int64_t   localDurationToCommonDuration(int64_t localDur) const;
+    uint64_t  getCommonFreq() const { return kCommonFreq; }
+    bool      isValid() const { return cur_trans_valid_; }
+    status_t  setSlew(int64_t change_time, int32_t ppm);
+    void      setBasis(int64_t local, int64_t common);
+    void      resetBasis();
+  private:
+    mutable Mutex lock_;
+
+    int32_t  cur_slew_;
+    uint32_t local_to_common_freq_numer_;
+    uint32_t local_to_common_freq_denom_;
+
+    LinearTransform duration_trans_;
+    LinearTransform cur_trans_;
+    bool cur_trans_valid_;
+
+    static const uint64_t kCommonFreq = 1000000ull;
+};
+
+}  // namespace android
+#endif  // __COMMON_CLOCK_H__
diff --git a/services/common_time/common_clock_service.cpp b/services/common_time/common_clock_service.cpp
new file mode 100644
index 0000000..9ca6f35
--- /dev/null
+++ b/services/common_time/common_clock_service.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+#include <common_time/local_clock.h>
+#include <utils/String8.h>
+
+#include "common_clock_service.h"
+#include "common_clock.h"
+#include "common_time_server.h"
+
+namespace android {
+
+sp<CommonClockService> CommonClockService::instantiate(
+        CommonTimeServer& timeServer) {
+    sp<CommonClockService> tcc = new CommonClockService(timeServer);
+    if (tcc == NULL)
+        return NULL;
+
+    defaultServiceManager()->addService(ICommonClock::kServiceName, tcc);
+    return tcc;
+}
+
+status_t CommonClockService::dump(int fd, const Vector<String16>& args) {
+    Mutex::Autolock lock(mRegistrationLock);
+    return mTimeServer.dumpClockInterface(fd, args, mListeners.size());
+}
+
+status_t CommonClockService::isCommonTimeValid(bool* valid,
+                                               uint32_t* timelineID) {
+    return mTimeServer.isCommonTimeValid(valid, timelineID);
+}
+
+status_t CommonClockService::commonTimeToLocalTime(int64_t  commonTime,
+                                                   int64_t* localTime) {
+    return mTimeServer.getCommonClock().commonToLocal(commonTime, localTime);
+}
+
+status_t CommonClockService::localTimeToCommonTime(int64_t  localTime,
+                                                   int64_t* commonTime) {
+    return mTimeServer.getCommonClock().localToCommon(localTime, commonTime);
+}
+
+status_t CommonClockService::getCommonTime(int64_t* commonTime) {
+    return localTimeToCommonTime(mTimeServer.getLocalClock().getLocalTime(), commonTime);
+}
+
+status_t CommonClockService::getCommonFreq(uint64_t* freq) {
+    *freq = mTimeServer.getCommonClock().getCommonFreq();
+    return OK;
+}
+
+status_t CommonClockService::getLocalTime(int64_t* localTime) {
+    *localTime = mTimeServer.getLocalClock().getLocalTime();
+    return OK;
+}
+
+status_t CommonClockService::getLocalFreq(uint64_t* freq) {
+    *freq = mTimeServer.getLocalClock().getLocalFreq();
+    return OK;
+}
+
+status_t CommonClockService::getEstimatedError(int32_t* estimate) {
+    *estimate = mTimeServer.getEstimatedError();
+    return OK;
+}
+
+status_t CommonClockService::getTimelineID(uint64_t* id) {
+    *id = mTimeServer.getTimelineID();
+    return OK;
+}
+
+status_t CommonClockService::getState(State* state) {
+    *state = mTimeServer.getState();
+    return OK;
+}
+
+status_t CommonClockService::getMasterAddr(struct sockaddr_storage* addr) {
+    return mTimeServer.getMasterAddr(addr);
+}
+
+status_t CommonClockService::registerListener(
+        const sp<ICommonClockListener>& listener) {
+    Mutex::Autolock lock(mRegistrationLock);
+
+    {   // scoping for autolock pattern
+        Mutex::Autolock lock(mCallbackLock);
+        // check whether this is a duplicate
+        for (size_t i = 0; i < mListeners.size(); i++) {
+            if (mListeners[i]->asBinder() == listener->asBinder())
+                return ALREADY_EXISTS;
+        }
+    }
+
+    mListeners.add(listener);
+    mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
+    return listener->asBinder()->linkToDeath(this);
+}
+
+status_t CommonClockService::unregisterListener(
+        const sp<ICommonClockListener>& listener) {
+    Mutex::Autolock lock(mRegistrationLock);
+    status_t ret_val = NAME_NOT_FOUND;
+
+    {   // scoping for autolock pattern
+        Mutex::Autolock lock(mCallbackLock);
+        for (size_t i = 0; i < mListeners.size(); i++) {
+            if (mListeners[i]->asBinder() == listener->asBinder()) {
+                mListeners[i]->asBinder()->unlinkToDeath(this);
+                mListeners.removeAt(i);
+                ret_val = OK;
+                break;
+            }
+        }
+    }
+
+    mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
+    return ret_val;
+}
+
+void CommonClockService::binderDied(const wp<IBinder>& who) {
+    Mutex::Autolock lock(mRegistrationLock);
+
+    {   // scoping for autolock pattern
+        Mutex::Autolock lock(mCallbackLock);
+        for (size_t i = 0; i < mListeners.size(); i++) {
+            if (mListeners[i]->asBinder() == who) {
+                mListeners.removeAt(i);
+                break;
+            }
+        }
+    }
+
+    mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
+}
+
+void CommonClockService::notifyOnTimelineChanged(uint64_t timelineID) {
+    Mutex::Autolock lock(mCallbackLock);
+
+    for (size_t i = 0; i < mListeners.size(); i++) {
+        mListeners[i]->onTimelineChanged(timelineID);
+    }
+}
+
+}; // namespace android
diff --git a/services/common_time/common_clock_service.h b/services/common_time/common_clock_service.h
new file mode 100644
index 0000000..a65e398
--- /dev/null
+++ b/services/common_time/common_clock_service.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#include <common_time/ICommonClock.h>
+
+#ifndef ANDROID_COMMON_CLOCK_SERVICE_H
+#define ANDROID_COMMON_CLOCK_SERVICE_H
+
+namespace android {
+
+class CommonTimeServer;
+
+class CommonClockService : public BnCommonClock,
+                           public android::IBinder::DeathRecipient {
+  public:
+    static sp<CommonClockService> instantiate(CommonTimeServer& timeServer);
+
+    virtual status_t dump(int fd, const Vector<String16>& args);
+
+    virtual status_t isCommonTimeValid(bool* valid, uint32_t *timelineID);
+    virtual status_t commonTimeToLocalTime(int64_t  common_time,
+                                           int64_t* local_time);
+    virtual status_t localTimeToCommonTime(int64_t  local_time,
+                                           int64_t* common_time);
+    virtual status_t getCommonTime(int64_t* common_time);
+    virtual status_t getCommonFreq(uint64_t* freq);
+    virtual status_t getLocalTime(int64_t* local_time);
+    virtual status_t getLocalFreq(uint64_t* freq);
+    virtual status_t getEstimatedError(int32_t* estimate);
+    virtual status_t getTimelineID(uint64_t* id);
+    virtual status_t getState(ICommonClock::State* state);
+    virtual status_t getMasterAddr(struct sockaddr_storage* addr);
+
+    virtual status_t registerListener(
+            const sp<ICommonClockListener>& listener);
+    virtual status_t unregisterListener(
+            const sp<ICommonClockListener>& listener);
+
+    void notifyOnTimelineChanged(uint64_t timelineID);
+
+  private:
+    CommonClockService(CommonTimeServer& timeServer)
+        : mTimeServer(timeServer) { };
+
+    virtual void binderDied(const wp<IBinder>& who);
+
+    CommonTimeServer& mTimeServer;
+
+    // locks used to synchronize access to the list of registered listeners.
+    // The callback lock is held whenever the list is used to perform callbacks
+    // or while the list is being modified.  The registration lock used to
+    // serialize access across registerListener, unregisterListener, and
+    // binderDied.
+    //
+    // The reason for two locks is that registerListener, unregisterListener,
+    // and binderDied each call into the core service and obtain the core
+    // service thread lock when they call reevaluateAutoDisableState.  The core
+    // service thread obtains the main thread lock whenever its thread is
+    // running, and sometimes needs to call notifyOnTimelineChanged which then
+    // obtains the callback lock.  If callers of registration functions were
+    // holding the callback lock when they called into the core service, we
+    // would have a classic A/B, B/A ordering deadlock.  To avoid this, the
+    // registration functions hold the registration lock for the duration of
+    // their call, but hold the callback lock only while they mutate the list.
+    // This way, the list's size cannot change (because of the registration
+    // lock) during the call into reevaluateAutoDisableState, but the core work
+    // thread can still safely call notifyOnTimelineChanged while holding the
+    // main thread lock.
+    Mutex mCallbackLock;
+    Mutex mRegistrationLock;
+
+    Vector<sp<ICommonClockListener> > mListeners;
+};
+
+};  // namespace android
+
+#endif  // ANDROID_COMMON_CLOCK_SERVICE_H
diff --git a/services/common_time/common_time_config_service.cpp b/services/common_time/common_time_config_service.cpp
new file mode 100644
index 0000000..9585618
--- /dev/null
+++ b/services/common_time/common_time_config_service.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <utils/String8.h>
+
+#include "common_time_config_service.h"
+#include "common_time_server.h"
+
+namespace android {
+
+sp<CommonTimeConfigService> CommonTimeConfigService::instantiate(
+        CommonTimeServer& timeServer) {
+    sp<CommonTimeConfigService> ctcs = new CommonTimeConfigService(timeServer);
+    if (ctcs == NULL)
+        return NULL;
+
+    defaultServiceManager()->addService(ICommonTimeConfig::kServiceName, ctcs);
+    return ctcs;
+}
+
+status_t CommonTimeConfigService::dump(int fd, const Vector<String16>& args) {
+    return mTimeServer.dumpConfigInterface(fd, args);
+}
+
+status_t CommonTimeConfigService::getMasterElectionPriority(uint8_t *priority) {
+    return mTimeServer.getMasterElectionPriority(priority);
+}
+
+status_t CommonTimeConfigService::setMasterElectionPriority(uint8_t priority) {
+    return mTimeServer.setMasterElectionPriority(priority);
+}
+
+status_t CommonTimeConfigService::getMasterElectionEndpoint(
+        struct sockaddr_storage *addr) {
+    return mTimeServer.getMasterElectionEndpoint(addr);
+}
+
+status_t CommonTimeConfigService::setMasterElectionEndpoint(
+        const struct sockaddr_storage *addr) {
+    return mTimeServer.setMasterElectionEndpoint(addr);
+}
+
+status_t CommonTimeConfigService::getMasterElectionGroupId(uint64_t *id) {
+    return mTimeServer.getMasterElectionGroupId(id);
+}
+
+status_t CommonTimeConfigService::setMasterElectionGroupId(uint64_t id) {
+    return mTimeServer.setMasterElectionGroupId(id);
+}
+
+status_t CommonTimeConfigService::getInterfaceBinding(String16& ifaceName) {
+    String8 tmp;
+    status_t ret = mTimeServer.getInterfaceBinding(tmp);
+    ifaceName = String16(tmp);
+    return ret;
+}
+
+status_t CommonTimeConfigService::setInterfaceBinding(const String16& ifaceName) {
+    String8 tmp(ifaceName);
+    return mTimeServer.setInterfaceBinding(tmp);
+}
+
+status_t CommonTimeConfigService::getMasterAnnounceInterval(int *interval) {
+    return mTimeServer.getMasterAnnounceInterval(interval);
+}
+
+status_t CommonTimeConfigService::setMasterAnnounceInterval(int interval) {
+    return mTimeServer.setMasterAnnounceInterval(interval);
+}
+
+status_t CommonTimeConfigService::getClientSyncInterval(int *interval) {
+    return mTimeServer.getClientSyncInterval(interval);
+}
+
+status_t CommonTimeConfigService::setClientSyncInterval(int interval) {
+    return mTimeServer.setClientSyncInterval(interval);
+}
+
+status_t CommonTimeConfigService::getPanicThreshold(int *threshold) {
+    return mTimeServer.getPanicThreshold(threshold);
+}
+
+status_t CommonTimeConfigService::setPanicThreshold(int threshold) {
+    return mTimeServer.setPanicThreshold(threshold);
+}
+
+status_t CommonTimeConfigService::getAutoDisable(bool *autoDisable) {
+    return mTimeServer.getAutoDisable(autoDisable);
+}
+
+status_t CommonTimeConfigService::setAutoDisable(bool autoDisable) {
+    return mTimeServer.setAutoDisable(autoDisable);
+}
+
+status_t CommonTimeConfigService::forceNetworklessMasterMode() {
+    return mTimeServer.forceNetworklessMasterMode();
+}
+
+}; // namespace android
diff --git a/services/common_time/common_time_config_service.h b/services/common_time/common_time_config_service.h
new file mode 100644
index 0000000..8283c24
--- /dev/null
+++ b/services/common_time/common_time_config_service.h
@@ -0,0 +1,59 @@
+/* * Copyright (C) 2012 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.
+ */
+
+#include <common_time/ICommonTimeConfig.h>
+
+#ifndef ANDROID_COMMON_TIME_CONFIG_SERVICE_H
+#define ANDROID_COMMON_TIME_CONFIG_SERVICE_H
+
+namespace android {
+
+class String16;
+class CommonTimeServer;
+
+class CommonTimeConfigService : public BnCommonTimeConfig {
+  public:
+    static sp<CommonTimeConfigService> instantiate(CommonTimeServer& timeServer);
+
+    virtual status_t dump(int fd, const Vector<String16>& args);
+
+    virtual status_t getMasterElectionPriority(uint8_t *priority);
+    virtual status_t setMasterElectionPriority(uint8_t priority);
+    virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr);
+    virtual status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr);
+    virtual status_t getMasterElectionGroupId(uint64_t *id);
+    virtual status_t setMasterElectionGroupId(uint64_t id);
+    virtual status_t getInterfaceBinding(String16& ifaceName);
+    virtual status_t setInterfaceBinding(const String16& ifaceName);
+    virtual status_t getMasterAnnounceInterval(int *interval);
+    virtual status_t setMasterAnnounceInterval(int interval);
+    virtual status_t getClientSyncInterval(int *interval);
+    virtual status_t setClientSyncInterval(int interval);
+    virtual status_t getPanicThreshold(int *threshold);
+    virtual status_t setPanicThreshold(int threshold);
+    virtual status_t getAutoDisable(bool *autoDisable);
+    virtual status_t setAutoDisable(bool autoDisable);
+    virtual status_t forceNetworklessMasterMode();
+
+  private:
+    CommonTimeConfigService(CommonTimeServer& timeServer)
+        : mTimeServer(timeServer) { }
+    CommonTimeServer& mTimeServer;
+
+};
+
+};  // namespace android
+
+#endif  // ANDROID_COMMON_TIME_CONFIG_SERVICE_H
diff --git a/services/common_time/common_time_server.cpp b/services/common_time/common_time_server.cpp
new file mode 100644
index 0000000..ef7fa168
--- /dev/null
+++ b/services/common_time/common_time_server.cpp
@@ -0,0 +1,1380 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*
+ * A service that exchanges time synchronization information between
+ * a master that defines a timeline and clients that follow the timeline.
+ */
+
+#define LOG_TAG "common_time"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <linux/if_ether.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/ip.h>
+#include <poll.h>
+#include <stdio.h>
+#include <sys/eventfd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <common_time/local_clock.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <utils/Timers.h>
+
+#include "common_clock_service.h"
+#include "common_time_config_service.h"
+#include "common_time_server.h"
+#include "common_time_server_packets.h"
+#include "clock_recovery.h"
+#include "common_clock.h"
+
+#define MAX_INT ((int)0x7FFFFFFF)
+
+namespace android {
+
+const char*    CommonTimeServer::kDefaultMasterElectionAddr = "239.195.128.88";
+const uint16_t CommonTimeServer::kDefaultMasterElectionPort = 8887;
+const uint64_t CommonTimeServer::kDefaultSyncGroupID = 0;
+const uint8_t  CommonTimeServer::kDefaultMasterPriority = 1;
+const uint32_t CommonTimeServer::kDefaultMasterAnnounceIntervalMs = 10000;
+const uint32_t CommonTimeServer::kDefaultSyncRequestIntervalMs = 1000;
+const uint32_t CommonTimeServer::kDefaultPanicThresholdUsec = 50000;
+const bool     CommonTimeServer::kDefaultAutoDisable = true;
+const int      CommonTimeServer::kSetupRetryTimeoutMs = 30000;
+const int64_t  CommonTimeServer::kNoGoodDataPanicThresholdUsec = 600000000ll;
+const uint32_t CommonTimeServer::kRTTDiscardPanicThreshMultiplier = 5;
+
+// timeout value representing an infinite timeout
+const int CommonTimeServer::kInfiniteTimeout = -1;
+
+/*** Initial state constants ***/
+
+// number of WhoIsMaster attempts sent before giving up
+const int CommonTimeServer::kInitial_NumWhoIsMasterRetries = 6;
+
+// timeout used when waiting for a response to a WhoIsMaster request
+const int CommonTimeServer::kInitial_WhoIsMasterTimeoutMs = 500;
+
+/*** Client state constants ***/
+
+// number of sync requests that can fail before a client assumes its master
+// is dead
+const int CommonTimeServer::kClient_NumSyncRequestRetries = 5;
+
+/*** Master state constants ***/
+
+/*** Ronin state constants ***/
+
+// number of WhoIsMaster attempts sent before declaring ourselves master
+const int CommonTimeServer::kRonin_NumWhoIsMasterRetries = 4;
+
+// timeout used when waiting for a response to a WhoIsMaster request
+const int CommonTimeServer::kRonin_WhoIsMasterTimeoutMs = 500;
+
+/*** WaitForElection state constants ***/
+
+// how long do we wait for an announcement from a master before
+// trying another election?
+const int CommonTimeServer::kWaitForElection_TimeoutMs = 5000;
+
+CommonTimeServer::CommonTimeServer()
+    : Thread(false)
+    , mState(ICommonClock::STATE_INITIAL)
+    , mClockRecovery(&mLocalClock, &mCommonClock)
+    , mSocket(-1)
+    , mLastPacketRxLocalTime(0)
+    , mTimelineID(ICommonClock::kInvalidTimelineID)
+    , mClockSynced(false)
+    , mCommonClockHasClients(false)
+    , mInitial_WhoIsMasterRequestTimeouts(0)
+    , mClient_MasterDeviceID(0)
+    , mClient_MasterDevicePriority(0)
+    , mRonin_WhoIsMasterRequestTimeouts(0) {
+    // zero out sync stats
+    resetSyncStats();
+
+    // Setup the master election endpoint to use the default.
+    struct sockaddr_in* meep =
+        reinterpret_cast<struct sockaddr_in*>(&mMasterElectionEP);
+    memset(&mMasterElectionEP, 0, sizeof(mMasterElectionEP));
+    inet_aton(kDefaultMasterElectionAddr, &meep->sin_addr);
+    meep->sin_family = AF_INET;
+    meep->sin_port   = htons(kDefaultMasterElectionPort);
+
+    // Zero out the master endpoint.
+    memset(&mMasterEP, 0, sizeof(mMasterEP));
+    mMasterEPValid    = false;
+    mBindIfaceValid   = false;
+    setForceLowPriority(false);
+
+    // Set all remaining configuration parameters to their defaults.
+    mDeviceID                 = 0;
+    mSyncGroupID              = kDefaultSyncGroupID;
+    mMasterPriority           = kDefaultMasterPriority;
+    mMasterAnnounceIntervalMs = kDefaultMasterAnnounceIntervalMs;
+    mSyncRequestIntervalMs    = kDefaultSyncRequestIntervalMs;
+    mPanicThresholdUsec       = kDefaultPanicThresholdUsec;
+    mAutoDisable              = kDefaultAutoDisable;
+
+    // Create the eventfd we will use to signal our thread to wake up when
+    // needed.
+    mWakeupThreadFD = eventfd(0, EFD_NONBLOCK);
+
+    // seed the random number generator (used to generated timeline IDs)
+    srand48(static_cast<unsigned int>(systemTime()));
+}
+
+CommonTimeServer::~CommonTimeServer() {
+    shutdownThread();
+
+    // No need to grab the lock here.  We are in the destructor; if the the user
+    // has a thread in any of the APIs while the destructor is being called,
+    // there is a threading problem a the application level we cannot reasonably
+    // do anything about.
+    cleanupSocket_l();
+
+    if (mWakeupThreadFD >= 0) {
+        close(mWakeupThreadFD);
+        mWakeupThreadFD = -1;
+    }
+}
+
+bool CommonTimeServer::startServices() {
+    // start the ICommonClock service
+    mICommonClock = CommonClockService::instantiate(*this);
+    if (mICommonClock == NULL)
+        return false;
+
+    // start the ICommonTimeConfig service
+    mICommonTimeConfig = CommonTimeConfigService::instantiate(*this);
+    if (mICommonTimeConfig == NULL)
+        return false;
+
+    return true;
+}
+
+bool CommonTimeServer::threadLoop() {
+    // Register our service interfaces.
+    if (!startServices())
+        return false;
+
+    // Hold the lock while we are in the main thread loop.  It will release the
+    // lock when it blocks, and hold the lock at all other times.
+    mLock.lock();
+    runStateMachine_l();
+    mLock.unlock();
+
+    IPCThreadState::self()->stopProcess();
+    return false;
+}
+
+bool CommonTimeServer::runStateMachine_l() {
+    if (!mLocalClock.initCheck())
+        return false;
+
+    if (!mCommonClock.init(mLocalClock.getLocalFreq()))
+        return false;
+
+    // Enter the initial state.
+    becomeInitial("startup");
+
+    // run the state machine
+    while (!exitPending()) {
+        struct pollfd pfds[2];
+        int rc;
+        int eventCnt = 0;
+        int64_t wakeupTime;
+
+        // We are always interested in our wakeup FD.
+        pfds[eventCnt].fd      = mWakeupThreadFD;
+        pfds[eventCnt].events  = POLLIN;
+        pfds[eventCnt].revents = 0;
+        eventCnt++;
+
+        // If we have a valid socket, then we are interested in what it has to
+        // say as well.
+        if (mSocket >= 0) {
+            pfds[eventCnt].fd      = mSocket;
+            pfds[eventCnt].events  = POLLIN;
+            pfds[eventCnt].revents = 0;
+            eventCnt++;
+        }
+
+        // Note, we were holding mLock when this function was called.  We
+        // release it only while we are blocking and hold it at all other times.
+        mLock.unlock();
+        rc          = poll(pfds, eventCnt, mCurTimeout.msecTillTimeout());
+        wakeupTime  = mLocalClock.getLocalTime();
+        mLock.lock();
+
+        // Is it time to shutdown?  If so, don't hesitate... just do it.
+        if (exitPending())
+            break;
+
+        // Did the poll fail?  This should never happen and is fatal if it does.
+        if (rc < 0) {
+            ALOGE("%s:%d poll failed", __PRETTY_FUNCTION__, __LINE__);
+            return false;
+        }
+
+        if (rc == 0)
+            mCurTimeout.setTimeout(kInfiniteTimeout);
+
+        // Were we woken up on purpose?  If so, clear the eventfd with a read.
+        if (pfds[0].revents)
+            clearPendingWakeupEvents_l();
+
+        // Is out bind address dirty?  If so, clean up our socket (if any).
+        // Alternatively, do we have an active socket but should be auto
+        // disabled?  If so, release the socket and enter the proper sync state.
+        bool droppedSocket = false;
+        if (mBindIfaceDirty || ((mSocket >= 0) && shouldAutoDisable())) {
+            cleanupSocket_l();
+            mBindIfaceDirty = false;
+            droppedSocket = true;
+        }
+
+        // Do we not have a socket but should have one?  If so, try to set one
+        // up.
+        if ((mSocket < 0) && mBindIfaceValid && !shouldAutoDisable()) {
+            if (setupSocket_l()) {
+                // Success!  We are now joining a new network (either coming
+                // from no network, or coming from a potentially different
+                // network).  Force our priority to be lower so that we defer to
+                // any other masters which may already be on the network we are
+                // joining.  Later, when we enter either the client or the
+                // master state, we will clear this flag and go back to our
+                // normal election priority.
+                setForceLowPriority(true);
+                switch (mState) {
+                    // If we were in initial (whether we had a immediately
+                    // before this network or not) we want to simply reset the
+                    // system and start again.  Forcing a transition from
+                    // INITIAL to INITIAL should do the job.
+                    case CommonClockService::STATE_INITIAL:
+                        becomeInitial("bound interface");
+                        break;
+
+                    // If we were in the master state, then either we were the
+                    // master in a no-network situation, or we were the master
+                    // of a different network and have moved to a new interface.
+                    // In either case, immediately send out a master
+                    // announcement at low priority.
+                    case CommonClockService::STATE_MASTER:
+                        sendMasterAnnouncement();
+                        break;
+
+                    // If we were in any other state (CLIENT, RONIN, or
+                    // WAIT_FOR_ELECTION) then we must be moving from one
+                    // network to another.  We have lost our old master;
+                    // transition to RONIN in an attempt to find a new master.
+                    // If there are none out there, we will just assume
+                    // responsibility for the timeline we used to be a client
+                    // of.
+                    default:
+                        becomeRonin("bound interface");
+                        break;
+                }
+            } else {
+                // That's odd... we failed to set up our socket.  This could be
+                // due to some transient network change which will work itself
+                // out shortly; schedule a retry attempt in the near future.
+                mCurTimeout.setTimeout(kSetupRetryTimeoutMs);
+            }
+
+            // One way or the other, we don't have any data to process at this
+            // point (since we just tried to bulid a new socket).  Loop back
+            // around and wait for the next thing to do.
+            continue;
+        } else if (droppedSocket) {
+            // We just lost our socket, and for whatever reason (either no
+            // config, or auto disable engaged) we are not supposed to rebuild
+            // one at this time.  We are not going to rebuild our socket until
+            // something about our config/auto-disabled status changes, so we
+            // are basically in network-less mode.  If we are already in either
+            // INITIAL or MASTER, just stay there until something changes.  If
+            // we are in any other state (CLIENT, RONIN or WAIT_FOR_ELECTION),
+            // then transition to either INITIAL or MASTER depending on whether
+            // or not our timeline is valid.
+            ALOGI("Entering networkless mode interface is %s, "
+                 "shouldAutoDisable = %s",
+                 mBindIfaceValid ? "valid" : "invalid",
+                 shouldAutoDisable() ? "true" : "false");
+            if ((mState != ICommonClock::STATE_INITIAL) &&
+                (mState != ICommonClock::STATE_MASTER)) {
+                if (mTimelineID == ICommonClock::kInvalidTimelineID)
+                    becomeInitial("network-less mode");
+                else
+                    becomeMaster("network-less mode");
+            }
+
+            continue;
+        }
+
+        // Did we wakeup with no signalled events across all of our FDs?  If so,
+        // we must have hit our timeout.
+        if (rc == 0) {
+            if (!handleTimeout())
+                ALOGE("handleTimeout failed");
+            continue;
+        }
+
+        // Does our socket have data for us (assuming we still have one, we
+        // may have RXed a packet at the same time as a config change telling us
+        // to shut our socket down)?  If so, process its data.
+        if ((mSocket >= 0) && (eventCnt > 1) && (pfds[1].revents)) {
+            mLastPacketRxLocalTime = wakeupTime;
+            if (!handlePacket())
+                ALOGE("handlePacket failed");
+        }
+    }
+
+    cleanupSocket_l();
+    return true;
+}
+
+void CommonTimeServer::clearPendingWakeupEvents_l() {
+    int64_t tmp;
+    read(mWakeupThreadFD, &tmp, sizeof(tmp));
+}
+
+void CommonTimeServer::wakeupThread_l() {
+    int64_t tmp = 1;
+    write(mWakeupThreadFD, &tmp, sizeof(tmp));
+}
+
+void CommonTimeServer::cleanupSocket_l() {
+    if (mSocket >= 0) {
+        close(mSocket);
+        mSocket = -1;
+    }
+}
+
+void CommonTimeServer::shutdownThread() {
+    // Flag the work thread for shutdown.
+    this->requestExit();
+
+    // Signal the thread in case its sleeping.
+    mLock.lock();
+    wakeupThread_l();
+    mLock.unlock();
+
+    // Wait for the thread to exit.
+    this->join();
+}
+
+bool CommonTimeServer::setupSocket_l() {
+    int rc;
+    bool ret_val = false;
+    struct sockaddr_in* ipv4_addr = NULL;
+    char masterElectionEPStr[64];
+    const int one = 1;
+
+    // This should never be needed, but if we happened to have an old socket
+    // lying around, be sure to not leak it before proceeding.
+    cleanupSocket_l();
+
+    // If we don't have a valid endpoint to bind to, then how did we get here in
+    // the first place?  Regardless, we know that we are going to fail to bind,
+    // so don't even try.
+    if (!mBindIfaceValid)
+        return false;
+
+    sockaddrToString(mMasterElectionEP, true, masterElectionEPStr,
+                     sizeof(masterElectionEPStr));
+    ALOGI("Building socket :: bind = %s master election = %s",
+         mBindIface.string(), masterElectionEPStr);
+
+    // TODO: add proper support for IPv6.  Right now, we block IPv6 addresses at
+    // the configuration interface level.
+    if (AF_INET != mMasterElectionEP.ss_family) {
+        ALOGW("TODO: add proper IPv6 support");
+        goto bailout;
+    }
+
+    // open a UDP socket for the timeline serivce
+    mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    if (mSocket < 0) {
+        ALOGE("Failed to create socket (errno = %d)", errno);
+        goto bailout;
+    }
+
+    // Bind to the selected interface using Linux's spiffy SO_BINDTODEVICE.
+    struct ifreq ifr;
+    memset(&ifr, 0, sizeof(ifr));
+    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", mBindIface.string());
+    ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = 0;
+    rc = setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE,
+                    (void *)&ifr, sizeof(ifr));
+    if (rc) {
+        ALOGE("Failed to bind socket at to interface %s (errno = %d)",
+              ifr.ifr_name, errno);
+        goto bailout;
+    }
+
+    // Bind our socket to INADDR_ANY and the master election port.  The
+    // interface binding we made using SO_BINDTODEVICE should limit us to
+    // traffic only on the interface we are interested in.  We need to bind to
+    // INADDR_ANY and the specific master election port in order to be able to
+    // receive both unicast traffic and master election multicast traffic with
+    // just a single socket.
+    struct sockaddr_in bindAddr;
+    ipv4_addr = reinterpret_cast<struct sockaddr_in*>(&mMasterElectionEP);
+    memcpy(&bindAddr, ipv4_addr, sizeof(bindAddr));
+    bindAddr.sin_addr.s_addr = INADDR_ANY;
+    rc = bind(mSocket,
+              reinterpret_cast<const sockaddr *>(&bindAddr),
+              sizeof(bindAddr));
+    if (rc) {
+        ALOGE("Failed to bind socket to port %hu (errno = %d)",
+              ntohs(bindAddr.sin_port), errno);
+        goto bailout;
+    }
+
+    if (0xE0000000 == (ntohl(ipv4_addr->sin_addr.s_addr) & 0xF0000000)) {
+        // If our master election endpoint is a multicast address, be sure to join
+        // the multicast group.
+        struct ip_mreq mreq;
+        mreq.imr_multiaddr = ipv4_addr->sin_addr;
+        mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+        rc = setsockopt(mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                        &mreq, sizeof(mreq));
+        if (rc == -1) {
+            ALOGE("Failed to join multicast group at %s.  (errno = %d)",
+                 masterElectionEPStr, errno);
+            goto bailout;
+        }
+
+        // disable loopback of multicast packets
+        const int zero = 0;
+        rc = setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_LOOP,
+                        &zero, sizeof(zero));
+        if (rc == -1) {
+            ALOGE("Failed to disable multicast loopback (errno = %d)", errno);
+            goto bailout;
+        }
+    } else
+    if (ntohl(ipv4_addr->sin_addr.s_addr) != 0xFFFFFFFF) {
+        // If the master election address is neither broadcast, nor multicast,
+        // then we are misconfigured.  The config API layer should prevent this
+        // from ever happening.
+        goto bailout;
+    }
+
+    // Set the TTL of sent packets to 1.  (Time protocol sync should never leave
+    // the local subnet)
+    rc = setsockopt(mSocket, IPPROTO_IP, IP_TTL, &one, sizeof(one));
+    if (rc == -1) {
+        ALOGE("Failed to set TTL to %d (errno = %d)", one, errno);
+        goto bailout;
+    }
+
+    // get the device's unique ID
+    if (!assignDeviceID())
+        goto bailout;
+
+    ret_val = true;
+
+bailout:
+    if (!ret_val)
+        cleanupSocket_l();
+    return ret_val;
+}
+
+// generate a unique device ID that can be used for arbitration
+bool CommonTimeServer::assignDeviceID() {
+    if (!mBindIfaceValid)
+        return false;
+
+    struct ifreq ifr;
+    memset(&ifr, 0, sizeof(ifr));
+    ifr.ifr_addr.sa_family = AF_INET;
+    strlcpy(ifr.ifr_name, mBindIface.string(), IFNAMSIZ);
+
+    int rc = ioctl(mSocket, SIOCGIFHWADDR, &ifr);
+    if (rc) {
+        ALOGE("%s:%d ioctl failed", __PRETTY_FUNCTION__, __LINE__);
+        return false;
+    }
+
+    if (ifr.ifr_addr.sa_family != ARPHRD_ETHER) {
+        ALOGE("%s:%d got non-Ethernet address", __PRETTY_FUNCTION__, __LINE__);
+        return false;
+    }
+
+    mDeviceID = 0;
+    for (int i = 0; i < ETH_ALEN; i++) {
+        mDeviceID = (mDeviceID << 8) | ifr.ifr_hwaddr.sa_data[i];
+    }
+
+    return true;
+}
+
+// generate a new timeline ID
+void CommonTimeServer::assignTimelineID() {
+    do {
+        mTimelineID = (static_cast<uint64_t>(lrand48()) << 32)
+                    |  static_cast<uint64_t>(lrand48());
+    } while (mTimelineID == ICommonClock::kInvalidTimelineID);
+}
+
+// Select a preference between the device IDs of two potential masters.
+// Returns true if the first ID wins, or false if the second ID wins.
+bool CommonTimeServer::arbitrateMaster(
+        uint64_t deviceID1, uint8_t devicePrio1,
+        uint64_t deviceID2, uint8_t devicePrio2) {
+    return ((devicePrio1 >  devicePrio2) ||
+           ((devicePrio1 == devicePrio2) && (deviceID1 > deviceID2)));
+}
+
+bool CommonTimeServer::handlePacket() {
+    uint8_t buf[256];
+    struct sockaddr_storage srcAddr;
+    socklen_t srcAddrLen = sizeof(srcAddr);
+
+    ssize_t recvBytes = recvfrom(
+            mSocket, buf, sizeof(buf), 0,
+            reinterpret_cast<const sockaddr *>(&srcAddr), &srcAddrLen);
+
+    if (recvBytes < 0) {
+        ALOGE("%s:%d recvfrom failed", __PRETTY_FUNCTION__, __LINE__);
+        return false;
+    }
+
+    UniversalTimeServicePacket pkt;
+    recvBytes = pkt.deserializePacket(buf, recvBytes, mSyncGroupID);
+    if (recvBytes < 0)
+        return false;
+
+    bool result;
+    switch (pkt.packetType) {
+        case TIME_PACKET_WHO_IS_MASTER_REQUEST:
+            result = handleWhoIsMasterRequest(&pkt.p.who_is_master_request,
+                                              srcAddr);
+            break;
+
+        case TIME_PACKET_WHO_IS_MASTER_RESPONSE:
+            result = handleWhoIsMasterResponse(&pkt.p.who_is_master_response,
+                                               srcAddr);
+            break;
+
+        case TIME_PACKET_SYNC_REQUEST:
+            result = handleSyncRequest(&pkt.p.sync_request, srcAddr);
+            break;
+
+        case TIME_PACKET_SYNC_RESPONSE:
+            result = handleSyncResponse(&pkt.p.sync_response, srcAddr);
+            break;
+
+        case TIME_PACKET_MASTER_ANNOUNCEMENT:
+            result = handleMasterAnnouncement(&pkt.p.master_announcement,
+                                              srcAddr);
+            break;
+
+        default: {
+            ALOGD("%s:%d unknown packet type(%d)",
+                    __PRETTY_FUNCTION__, __LINE__, pkt.packetType);
+            result = false;
+        } break;
+    }
+
+    return result;
+}
+
+bool CommonTimeServer::handleTimeout() {
+    // If we have no socket, then this must be a timeout to retry socket setup.
+    if (mSocket < 0)
+        return true;
+
+    switch (mState) {
+        case ICommonClock::STATE_INITIAL:
+            return handleTimeoutInitial();
+        case ICommonClock::STATE_CLIENT:
+            return handleTimeoutClient();
+        case ICommonClock::STATE_MASTER:
+            return handleTimeoutMaster();
+        case ICommonClock::STATE_RONIN:
+            return handleTimeoutRonin();
+        case ICommonClock::STATE_WAIT_FOR_ELECTION:
+            return handleTimeoutWaitForElection();
+    }
+
+    return false;
+}
+
+bool CommonTimeServer::handleTimeoutInitial() {
+    if (++mInitial_WhoIsMasterRequestTimeouts ==
+            kInitial_NumWhoIsMasterRetries) {
+        // none of our attempts to discover a master succeeded, so make
+        // this device the master
+        return becomeMaster("initial timeout");
+    } else {
+        // retry the WhoIsMaster request
+        return sendWhoIsMasterRequest();
+    }
+}
+
+bool CommonTimeServer::handleTimeoutClient() {
+    if (shouldPanicNotGettingGoodData())
+        return becomeInitial("timeout panic, no good data");
+
+    if (mClient_SyncRequestPending) {
+        mClient_SyncRequestPending = false;
+
+        if (++mClient_SyncRequestTimeouts < kClient_NumSyncRequestRetries) {
+            // a sync request has timed out, so retry
+            return sendSyncRequest();
+        } else {
+            // The master has failed to respond to a sync request for too many
+            // times in a row.  Assume the master is dead and start electing
+            // a new master.
+            return becomeRonin("master not responding");
+        }
+    } else {
+        // initiate the next sync request
+        return sendSyncRequest();
+    }
+}
+
+bool CommonTimeServer::handleTimeoutMaster() {
+    // send another announcement from the master
+    return sendMasterAnnouncement();
+}
+
+bool CommonTimeServer::handleTimeoutRonin() {
+    if (++mRonin_WhoIsMasterRequestTimeouts == kRonin_NumWhoIsMasterRetries) {
+        // no other master is out there, so we won the election
+        return becomeMaster("no better masters detected");
+    } else {
+        return sendWhoIsMasterRequest();
+    }
+}
+
+bool CommonTimeServer::handleTimeoutWaitForElection() {
+    return becomeRonin("timeout waiting for election conclusion");
+}
+
+bool CommonTimeServer::handleWhoIsMasterRequest(
+        const WhoIsMasterRequestPacket* request,
+        const sockaddr_storage& srcAddr) {
+    if (mState == ICommonClock::STATE_MASTER) {
+        // is this request related to this master's timeline?
+        if (request->timelineID != ICommonClock::kInvalidTimelineID &&
+            request->timelineID != mTimelineID)
+            return true;
+
+        WhoIsMasterResponsePacket pkt;
+        pkt.initHeader(mTimelineID, mSyncGroupID);
+        pkt.deviceID = mDeviceID;
+        pkt.devicePriority = effectivePriority();
+
+        uint8_t buf[256];
+        ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
+        if (bufSz < 0)
+            return false;
+
+        ssize_t sendBytes = sendto(
+                mSocket, buf, bufSz, 0,
+                reinterpret_cast<const sockaddr *>(&srcAddr),
+                sizeof(srcAddr));
+        if (sendBytes == -1) {
+            ALOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
+            return false;
+        }
+    } else if (mState == ICommonClock::STATE_RONIN) {
+        // if we hear a WhoIsMaster request from another device following
+        // the same timeline and that device wins arbitration, then we will stop
+        // trying to elect ourselves master and will instead wait for an
+        // announcement from the election winner
+        if (request->timelineID != mTimelineID)
+            return true;
+
+        if (arbitrateMaster(request->senderDeviceID,
+                            request->senderDevicePriority,
+                            mDeviceID,
+                            effectivePriority()))
+            return becomeWaitForElection("would lose election");
+
+        return true;
+    } else if (mState == ICommonClock::STATE_INITIAL) {
+        // If a group of devices booted simultaneously (e.g. after a power
+        // outage) and all of them are in the initial state and there is no
+        // master, then each device may time out and declare itself master at
+        // the same time.  To avoid this, listen for
+        // WhoIsMaster(InvalidTimeline) requests from peers.  If we would lose
+        // arbitration against that peer, reset our timeout count so that the
+        // peer has a chance to become master before we time out.
+        if (request->timelineID == ICommonClock::kInvalidTimelineID &&
+                arbitrateMaster(request->senderDeviceID,
+                                request->senderDevicePriority,
+                                mDeviceID,
+                                effectivePriority())) {
+            mInitial_WhoIsMasterRequestTimeouts = 0;
+        }
+    }
+
+    return true;
+}
+
+bool CommonTimeServer::handleWhoIsMasterResponse(
+        const WhoIsMasterResponsePacket* response,
+        const sockaddr_storage& srcAddr) {
+    if (mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN) {
+        return becomeClient(srcAddr,
+                            response->deviceID,
+                            response->devicePriority,
+                            response->timelineID,
+                            "heard whois response");
+    } else if (mState == ICommonClock::STATE_CLIENT) {
+        // if we get multiple responses because there are multiple devices
+        // who believe that they are master, then follow the master that
+        // wins arbitration
+        if (arbitrateMaster(response->deviceID,
+                            response->devicePriority,
+                            mClient_MasterDeviceID,
+                            mClient_MasterDevicePriority)) {
+            return becomeClient(srcAddr,
+                                response->deviceID,
+                                response->devicePriority,
+                                response->timelineID,
+                                "heard whois response");
+        }
+    }
+
+    return true;
+}
+
+bool CommonTimeServer::handleSyncRequest(const SyncRequestPacket* request,
+                                         const sockaddr_storage& srcAddr) {
+    SyncResponsePacket pkt;
+    pkt.initHeader(mTimelineID, mSyncGroupID);
+
+    if ((mState == ICommonClock::STATE_MASTER) &&
+        (mTimelineID == request->timelineID)) {
+        int64_t rxLocalTime = mLastPacketRxLocalTime;
+        int64_t rxCommonTime;
+
+        // If we are master on an actual network and have actual clients, then
+        // we are no longer low priority.
+        setForceLowPriority(false);
+
+        if (OK != mCommonClock.localToCommon(rxLocalTime, &rxCommonTime)) {
+            return false;
+        }
+
+        int64_t txLocalTime = mLocalClock.getLocalTime();;
+        int64_t txCommonTime;
+        if (OK != mCommonClock.localToCommon(txLocalTime, &txCommonTime)) {
+            return false;
+        }
+
+        pkt.nak = 0;
+        pkt.clientTxLocalTime  = request->clientTxLocalTime;
+        pkt.masterRxCommonTime = rxCommonTime;
+        pkt.masterTxCommonTime = txCommonTime;
+    } else {
+        pkt.nak = 1;
+        pkt.clientTxLocalTime  = 0;
+        pkt.masterRxCommonTime = 0;
+        pkt.masterTxCommonTime = 0;
+    }
+
+    uint8_t buf[256];
+    ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
+    if (bufSz < 0)
+        return false;
+
+    ssize_t sendBytes = sendto(
+            mSocket, &buf, bufSz, 0,
+            reinterpret_cast<const sockaddr *>(&srcAddr),
+            sizeof(srcAddr));
+    if (sendBytes == -1) {
+        ALOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
+        return false;
+    }
+
+    return true;
+}
+
+bool CommonTimeServer::handleSyncResponse(
+        const SyncResponsePacket* response,
+        const sockaddr_storage& srcAddr) {
+    if (mState != ICommonClock::STATE_CLIENT)
+        return true;
+
+    assert(mMasterEPValid);
+    if (!sockaddrMatch(srcAddr, mMasterEP, true)) {
+        char srcEP[64], expectedEP[64];
+        sockaddrToString(srcAddr, true, srcEP, sizeof(srcEP));
+        sockaddrToString(mMasterEP, true, expectedEP, sizeof(expectedEP));
+        ALOGI("Dropping sync response from unexpected address."
+             " Expected %s Got %s", expectedEP, srcEP);
+        return true;
+    }
+
+    if (response->nak) {
+        // if our master is no longer accepting requests, then we need to find
+        // a new master
+        return becomeRonin("master NAK'ed");
+    }
+
+    mClient_SyncRequestPending = 0;
+    mClient_SyncRequestTimeouts = 0;
+    mClient_PacketRTTLog.logRX(response->clientTxLocalTime,
+                               mLastPacketRxLocalTime);
+
+    bool result;
+    if (!(mClient_SyncRespsRXedFromCurMaster++)) {
+        // the first request/response exchange between a client and a master
+        // may take unusually long due to ARP, so discard it.
+        result = true;
+    } else {
+        int64_t clientTxLocalTime  = response->clientTxLocalTime;
+        int64_t clientRxLocalTime  = mLastPacketRxLocalTime;
+        int64_t masterTxCommonTime = response->masterTxCommonTime;
+        int64_t masterRxCommonTime = response->masterRxCommonTime;
+
+        int64_t rtt       = (clientRxLocalTime - clientTxLocalTime);
+        int64_t avgLocal  = (clientTxLocalTime + clientRxLocalTime) >> 1;
+        int64_t avgCommon = (masterTxCommonTime + masterRxCommonTime) >> 1;
+
+        // if the RTT of the packet is significantly larger than the panic
+        // threshold, we should simply discard it.  Its better to do nothing
+        // than to take cues from a packet like that.
+        int rttCommon = mCommonClock.localDurationToCommonDuration(rtt);
+        if (rttCommon > (static_cast<int64_t>(mPanicThresholdUsec) * 
+                         kRTTDiscardPanicThreshMultiplier)) {
+            ALOGV("Dropping sync response with RTT of %lld uSec", rttCommon);
+            mClient_ExpiredSyncRespsRXedFromCurMaster++;
+            if (shouldPanicNotGettingGoodData())
+                return becomeInitial("RX panic, no good data");
+        } else {
+            result = mClockRecovery.pushDisciplineEvent(avgLocal, avgCommon, rttCommon);
+            mClient_LastGoodSyncRX = clientRxLocalTime;
+
+            if (result) {
+                // indicate to listeners that we've synced to the common timeline
+                notifyClockSync();
+            } else {
+                ALOGE("Panic!  Observed clock sync error is too high to tolerate,"
+                        " resetting state machine and starting over.");
+                notifyClockSyncLoss();
+                return becomeInitial("panic");
+            }
+        }
+    }
+
+    mCurTimeout.setTimeout(mSyncRequestIntervalMs);
+    return result;
+}
+
+bool CommonTimeServer::handleMasterAnnouncement(
+        const MasterAnnouncementPacket* packet,
+        const sockaddr_storage& srcAddr) {
+    uint64_t newDeviceID   = packet->deviceID;
+    uint8_t  newDevicePrio = packet->devicePriority;
+    uint64_t newTimelineID = packet->timelineID;
+
+    if (mState == ICommonClock::STATE_INITIAL ||
+        mState == ICommonClock::STATE_RONIN ||
+        mState == ICommonClock::STATE_WAIT_FOR_ELECTION) {
+        // if we aren't currently following a master, then start following
+        // this new master
+        return becomeClient(srcAddr,
+                            newDeviceID,
+                            newDevicePrio,
+                            newTimelineID,
+                            "heard master announcement");
+    } else if (mState == ICommonClock::STATE_CLIENT) {
+        // if the new master wins arbitration against our current master,
+        // then become a client of the new master
+        if (arbitrateMaster(newDeviceID,
+                            newDevicePrio,
+                            mClient_MasterDeviceID,
+                            mClient_MasterDevicePriority))
+            return becomeClient(srcAddr,
+                                newDeviceID,
+                                newDevicePrio,
+                                newTimelineID,
+                                "heard master announcement");
+    } else if (mState == ICommonClock::STATE_MASTER) {
+        // two masters are competing - if the new one wins arbitration, then
+        // cease acting as master
+        if (arbitrateMaster(newDeviceID, newDevicePrio,
+                            mDeviceID, effectivePriority()))
+            return becomeClient(srcAddr, newDeviceID,
+                                newDevicePrio, newTimelineID,
+                                "heard master announcement");
+    }
+
+    return true;
+}
+
+bool CommonTimeServer::sendWhoIsMasterRequest() {
+    assert(mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN);
+
+    // If we have no socket, then we must be in the unconfigured initial state.
+    // Don't report any errors, just don't try to send the initial who-is-master
+    // query.  Eventually, our network will either become configured, or we will
+    // be forced into network-less master mode by higher level code.
+    if (mSocket < 0) {
+        assert(mState == ICommonClock::STATE_INITIAL);
+        return true;
+    }
+
+    bool ret = false;
+    WhoIsMasterRequestPacket pkt;
+    pkt.initHeader(mSyncGroupID);
+    pkt.senderDeviceID = mDeviceID;
+    pkt.senderDevicePriority = effectivePriority();
+
+    uint8_t buf[256];
+    ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
+    if (bufSz >= 0) {
+        ssize_t sendBytes = sendto(
+                mSocket, buf, bufSz, 0,
+                reinterpret_cast<const sockaddr *>(&mMasterElectionEP),
+                sizeof(mMasterElectionEP));
+        if (sendBytes < 0)
+            ALOGE("WhoIsMaster sendto failed (errno %d)", errno);
+        ret = true;
+    }
+
+    if (mState == ICommonClock::STATE_INITIAL) {
+        mCurTimeout.setTimeout(kInitial_WhoIsMasterTimeoutMs);
+    } else {
+        mCurTimeout.setTimeout(kRonin_WhoIsMasterTimeoutMs);
+    }
+
+    return ret;
+}
+
+bool CommonTimeServer::sendSyncRequest() {
+    // If we are sending sync requests, then we must be in the client state and
+    // we must have a socket (when we have no network, we are only supposed to
+    // be in INITIAL or MASTER)
+    assert(mState == ICommonClock::STATE_CLIENT);
+    assert(mSocket >= 0);
+
+    bool ret = false;
+    SyncRequestPacket pkt;
+    pkt.initHeader(mTimelineID, mSyncGroupID);
+    pkt.clientTxLocalTime = mLocalClock.getLocalTime();
+
+    if (!mClient_FirstSyncTX)
+        mClient_FirstSyncTX = pkt.clientTxLocalTime;
+
+    mClient_PacketRTTLog.logTX(pkt.clientTxLocalTime);
+
+    uint8_t buf[256];
+    ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
+    if (bufSz >= 0) {
+        ssize_t sendBytes = sendto(
+                mSocket, buf, bufSz, 0,
+                reinterpret_cast<const sockaddr *>(&mMasterEP),
+                sizeof(mMasterEP));
+        if (sendBytes < 0)
+            ALOGE("SyncRequest sendto failed (errno %d)", errno);
+        ret = true;
+    }
+
+    mClient_SyncsSentToCurMaster++;
+    mCurTimeout.setTimeout(mSyncRequestIntervalMs);
+    mClient_SyncRequestPending = true;
+
+    return ret;
+}
+
+bool CommonTimeServer::sendMasterAnnouncement() {
+    bool ret = false;
+    assert(mState == ICommonClock::STATE_MASTER);
+
+    // If we are being asked to send a master announcement, but we have no
+    // socket, we must be in network-less master mode.  Don't bother to send the
+    // announcement, and don't bother to schedule a timeout.  When the network
+    // comes up, the work thread will get poked and start the process of
+    // figuring out who the current master should be.
+    if (mSocket < 0) {
+        mCurTimeout.setTimeout(kInfiniteTimeout);
+        return true;
+    }
+
+    MasterAnnouncementPacket pkt;
+    pkt.initHeader(mTimelineID, mSyncGroupID);
+    pkt.deviceID = mDeviceID;
+    pkt.devicePriority = effectivePriority();
+
+    uint8_t buf[256];
+    ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
+    if (bufSz >= 0) {
+        ssize_t sendBytes = sendto(
+                mSocket, buf, bufSz, 0,
+                reinterpret_cast<const sockaddr *>(&mMasterElectionEP),
+                sizeof(mMasterElectionEP));
+        if (sendBytes < 0)
+            ALOGE("MasterAnnouncement sendto failed (errno %d)", errno);
+        ret = true;
+    }
+
+    mCurTimeout.setTimeout(mMasterAnnounceIntervalMs);
+    return ret;
+}
+
+bool CommonTimeServer::becomeClient(const sockaddr_storage& masterEP,
+                                    uint64_t masterDeviceID,
+                                    uint8_t  masterDevicePriority,
+                                    uint64_t timelineID,
+                                    const char* cause) {
+    char newEPStr[64], oldEPStr[64];
+    sockaddrToString(masterEP, true, newEPStr, sizeof(newEPStr));
+    sockaddrToString(mMasterEP, mMasterEPValid, oldEPStr, sizeof(oldEPStr));
+
+    ALOGI("%s --> CLIENT (%s) :%s"
+         " OldMaster: %02x-%014llx::%016llx::%s"
+         " NewMaster: %02x-%014llx::%016llx::%s",
+         stateToString(mState), cause,
+         (mTimelineID != timelineID) ? " (new timeline)" : "",
+         mClient_MasterDevicePriority, mClient_MasterDeviceID,
+         mTimelineID, oldEPStr,
+         masterDevicePriority, masterDeviceID,
+         timelineID, newEPStr);
+
+    if (mTimelineID != timelineID) {
+        // start following a new timeline
+        mTimelineID = timelineID;
+        mClockRecovery.reset(true, true);
+        notifyClockSyncLoss();
+    } else {
+        // start following a new master on the existing timeline
+        mClockRecovery.reset(false, true);
+    }
+
+    mMasterEP = masterEP;
+    mMasterEPValid = true;
+    setForceLowPriority(false);
+
+    mClient_MasterDeviceID = masterDeviceID;
+    mClient_MasterDevicePriority = masterDevicePriority;
+    resetSyncStats();
+
+    setState(ICommonClock::STATE_CLIENT);
+
+    // add some jitter to when the various clients send their requests
+    // in order to reduce the likelihood that a group of clients overload
+    // the master after receiving a master announcement
+    usleep((lrand48() % 100) * 1000);
+
+    return sendSyncRequest();
+}
+
+bool CommonTimeServer::becomeMaster(const char* cause) {
+    uint64_t oldTimelineID = mTimelineID;
+    if (mTimelineID == ICommonClock::kInvalidTimelineID) {
+        // this device has not been following any existing timeline,
+        // so it will create a new timeline and declare itself master
+        assert(!mCommonClock.isValid());
+
+        // set the common time basis
+        mCommonClock.setBasis(mLocalClock.getLocalTime(), 0);
+
+        // assign an arbitrary timeline iD
+        assignTimelineID();
+
+        // notify listeners that we've created a common timeline
+        notifyClockSync();
+    }
+
+    ALOGI("%s --> MASTER (%s) : %s timeline %016llx",
+         stateToString(mState), cause,
+         (oldTimelineID == mTimelineID) ? "taking ownership of"
+                                        : "creating new",
+         mTimelineID);
+
+    memset(&mMasterEP, 0, sizeof(mMasterEP));
+    mMasterEPValid = false;
+    setForceLowPriority(false);
+    mClient_MasterDevicePriority = effectivePriority();
+    mClient_MasterDeviceID = mDeviceID;
+    mClockRecovery.reset(false, true);
+    resetSyncStats();
+
+    setState(ICommonClock::STATE_MASTER);
+    return sendMasterAnnouncement();
+}
+
+bool CommonTimeServer::becomeRonin(const char* cause) {
+    // If we were the client of a given timeline, but had never received even a
+    // single time sync packet, then we transition back to Initial instead of
+    // Ronin.  If we transition to Ronin and end up becoming the new Master, we
+    // will be unable to service requests for other clients because we never
+    // actually knew what time it was.  By going to initial, we ensure that
+    // other clients who know what time it is, but would lose master arbitration
+    // in the Ronin case, will step up and become the proper new master of the
+    // old timeline.
+
+    char oldEPStr[64];
+    sockaddrToString(mMasterEP, mMasterEPValid, oldEPStr, sizeof(oldEPStr));
+    memset(&mMasterEP, 0, sizeof(mMasterEP));
+    mMasterEPValid = false;
+
+    if (mCommonClock.isValid()) {
+        ALOGI("%s --> RONIN (%s) : lost track of previously valid timeline "
+             "%02x-%014llx::%016llx::%s (%d TXed %d RXed %d RXExpired)",
+             stateToString(mState), cause,
+             mClient_MasterDevicePriority, mClient_MasterDeviceID,
+             mTimelineID, oldEPStr,
+             mClient_SyncsSentToCurMaster,
+             mClient_SyncRespsRXedFromCurMaster,
+             mClient_ExpiredSyncRespsRXedFromCurMaster);
+
+        mRonin_WhoIsMasterRequestTimeouts = 0;
+        setState(ICommonClock::STATE_RONIN);
+        return sendWhoIsMasterRequest();
+    } else {
+        ALOGI("%s --> INITIAL (%s) : never synced timeline "
+             "%02x-%014llx::%016llx::%s (%d TXed %d RXed %d RXExpired)",
+             stateToString(mState), cause,
+             mClient_MasterDevicePriority, mClient_MasterDeviceID,
+             mTimelineID, oldEPStr,
+             mClient_SyncsSentToCurMaster,
+             mClient_SyncRespsRXedFromCurMaster,
+             mClient_ExpiredSyncRespsRXedFromCurMaster);
+
+        return becomeInitial("ronin, no timeline");
+    }
+}
+
+bool CommonTimeServer::becomeWaitForElection(const char* cause) {
+    ALOGI("%s --> WAIT_FOR_ELECTION (%s) : dropping out of election,"
+         " waiting %d mSec for completion.",
+         stateToString(mState), cause, kWaitForElection_TimeoutMs);
+
+    setState(ICommonClock::STATE_WAIT_FOR_ELECTION);
+    mCurTimeout.setTimeout(kWaitForElection_TimeoutMs);
+    return true;
+}
+
+bool CommonTimeServer::becomeInitial(const char* cause) {
+    ALOGI("Entering INITIAL (%s), total reset.", cause);
+
+    setState(ICommonClock::STATE_INITIAL);
+
+    // reset clock recovery
+    mClockRecovery.reset(true, true);
+
+    // reset internal state bookkeeping.
+    mCurTimeout.setTimeout(kInfiniteTimeout);
+    memset(&mMasterEP, 0, sizeof(mMasterEP));
+    mMasterEPValid = false;
+    mLastPacketRxLocalTime = 0;
+    mTimelineID = ICommonClock::kInvalidTimelineID;
+    mClockSynced = false;
+    mInitial_WhoIsMasterRequestTimeouts = 0;
+    mClient_MasterDeviceID = 0;
+    mClient_MasterDevicePriority = 0;
+    mRonin_WhoIsMasterRequestTimeouts = 0;
+    resetSyncStats();
+
+    // send the first request to discover the master
+    return sendWhoIsMasterRequest();
+}
+
+void CommonTimeServer::notifyClockSync() {
+    if (!mClockSynced) {
+        mClockSynced = true;
+        mICommonClock->notifyOnTimelineChanged(mTimelineID);
+    }
+}
+
+void CommonTimeServer::notifyClockSyncLoss() {
+    if (mClockSynced) {
+        mClockSynced = false;
+        mICommonClock->notifyOnTimelineChanged(
+                ICommonClock::kInvalidTimelineID);
+    }
+}
+
+void CommonTimeServer::setState(ICommonClock::State s) {
+    mState = s;
+}
+
+const char* CommonTimeServer::stateToString(ICommonClock::State s) {
+    switch(s) {
+        case ICommonClock::STATE_INITIAL:
+            return "INITIAL";
+        case ICommonClock::STATE_CLIENT:
+            return "CLIENT";
+        case ICommonClock::STATE_MASTER:
+            return "MASTER";
+        case ICommonClock::STATE_RONIN:
+            return "RONIN";
+        case ICommonClock::STATE_WAIT_FOR_ELECTION:
+            return "WAIT_FOR_ELECTION";
+        default:
+            return "unknown";
+    }
+}
+
+void CommonTimeServer::sockaddrToString(const sockaddr_storage& addr,
+                                        bool addrValid,
+                                        char* buf, size_t bufLen) {
+    if (!bufLen || !buf)
+        return;
+
+    if (addrValid) {
+        switch (addr.ss_family) {
+            case AF_INET: {
+                const struct sockaddr_in* sa =
+                    reinterpret_cast<const struct sockaddr_in*>(&addr);
+                unsigned long a = ntohl(sa->sin_addr.s_addr);
+                uint16_t      p = ntohs(sa->sin_port);
+                snprintf(buf, bufLen, "%lu.%lu.%lu.%lu:%hu",
+                        ((a >> 24) & 0xFF), ((a >> 16) & 0xFF),
+                        ((a >>  8) & 0xFF),  (a        & 0xFF), p);
+            } break;
+
+            case AF_INET6: {
+                const struct sockaddr_in6* sa =
+                    reinterpret_cast<const struct sockaddr_in6*>(&addr);
+                const uint8_t* a = sa->sin6_addr.s6_addr;
+                uint16_t       p = ntohs(sa->sin6_port);
+                snprintf(buf, bufLen,
+                        "%02X%02X:%02X%02X:%02X%02X:%02X%02X:"
+                        "%02X%02X:%02X%02X:%02X%02X:%02X%02X port %hd",
+                        a[0], a[1], a[ 2], a[ 3], a[ 4], a[ 5], a[ 6], a[ 7],
+                        a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15],
+                        p);
+            } break;
+
+            default:
+                snprintf(buf, bufLen,
+                         "<unknown sockaddr family %d>", addr.ss_family);
+                break;
+        }
+    } else {
+        snprintf(buf, bufLen, "<none>");
+    }
+
+    buf[bufLen - 1] = 0;
+}
+
+bool CommonTimeServer::sockaddrMatch(const sockaddr_storage& a1,
+                                     const sockaddr_storage& a2,
+                                     bool matchAddressOnly) {
+    if (a1.ss_family != a2.ss_family)
+        return false;
+
+    switch (a1.ss_family) {
+        case AF_INET: {
+            const struct sockaddr_in* sa1 =
+                reinterpret_cast<const struct sockaddr_in*>(&a1);
+            const struct sockaddr_in* sa2 =
+                reinterpret_cast<const struct sockaddr_in*>(&a2);
+
+            if (sa1->sin_addr.s_addr != sa2->sin_addr.s_addr)
+                return false;
+
+            return (matchAddressOnly || (sa1->sin_port == sa2->sin_port));
+        } break;
+
+        case AF_INET6: {
+            const struct sockaddr_in6* sa1 =
+                reinterpret_cast<const struct sockaddr_in6*>(&a1);
+            const struct sockaddr_in6* sa2 =
+                reinterpret_cast<const struct sockaddr_in6*>(&a2);
+
+            if (memcmp(&sa1->sin6_addr, &sa2->sin6_addr, sizeof(sa2->sin6_addr)))
+                return false;
+
+            return (matchAddressOnly || (sa1->sin6_port == sa2->sin6_port));
+        } break;
+
+        // Huh?  We don't deal in non-IPv[46] addresses.  Not sure how we got
+        // here, but we don't know how to comapre these addresses and simply
+        // default to a no-match decision.
+        default: return false;
+    }
+}
+
+void CommonTimeServer::TimeoutHelper::setTimeout(int msec) {
+    mTimeoutValid = (msec >= 0);
+    if (mTimeoutValid)
+        mEndTime = systemTime() +
+                   (static_cast<nsecs_t>(msec) * 1000000);
+}
+
+int CommonTimeServer::TimeoutHelper::msecTillTimeout() {
+    if (!mTimeoutValid)
+        return kInfiniteTimeout;
+
+    nsecs_t now = systemTime();
+    if (now >= mEndTime)
+        return 0;
+
+    uint64_t deltaMsec = (((mEndTime - now) + 999999) / 1000000);
+
+    if (deltaMsec > static_cast<uint64_t>(MAX_INT))
+        return MAX_INT;
+
+    return static_cast<int>(deltaMsec);
+}
+
+bool CommonTimeServer::shouldPanicNotGettingGoodData() {
+    if (mClient_FirstSyncTX) {
+        int64_t now = mLocalClock.getLocalTime();
+        int64_t delta = now - (mClient_LastGoodSyncRX
+                             ? mClient_LastGoodSyncRX
+                             : mClient_FirstSyncTX);
+        int64_t deltaUsec = mCommonClock.localDurationToCommonDuration(delta);
+
+        if (deltaUsec >= kNoGoodDataPanicThresholdUsec)
+            return true;
+    }
+
+    return false;
+}
+
+void CommonTimeServer::PacketRTTLog::logTX(int64_t txTime) {
+    txTimes[wrPtr] = txTime;
+    rxTimes[wrPtr] = 0;
+    wrPtr = (wrPtr + 1) % RTT_LOG_SIZE;
+    if (!wrPtr)
+        logFull = true;
+}
+
+void CommonTimeServer::PacketRTTLog::logRX(int64_t txTime, int64_t rxTime) {
+    if (!logFull && !wrPtr)
+        return;
+
+    uint32_t i = logFull ? wrPtr : 0;
+    do {
+        if (txTimes[i] == txTime) {
+            rxTimes[i] = rxTime;
+            break;
+        }
+        i = (i + 1) % RTT_LOG_SIZE;
+    } while (i != wrPtr);
+}
+
+}  // namespace android
diff --git a/services/common_time/common_time_server.h b/services/common_time/common_time_server.h
new file mode 100644
index 0000000..a0f549f
--- /dev/null
+++ b/services/common_time/common_time_server.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_COMMON_TIME_SERVER_H
+#define ANDROID_COMMON_TIME_SERVER_H
+
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <linux/socket.h>
+
+#include <common_time/ICommonClock.h>
+#include <common_time/local_clock.h>
+#include <utils/String8.h>
+
+#include "clock_recovery.h"
+#include "common_clock.h"
+#include "common_time_server_packets.h"
+
+#define RTT_LOG_SIZE 30
+
+namespace android {
+
+class CommonClockService;
+class CommonTimeConfigService;
+
+/***** time service implementation *****/
+
+class CommonTimeServer : public Thread {
+  public:
+    CommonTimeServer();
+    ~CommonTimeServer();
+
+    bool startServices();
+
+    // Common Clock API methods
+    CommonClock&        getCommonClock()        { return mCommonClock; }
+    LocalClock&         getLocalClock()         { return mLocalClock; }
+    uint64_t            getTimelineID();
+    int32_t             getEstimatedError();
+    ICommonClock::State getState();
+    status_t            getMasterAddr(struct sockaddr_storage* addr);
+    status_t            isCommonTimeValid(bool* valid, uint32_t* timelineID);
+
+    // Config API methods
+    status_t getMasterElectionPriority(uint8_t *priority);
+    status_t setMasterElectionPriority(uint8_t priority);
+    status_t getMasterElectionEndpoint(struct sockaddr_storage *addr);
+    status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr);
+    status_t getMasterElectionGroupId(uint64_t *id);
+    status_t setMasterElectionGroupId(uint64_t id);
+    status_t getInterfaceBinding(String8& ifaceName);
+    status_t setInterfaceBinding(const String8& ifaceName);
+    status_t getMasterAnnounceInterval(int *interval);
+    status_t setMasterAnnounceInterval(int interval);
+    status_t getClientSyncInterval(int *interval);
+    status_t setClientSyncInterval(int interval);
+    status_t getPanicThreshold(int *threshold);
+    status_t setPanicThreshold(int threshold);
+    status_t getAutoDisable(bool *autoDisable);
+    status_t setAutoDisable(bool autoDisable);
+    status_t forceNetworklessMasterMode();
+
+    // Method used by the CommonClockService to notify the core service about
+    // changes in the number of active common clock clients.
+    void reevaluateAutoDisableState(bool commonClockHasClients);
+
+    status_t dumpClockInterface(int fd, const Vector<String16>& args,
+                                size_t activeClients);
+    status_t dumpConfigInterface(int fd, const Vector<String16>& args);
+
+  private:
+    class PacketRTTLog {
+      public:
+        PacketRTTLog() {
+            resetLog();
+        }
+
+        void resetLog() {
+            wrPtr = 0;
+            logFull = 0;
+        }
+
+        void logTX(int64_t txTime);
+        void logRX(int64_t txTime, int64_t rxTime);
+        void dumpLog(int fd, const CommonClock& cclk);
+
+      private:
+        uint32_t wrPtr;
+        bool logFull;
+        int64_t txTimes[RTT_LOG_SIZE];
+        int64_t rxTimes[RTT_LOG_SIZE];
+    };
+
+    class TimeoutHelper {
+      public:
+        TimeoutHelper() : mTimeoutValid(false) { }
+
+        void setTimeout(int msec);
+        int msecTillTimeout();
+
+      private:
+        bool        mTimeoutValid;
+        nsecs_t     mEndTime;
+    };
+
+    bool threadLoop();
+
+    bool runStateMachine_l();
+    bool setupSocket_l();
+
+    void assignTimelineID();
+    bool assignDeviceID();
+
+    static bool arbitrateMaster(uint64_t deviceID1, uint8_t devicePrio1,
+                                uint64_t deviceID2, uint8_t devicePrio2);
+
+    bool handlePacket();
+    bool handleWhoIsMasterRequest (const WhoIsMasterRequestPacket* request,
+                                   const sockaddr_storage& srcAddr);
+    bool handleWhoIsMasterResponse(const WhoIsMasterResponsePacket* response,
+                                   const sockaddr_storage& srcAddr);
+    bool handleSyncRequest        (const SyncRequestPacket* request,
+                                   const sockaddr_storage& srcAddr);
+    bool handleSyncResponse       (const SyncResponsePacket* response,
+                                   const sockaddr_storage& srcAddr);
+    bool handleMasterAnnouncement (const MasterAnnouncementPacket* packet,
+                                   const sockaddr_storage& srcAddr);
+
+    bool handleTimeout();
+    bool handleTimeoutInitial();
+    bool handleTimeoutClient();
+    bool handleTimeoutMaster();
+    bool handleTimeoutRonin();
+    bool handleTimeoutWaitForElection();
+
+    bool sendWhoIsMasterRequest();
+    bool sendSyncRequest();
+    bool sendMasterAnnouncement();
+
+    bool becomeClient(const sockaddr_storage& masterAddr,
+                      uint64_t masterDeviceID,
+                      uint8_t  masterDevicePriority,
+                      uint64_t timelineID,
+                      const char* cause);
+    bool becomeMaster(const char* cause);
+    bool becomeRonin(const char* cause);
+    bool becomeWaitForElection(const char* cause);
+    bool becomeInitial(const char* cause);
+
+    void notifyClockSync();
+    void notifyClockSyncLoss();
+
+    ICommonClock::State mState;
+    void setState(ICommonClock::State s);
+
+    void clearPendingWakeupEvents_l();
+    void wakeupThread_l();
+    void cleanupSocket_l();
+    void shutdownThread();
+
+    inline uint8_t effectivePriority() const {
+        return (mMasterPriority & 0x7F) |
+               (mForceLowPriority ? 0x00 : 0x80);
+    }
+
+    inline bool shouldAutoDisable() const {
+        return (mAutoDisable && !mCommonClockHasClients);
+    }
+
+    inline void resetSyncStats() {
+        mClient_SyncRequestPending = false;
+        mClient_SyncRequestTimeouts = 0;
+        mClient_SyncsSentToCurMaster = 0;
+        mClient_SyncRespsRXedFromCurMaster = 0;
+        mClient_ExpiredSyncRespsRXedFromCurMaster = 0;
+        mClient_FirstSyncTX = 0;
+        mClient_LastGoodSyncRX = 0;
+        mClient_PacketRTTLog.resetLog();
+    }
+
+    bool shouldPanicNotGettingGoodData();
+
+    // Helper to keep track of the state machine's current timeout
+    TimeoutHelper mCurTimeout;
+
+    // common clock, local clock abstraction, and clock recovery loop
+    CommonClock mCommonClock;
+    LocalClock mLocalClock;
+    ClockRecoveryLoop mClockRecovery;
+
+    // implementation of ICommonClock
+    sp<CommonClockService> mICommonClock;
+
+    // implementation of ICommonTimeConfig
+    sp<CommonTimeConfigService> mICommonTimeConfig;
+
+    // UDP socket for the time sync protocol
+    int mSocket;
+
+    // eventfd used to wakeup the work thread in response to configuration
+    // changes.
+    int mWakeupThreadFD;
+
+    // timestamp captured when a packet is received
+    int64_t mLastPacketRxLocalTime;
+
+    // ID of the timeline that this device is following
+    uint64_t mTimelineID;
+
+    // flag for whether the clock has been synced to a timeline
+    bool mClockSynced;
+
+    // flag used to indicate that clients should be considered to be lower
+    // priority than all of their peers during elections.  This flag is set and
+    // cleared by the state machine.  It is set when the client joins a new
+    // network.  If the client had been a master in the old network (or an
+    // isolated master with no network connectivity) it should defer to any
+    // masters which may already be on the network.  It will be cleared whenever
+    // the state machine transitions to the master state.
+    bool mForceLowPriority;
+    inline void setForceLowPriority(bool val) {
+        mForceLowPriority = val;
+        if (mState == ICommonClock::STATE_MASTER)
+            mClient_MasterDevicePriority = effectivePriority();
+    }
+
+    // Lock to synchronize access to internal state and configuration.
+    Mutex mLock;
+
+    // Flag updated by the common clock service to indicate that it does or does
+    // not currently have registered clients.  When the the auto disable flag is
+    // cleared on the common time service, the service will participate in
+    // network synchronization whenever it has a valid network interface to bind
+    // to.  When the auto disable flag is set on the common time service, it
+    // will only participate in network synchronization when it has both a valid
+    // interface AND currently active common clock clients.
+    bool mCommonClockHasClients;
+
+    // Configuration info
+    struct sockaddr_storage mMasterElectionEP;          // Endpoint over which we conduct master election
+    String8                 mBindIface;                 // Endpoint for the service to bind to.
+    bool                    mBindIfaceValid;            // whether or not the bind Iface is valid.
+    bool                    mBindIfaceDirty;            // whether or not the bind Iface is valid.
+    struct sockaddr_storage mMasterEP;                  // Endpoint of our current master (if any)
+    bool                    mMasterEPValid;
+    uint64_t                mDeviceID;                  // unique ID of this device
+    uint64_t                mSyncGroupID;               // synchronization group ID of this device.
+    uint8_t                 mMasterPriority;            // Priority of this device in master election.
+    uint32_t                mMasterAnnounceIntervalMs;
+    uint32_t                mSyncRequestIntervalMs;
+    uint32_t                mPanicThresholdUsec;
+    bool                    mAutoDisable;
+
+    // Config defaults.
+    static const char*      kDefaultMasterElectionAddr;
+    static const uint16_t   kDefaultMasterElectionPort;
+    static const uint64_t   kDefaultSyncGroupID;
+    static const uint8_t    kDefaultMasterPriority;
+    static const uint32_t   kDefaultMasterAnnounceIntervalMs;
+    static const uint32_t   kDefaultSyncRequestIntervalMs;
+    static const uint32_t   kDefaultPanicThresholdUsec;
+    static const bool       kDefaultAutoDisable;
+
+    // Priority mask and shift fields.
+    static const uint64_t kDeviceIDMask;
+    static const uint8_t  kDevicePriorityMask;
+    static const uint8_t  kDevicePriorityHiLowBit;
+    static const uint32_t kDevicePriorityShift;
+
+    // Unconfgurable constants
+    static const int      kSetupRetryTimeoutMs;
+    static const int64_t  kNoGoodDataPanicThresholdUsec;
+    static const uint32_t kRTTDiscardPanicThreshMultiplier;
+
+    /*** status while in the Initial state ***/
+    int mInitial_WhoIsMasterRequestTimeouts;
+    static const int kInitial_NumWhoIsMasterRetries;
+    static const int kInitial_WhoIsMasterTimeoutMs;
+
+    /*** status while in the Client state ***/
+    uint64_t mClient_MasterDeviceID;
+    uint8_t mClient_MasterDevicePriority;
+    bool mClient_SyncRequestPending;
+    int mClient_SyncRequestTimeouts;
+    uint32_t mClient_SyncsSentToCurMaster;
+    uint32_t mClient_SyncRespsRXedFromCurMaster;
+    uint32_t mClient_ExpiredSyncRespsRXedFromCurMaster;
+    int64_t mClient_FirstSyncTX;
+    int64_t mClient_LastGoodSyncRX;
+    PacketRTTLog mClient_PacketRTTLog;
+    static const int kClient_NumSyncRequestRetries;
+
+
+    /*** status while in the Master state ***/
+    static const uint32_t kDefaultMaster_AnnouncementIntervalMs;
+
+    /*** status while in the Ronin state ***/
+    int mRonin_WhoIsMasterRequestTimeouts;
+    static const int kRonin_NumWhoIsMasterRetries;
+    static const int kRonin_WhoIsMasterTimeoutMs;
+
+    /*** status while in the WaitForElection state ***/
+    static const int kWaitForElection_TimeoutMs;
+
+    static const int kInfiniteTimeout;
+
+    static const char* stateToString(ICommonClock::State s);
+    static void sockaddrToString(const sockaddr_storage& addr, bool addrValid,
+                                 char* buf, size_t bufLen);
+    static bool sockaddrMatch(const sockaddr_storage& a1,
+                              const sockaddr_storage& a2,
+                              bool matchAddressOnly);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_COMMON_TIME_SERVER_H
diff --git a/services/common_time/common_time_server_api.cpp b/services/common_time/common_time_server_api.cpp
new file mode 100644
index 0000000..fb8c261
--- /dev/null
+++ b/services/common_time/common_time_server_api.cpp
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*
+ * A service that exchanges time synchronization information between
+ * a master that defines a timeline and clients that follow the timeline.
+ */
+
+#define LOG_TAG "common_time"
+#include <utils/Log.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+
+#include "common_time_server.h"
+
+namespace android {
+
+//
+// Clock API
+//
+uint64_t CommonTimeServer::getTimelineID() {
+    AutoMutex _lock(&mLock);
+    return mTimelineID;
+}
+
+ICommonClock::State CommonTimeServer::getState() {
+    AutoMutex _lock(&mLock);
+    return mState;
+}
+
+status_t CommonTimeServer::getMasterAddr(struct sockaddr_storage* addr) {
+    AutoMutex _lock(&mLock);
+    if (mMasterEPValid) {
+        memcpy(addr, &mMasterEP, sizeof(*addr));
+        return OK;
+    }
+
+    return UNKNOWN_ERROR;
+}
+
+int32_t CommonTimeServer::getEstimatedError() {
+    AutoMutex _lock(&mLock);
+
+    if (ICommonClock::STATE_MASTER == mState)
+        return 0;
+
+    if (!mClockSynced)
+        return ICommonClock::kErrorEstimateUnknown;
+
+    return mClockRecovery.getLastErrorEstimate();
+}
+
+status_t CommonTimeServer::isCommonTimeValid(bool* valid,
+                                             uint32_t* timelineID) {
+    AutoMutex _lock(&mLock);
+    *valid = mCommonClock.isValid();
+    *timelineID = mTimelineID;
+    return OK;
+}
+
+//
+// Config API
+//
+status_t CommonTimeServer::getMasterElectionPriority(uint8_t *priority) {
+    AutoMutex _lock(&mLock);
+    *priority = mMasterPriority;
+    return OK;
+}
+
+status_t CommonTimeServer::setMasterElectionPriority(uint8_t priority) {
+    AutoMutex _lock(&mLock);
+
+    if (priority > 0x7F)
+        return BAD_VALUE;
+
+    mMasterPriority = priority;
+    return OK;
+}
+
+status_t CommonTimeServer::getMasterElectionEndpoint(
+        struct sockaddr_storage *addr) {
+    AutoMutex _lock(&mLock);
+    memcpy(addr, &mMasterElectionEP, sizeof(*addr));
+    return OK;
+}
+
+status_t CommonTimeServer::setMasterElectionEndpoint(
+        const struct sockaddr_storage *addr) {
+    AutoMutex _lock(&mLock);
+
+    if (!addr)
+        return BAD_VALUE;
+
+    // TODO: add proper support for IPv6
+    if (addr->ss_family != AF_INET)
+        return BAD_VALUE;
+
+    // Only multicast and broadcast endpoints with explicit ports are allowed.
+    uint16_t ipv4Port = ntohs(
+        reinterpret_cast<const struct sockaddr_in*>(addr)->sin_port);
+    if (!ipv4Port)
+        return BAD_VALUE;
+
+    uint32_t ipv4Addr = ntohl(
+        reinterpret_cast<const struct sockaddr_in*>(addr)->sin_addr.s_addr);
+    if ((ipv4Addr != 0xFFFFFFFF) && (0xE0000000 != (ipv4Addr & 0xF0000000)))
+        return BAD_VALUE;
+
+    memcpy(&mMasterElectionEP, addr, sizeof(mMasterElectionEP));
+
+    // Force a rebind in order to change election enpoints.
+    mBindIfaceDirty = true;
+    wakeupThread_l();
+    return OK;
+}
+
+status_t CommonTimeServer::getMasterElectionGroupId(uint64_t *id) {
+    AutoMutex _lock(&mLock);
+    *id = mSyncGroupID;
+    return OK;
+}
+
+status_t CommonTimeServer::setMasterElectionGroupId(uint64_t id) {
+    AutoMutex _lock(&mLock);
+    mSyncGroupID = id;
+    return OK;
+}
+
+status_t CommonTimeServer::getInterfaceBinding(String8& ifaceName) {
+    AutoMutex _lock(&mLock);
+    if (!mBindIfaceValid)
+        return INVALID_OPERATION;
+    ifaceName = mBindIface;
+    return OK;
+}
+
+status_t CommonTimeServer::setInterfaceBinding(const String8& ifaceName) {
+    AutoMutex _lock(&mLock);
+
+    mBindIfaceDirty = true;
+    if (ifaceName.size()) {
+        mBindIfaceValid = true;
+        mBindIface = ifaceName;
+    } else {
+        mBindIfaceValid = false;
+        mBindIface.clear();
+    }
+
+    wakeupThread_l();
+    return OK;
+}
+
+status_t CommonTimeServer::getMasterAnnounceInterval(int *interval) {
+    AutoMutex _lock(&mLock);
+    *interval = mMasterAnnounceIntervalMs;
+    return OK;
+}
+
+status_t CommonTimeServer::setMasterAnnounceInterval(int interval) {
+    AutoMutex _lock(&mLock);
+
+    if (interval > (6 *3600000)) // Max interval is once every 6 hrs
+        return BAD_VALUE;
+
+    if (interval < 500) // Min interval is once per 0.5 seconds
+        return BAD_VALUE;
+
+    mMasterAnnounceIntervalMs = interval;
+    if (ICommonClock::STATE_MASTER == mState) {
+        int pendingTimeout = mCurTimeout.msecTillTimeout();
+        if ((kInfiniteTimeout == pendingTimeout) ||
+            (pendingTimeout > interval)) {
+            mCurTimeout.setTimeout(mMasterAnnounceIntervalMs);
+            wakeupThread_l();
+        }
+    }
+
+    return OK;
+}
+
+status_t CommonTimeServer::getClientSyncInterval(int *interval) {
+    AutoMutex _lock(&mLock);
+    *interval = mSyncRequestIntervalMs;
+    return OK;
+}
+
+status_t CommonTimeServer::setClientSyncInterval(int interval) {
+    AutoMutex _lock(&mLock);
+
+    if (interval > (3600000)) // Max interval is once every 60 min
+        return BAD_VALUE;
+
+    if (interval < 250) // Min interval is once per 0.25 seconds
+        return BAD_VALUE;
+
+    mSyncRequestIntervalMs = interval;
+    if (ICommonClock::STATE_CLIENT == mState) {
+        int pendingTimeout = mCurTimeout.msecTillTimeout();
+        if ((kInfiniteTimeout == pendingTimeout) ||
+            (pendingTimeout > interval)) {
+            mCurTimeout.setTimeout(mSyncRequestIntervalMs);
+            wakeupThread_l();
+        }
+    }
+
+    return OK;
+}
+
+status_t CommonTimeServer::getPanicThreshold(int *threshold) {
+    AutoMutex _lock(&mLock);
+    *threshold = mPanicThresholdUsec;
+    return OK;
+}
+
+status_t CommonTimeServer::setPanicThreshold(int threshold) {
+    AutoMutex _lock(&mLock);
+
+    if (threshold < 1000) // Min threshold is 1mSec
+        return BAD_VALUE;
+
+    mPanicThresholdUsec = threshold;
+    return OK;
+}
+
+status_t CommonTimeServer::getAutoDisable(bool *autoDisable) {
+    AutoMutex _lock(&mLock);
+    *autoDisable = mAutoDisable;
+    return OK;
+}
+
+status_t CommonTimeServer::setAutoDisable(bool autoDisable) {
+    AutoMutex _lock(&mLock);
+    mAutoDisable = autoDisable;
+    wakeupThread_l();
+    return OK;
+}
+
+status_t CommonTimeServer::forceNetworklessMasterMode() {
+    AutoMutex _lock(&mLock);
+
+    // Can't force networkless master mode if we are currently bound to a
+    // network.
+    if (mSocket >= 0)
+        return INVALID_OPERATION;
+
+    becomeMaster("force networkless");
+
+    return OK;
+}
+
+void CommonTimeServer::reevaluateAutoDisableState(bool commonClockHasClients) {
+    AutoMutex _lock(&mLock);
+    bool needWakeup = (mAutoDisable && mMasterEPValid &&
+                      (commonClockHasClients != mCommonClockHasClients));
+
+    mCommonClockHasClients = commonClockHasClients;
+
+    if (needWakeup) {
+        ALOGI("Waking up service, auto-disable is engaged and service now has%s"
+             " clients", mCommonClockHasClients ? "" : " no");
+        wakeupThread_l();
+    }
+}
+
+#define dump_printf(a, b...) do {                 \
+    int res;                                      \
+    res = snprintf(buffer, sizeof(buffer), a, b); \
+    buffer[sizeof(buffer) - 1] = 0;               \
+    if (res > 0)                                  \
+        write(fd, buffer, res);                   \
+} while (0)
+#define checked_percentage(a, b) ((0 == b) ? 0.0f : ((100.0f * a) / b))
+
+status_t CommonTimeServer::dumpClockInterface(int fd,
+                                              const Vector<String16>& args,
+                                              size_t activeClients) {
+    AutoMutex _lock(&mLock);
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+
+    if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+        snprintf(buffer, SIZE, "Permission Denial: "
+                 "can't dump CommonClockService from pid=%d, uid=%d\n",
+                 IPCThreadState::self()->getCallingPid(),
+                 IPCThreadState::self()->getCallingUid());
+        write(fd, buffer, strlen(buffer));
+    } else {
+        int64_t commonTime;
+        int64_t localTime;
+        bool    synced;
+        char maStr[64];
+
+        localTime  = mLocalClock.getLocalTime();
+        synced     = (OK == mCommonClock.localToCommon(localTime, &commonTime));
+        sockaddrToString(mMasterEP, mMasterEPValid, maStr, sizeof(maStr));
+
+        dump_printf("Common Clock Service Status\nLocal time     : %lld\n",
+                    localTime);
+
+        if (synced)
+            dump_printf("Common time    : %lld\n", commonTime);
+        else
+            dump_printf("Common time    : %s\n", "not synced");
+
+        dump_printf("Timeline ID    : %016llx\n", mTimelineID);
+        dump_printf("State          : %s\n", stateToString(mState));
+        dump_printf("Master Addr    : %s\n", maStr);
+
+
+        if (synced) {
+            int32_t est = (ICommonClock::STATE_MASTER != mState)
+                        ? mClockRecovery.getLastErrorEstimate()
+                        : 0;
+            dump_printf("Error Est.     : %.3f msec\n",
+                        static_cast<float>(est) / 1000.0);
+        } else {
+            dump_printf("Error Est.     : %s\n", "unknown");
+        }
+
+        dump_printf("Syncs TXes     : %u\n", mClient_SyncsSentToCurMaster);
+        dump_printf("Syncs RXes     : %u (%.2f%%)\n",
+                    mClient_SyncRespsRXedFromCurMaster,
+                    checked_percentage(
+                        mClient_SyncRespsRXedFromCurMaster,
+                        mClient_SyncsSentToCurMaster));
+        dump_printf("RXs Expired    : %u (%.2f%%)\n",
+                    mClient_ExpiredSyncRespsRXedFromCurMaster,
+                    checked_percentage(
+                        mClient_ExpiredSyncRespsRXedFromCurMaster,
+                        mClient_SyncsSentToCurMaster));
+
+        if (!mClient_LastGoodSyncRX) {
+            dump_printf("Last Good RX   : %s\n", "unknown");
+        } else {
+            int64_t localDelta, usecDelta;
+            localDelta = localTime - mClient_LastGoodSyncRX;
+            usecDelta  = mCommonClock.localDurationToCommonDuration(localDelta);
+            dump_printf("Last Good RX   : %lld uSec ago\n", usecDelta);
+        }
+
+        dump_printf("Active Clients : %u\n", activeClients);
+        mClient_PacketRTTLog.dumpLog(fd, mCommonClock);
+    }
+
+    return NO_ERROR;
+}
+
+status_t CommonTimeServer::dumpConfigInterface(int fd,
+                                               const Vector<String16>& args) {
+    AutoMutex _lock(&mLock);
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+
+    if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+        snprintf(buffer, SIZE, "Permission Denial: "
+                 "can't dump CommonTimeConfigService from pid=%d, uid=%d\n",
+                 IPCThreadState::self()->getCallingPid(),
+                 IPCThreadState::self()->getCallingUid());
+        write(fd, buffer, strlen(buffer));
+    } else {
+        char meStr[64];
+
+        sockaddrToString(mMasterElectionEP, true, meStr, sizeof(meStr));
+
+        dump_printf("Common Time Config Service Status\n"
+                    "Bound Interface           : %s\n",
+                    mBindIfaceValid ? mBindIface.string() : "<unbound>");
+        dump_printf("Master Election Endpoint  : %s\n", meStr);
+        dump_printf("Master Election Group ID  : %016llx\n", mSyncGroupID);
+        dump_printf("Master Announce Interval  : %d mSec\n",
+                    mMasterAnnounceIntervalMs);
+        dump_printf("Client Sync Interval      : %d mSec\n",
+                    mSyncRequestIntervalMs);
+        dump_printf("Panic Threshold           : %d uSec\n",
+                    mPanicThresholdUsec);
+        dump_printf("Base ME Prio              : 0x%02x\n",
+                    static_cast<uint32_t>(mMasterPriority));
+        dump_printf("Effective ME Prio         : 0x%02x\n",
+                    static_cast<uint32_t>(effectivePriority()));
+        dump_printf("Auto Disable Allowed      : %s\n",
+                    mAutoDisable ? "yes" : "no");
+        dump_printf("Auto Disable Engaged      : %s\n",
+                    shouldAutoDisable() ? "yes" : "no");
+    }
+
+    return NO_ERROR;
+}
+
+void CommonTimeServer::PacketRTTLog::dumpLog(int fd, const CommonClock& cclk) {
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    uint32_t avail = !logFull ? wrPtr : RTT_LOG_SIZE;
+
+    if (!avail)
+        return;
+
+    dump_printf("\nPacket Log (%d entries)\n", avail);
+
+    uint32_t ndx = 0;
+    uint32_t i = logFull ? wrPtr : 0;
+    do {
+        if (rxTimes[i]) {
+            int64_t delta = rxTimes[i] - txTimes[i];
+            int64_t deltaUsec = cclk.localDurationToCommonDuration(delta);
+            dump_printf("pkt[%2d] : localTX %12lld localRX %12lld "
+                        "(%.3f msec RTT)\n",
+                        ndx, txTimes[i], rxTimes[i],
+                        static_cast<float>(deltaUsec) / 1000.0);
+        } else {
+            dump_printf("pkt[%2d] : localTX %12lld localRX never\n",
+                        ndx, txTimes[i]);
+        }
+        i = (i + 1) % RTT_LOG_SIZE;
+        ndx++;
+    } while (i != wrPtr);
+}
+
+#undef dump_printf
+#undef checked_percentage
+
+}  // namespace android
diff --git a/services/common_time/common_time_server_packets.cpp b/services/common_time/common_time_server_packets.cpp
new file mode 100644
index 0000000..9833c37
--- /dev/null
+++ b/services/common_time/common_time_server_packets.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*
+ * A service that exchanges time synchronization information between
+ * a master that defines a timeline and clients that follow the timeline.
+ */
+
+#define LOG_TAG "common_time"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <stdint.h>
+
+#include "common_time_server_packets.h"
+
+namespace android {
+
+const uint32_t TimeServicePacketHeader::kMagic =
+    (static_cast<uint32_t>('c') << 24) |
+    (static_cast<uint32_t>('c') << 16) |
+    (static_cast<uint32_t>('l') <<  8) |
+     static_cast<uint32_t>('k');
+
+const uint16_t TimeServicePacketHeader::kCurVersion = 1;
+
+#define SERIALIZE_FIELD(field_name, type, converter)        \
+    do {                                                    \
+        if ((offset + sizeof(field_name)) > length)         \
+            return -1;                                      \
+        *((type*)(data + offset)) = converter(field_name);  \
+        offset += sizeof(field_name);                       \
+    } while (0)
+#define SERIALIZE_INT16(field_name) SERIALIZE_FIELD(field_name, int16_t, htons)
+#define SERIALIZE_INT32(field_name) SERIALIZE_FIELD(field_name, int32_t, htonl)
+#define SERIALIZE_INT64(field_name) SERIALIZE_FIELD(field_name, int64_t, htonq)
+
+#define DESERIALIZE_FIELD(field_name, type, converter)      \
+    do {                                                    \
+        if ((offset + sizeof(field_name)) > length)         \
+            return -1;                                      \
+        field_name = converter(*((type*)(data + offset)));  \
+        offset += sizeof(field_name);                       \
+    } while (0)
+#define DESERIALIZE_INT16(field_name) DESERIALIZE_FIELD(field_name, int16_t, ntohs)
+#define DESERIALIZE_INT32(field_name) DESERIALIZE_FIELD(field_name, int32_t, ntohl)
+#define DESERIALIZE_INT64(field_name) DESERIALIZE_FIELD(field_name, int64_t, ntohq)
+
+#define kDevicePriorityShift 56
+#define kDeviceIDMask ((static_cast<uint64_t>(1) << kDevicePriorityShift) - 1)
+
+inline uint64_t packDeviceID(uint64_t devID, uint8_t prio) {
+    return (devID & kDeviceIDMask) |
+           (static_cast<uint64_t>(prio) << kDevicePriorityShift);
+}
+
+inline uint64_t unpackDeviceID(uint64_t packed) {
+    return (packed & kDeviceIDMask);
+}
+
+inline uint8_t unpackDevicePriority(uint64_t packed) {
+    return static_cast<uint8_t>(packed >> kDevicePriorityShift);
+}
+
+ssize_t TimeServicePacketHeader::serializeHeader(uint8_t* data,
+                                                 uint32_t length) {
+    ssize_t offset = 0;
+    int16_t pktType = static_cast<int16_t>(packetType);
+    SERIALIZE_INT32(magic);
+    SERIALIZE_INT16(version);
+    SERIALIZE_INT16(pktType);
+    SERIALIZE_INT64(timelineID);
+    SERIALIZE_INT64(syncGroupID);
+    return offset;
+}
+
+ssize_t TimeServicePacketHeader::deserializeHeader(const uint8_t* data,
+                                                   uint32_t length) {
+    ssize_t offset = 0;
+    int16_t tmp;
+    DESERIALIZE_INT32(magic);
+    DESERIALIZE_INT16(version);
+    DESERIALIZE_INT16(tmp);
+    DESERIALIZE_INT64(timelineID);
+    DESERIALIZE_INT64(syncGroupID);
+    packetType = static_cast<TimeServicePacketType>(tmp);
+    return offset;
+}
+
+ssize_t TimeServicePacketHeader::serializePacket(uint8_t* data,
+                                                 uint32_t length) {
+    ssize_t ret, tmp;
+
+    ret = serializeHeader(data, length);
+    if (ret < 0)
+        return ret;
+
+    data += ret;
+    length -= ret;
+
+    switch (packetType) {
+        case TIME_PACKET_WHO_IS_MASTER_REQUEST:
+            tmp =((WhoIsMasterRequestPacket*)(this))->serializePacket(data,
+                                                                      length);
+            break;
+        case TIME_PACKET_WHO_IS_MASTER_RESPONSE:
+            tmp =((WhoIsMasterResponsePacket*)(this))->serializePacket(data,
+                                                                       length);
+            break;
+        case TIME_PACKET_SYNC_REQUEST:
+            tmp =((SyncRequestPacket*)(this))->serializePacket(data, length);
+            break;
+        case TIME_PACKET_SYNC_RESPONSE:
+            tmp =((SyncResponsePacket*)(this))->serializePacket(data, length);
+            break;
+        case TIME_PACKET_MASTER_ANNOUNCEMENT:
+            tmp =((MasterAnnouncementPacket*)(this))->serializePacket(data,
+                                                                      length);
+            break;
+        default:
+            return -1;
+    }
+
+    if (tmp < 0)
+        return tmp;
+
+    return ret + tmp;
+}
+
+ssize_t UniversalTimeServicePacket::deserializePacket(
+        const uint8_t* data,
+        uint32_t length,
+        uint64_t expectedSyncGroupID) {
+    ssize_t ret;
+    TimeServicePacketHeader* header;
+    if (length < 8)
+        return -1;
+
+    packetType = ntohs(*((uint16_t*)(data + 6)));
+    switch (packetType) {
+        case TIME_PACKET_WHO_IS_MASTER_REQUEST:
+            ret = p.who_is_master_request.deserializePacket(data, length);
+            header = &p.who_is_master_request;
+            break;
+        case TIME_PACKET_WHO_IS_MASTER_RESPONSE:
+            ret = p.who_is_master_response.deserializePacket(data, length);
+            header = &p.who_is_master_response;
+            break;
+        case TIME_PACKET_SYNC_REQUEST:
+            ret = p.sync_request.deserializePacket(data, length);
+            header = &p.sync_request;
+            break;
+        case TIME_PACKET_SYNC_RESPONSE:
+            ret = p.sync_response.deserializePacket(data, length);
+            header = &p.sync_response;
+            break;
+        case TIME_PACKET_MASTER_ANNOUNCEMENT:
+            ret = p.master_announcement.deserializePacket(data, length);
+            header = &p.master_announcement;
+            break;
+        default:
+            return -1;
+    }
+
+    if ((ret >= 0) && !header->checkPacket(expectedSyncGroupID))
+        ret = -1;
+
+    return ret;
+}
+
+ssize_t WhoIsMasterRequestPacket::serializePacket(uint8_t* data,
+                                                  uint32_t length) {
+    ssize_t offset = serializeHeader(data, length);
+    if (offset > 0) {
+        uint64_t packed = packDeviceID(senderDeviceID, senderDevicePriority);
+        SERIALIZE_INT64(packed);
+    }
+    return offset;
+}
+
+ssize_t WhoIsMasterRequestPacket::deserializePacket(const uint8_t* data,
+                                                    uint32_t length) {
+    ssize_t offset = deserializeHeader(data, length);
+    if (offset > 0) {
+        uint64_t packed;
+        DESERIALIZE_INT64(packed);
+        senderDeviceID       = unpackDeviceID(packed);
+        senderDevicePriority = unpackDevicePriority(packed);
+    }
+    return offset;
+}
+
+ssize_t WhoIsMasterResponsePacket::serializePacket(uint8_t* data,
+                                                   uint32_t length) {
+    ssize_t offset = serializeHeader(data, length);
+    if (offset > 0) {
+        uint64_t packed = packDeviceID(deviceID, devicePriority);
+        SERIALIZE_INT64(packed);
+    }
+    return offset;
+}
+
+ssize_t WhoIsMasterResponsePacket::deserializePacket(const uint8_t* data,
+                                                     uint32_t length) {
+    ssize_t offset = deserializeHeader(data, length);
+    if (offset > 0) {
+        uint64_t packed;
+        DESERIALIZE_INT64(packed);
+        deviceID       = unpackDeviceID(packed);
+        devicePriority = unpackDevicePriority(packed);
+    }
+    return offset;
+}
+
+ssize_t SyncRequestPacket::serializePacket(uint8_t* data,
+                                           uint32_t length) {
+    ssize_t offset = serializeHeader(data, length);
+    if (offset > 0) {
+        SERIALIZE_INT64(clientTxLocalTime);
+    }
+    return offset;
+}
+
+ssize_t SyncRequestPacket::deserializePacket(const uint8_t* data,
+                                             uint32_t length) {
+    ssize_t offset = deserializeHeader(data, length);
+    if (offset > 0) {
+        DESERIALIZE_INT64(clientTxLocalTime);
+    }
+    return offset;
+}
+
+ssize_t SyncResponsePacket::serializePacket(uint8_t* data,
+                                            uint32_t length) {
+    ssize_t offset = serializeHeader(data, length);
+    if (offset > 0) {
+        SERIALIZE_INT64(clientTxLocalTime);
+        SERIALIZE_INT64(masterRxCommonTime);
+        SERIALIZE_INT64(masterTxCommonTime);
+        SERIALIZE_INT32(nak);
+    }
+    return offset;
+}
+
+ssize_t SyncResponsePacket::deserializePacket(const uint8_t* data,
+                                              uint32_t length) {
+    ssize_t offset = deserializeHeader(data, length);
+    if (offset > 0) {
+        DESERIALIZE_INT64(clientTxLocalTime);
+        DESERIALIZE_INT64(masterRxCommonTime);
+        DESERIALIZE_INT64(masterTxCommonTime);
+        DESERIALIZE_INT32(nak);
+    }
+    return offset;
+}
+
+ssize_t MasterAnnouncementPacket::serializePacket(uint8_t* data,
+                                                  uint32_t length) {
+    ssize_t offset = serializeHeader(data, length);
+    if (offset > 0) {
+        uint64_t packed = packDeviceID(deviceID, devicePriority);
+        SERIALIZE_INT64(packed);
+    }
+    return offset;
+}
+
+ssize_t MasterAnnouncementPacket::deserializePacket(const uint8_t* data,
+                                                    uint32_t length) {
+    ssize_t offset = deserializeHeader(data, length);
+    if (offset > 0) {
+        uint64_t packed;
+        DESERIALIZE_INT64(packed);
+        deviceID       = unpackDeviceID(packed);
+        devicePriority = unpackDevicePriority(packed);
+    }
+    return offset;
+}
+
+}  // namespace android
+
diff --git a/services/common_time/common_time_server_packets.h b/services/common_time/common_time_server_packets.h
new file mode 100644
index 0000000..57ba8a2
--- /dev/null
+++ b/services/common_time/common_time_server_packets.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_COMMON_TIME_SERVER_PACKETS_H
+#define ANDROID_COMMON_TIME_SERVER_PACKETS_H
+
+#include <stdint.h>
+#include <common_time/ICommonClock.h>
+
+namespace android {
+
+/***** time sync protocol packets *****/
+
+enum TimeServicePacketType {
+    TIME_PACKET_WHO_IS_MASTER_REQUEST = 1,
+    TIME_PACKET_WHO_IS_MASTER_RESPONSE,
+    TIME_PACKET_SYNC_REQUEST,
+    TIME_PACKET_SYNC_RESPONSE,
+    TIME_PACKET_MASTER_ANNOUNCEMENT,
+};
+
+class TimeServicePacketHeader {
+  public:
+    friend class UniversalTimeServicePacket;
+    // magic number identifying the protocol
+    uint32_t magic;
+
+    // protocol version of the packet
+    uint16_t version;
+
+    // type of the packet
+    TimeServicePacketType packetType;
+
+    // the timeline ID
+    uint64_t timelineID;
+
+    // synchronization group this packet belongs to (used to operate multiple
+    // synchronization domains which all use the same master election endpoint)
+    uint64_t syncGroupID;
+
+    ssize_t serializePacket(uint8_t* data, uint32_t length);
+
+  protected:
+    void initHeader(TimeServicePacketType type,
+                    const uint64_t tlID,
+                    const uint64_t groupID) {
+        magic              = kMagic;
+        version            = kCurVersion;
+        packetType         = type;
+        timelineID         = tlID;
+        syncGroupID        = groupID;
+    }
+
+    bool checkPacket(uint64_t expectedSyncGroupID) const {
+        return ((magic       == kMagic) &&
+                (version     == kCurVersion) &&
+                (!expectedSyncGroupID || (syncGroupID == expectedSyncGroupID)));
+    }
+
+    ssize_t serializeHeader(uint8_t* data, uint32_t length);
+    ssize_t deserializeHeader(const uint8_t* data, uint32_t length);
+
+  private:
+    static const uint32_t kMagic;
+    static const uint16_t kCurVersion;
+};
+
+// packet querying for a suitable master
+class WhoIsMasterRequestPacket : public TimeServicePacketHeader {
+  public:
+    uint64_t senderDeviceID;
+    uint8_t senderDevicePriority;
+
+    void initHeader(const uint64_t groupID) {
+        TimeServicePacketHeader::initHeader(TIME_PACKET_WHO_IS_MASTER_REQUEST,
+                                            ICommonClock::kInvalidTimelineID,
+                                            groupID);
+    }
+
+    ssize_t serializePacket(uint8_t* data, uint32_t length);
+    ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+// response to a WhoIsMaster request
+class WhoIsMasterResponsePacket : public TimeServicePacketHeader {
+  public:
+    uint64_t deviceID;
+    uint8_t devicePriority;
+
+    void initHeader(const uint64_t tlID, const uint64_t groupID) {
+        TimeServicePacketHeader::initHeader(TIME_PACKET_WHO_IS_MASTER_RESPONSE,
+                                            tlID, groupID);
+    }
+
+    ssize_t serializePacket(uint8_t* data, uint32_t length);
+    ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+// packet sent by a client requesting correspondence between local
+// and common time
+class SyncRequestPacket : public TimeServicePacketHeader {
+  public:
+    // local time when this request was transmitted
+    int64_t clientTxLocalTime;
+
+    void initHeader(const uint64_t tlID, const uint64_t groupID) {
+        TimeServicePacketHeader::initHeader(TIME_PACKET_SYNC_REQUEST,
+                                            tlID, groupID);
+    }
+
+    ssize_t serializePacket(uint8_t* data, uint32_t length);
+    ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+// response to a sync request sent by the master
+class SyncResponsePacket : public TimeServicePacketHeader {
+  public:
+    // local time when this request was transmitted by the client
+    int64_t clientTxLocalTime;
+
+    // common time when the master received the request
+    int64_t masterRxCommonTime;
+
+    // common time when the master transmitted the response
+    int64_t masterTxCommonTime;
+
+    // flag that is set if the recipient of the sync request is not acting
+    // as a master for the requested timeline
+    uint32_t nak;
+
+    void initHeader(const uint64_t tlID, const uint64_t groupID) {
+        TimeServicePacketHeader::initHeader(TIME_PACKET_SYNC_RESPONSE,
+                                            tlID, groupID);
+    }
+
+    ssize_t serializePacket(uint8_t* data, uint32_t length);
+    ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+// announcement of the master's presence
+class MasterAnnouncementPacket : public TimeServicePacketHeader {
+  public:
+    // the master's device ID
+    uint64_t deviceID;
+    uint8_t devicePriority;
+
+    void initHeader(const uint64_t tlID, const uint64_t groupID) {
+        TimeServicePacketHeader::initHeader(TIME_PACKET_MASTER_ANNOUNCEMENT,
+                                            tlID, groupID);
+    }
+
+    ssize_t serializePacket(uint8_t* data, uint32_t length);
+    ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+class UniversalTimeServicePacket {
+  public:
+    uint16_t packetType;
+    union {
+        WhoIsMasterRequestPacket  who_is_master_request;
+        WhoIsMasterResponsePacket who_is_master_response;
+        SyncRequestPacket         sync_request;
+        SyncResponsePacket        sync_response;
+        MasterAnnouncementPacket  master_announcement;
+    } p;
+
+    ssize_t deserializePacket(const uint8_t* data,
+                              uint32_t       length,
+                              uint64_t       expectedSyncGroupID);
+};
+
+};  // namespace android
+
+#endif  // ANDROID_COMMON_TIME_SERVER_PACKETS_H
+
+
diff --git a/services/common_time/diag_thread.cpp b/services/common_time/diag_thread.cpp
new file mode 100644
index 0000000..4cb9551
--- /dev/null
+++ b/services/common_time/diag_thread.cpp
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "common_time"
+#include <utils/Log.h>
+
+#include <fcntl.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utils/Errors.h>
+#include <utils/misc.h>
+
+#include <common_time/local_clock.h>
+
+#include "common_clock.h"
+#include "diag_thread.h"
+
+#define kMaxEvents 16
+#define kListenPort 9876
+
+static bool setNonblocking(int fd) {
+    int flags = fcntl(fd, F_GETFL);
+    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+        ALOGE("Failed to set socket (%d) to non-blocking mode (errno %d)",
+             fd, errno);
+        return false;
+    }
+
+    return true;
+}
+
+static bool setNodelay(int fd) {
+    int tmp = 1;
+    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp)) < 0) {
+        ALOGE("Failed to set socket (%d) to no-delay mode (errno %d)",
+             fd, errno);
+        return false;
+    }
+
+    return true;
+}
+
+namespace android {
+
+DiagThread::DiagThread(CommonClock* common_clock, LocalClock* local_clock) {
+    common_clock_ = common_clock;
+    local_clock_ = local_clock;
+    listen_fd_ = -1;
+    data_fd_ = -1;
+    kernel_logID_basis_known_ = false;
+    discipline_log_ID_ = 0;
+}
+
+DiagThread::~DiagThread() {
+}
+
+status_t DiagThread::startWorkThread() {
+    status_t res;
+    stopWorkThread();
+    res = run("Diag");
+
+    if (res != OK)
+        ALOGE("Failed to start work thread (res = %d)", res);
+
+    return res;
+}
+
+void DiagThread::stopWorkThread() {
+    status_t res;
+    res = requestExitAndWait(); // block until thread exit.
+    if (res != OK)
+        ALOGE("Failed to stop work thread (res = %d)", res);
+}
+
+bool DiagThread::openListenSocket() {
+    bool ret = false;
+    int flags;
+    cleanupListenSocket();
+
+    if ((listen_fd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+        ALOGE("Socket failed.");
+        goto bailout;
+    }
+
+    // Set non-blocking operation
+    if (!setNonblocking(listen_fd_))
+        goto bailout;
+
+    struct sockaddr_in addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = INADDR_ANY;
+    addr.sin_port = htons(kListenPort);
+
+    if (bind(listen_fd_, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+        ALOGE("Bind failed.");
+        goto bailout;
+    }
+
+    if (listen(listen_fd_, 1) < 0) {
+        ALOGE("Listen failed.");
+        goto bailout;
+    }
+
+    ret = true;
+bailout:
+    if (!ret)
+        cleanupListenSocket();
+
+    return ret;
+}
+
+void DiagThread::cleanupListenSocket() {
+    if (listen_fd_ >= 0) {
+        int res;
+
+        struct linger l;
+        l.l_onoff  = 1;
+        l.l_linger = 0;
+
+        setsockopt(listen_fd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+        shutdown(listen_fd_, SHUT_RDWR);
+        close(listen_fd_);
+        listen_fd_ = -1;
+    }
+}
+
+void DiagThread::cleanupDataSocket() {
+    if (data_fd_ >= 0) {
+        int res;
+
+        struct linger l;
+        l.l_onoff  = 1;
+        l.l_linger = 0;
+
+        setsockopt(data_fd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+        shutdown(data_fd_, SHUT_RDWR);
+        close(data_fd_);
+        data_fd_ = -1;
+    }
+}
+
+void DiagThread::resetLogIDs() {
+    // Drain and discard all of the events from the kernel
+    struct local_time_debug_event events[kMaxEvents];
+    while(local_clock_->getDebugLog(events, kMaxEvents) > 0)
+        ;
+
+    {
+        Mutex::Autolock lock(&discipline_log_lock_);
+        discipline_log_.clear();
+        discipline_log_ID_ = 0;
+    }
+
+    kernel_logID_basis_known_ = false;
+}
+
+void DiagThread::pushDisciplineEvent(int64_t observed_local_time,
+                                     int64_t observed_common_time,
+                                     int64_t nominal_common_time,
+                                     int32_t total_correction,
+                                     int32_t rtt) {
+    Mutex::Autolock lock(&discipline_log_lock_);
+
+    DisciplineEventRecord evt;
+
+    evt.event_id = discipline_log_ID_++;
+
+    evt.action_local_time = local_clock_->getLocalTime();
+    common_clock_->localToCommon(evt.action_local_time,
+            &evt.action_common_time);
+
+    evt.observed_local_time  = observed_local_time;
+    evt.observed_common_time = observed_common_time;
+    evt.nominal_common_time  = nominal_common_time;
+    evt.total_correction     = total_correction;
+    evt.rtt                  = rtt;
+
+    discipline_log_.push_back(evt);
+    while (discipline_log_.size() > kMaxDisciplineLogSize)
+        discipline_log_.erase(discipline_log_.begin());
+}
+
+bool DiagThread::threadLoop() {
+    struct pollfd poll_fds[1];
+
+    if (!openListenSocket()) {
+        ALOGE("Failed to open listen socket");
+        goto bailout;
+    }
+
+    while (!exitPending()) {
+        memset(&poll_fds, 0, sizeof(poll_fds));
+
+        if (data_fd_ < 0) {
+            poll_fds[0].fd     = listen_fd_;
+            poll_fds[0].events = POLLIN;
+        } else {
+            poll_fds[0].fd     = data_fd_;
+            poll_fds[0].events = POLLRDHUP | POLLIN;
+        }
+
+        int poll_res = poll(poll_fds, NELEM(poll_fds), 50);
+        if (poll_res < 0) {
+            ALOGE("Fatal error (%d,%d) while waiting on events",
+                 poll_res, errno);
+            goto bailout;
+        }
+
+        if (exitPending())
+            break;
+
+        if (poll_fds[0].revents) {
+            if (poll_fds[0].fd == listen_fd_) {
+                data_fd_ = accept(listen_fd_, NULL, NULL);
+
+                if (data_fd_ < 0) {
+                    ALOGW("Failed accept on socket %d with err %d",
+                         listen_fd_, errno);
+                } else {
+                    if (!setNonblocking(data_fd_))
+                        cleanupDataSocket();
+                    if (!setNodelay(data_fd_))
+                        cleanupDataSocket();
+                }
+            } else
+                if (poll_fds[0].fd == data_fd_) {
+                    if (poll_fds[0].revents & POLLRDHUP) {
+                        // Connection hung up; time to clean up.
+                        cleanupDataSocket();
+                    } else
+                        if (poll_fds[0].revents & POLLIN) {
+                            uint8_t cmd;
+                            if (read(data_fd_, &cmd, sizeof(cmd)) > 0) {
+                                switch(cmd) {
+                                    case 'r':
+                                    case 'R':
+                                        resetLogIDs();
+                                        break;
+                                }
+                            }
+                        }
+                }
+        }
+
+        struct local_time_debug_event events[kMaxEvents];
+        int amt = local_clock_->getDebugLog(events, kMaxEvents);
+
+        if (amt > 0) {
+            for (int i = 0; i < amt; i++) {
+                struct local_time_debug_event& e = events[i];
+
+                if (!kernel_logID_basis_known_) {
+                    kernel_logID_basis_ = e.local_timesync_event_id;
+                    kernel_logID_basis_known_ = true;
+                }
+
+                char buf[1024];
+                int64_t common_time;
+                status_t res = common_clock_->localToCommon(e.local_time,
+                                                            &common_time);
+                snprintf(buf, sizeof(buf), "E,%lld,%lld,%lld,%d\n",
+                         e.local_timesync_event_id - kernel_logID_basis_,
+                         e.local_time,
+                         common_time,
+                         (OK == res) ? 1 : 0);
+                buf[sizeof(buf) - 1] = 0;
+
+                if (data_fd_ >= 0)
+                    write(data_fd_, buf, strlen(buf));
+            }
+        }
+
+        { // scope for autolock pattern
+            Mutex::Autolock lock(&discipline_log_lock_);
+
+            while (discipline_log_.size() > 0) {
+                char buf[1024];
+                DisciplineEventRecord& e = *discipline_log_.begin();
+                snprintf(buf, sizeof(buf),
+                         "D,%lld,%lld,%lld,%lld,%lld,%lld,%d,%d\n",
+                         e.event_id,
+                         e.action_local_time,
+                         e.action_common_time,
+                         e.observed_local_time,
+                         e.observed_common_time,
+                         e.nominal_common_time,
+                         e.total_correction,
+                         e.rtt);
+                buf[sizeof(buf) - 1] = 0;
+
+                if (data_fd_ >= 0)
+                    write(data_fd_, buf, strlen(buf));
+
+                discipline_log_.erase(discipline_log_.begin());
+            }
+        }
+    }
+
+bailout:
+    cleanupDataSocket();
+    cleanupListenSocket();
+    return false;
+}
+
+}  // namespace android
diff --git a/services/common_time/diag_thread.h b/services/common_time/diag_thread.h
new file mode 100644
index 0000000..c630e0d
--- /dev/null
+++ b/services/common_time/diag_thread.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef __DIAG_THREAD_H__
+#define __DIAG_THREAD_H__
+
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class CommonClock;
+class LocalClock;
+
+class DiagThread : public Thread {
+  public:
+    DiagThread(CommonClock* common_clock, LocalClock* local_clock);
+    ~DiagThread();
+
+    status_t      startWorkThread();
+    void          stopWorkThread();
+    virtual bool  threadLoop();
+
+    void pushDisciplineEvent(int64_t observed_local_time,
+                             int64_t observed_common_time,
+                             int64_t nominal_common_time,
+                             int32_t total_correction,
+                             int32_t rtt);
+
+  private:
+    typedef struct {
+        int64_t event_id;
+        int64_t action_local_time;
+        int64_t action_common_time;
+        int64_t observed_local_time;
+        int64_t observed_common_time;
+        int64_t nominal_common_time;
+        int32_t total_correction;
+        int32_t rtt;
+    } DisciplineEventRecord;
+
+    bool            openListenSocket();
+    void            cleanupListenSocket();
+    void            cleanupDataSocket();
+    void            resetLogIDs();
+
+    CommonClock*    common_clock_;
+    LocalClock*     local_clock_;
+    int             listen_fd_;
+    int             data_fd_;
+
+    int64_t         kernel_logID_basis_;
+    bool            kernel_logID_basis_known_;
+
+    static const size_t         kMaxDisciplineLogSize = 16;
+    Mutex                       discipline_log_lock_;
+    List<DisciplineEventRecord> discipline_log_;
+    int64_t                     discipline_log_ID_;
+};
+
+}  // namespace android
+
+#endif  //__ DIAG_THREAD_H__
diff --git a/services/common_time/main.cpp b/services/common_time/main.cpp
new file mode 100644
index 0000000..49eb30a
--- /dev/null
+++ b/services/common_time/main.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*
+ * A service that exchanges time synchronization information between
+ * a master that defines a timeline and clients that follow the timeline.
+ */
+
+#define LOG_TAG "common_time"
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+
+#include "common_time_server.h"
+
+int main(int argc, char *argv[]) {
+    using namespace android;
+
+    sp<CommonTimeServer> service = new CommonTimeServer();
+    if (service == NULL)
+        return 1;
+
+    ProcessState::self()->startThreadPool();
+    service->run("CommonTimeServer", ANDROID_PRIORITY_NORMAL);
+
+    IPCThreadState::self()->joinThreadPool();
+    return 0;
+}
+
diff --git a/services/input/Android.mk b/services/input/Android.mk
index 86c6c8a..159800f 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -29,6 +29,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
+    libandroidfw \
     libutils \
     libhardware \
     libhardware_legacy \
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 296c95e..1b74aa6 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -36,9 +36,9 @@
 #include <errno.h>
 #include <assert.h>
 
-#include <ui/KeyLayoutMap.h>
-#include <ui/KeyCharacterMap.h>
-#include <ui/VirtualKeyMap.h>
+#include <androidfw/KeyLayoutMap.h>
+#include <androidfw/KeyCharacterMap.h>
+#include <androidfw/VirtualKeyMap.h>
 
 #include <string.h>
 #include <stdint.h>
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 8a2afd3..4eb47c6 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -18,11 +18,11 @@
 #ifndef _RUNTIME_EVENT_HUB_H
 #define _RUNTIME_EVENT_HUB_H
 
-#include <ui/Input.h>
-#include <ui/Keyboard.h>
-#include <ui/KeyLayoutMap.h>
-#include <ui/KeyCharacterMap.h>
-#include <ui/VirtualKeyMap.h>
+#include <androidfw/Input.h>
+#include <androidfw/Keyboard.h>
+#include <androidfw/KeyLayoutMap.h>
+#include <androidfw/KeyCharacterMap.h>
+#include <androidfw/VirtualKeyMap.h>
 #include <utils/String8.h>
 #include <utils/threads.h>
 #include <utils/Log.h>
diff --git a/services/input/InputApplication.h b/services/input/InputApplication.h
index 67ae94b..c04a935 100644
--- a/services/input/InputApplication.h
+++ b/services/input/InputApplication.h
@@ -17,7 +17,7 @@
 #ifndef _UI_INPUT_APPLICATION_H
 #define _UI_INPUT_APPLICATION_H
 
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index ad64ccd..149c0d3 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -24,24 +24,15 @@
 // Log detailed debug messages about each outbound event processed by the dispatcher.
 #define DEBUG_OUTBOUND_EVENT_DETAILS 0
 
-// Log debug messages about batching.
-#define DEBUG_BATCHING 0
-
 // Log debug messages about the dispatch cycle.
 #define DEBUG_DISPATCH_CYCLE 0
 
 // Log debug messages about registrations.
 #define DEBUG_REGISTRATION 0
 
-// Log debug messages about performance statistics.
-#define DEBUG_PERFORMANCE_STATISTICS 0
-
 // Log debug messages about input event injection.
 #define DEBUG_INJECTION 0
 
-// Log debug messages about input event throttling.
-#define DEBUG_THROTTLING 0
-
 // Log debug messages about input focus tracking.
 #define DEBUG_FOCUS 0
 
@@ -54,7 +45,7 @@
 #include "InputDispatcher.h"
 
 #include <cutils/log.h>
-#include <ui/PowerManager.h>
+#include <androidfw/PowerManager.h>
 
 #include <stddef.h>
 #include <unistd.h>
@@ -79,21 +70,11 @@
 // before considering it stale and dropping it.
 const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
 
-// Motion samples that are received within this amount of time are simply coalesced
-// when batched instead of being appended.  This is done because some drivers update
-// the location of pointers one at a time instead of all at once.
-// For example, when there are 10 fingers down, the input dispatcher may receive 10
-// samples in quick succession with only one finger's location changed in each sample.
-//
-// This value effectively imposes an upper bound on the touch sampling rate.
-// Touch sensors typically have a 50Hz - 200Hz sampling rate, so we expect distinct
-// samples to become available 5-20ms apart but individual finger reports can trickle
-// in over a period of 2-4ms or so.
-//
-// Empirical testing shows that a 2ms coalescing interval (500Hz) is not enough,
-// a 3ms coalescing interval (333Hz) works well most of the time and doesn't introduce
-// significant quantization noise on current hardware.
-const nsecs_t MOTION_SAMPLE_COALESCE_INTERVAL = 3 * 1000000LL; // 3ms, 333Hz
+// Amount of time to allow touch events to be streamed out to a connection before requiring
+// that the first event be finished.  This value extends the ANR timeout by the specified
+// amount.  For example, if streaming is allowed to get ahead by one second relative to the
+// queue of waiting unfinished events, then ANRs will similarly be delayed by one second.
+const nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec
 
 
 static inline nsecs_t now() {
@@ -203,21 +184,12 @@
     mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
     mNextUnblockedEvent(NULL),
     mDispatchEnabled(true), mDispatchFrozen(false), mInputFilterEnabled(false),
-    mCurrentInputTargetsValid(false),
     mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
     mLooper = new Looper(false);
 
     mKeyRepeatState.lastKeyEntry = NULL;
 
     policy->getDispatcherConfiguration(&mConfig);
-
-    mThrottleState.minTimeBetweenEvents = 1000000000LL / mConfig.maxEventsPerSecond;
-    mThrottleState.lastDeviceId = -1;
-
-#if DEBUG_THROTTLING
-    mThrottleState.originalSampleCount = 0;
-    ALOGD("Throttling - Max events per second = %d", mConfig.maxEventsPerSecond);
-#endif
 }
 
 InputDispatcher::~InputDispatcher() {
@@ -229,8 +201,8 @@
         drainInboundQueueLocked();
     }
 
-    while (mConnectionsByReceiveFd.size() != 0) {
-        unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
+    while (mConnectionsByFd.size() != 0) {
+        unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel);
     }
 }
 
@@ -302,78 +274,21 @@
             }
 
             // Nothing to do if there is no pending event.
-            if (! mPendingEvent) {
-                if (mActiveConnections.isEmpty()) {
-                    dispatchIdleLocked();
-                }
+            if (!mPendingEvent) {
                 return;
             }
         } else {
             // Inbound queue has at least one entry.
-            EventEntry* entry = mInboundQueue.head;
-
-            // Throttle the entry if it is a move event and there are no
-            // other events behind it in the queue.  Due to movement batching, additional
-            // samples may be appended to this event by the time the throttling timeout
-            // expires.
-            // TODO Make this smarter and consider throttling per device independently.
-            if (entry->type == EventEntry::TYPE_MOTION
-                    && !isAppSwitchDue
-                    && mDispatchEnabled
-                    && (entry->policyFlags & POLICY_FLAG_PASS_TO_USER)
-                    && !entry->isInjected()) {
-                MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-                int32_t deviceId = motionEntry->deviceId;
-                uint32_t source = motionEntry->source;
-                if (! isAppSwitchDue
-                        && !motionEntry->next // exactly one event, no successors
-                        && (motionEntry->action == AMOTION_EVENT_ACTION_MOVE
-                                || motionEntry->action == AMOTION_EVENT_ACTION_HOVER_MOVE)
-                        && deviceId == mThrottleState.lastDeviceId
-                        && source == mThrottleState.lastSource) {
-                    nsecs_t nextTime = mThrottleState.lastEventTime
-                            + mThrottleState.minTimeBetweenEvents;
-                    if (currentTime < nextTime) {
-                        // Throttle it!
-#if DEBUG_THROTTLING
-                        ALOGD("Throttling - Delaying motion event for "
-                                "device %d, source 0x%08x by up to %0.3fms.",
-                                deviceId, source, (nextTime - currentTime) * 0.000001);
-#endif
-                        if (nextTime < *nextWakeupTime) {
-                            *nextWakeupTime = nextTime;
-                        }
-                        if (mThrottleState.originalSampleCount == 0) {
-                            mThrottleState.originalSampleCount =
-                                    motionEntry->countSamples();
-                        }
-                        return;
-                    }
-                }
-
-#if DEBUG_THROTTLING
-                if (mThrottleState.originalSampleCount != 0) {
-                    uint32_t count = motionEntry->countSamples();
-                    ALOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
-                            count - mThrottleState.originalSampleCount,
-                            mThrottleState.originalSampleCount, count);
-                    mThrottleState.originalSampleCount = 0;
-                }
-#endif
-
-                mThrottleState.lastEventTime = currentTime;
-                mThrottleState.lastDeviceId = deviceId;
-                mThrottleState.lastSource = source;
-            }
-
-            mInboundQueue.dequeue(entry);
-            mPendingEvent = entry;
+            mPendingEvent = mInboundQueue.dequeueAtHead();
         }
 
         // Poke user activity for this event.
         if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
             pokeUserActivityLocked(mPendingEvent);
         }
+
+        // Get ready to dispatch the event.
+        resetANRTimeoutsLocked();
     }
 
     // Now we have an event to dispatch.
@@ -461,16 +376,6 @@
     }
 }
 
-void InputDispatcher::dispatchIdleLocked() {
-#if DEBUG_FOCUS
-    ALOGD("Dispatcher idle.  There are no pending events or active connections.");
-#endif
-
-    // Reset targets when idle, to release input channels and other resources
-    // they are holding onto.
-    resetTargetsLocked();
-}
-
 bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
     bool needWake = mInboundQueue.isEmpty();
     mInboundQueue.enqueueAtTail(entry);
@@ -508,9 +413,9 @@
                 && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                 && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
                 && mInputTargetWaitApplicationHandle != NULL) {
-            int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0].
+            int32_t x = int32_t(motionEntry->pointerCoords[0].
                     getAxisValue(AMOTION_EVENT_AXIS_X));
-            int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0].
+            int32_t y = int32_t(motionEntry->pointerCoords[0].
                     getAxisValue(AMOTION_EVENT_AXIS_Y));
             sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(x, y);
             if (touchedWindowHandle != NULL
@@ -671,6 +576,7 @@
 
 void InputDispatcher::releasePendingEventLocked() {
     if (mPendingEvent) {
+        resetANRTimeoutsLocked();
         releaseInboundEventLocked(mPendingEvent);
         mPendingEvent = NULL;
     }
@@ -793,7 +699,6 @@
         }
 
         entry->dispatchInProgress = true;
-        resetTargetsLocked();
 
         logOutboundKeyDetailsLocked("dispatchKey - ", entry);
     }
@@ -832,31 +737,28 @@
 
     // Clean up if dropping the event.
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
-        resetTargetsLocked();
         setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
                 ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
         return true;
     }
 
     // Identify targets.
-    if (! mCurrentInputTargetsValid) {
-        int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
-                entry, nextWakeupTime);
-        if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
-            return false;
-        }
-
-        setInjectionResultLocked(entry, injectionResult);
-        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-            return true;
-        }
-
-        addMonitoringTargetsLocked();
-        commitTargetsLocked();
+    Vector<InputTarget> inputTargets;
+    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
+            entry, inputTargets, nextWakeupTime);
+    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+        return false;
     }
 
+    setInjectionResultLocked(entry, injectionResult);
+    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+        return true;
+    }
+
+    addMonitoringTargetsLocked(inputTargets);
+
     // Dispatch the key.
-    dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+    dispatchEventLocked(currentTime, entry, inputTargets);
     return true;
 }
 
@@ -877,14 +779,12 @@
     // Preprocessing.
     if (! entry->dispatchInProgress) {
         entry->dispatchInProgress = true;
-        resetTargetsLocked();
 
         logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
     }
 
     // Clean up if dropping the event.
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
-        resetTargetsLocked();
         setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
                 ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
         return true;
@@ -893,67 +793,29 @@
     bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
 
     // Identify targets.
+    Vector<InputTarget> inputTargets;
+
     bool conflictingPointerActions = false;
-    if (! mCurrentInputTargetsValid) {
-        int32_t injectionResult;
-        const MotionSample* splitBatchAfterSample = NULL;
-        if (isPointerEvent) {
-            // Pointer event.  (eg. touchscreen)
-            injectionResult = findTouchedWindowTargetsLocked(currentTime,
-                    entry, nextWakeupTime, &conflictingPointerActions, &splitBatchAfterSample);
-        } else {
-            // Non touch event.  (eg. trackball)
-            injectionResult = findFocusedWindowTargetsLocked(currentTime,
-                    entry, nextWakeupTime);
-        }
-        if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
-            return false;
-        }
-
-        setInjectionResultLocked(entry, injectionResult);
-        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-            return true;
-        }
-
-        addMonitoringTargetsLocked();
-        commitTargetsLocked();
-
-        // Unbatch the event if necessary by splitting it into two parts after the
-        // motion sample indicated by splitBatchAfterSample.
-        if (splitBatchAfterSample && splitBatchAfterSample->next) {
-#if DEBUG_BATCHING
-            uint32_t originalSampleCount = entry->countSamples();
-#endif
-            MotionSample* nextSample = splitBatchAfterSample->next;
-            MotionEntry* nextEntry = new MotionEntry(nextSample->eventTime,
-                    entry->deviceId, entry->source, entry->policyFlags,
-                    entry->action, entry->flags,
-                    entry->metaState, entry->buttonState, entry->edgeFlags,
-                    entry->xPrecision, entry->yPrecision, entry->downTime,
-                    entry->pointerCount, entry->pointerProperties, nextSample->pointerCoords);
-            if (nextSample != entry->lastSample) {
-                nextEntry->firstSample.next = nextSample->next;
-                nextEntry->lastSample = entry->lastSample;
-            }
-            delete nextSample;
-
-            entry->lastSample = const_cast<MotionSample*>(splitBatchAfterSample);
-            entry->lastSample->next = NULL;
-
-            if (entry->injectionState) {
-                nextEntry->injectionState = entry->injectionState;
-                entry->injectionState->refCount += 1;
-            }
-
-#if DEBUG_BATCHING
-            ALOGD("Split batch of %d samples into two parts, first part has %d samples, "
-                    "second part has %d samples.", originalSampleCount,
-                    entry->countSamples(), nextEntry->countSamples());
-#endif
-
-            mInboundQueue.enqueueAtHead(nextEntry);
-        }
+    int32_t injectionResult;
+    if (isPointerEvent) {
+        // Pointer event.  (eg. touchscreen)
+        injectionResult = findTouchedWindowTargetsLocked(currentTime,
+                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
+    } else {
+        // Non touch event.  (eg. trackball)
+        injectionResult = findFocusedWindowTargetsLocked(currentTime,
+                entry, inputTargets, nextWakeupTime);
     }
+    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+        return false;
+    }
+
+    setInjectionResultLocked(entry, injectionResult);
+    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+        return true;
+    }
+
+    addMonitoringTargetsLocked(inputTargets);
 
     // Dispatch the motion.
     if (conflictingPointerActions) {
@@ -961,7 +823,7 @@
                 "conflicting pointer actions");
         synthesizeCancelationEventsForAllConnectionsLocked(options);
     }
-    dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+    dispatchEventLocked(currentTime, entry, inputTargets);
     return true;
 }
 
@@ -979,12 +841,6 @@
             entry->edgeFlags, entry->xPrecision, entry->yPrecision,
             entry->downTime);
 
-    // Print the most recent sample that we have available, this may change due to batching.
-    size_t sampleCount = 1;
-    const MotionSample* sample = & entry->firstSample;
-    for (; sample->next != NULL; sample = sample->next) {
-        sampleCount += 1;
-    }
     for (uint32_t i = 0; i < entry->pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
                 "x=%f, y=%f, pressure=%f, size=%f, "
@@ -992,45 +848,36 @@
                 "orientation=%f",
                 i, entry->pointerProperties[i].id,
                 entry->pointerProperties[i].toolType,
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
-    }
-
-    // Keep in mind that due to batching, it is possible for the number of samples actually
-    // dispatched to change before the application finally consumed them.
-    if (entry->action == AMOTION_EVENT_ACTION_MOVE) {
-        ALOGD("  ... Total movement samples currently batched %d ...", sampleCount);
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
     }
 #endif
 }
 
-void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
-        EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
+void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
+        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
 #if DEBUG_DISPATCH_CYCLE
-    ALOGD("dispatchEventToCurrentInputTargets - "
-            "resumeWithAppendedMotionSample=%s",
-            toString(resumeWithAppendedMotionSample));
+    ALOGD("dispatchEventToCurrentInputTargets");
 #endif
 
     ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
 
     pokeUserActivityLocked(eventEntry);
 
-    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
-        const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
+    for (size_t i = 0; i < inputTargets.size(); i++) {
+        const InputTarget& inputTarget = inputTargets.itemAt(i);
 
         ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
         if (connectionIndex >= 0) {
-            sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
-                    resumeWithAppendedMotionSample);
+            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
+            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
         } else {
 #if DEBUG_FOCUS
             ALOGD("Dropping event delivery to target with channel '%s' because it "
@@ -1041,16 +888,6 @@
     }
 }
 
-void InputDispatcher::resetTargetsLocked() {
-    mCurrentInputTargetsValid = false;
-    mCurrentInputTargets.clear();
-    resetANRTimeoutsLocked();
-}
-
-void InputDispatcher::commitTargetsLocked() {
-    mCurrentInputTargetsValid = true;
-}
-
 int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
         const EventEntry* entry,
         const sp<InputApplicationHandle>& applicationHandle,
@@ -1135,7 +972,7 @@
         if (inputChannel.get()) {
             ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
             if (connectionIndex >= 0) {
-                sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+                sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
                 if (connection->status == Connection::STATUS_NORMAL) {
                     CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
                             "application not responding");
@@ -1165,9 +1002,7 @@
 }
 
 int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
-        const EventEntry* entry, nsecs_t* nextWakeupTime) {
-    mCurrentInputTargets.clear();
-
+        const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
     int32_t injectionResult;
 
     // If there is no currently focused window and no focused application
@@ -1206,7 +1041,7 @@
     }
 
     // If the currently focused window is still working on previous events then keep waiting.
-    if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindowHandle)) {
+    if (!isWindowReadyForMoreInputLocked(currentTime, mFocusedWindowHandle, entry)) {
 #if DEBUG_FOCUS
         ALOGD("Waiting because focused window still processing previous input.");
 #endif
@@ -1218,7 +1053,8 @@
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
     addWindowTargetLocked(mFocusedWindowHandle,
-            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0));
+            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
+            inputTargets);
 
     // Done.
 Failed:
@@ -1235,16 +1071,14 @@
 }
 
 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
-        const MotionEntry* entry, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions,
-        const MotionSample** outSplitBatchAfterSample) {
+        const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
+        bool* outConflictingPointerActions) {
     enum InjectionPermission {
         INJECTION_PERMISSION_UNKNOWN,
         INJECTION_PERMISSION_GRANTED,
         INJECTION_PERMISSION_DENIED
     };
 
-    mCurrentInputTargets.clear();
-
     nsecs_t startTime = now();
 
     // For security reasons, we defer updating the touch state until we are sure that
@@ -1316,11 +1150,10 @@
     if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
         /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
 
-        const MotionSample* sample = &entry->firstSample;
         int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-        int32_t x = int32_t(sample->pointerCoords[pointerIndex].
+        int32_t x = int32_t(entry->pointerCoords[pointerIndex].
                 getAxisValue(AMOTION_EVENT_AXIS_X));
-        int32_t y = int32_t(sample->pointerCoords[pointerIndex].
+        int32_t y = int32_t(entry->pointerCoords[pointerIndex].
                 getAxisValue(AMOTION_EVENT_AXIS_Y));
         sp<InputWindowHandle> newTouchedWindowHandle;
         sp<InputWindowHandle> topErrorWindowHandle;
@@ -1420,21 +1253,6 @@
         // Update hover state.
         if (isHoverAction) {
             newHoverWindowHandle = newTouchedWindowHandle;
-
-            // Ensure all subsequent motion samples are also within the touched window.
-            // Set *outSplitBatchAfterSample to the sample before the first one that is not
-            // within the touched window.
-            if (!isTouchModal) {
-                while (sample->next) {
-                    if (!newHoverWindowHandle->getInfo()->touchableRegionContainsPoint(
-                            sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
-                            sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y))) {
-                        *outSplitBatchAfterSample = sample;
-                        break;
-                    }
-                    sample = sample->next;
-                }
-            }
         } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
             newHoverWindowHandle = mLastHoverWindowHandle;
         }
@@ -1463,9 +1281,8 @@
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE
                 && entry->pointerCount == 1
                 && mTempTouchState.isSlippery()) {
-            const MotionSample* sample = &entry->firstSample;
-            int32_t x = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-            int32_t y = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+            int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+            int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
 
             sp<InputWindowHandle> oldTouchedWindowHandle =
                     mTempTouchState.getFirstForegroundWindowHandle();
@@ -1500,17 +1317,11 @@
                     pointerIds.markBit(entry->pointerProperties[0].id);
                 }
                 mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
-
-                // Split the batch here so we send exactly one sample.
-                *outSplitBatchAfterSample = &entry->firstSample;
             }
         }
     }
 
     if (newHoverWindowHandle != mLastHoverWindowHandle) {
-        // Split the batch here so we send exactly one sample as part of ENTER or EXIT.
-        *outSplitBatchAfterSample = &entry->firstSample;
-
         // Let the previous window know that the hover sequence is over.
         if (mLastHoverWindowHandle != NULL) {
 #if DEBUG_HOVER
@@ -1593,7 +1404,7 @@
             }
 
             // If the touched window is still working on previous events then keep waiting.
-            if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.windowHandle)) {
+            if (!isWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, entry)) {
 #if DEBUG_FOCUS
                 ALOGD("Waiting because touched window still processing previous input.");
 #endif
@@ -1633,7 +1444,7 @@
     for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
         const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
         addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
-                touchedWindow.pointerIds);
+                touchedWindow.pointerIds, inputTargets);
     }
 
     // Drop the outside or hover touch windows since we will not care about them
@@ -1738,11 +1549,11 @@
 }
 
 void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
-        int32_t targetFlags, BitSet32 pointerIds) {
-    mCurrentInputTargets.push();
+        int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
+    inputTargets.push();
 
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
-    InputTarget& target = mCurrentInputTargets.editTop();
+    InputTarget& target = inputTargets.editTop();
     target.inputChannel = windowInfo->inputChannel;
     target.flags = targetFlags;
     target.xOffset = - windowInfo->frameLeft;
@@ -1751,11 +1562,11 @@
     target.pointerIds = pointerIds;
 }
 
-void InputDispatcher::addMonitoringTargetsLocked() {
+void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) {
     for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-        mCurrentInputTargets.push();
+        inputTargets.push();
 
-        InputTarget& target = mCurrentInputTargets.editTop();
+        InputTarget& target = inputTargets.editTop();
         target.inputChannel = mMonitoringChannels[i];
         target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
         target.xOffset = 0;
@@ -1804,15 +1615,51 @@
     return false;
 }
 
-bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(
-        const sp<InputWindowHandle>& windowHandle) {
+bool InputDispatcher::isWindowReadyForMoreInputLocked(nsecs_t currentTime,
+        const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry) {
     ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
     if (connectionIndex >= 0) {
-        sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-        return connection->outboundQueue.isEmpty();
-    } else {
-        return true;
+        sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
+        if (connection->inputPublisherBlocked) {
+            return false;
+        }
+        if (eventEntry->type == EventEntry::TYPE_KEY) {
+            // If the event is a key event, then we must wait for all previous events to
+            // complete before delivering it because previous events may have the
+            // side-effect of transferring focus to a different window and we want to
+            // ensure that the following keys are sent to the new window.
+            //
+            // Suppose the user touches a button in a window then immediately presses "A".
+            // If the button causes a pop-up window to appear then we want to ensure that
+            // the "A" key is delivered to the new pop-up window.  This is because users
+            // often anticipate pending UI changes when typing on a keyboard.
+            // To obtain this behavior, we must serialize key events with respect to all
+            // prior input events.
+            return connection->outboundQueue.isEmpty()
+                    && connection->waitQueue.isEmpty();
+        }
+        // Touch events can always be sent to a window immediately because the user intended
+        // to touch whatever was visible at the time.  Even if focus changes or a new
+        // window appears moments later, the touch event was meant to be delivered to
+        // whatever window happened to be on screen at the time.
+        //
+        // Generic motion events, such as trackball or joystick events are a little trickier.
+        // Like key events, generic motion events are delivered to the focused window.
+        // Unlike key events, generic motion events don't tend to transfer focus to other
+        // windows and it is not important for them to be serialized.  So we prefer to deliver
+        // generic motion events as soon as possible to improve efficiency and reduce lag
+        // through batching.
+        //
+        // The one case where we pause input event delivery is when the wait queue is piling
+        // up with lots of events because the application is not responding.
+        // This condition ensures that ANRs are detected reliably.
+        if (!connection->waitQueue.isEmpty()
+                && currentTime >= connection->waitQueue.head->eventEntry->eventTime
+                        + STREAM_AHEAD_EVENT_TIMEOUT) {
+            return false;
+        }
     }
+    return true;
 }
 
 String8 InputDispatcher::getApplicationWindowLabelLocked(
@@ -1865,23 +1712,16 @@
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
-        bool resumeWithAppendedMotionSample) {
+        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
             "xOffset=%f, yOffset=%f, scaleFactor=%f, "
-            "pointerIds=0x%x, "
-            "resumeWithAppendedMotionSample=%s",
+            "pointerIds=0x%x",
             connection->getInputChannelName(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->scaleFactor, inputTarget->pointerIds.value,
-            toString(resumeWithAppendedMotionSample));
+            inputTarget->scaleFactor, inputTarget->pointerIds.value);
 #endif
 
-    // Make sure we are never called for streaming when splitting across multiple windows.
-    bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
-    ALOG_ASSERT(! (resumeWithAppendedMotionSample && isSplit));
-
     // Skip this event if the connection status is not normal.
     // We don't want to enqueue additional outbound events if the connection is broken.
     if (connection->status != Connection::STATUS_NORMAL) {
@@ -1893,7 +1733,7 @@
     }
 
     // Split a motion event if needed.
-    if (isSplit) {
+    if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
         ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
 
         MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
@@ -1909,145 +1749,43 @@
             logOutboundMotionDetailsLocked("  ", splitMotionEntry);
 #endif
             enqueueDispatchEntriesLocked(currentTime, connection,
-                    splitMotionEntry, inputTarget, resumeWithAppendedMotionSample);
+                    splitMotionEntry, inputTarget);
             splitMotionEntry->release();
             return;
         }
     }
 
     // Not splitting.  Enqueue dispatch entries for the event as is.
-    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget,
-            resumeWithAppendedMotionSample);
+    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
 }
 
 void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
-        bool resumeWithAppendedMotionSample) {
-    // Resume the dispatch cycle with a freshly appended motion sample.
-    // First we check that the last dispatch entry in the outbound queue is for the same
-    // motion event to which we appended the motion sample.  If we find such a dispatch
-    // entry, and if it is currently in progress then we try to stream the new sample.
+        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
     bool wasEmpty = connection->outboundQueue.isEmpty();
 
-    if (! wasEmpty && resumeWithAppendedMotionSample) {
-        DispatchEntry* motionEventDispatchEntry =
-                connection->findQueuedDispatchEntryForEvent(eventEntry);
-        if (motionEventDispatchEntry) {
-            // If the dispatch entry is not in progress, then we must be busy dispatching an
-            // earlier event.  Not a problem, the motion event is on the outbound queue and will
-            // be dispatched later.
-            if (! motionEventDispatchEntry->inProgress) {
-#if DEBUG_BATCHING
-                ALOGD("channel '%s' ~ Not streaming because the motion event has "
-                        "not yet been dispatched.  "
-                        "(Waiting for earlier events to be consumed.)",
-                        connection->getInputChannelName());
-#endif
-                return;
-            }
-
-            // If the dispatch entry is in progress but it already has a tail of pending
-            // motion samples, then it must mean that the shared memory buffer filled up.
-            // Not a problem, when this dispatch cycle is finished, we will eventually start
-            // a new dispatch cycle to process the tail and that tail includes the newly
-            // appended motion sample.
-            if (motionEventDispatchEntry->tailMotionSample) {
-#if DEBUG_BATCHING
-                ALOGD("channel '%s' ~ Not streaming because no new samples can "
-                        "be appended to the motion event in this dispatch cycle.  "
-                        "(Waiting for next dispatch cycle to start.)",
-                        connection->getInputChannelName());
-#endif
-                return;
-            }
-
-            // If the motion event was modified in flight, then we cannot stream the sample.
-            if ((motionEventDispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_MASK)
-                    != InputTarget::FLAG_DISPATCH_AS_IS) {
-#if DEBUG_BATCHING
-                ALOGD("channel '%s' ~ Not streaming because the motion event was not "
-                        "being dispatched as-is.  "
-                        "(Waiting for next dispatch cycle to start.)",
-                        connection->getInputChannelName());
-#endif
-                return;
-            }
-
-            // The dispatch entry is in progress and is still potentially open for streaming.
-            // Try to stream the new motion sample.  This might fail if the consumer has already
-            // consumed the motion event (or if the channel is broken).
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
-            MotionSample* appendedMotionSample = motionEntry->lastSample;
-            status_t status;
-            if (motionEventDispatchEntry->scaleFactor == 1.0f) {
-                status = connection->inputPublisher.appendMotionSample(
-                        appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
-            } else {
-                PointerCoords scaledCoords[MAX_POINTERS];
-                for (size_t i = 0; i < motionEntry->pointerCount; i++) {
-                    scaledCoords[i] = appendedMotionSample->pointerCoords[i];
-                    scaledCoords[i].scale(motionEventDispatchEntry->scaleFactor);
-                }
-                status = connection->inputPublisher.appendMotionSample(
-                        appendedMotionSample->eventTime, scaledCoords);
-            }
-            if (status == OK) {
-#if DEBUG_BATCHING
-                ALOGD("channel '%s' ~ Successfully streamed new motion sample.",
-                        connection->getInputChannelName());
-#endif
-                return;
-            }
-
-#if DEBUG_BATCHING
-            if (status == NO_MEMORY) {
-                ALOGD("channel '%s' ~ Could not append motion sample to currently "
-                        "dispatched move event because the shared memory buffer is full.  "
-                        "(Waiting for next dispatch cycle to start.)",
-                        connection->getInputChannelName());
-            } else if (status == status_t(FAILED_TRANSACTION)) {
-                ALOGD("channel '%s' ~ Could not append motion sample to currently "
-                        "dispatched move event because the event has already been consumed.  "
-                        "(Waiting for next dispatch cycle to start.)",
-                        connection->getInputChannelName());
-            } else {
-                ALOGD("channel '%s' ~ Could not append motion sample to currently "
-                        "dispatched move event due to an error, status=%d.  "
-                        "(Waiting for next dispatch cycle to start.)",
-                        connection->getInputChannelName(), status);
-            }
-#endif
-            // Failed to stream.  Start a new tail of pending motion samples to dispatch
-            // in the next cycle.
-            motionEventDispatchEntry->tailMotionSample = appendedMotionSample;
-            return;
-        }
-    }
-
     // Enqueue dispatch entries for the requested modes.
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
+            InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
+            InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
+            InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_IS);
+            InputTarget::FLAG_DISPATCH_AS_IS);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
+            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
-            resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
+            InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
     if (wasEmpty && !connection->outboundQueue.isEmpty()) {
-        activateConnectionLocked(connection.get());
         startDispatchCycleLocked(currentTime, connection);
     }
 }
 
 void InputDispatcher::enqueueDispatchEntryLocked(
         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
-        bool resumeWithAppendedMotionSample, int32_t dispatchMode) {
+        int32_t dispatchMode) {
     int32_t inputTargetFlags = inputTarget->flags;
     if (!(inputTargetFlags & dispatchMode)) {
         return;
@@ -2060,20 +1798,6 @@
             inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
             inputTarget->scaleFactor);
 
-    // Handle the case where we could not stream a new motion sample because the consumer has
-    // already consumed the motion event (otherwise the corresponding dispatch entry would
-    // still be in the outbound queue for this connection).  We set the head motion sample
-    // to the list starting with the newly appended motion sample.
-    if (resumeWithAppendedMotionSample) {
-#if DEBUG_BATCHING
-        ALOGD("channel '%s' ~ Preparing a new dispatch cycle for additional motion samples "
-                "that cannot be streamed because the motion event has already been consumed.",
-                connection->getInputChannelName());
-#endif
-        MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample;
-        dispatchEntry->headMotionSample = appendedMotionSample;
-    }
-
     // Apply target flags and update the connection's input state.
     switch (eventEntry->type) {
     case EventEntry::TYPE_KEY: {
@@ -2152,227 +1876,128 @@
             connection->getInputChannelName());
 #endif
 
-    ALOG_ASSERT(connection->status == Connection::STATUS_NORMAL);
-    ALOG_ASSERT(! connection->outboundQueue.isEmpty());
+    while (connection->status == Connection::STATUS_NORMAL
+            && !connection->outboundQueue.isEmpty()) {
+        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
 
-    DispatchEntry* dispatchEntry = connection->outboundQueue.head;
-    ALOG_ASSERT(! dispatchEntry->inProgress);
+        // Publish the event.
+        status_t status;
+        EventEntry* eventEntry = dispatchEntry->eventEntry;
+        switch (eventEntry->type) {
+        case EventEntry::TYPE_KEY: {
+            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
 
-    // Mark the dispatch entry as in progress.
-    dispatchEntry->inProgress = true;
-
-    // Publish the event.
-    status_t status;
-    EventEntry* eventEntry = dispatchEntry->eventEntry;
-    switch (eventEntry->type) {
-    case EventEntry::TYPE_KEY: {
-        KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
-
-        // Publish the key event.
-        status = connection->inputPublisher.publishKeyEvent(
-                keyEntry->deviceId, keyEntry->source,
-                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
-                keyEntry->keyCode, keyEntry->scanCode,
-                keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
-                keyEntry->eventTime);
-
-        if (status) {
-            ALOGE("channel '%s' ~ Could not publish key event, "
-                    "status=%d", connection->getInputChannelName(), status);
-            abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
-            return;
-        }
-        break;
-    }
-
-    case EventEntry::TYPE_MOTION: {
-        MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
-
-        // If headMotionSample is non-NULL, then it points to the first new sample that we
-        // were unable to dispatch during the previous cycle so we resume dispatching from
-        // that point in the list of motion samples.
-        // Otherwise, we just start from the first sample of the motion event.
-        MotionSample* firstMotionSample = dispatchEntry->headMotionSample;
-        if (! firstMotionSample) {
-            firstMotionSample = & motionEntry->firstSample;
+            // Publish the key event.
+            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
+                    keyEntry->deviceId, keyEntry->source,
+                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
+                    keyEntry->keyCode, keyEntry->scanCode,
+                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
+                    keyEntry->eventTime);
+            break;
         }
 
-        PointerCoords scaledCoords[MAX_POINTERS];
-        const PointerCoords* usingCoords = firstMotionSample->pointerCoords;
+        case EventEntry::TYPE_MOTION: {
+            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
 
-        // Set the X and Y offset depending on the input source.
-        float xOffset, yOffset, scaleFactor;
-        if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER
-                && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
-            scaleFactor = dispatchEntry->scaleFactor;
-            xOffset = dispatchEntry->xOffset * scaleFactor;
-            yOffset = dispatchEntry->yOffset * scaleFactor;
-            if (scaleFactor != 1.0f) {
-                for (size_t i = 0; i < motionEntry->pointerCount; i++) {
-                    scaledCoords[i] = firstMotionSample->pointerCoords[i];
-                    scaledCoords[i].scale(scaleFactor);
-                }
-                usingCoords = scaledCoords;
-            }
-        } else {
-            xOffset = 0.0f;
-            yOffset = 0.0f;
-            scaleFactor = 1.0f;
+            PointerCoords scaledCoords[MAX_POINTERS];
+            const PointerCoords* usingCoords = motionEntry->pointerCoords;
 
-            // We don't want the dispatch target to know.
-            if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
-                for (size_t i = 0; i < motionEntry->pointerCount; i++) {
-                    scaledCoords[i].clear();
-                }
-                usingCoords = scaledCoords;
-            }
-        }
-
-        // Publish the motion event and the first motion sample.
-        status = connection->inputPublisher.publishMotionEvent(
-                motionEntry->deviceId, motionEntry->source,
-                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
-                motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
-                xOffset, yOffset,
-                motionEntry->xPrecision, motionEntry->yPrecision,
-                motionEntry->downTime, firstMotionSample->eventTime,
-                motionEntry->pointerCount, motionEntry->pointerProperties,
-                usingCoords);
-
-        if (status) {
-            ALOGE("channel '%s' ~ Could not publish motion event, "
-                    "status=%d", connection->getInputChannelName(), status);
-            abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
-            return;
-        }
-
-        if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_MOVE
-                || dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-            // Append additional motion samples.
-            MotionSample* nextMotionSample = firstMotionSample->next;
-            for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
-                if (usingCoords == scaledCoords) {
-                    if (!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
-                        for (size_t i = 0; i < motionEntry->pointerCount; i++) {
-                            scaledCoords[i] = nextMotionSample->pointerCoords[i];
-                            scaledCoords[i].scale(scaleFactor);
-                        }
+            // Set the X and Y offset depending on the input source.
+            float xOffset, yOffset, scaleFactor;
+            if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
+                    && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
+                scaleFactor = dispatchEntry->scaleFactor;
+                xOffset = dispatchEntry->xOffset * scaleFactor;
+                yOffset = dispatchEntry->yOffset * scaleFactor;
+                if (scaleFactor != 1.0f) {
+                    for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+                        scaledCoords[i] = motionEntry->pointerCoords[i];
+                        scaledCoords[i].scale(scaleFactor);
                     }
-                } else {
-                    usingCoords = nextMotionSample->pointerCoords;
+                    usingCoords = scaledCoords;
                 }
-                status = connection->inputPublisher.appendMotionSample(
-                        nextMotionSample->eventTime, usingCoords);
-                if (status == NO_MEMORY) {
+            } else {
+                xOffset = 0.0f;
+                yOffset = 0.0f;
+                scaleFactor = 1.0f;
+
+                // We don't want the dispatch target to know.
+                if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
+                    for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+                        scaledCoords[i].clear();
+                    }
+                    usingCoords = scaledCoords;
+                }
+            }
+
+            // Publish the motion event.
+            status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
+                    motionEntry->deviceId, motionEntry->source,
+                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
+                    motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
+                    xOffset, yOffset,
+                    motionEntry->xPrecision, motionEntry->yPrecision,
+                    motionEntry->downTime, motionEntry->eventTime,
+                    motionEntry->pointerCount, motionEntry->pointerProperties,
+                    usingCoords);
+            break;
+        }
+
+        default:
+            ALOG_ASSERT(false);
+            return;
+        }
+
+        // Check the result.
+        if (status) {
+            if (status == WOULD_BLOCK) {
+                if (connection->waitQueue.isEmpty()) {
+                    ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
+                            "This is unexpected because the wait queue is empty, so the pipe "
+                            "should be empty and we shouldn't have any problems writing an "
+                            "event to it, status=%d", connection->getInputChannelName(), status);
+                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
+                } else {
+                    // Pipe is full and we are waiting for the app to finish process some events
+                    // before sending more events to it.
 #if DEBUG_DISPATCH_CYCLE
-                    ALOGD("channel '%s' ~ Shared memory buffer full.  Some motion samples will "
-                            "be sent in the next dispatch cycle.",
+                    ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
+                            "waiting for the application to catch up",
                             connection->getInputChannelName());
 #endif
-                    break;
+                    connection->inputPublisherBlocked = true;
                 }
-                if (status != OK) {
-                    ALOGE("channel '%s' ~ Could not append motion sample "
-                            "for a reason other than out of memory, status=%d",
-                            connection->getInputChannelName(), status);
-                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
-                    return;
-                }
+            } else {
+                ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
+                        "status=%d", connection->getInputChannelName(), status);
+                abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
             }
-
-            // Remember the next motion sample that we could not dispatch, in case we ran out
-            // of space in the shared memory buffer.
-            dispatchEntry->tailMotionSample = nextMotionSample;
+            return;
         }
-        break;
-    }
 
-    default: {
-        ALOG_ASSERT(false);
+        // Re-enqueue the event on the wait queue.
+        connection->outboundQueue.dequeue(dispatchEntry);
+        connection->waitQueue.enqueueAtTail(dispatchEntry);
     }
-    }
-
-    // Send the dispatch signal.
-    status = connection->inputPublisher.sendDispatchSignal();
-    if (status) {
-        ALOGE("channel '%s' ~ Could not send dispatch signal, status=%d",
-                connection->getInputChannelName(), status);
-        abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
-        return;
-    }
-
-    // Record information about the newly started dispatch cycle.
-    connection->lastEventTime = eventEntry->eventTime;
-    connection->lastDispatchTime = currentTime;
-
-    // Notify other system components.
-    onDispatchCycleStartedLocked(currentTime, connection);
 }
 
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, bool handled) {
+        const sp<Connection>& connection, uint32_t seq, bool handled) {
 #if DEBUG_DISPATCH_CYCLE
-    ALOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, "
-            "%01.1fms since dispatch, handled=%s",
-            connection->getInputChannelName(),
-            connection->getEventLatencyMillis(currentTime),
-            connection->getDispatchLatencyMillis(currentTime),
-            toString(handled));
+    ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
+            connection->getInputChannelName(), seq, toString(handled));
 #endif
 
+    connection->inputPublisherBlocked = false;
+
     if (connection->status == Connection::STATUS_BROKEN
             || connection->status == Connection::STATUS_ZOMBIE) {
         return;
     }
 
-    // Reset the publisher since the event has been consumed.
-    // We do this now so that the publisher can release some of its internal resources
-    // while waiting for the next dispatch cycle to begin.
-    status_t status = connection->inputPublisher.reset();
-    if (status) {
-        ALOGE("channel '%s' ~ Could not reset publisher, status=%d",
-                connection->getInputChannelName(), status);
-        abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
-        return;
-    }
-
     // Notify other system components and prepare to start the next dispatch cycle.
-    onDispatchCycleFinishedLocked(currentTime, connection, handled);
-}
-
-void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection) {
-    // Start the next dispatch cycle for this connection.
-    while (! connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
-        if (dispatchEntry->inProgress) {
-             // Finish or resume current event in progress.
-            if (dispatchEntry->tailMotionSample) {
-                // We have a tail of undispatched motion samples.
-                // Reuse the same DispatchEntry and start a new cycle.
-                dispatchEntry->inProgress = false;
-                dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
-                dispatchEntry->tailMotionSample = NULL;
-                startDispatchCycleLocked(currentTime, connection);
-                return;
-            }
-            // Finished.
-            connection->outboundQueue.dequeueAtHead();
-            if (dispatchEntry->hasForegroundTarget()) {
-                decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
-            }
-            delete dispatchEntry;
-        } else {
-            // If the head is not in progress, then we must have already dequeued the in
-            // progress event, which means we actually aborted it.
-            // So just start the next event for this connection.
-            startDispatchCycleLocked(currentTime, connection);
-            return;
-        }
-    }
-
-    // Outbound queue is empty, deactivate the connection.
-    deactivateConnectionLocked(connection.get());
+    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
@@ -2382,8 +2007,9 @@
             connection->getInputChannelName(), toString(notify));
 #endif
 
-    // Clear the outbound queue.
-    drainOutboundQueueLocked(connection.get());
+    // Clear the dispatch queues.
+    drainDispatchQueueLocked(&connection->outboundQueue);
+    drainDispatchQueueLocked(&connection->waitQueue);
 
     // The connection appears to be unrecoverably broken.
     // Ignore already broken or zombie connections.
@@ -2397,33 +2023,35 @@
     }
 }
 
-void InputDispatcher::drainOutboundQueueLocked(Connection* connection) {
-    while (! connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
-        if (dispatchEntry->hasForegroundTarget()) {
-            decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
-        }
-        delete dispatchEntry;
+void InputDispatcher::drainDispatchQueueLocked(Queue<DispatchEntry>* queue) {
+    while (!queue->isEmpty()) {
+        DispatchEntry* dispatchEntry = queue->dequeueAtHead();
+        releaseDispatchEntryLocked(dispatchEntry);
     }
-
-    deactivateConnectionLocked(connection);
 }
 
-int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
+void InputDispatcher::releaseDispatchEntryLocked(DispatchEntry* dispatchEntry) {
+    if (dispatchEntry->hasForegroundTarget()) {
+        decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
+    }
+    delete dispatchEntry;
+}
+
+int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
     InputDispatcher* d = static_cast<InputDispatcher*>(data);
 
     { // acquire lock
         AutoMutex _l(d->mLock);
 
-        ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd);
+        ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
         if (connectionIndex < 0) {
             ALOGE("Received spurious receive callback for unknown input channel.  "
-                    "fd=%d, events=0x%x", receiveFd, events);
+                    "fd=%d, events=0x%x", fd, events);
             return 0; // remove the callback
         }
 
         bool notify;
-        sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
+        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
         if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
             if (!(events & ALOOPER_EVENT_INPUT)) {
                 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
@@ -2431,18 +2059,31 @@
                 return 1;
             }
 
-            bool handled = false;
-            status_t status = connection->inputPublisher.receiveFinishedSignal(&handled);
-            if (!status) {
-                nsecs_t currentTime = now();
-                d->finishDispatchCycleLocked(currentTime, connection, handled);
+            nsecs_t currentTime = now();
+            bool gotOne = false;
+            status_t status;
+            for (;;) {
+                uint32_t seq;
+                bool handled;
+                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
+                if (status) {
+                    break;
+                }
+                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
+                gotOne = true;
+            }
+            if (gotOne) {
                 d->runCommandsLockedInterruptible();
-                return 1;
+                if (status == WOULD_BLOCK) {
+                    return 1;
+                }
             }
 
-            ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
-                    connection->getInputChannelName(), status);
-            notify = true;
+            notify = status != DEAD_OBJECT || !connection->monitor;
+            if (notify) {
+                ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
+                        connection->getInputChannelName(), status);
+            }
         } else {
             // Monitor channels are never explicitly unregistered.
             // We do it automatically when the remote endpoint is closed so don't warn
@@ -2462,9 +2103,9 @@
 
 void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
         const CancelationOptions& options) {
-    for (size_t i = 0; i < mConnectionsByReceiveFd.size(); i++) {
+    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
         synthesizeCancelationEventsForConnectionLocked(
-                mConnectionsByReceiveFd.valueAt(i), options);
+                mConnectionsByFd.valueAt(i), options);
     }
 }
 
@@ -2473,7 +2114,7 @@
     ssize_t index = getConnectionIndexLocked(channel);
     if (index >= 0) {
         synthesizeCancelationEventsForConnectionLocked(
-                mConnectionsByReceiveFd.valueAt(index), options);
+                mConnectionsByFd.valueAt(index), options);
     }
 }
 
@@ -2485,19 +2126,19 @@
 
     nsecs_t currentTime = now();
 
-    mTempCancelationEvents.clear();
+    Vector<EventEntry*> cancelationEvents;
     connection->inputState.synthesizeCancelationEvents(currentTime,
-            mTempCancelationEvents, options);
+            cancelationEvents, options);
 
-    if (!mTempCancelationEvents.isEmpty()) {
+    if (!cancelationEvents.isEmpty()) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
         ALOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync "
                 "with reality: %s, mode=%d.",
-                connection->getInputChannelName(), mTempCancelationEvents.size(),
+                connection->getInputChannelName(), cancelationEvents.size(),
                 options.reason, options.mode);
 #endif
-        for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
-            EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
+        for (size_t i = 0; i < cancelationEvents.size(); i++) {
+            EventEntry* cancelationEventEntry = cancelationEvents.itemAt(i);
             switch (cancelationEventEntry->type) {
             case EventEntry::TYPE_KEY:
                 logOutboundKeyDetailsLocked("cancel - ",
@@ -2525,14 +2166,12 @@
             target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
             enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
-                    &target, false, InputTarget::FLAG_DISPATCH_AS_IS);
+                    &target, InputTarget::FLAG_DISPATCH_AS_IS);
 
             cancelationEventEntry->release();
         }
 
-        if (!connection->outboundQueue.head->inProgress) {
-            startDispatchCycleLocked(currentTime, connection);
-        }
+        startDispatchCycleLocked(currentTime, connection);
     }
 }
 
@@ -2556,7 +2195,7 @@
             splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
             splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
             splitPointerCoords[splitPointerCount].copyFrom(
-                    originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]);
+                    originalMotionEntry->pointerCoords[originalPointerIndex]);
             splitPointerCount += 1;
         }
     }
@@ -2617,18 +2256,6 @@
             originalMotionEntry->downTime,
             splitPointerCount, splitPointerProperties, splitPointerCoords);
 
-    for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next;
-            originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) {
-        for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount;
-                splitPointerIndex++) {
-            uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex];
-            splitPointerCoords[splitPointerIndex].copyFrom(
-                    originalMotionSample->pointerCoords[originalPointerIndex]);
-        }
-
-        splitMotionEntry->appendSample(originalMotionSample->eventTime, splitPointerCoords);
-    }
-
     if (originalMotionEntry->injectionState) {
         splitMotionEntry->injectionState = originalMotionEntry->injectionState;
         splitMotionEntry->injectionState->refCount += 1;
@@ -2789,163 +2416,6 @@
             mLock.lock();
         }
 
-        // Attempt batching and streaming of move events.
-        if (args->action == AMOTION_EVENT_ACTION_MOVE
-                || args->action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-            // BATCHING CASE
-            //
-            // Try to append a move sample to the tail of the inbound queue for this device.
-            // Give up if we encounter a non-move motion event for this device since that
-            // means we cannot append any new samples until a new motion event has started.
-            for (EventEntry* entry = mInboundQueue.tail; entry; entry = entry->prev) {
-                if (entry->type != EventEntry::TYPE_MOTION) {
-                    // Keep looking for motion events.
-                    continue;
-                }
-
-                MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-                if (motionEntry->deviceId != args->deviceId
-                        || motionEntry->source != args->source) {
-                    // Keep looking for this device and source.
-                    continue;
-                }
-
-                if (!motionEntry->canAppendSamples(args->action,
-                        args->pointerCount, args->pointerProperties)) {
-                    // Last motion event in the queue for this device and source is
-                    // not compatible for appending new samples.  Stop here.
-                    goto NoBatchingOrStreaming;
-                }
-
-                // Do the batching magic.
-                batchMotionLocked(motionEntry, args->eventTime,
-                        args->metaState, args->pointerCoords,
-                        "most recent motion event for this device and source in the inbound queue");
-                mLock.unlock();
-                return; // done!
-            }
-
-            // BATCHING ONTO PENDING EVENT CASE
-            //
-            // Try to append a move sample to the currently pending event, if there is one.
-            // We can do this as long as we are still waiting to find the targets for the
-            // event.  Once the targets are locked-in we can only do streaming.
-            if (mPendingEvent
-                    && (!mPendingEvent->dispatchInProgress || !mCurrentInputTargetsValid)
-                    && mPendingEvent->type == EventEntry::TYPE_MOTION) {
-                MotionEntry* motionEntry = static_cast<MotionEntry*>(mPendingEvent);
-                if (motionEntry->deviceId == args->deviceId
-                        && motionEntry->source == args->source) {
-                    if (!motionEntry->canAppendSamples(args->action,
-                            args->pointerCount, args->pointerProperties)) {
-                        // Pending motion event is for this device and source but it is
-                        // not compatible for appending new samples.  Stop here.
-                        goto NoBatchingOrStreaming;
-                    }
-
-                    // Do the batching magic.
-                    batchMotionLocked(motionEntry, args->eventTime,
-                            args->metaState, args->pointerCoords,
-                            "pending motion event");
-                    mLock.unlock();
-                    return; // done!
-                }
-            }
-
-            // STREAMING CASE
-            //
-            // There is no pending motion event (of any kind) for this device in the inbound queue.
-            // Search the outbound queue for the current foreground targets to find a dispatched
-            // motion event that is still in progress.  If found, then, appen the new sample to
-            // that event and push it out to all current targets.  The logic in
-            // prepareDispatchCycleLocked takes care of the case where some targets may
-            // already have consumed the motion event by starting a new dispatch cycle if needed.
-            if (mCurrentInputTargetsValid) {
-                for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
-                    const InputTarget& inputTarget = mCurrentInputTargets[i];
-                    if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) {
-                        // Skip non-foreground targets.  We only want to stream if there is at
-                        // least one foreground target whose dispatch is still in progress.
-                        continue;
-                    }
-
-                    ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
-                    if (connectionIndex < 0) {
-                        // Connection must no longer be valid.
-                        continue;
-                    }
-
-                    sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-                    if (connection->outboundQueue.isEmpty()) {
-                        // This foreground target has an empty outbound queue.
-                        continue;
-                    }
-
-                    DispatchEntry* dispatchEntry = connection->outboundQueue.head;
-                    if (! dispatchEntry->inProgress
-                            || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION
-                            || dispatchEntry->isSplit()) {
-                        // No motion event is being dispatched, or it is being split across
-                        // windows in which case we cannot stream.
-                        continue;
-                    }
-
-                    MotionEntry* motionEntry = static_cast<MotionEntry*>(
-                            dispatchEntry->eventEntry);
-                    if (motionEntry->action != args->action
-                            || motionEntry->deviceId != args->deviceId
-                            || motionEntry->source != args->source
-                            || motionEntry->pointerCount != args->pointerCount
-                            || motionEntry->isInjected()) {
-                        // The motion event is not compatible with this move.
-                        continue;
-                    }
-
-                    if (args->action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                        if (mLastHoverWindowHandle == NULL) {
-#if DEBUG_BATCHING
-                            ALOGD("Not streaming hover move because there is no "
-                                    "last hovered window.");
-#endif
-                            goto NoBatchingOrStreaming;
-                        }
-
-                        sp<InputWindowHandle> hoverWindowHandle = findTouchedWindowAtLocked(
-                                args->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
-                                args->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-                        if (mLastHoverWindowHandle != hoverWindowHandle) {
-#if DEBUG_BATCHING
-                            ALOGD("Not streaming hover move because the last hovered window "
-                                    "is '%s' but the currently hovered window is '%s'.",
-                                    mLastHoverWindowHandle->getName().string(),
-                                    hoverWindowHandle != NULL
-                                            ? hoverWindowHandle->getName().string() : "<null>");
-#endif
-                            goto NoBatchingOrStreaming;
-                        }
-                    }
-
-                    // Hurray!  This foreground target is currently dispatching a move event
-                    // that we can stream onto.  Append the motion sample and resume dispatch.
-                    motionEntry->appendSample(args->eventTime, args->pointerCoords);
-#if DEBUG_BATCHING
-                    ALOGD("Appended motion sample onto batch for most recently dispatched "
-                            "motion event for this device and source in the outbound queues.  "
-                            "Attempting to stream the motion sample.");
-#endif
-                    nsecs_t currentTime = now();
-                    dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry,
-                            true /*resumeWithAppendedMotionSample*/);
-
-                    runCommandsLockedInterruptible();
-                    mLock.unlock();
-                    return; // done!
-                }
-            }
-
-NoBatchingOrStreaming:;
-        }
-
         // Just enqueue a new motion event.
         MotionEntry* newEntry = new MotionEntry(args->eventTime,
                 args->deviceId, args->source, policyFlags,
@@ -2962,36 +2432,6 @@
     }
 }
 
-void InputDispatcher::batchMotionLocked(MotionEntry* entry, nsecs_t eventTime,
-        int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription) {
-    // Combine meta states.
-    entry->metaState |= metaState;
-
-    // Coalesce this sample if not enough time has elapsed since the last sample was
-    // initially appended to the batch.
-    MotionSample* lastSample = entry->lastSample;
-    long interval = eventTime - lastSample->eventTimeBeforeCoalescing;
-    if (interval <= MOTION_SAMPLE_COALESCE_INTERVAL) {
-        uint32_t pointerCount = entry->pointerCount;
-        for (uint32_t i = 0; i < pointerCount; i++) {
-            lastSample->pointerCoords[i].copyFrom(pointerCoords[i]);
-        }
-        lastSample->eventTime = eventTime;
-#if DEBUG_BATCHING
-        ALOGD("Coalesced motion into last sample of batch for %s, events were %0.3f ms apart",
-                eventDescription, interval * 0.000001f);
-#endif
-        return;
-    }
-
-    // Append the sample.
-    entry->appendSample(eventTime, pointerCoords);
-#if DEBUG_BATCHING
-    ALOGD("Appended motion sample onto batch for %s, events were %0.3f ms apart",
-            eventDescription, interval * 0.000001f);
-#endif
-}
-
 void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("notifySwitch - eventTime=%lld, policyFlags=0x%x, switchCode=%d, switchValue=%d",
@@ -3040,7 +2480,8 @@
         policyFlags |= POLICY_FLAG_TRUSTED;
     }
 
-    EventEntry* injectedEntry;
+    EventEntry* firstInjectedEntry;
+    EventEntry* lastInjectedEntry;
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY: {
         const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
@@ -3063,11 +2504,12 @@
         }
 
         mLock.lock();
-        injectedEntry = new KeyEntry(keyEvent->getEventTime(),
+        firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(),
                 keyEvent->getDeviceId(), keyEvent->getSource(),
                 policyFlags, action, flags,
                 keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
                 keyEvent->getRepeatCount(), keyEvent->getDownTime());
+        lastInjectedEntry = firstInjectedEntry;
         break;
     }
 
@@ -3088,7 +2530,7 @@
         mLock.lock();
         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
         const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
-        MotionEntry* motionEntry = new MotionEntry(*sampleEventTimes,
+        firstInjectedEntry = new MotionEntry(*sampleEventTimes,
                 motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
                 action, motionEvent->getFlags(),
                 motionEvent->getMetaState(), motionEvent->getButtonState(),
@@ -3096,12 +2538,21 @@
                 motionEvent->getXPrecision(), motionEvent->getYPrecision(),
                 motionEvent->getDownTime(), uint32_t(pointerCount),
                 pointerProperties, samplePointerCoords);
+        lastInjectedEntry = firstInjectedEntry;
         for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
             sampleEventTimes += 1;
             samplePointerCoords += pointerCount;
-            motionEntry->appendSample(*sampleEventTimes, samplePointerCoords);
+            MotionEntry* nextInjectedEntry = new MotionEntry(*sampleEventTimes,
+                    motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
+                    action, motionEvent->getFlags(),
+                    motionEvent->getMetaState(), motionEvent->getButtonState(),
+                    motionEvent->getEdgeFlags(),
+                    motionEvent->getXPrecision(), motionEvent->getYPrecision(),
+                    motionEvent->getDownTime(), uint32_t(pointerCount),
+                    pointerProperties, samplePointerCoords);
+            lastInjectedEntry->next = nextInjectedEntry;
+            lastInjectedEntry = nextInjectedEntry;
         }
-        injectedEntry = motionEntry;
         break;
     }
 
@@ -3116,9 +2567,15 @@
     }
 
     injectionState->refCount += 1;
-    injectedEntry->injectionState = injectionState;
+    lastInjectedEntry->injectionState = injectionState;
 
-    bool needWake = enqueueInboundEventLocked(injectedEntry);
+    bool needWake = false;
+    for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) {
+        EventEntry* nextEntry = entry->next;
+        needWake |= enqueueInboundEventLocked(entry);
+        entry = nextEntry;
+    }
+
     mLock.unlock();
 
     if (needWake) {
@@ -3366,13 +2823,13 @@
         if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) {
             if (mFocusedApplicationHandle != inputApplicationHandle) {
                 if (mFocusedApplicationHandle != NULL) {
-                    resetTargetsLocked();
+                    resetANRTimeoutsLocked();
                     mFocusedApplicationHandle->releaseInfo();
                 }
                 mFocusedApplicationHandle = inputApplicationHandle;
             }
         } else if (mFocusedApplicationHandle != NULL) {
-            resetTargetsLocked();
+            resetANRTimeoutsLocked();
             mFocusedApplicationHandle->releaseInfo();
             mFocusedApplicationHandle.clear();
         }
@@ -3495,8 +2952,8 @@
         ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
         ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
         if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
-            sp<Connection> fromConnection = mConnectionsByReceiveFd.valueAt(fromConnectionIndex);
-            sp<Connection> toConnection = mConnectionsByReceiveFd.valueAt(toConnectionIndex);
+            sp<Connection> fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex);
+            sp<Connection> toConnection = mConnectionsByFd.valueAt(toConnectionIndex);
 
             fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
             CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -3525,7 +2982,7 @@
     resetKeyRepeatLocked();
     releasePendingEventLocked();
     drainInboundQueueLocked();
-    resetTargetsLocked();
+    resetANRTimeoutsLocked();
 
     mTouchState.reset();
     mLastHoverWindowHandle.clear();
@@ -3622,20 +3079,6 @@
 
     dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count());
 
-    if (!mActiveConnections.isEmpty()) {
-        dump.append(INDENT "ActiveConnections:\n");
-        for (size_t i = 0; i < mActiveConnections.size(); i++) {
-            const Connection* connection = mActiveConnections[i];
-            dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u, "
-                    "inputState.isNeutral=%s\n",
-                    i, connection->getInputChannelName(), connection->getStatusLabel(),
-                    connection->outboundQueue.count(),
-                    toString(connection->inputState.isNeutral()));
-        }
-    } else {
-        dump.append(INDENT "ActiveConnections: <none>\n");
-    }
-
     if (isAppSwitchPendingLocked()) {
         dump.appendFormat(INDENT "AppSwitch: pending, due in %01.1fms\n",
                 (mAppSwitchDueTime - now()) / 1000000.0);
@@ -3661,21 +3104,15 @@
         }
 
         sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
-        status_t status = connection->initialize();
-        if (status) {
-            ALOGE("Failed to initialize input publisher for input channel '%s', status=%d",
-                    inputChannel->getName().string(), status);
-            return status;
-        }
 
-        int32_t receiveFd = inputChannel->getReceivePipeFd();
-        mConnectionsByReceiveFd.add(receiveFd, connection);
+        int fd = inputChannel->getFd();
+        mConnectionsByFd.add(fd, connection);
 
         if (monitor) {
             mMonitoringChannels.push(inputChannel);
         }
 
-        mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
 
         runCommandsLockedInterruptible();
     } // release lock
@@ -3711,14 +3148,14 @@
         return BAD_VALUE;
     }
 
-    sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-    mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
+    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
+    mConnectionsByFd.removeItemsAt(connectionIndex);
 
     if (connection->monitor) {
         removeMonitorChannelLocked(inputChannel);
     }
 
-    mLooper->removeFd(inputChannel->getReceivePipeFd());
+    mLooper->removeFd(inputChannel->getFd());
 
     nsecs_t currentTime = now();
     abortBrokenDispatchCycleLocked(currentTime, connection, notify);
@@ -3739,9 +3176,9 @@
 }
 
 ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
-    ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+    ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
     if (connectionIndex >= 0) {
-        sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+        sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
         if (connection->inputChannel.get() == inputChannel.get()) {
             return connectionIndex;
         }
@@ -3750,33 +3187,12 @@
     return -1;
 }
 
-void InputDispatcher::activateConnectionLocked(Connection* connection) {
-    for (size_t i = 0; i < mActiveConnections.size(); i++) {
-        if (mActiveConnections.itemAt(i) == connection) {
-            return;
-        }
-    }
-    mActiveConnections.add(connection);
-}
-
-void InputDispatcher::deactivateConnectionLocked(Connection* connection) {
-    for (size_t i = 0; i < mActiveConnections.size(); i++) {
-        if (mActiveConnections.itemAt(i) == connection) {
-            mActiveConnections.removeAt(i);
-            return;
-        }
-    }
-}
-
-void InputDispatcher::onDispatchCycleStartedLocked(
-        nsecs_t currentTime, const sp<Connection>& connection) {
-}
-
 void InputDispatcher::onDispatchCycleFinishedLocked(
-        nsecs_t currentTime, const sp<Connection>& connection, bool handled) {
+        nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
     CommandEntry* commandEntry = postCommandLocked(
             & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
     commandEntry->connection = connection;
+    commandEntry->seq = seq;
     commandEntry->handled = handled;
 }
 
@@ -3870,26 +3286,40 @@
 void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
         CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
+    uint32_t seq = commandEntry->seq;
     bool handled = commandEntry->handled;
 
-    bool skipNext = false;
-    if (!connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
-        if (dispatchEntry->inProgress) {
-            if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
-                KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
-                skipNext = afterKeyEventLockedInterruptible(connection,
-                        dispatchEntry, keyEntry, handled);
-            } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
-                MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
-                skipNext = afterMotionEventLockedInterruptible(connection,
-                        dispatchEntry, motionEntry, handled);
+    // Handle post-event policy actions.
+    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
+    if (dispatchEntry) {
+        bool restartEvent;
+        if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
+            KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+            restartEvent = afterKeyEventLockedInterruptible(connection,
+                    dispatchEntry, keyEntry, handled);
+        } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
+            MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+            restartEvent = afterMotionEventLockedInterruptible(connection,
+                    dispatchEntry, motionEntry, handled);
+        } else {
+            restartEvent = false;
+        }
+
+        // Dequeue the event and start the next cycle.
+        // Note that because the lock might have been released, it is possible that the
+        // contents of the wait queue to have been drained, so we need to double-check
+        // a few things.
+        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
+            connection->waitQueue.dequeue(dispatchEntry);
+            if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
+                connection->outboundQueue.enqueueAtHead(dispatchEntry);
+            } else {
+                releaseDispatchEntryLocked(dispatchEntry);
             }
         }
-    }
 
-    if (!skipNext) {
-        startNextDispatchCycleLocked(now(), connection);
+        // Start the next dispatch cycle for this connection.
+        startDispatchCycleLocked(now(), connection);
     }
 }
 
@@ -3953,11 +3383,9 @@
 
             if (connection->status != Connection::STATUS_NORMAL) {
                 connection->inputState.removeFallbackKey(originalKeyCode);
-                return true; // skip next cycle
+                return false;
             }
 
-            ALOG_ASSERT(connection->outboundQueue.head == dispatchEntry);
-
             // Latch the fallback keycode for this key on an initial down.
             // The fallback keycode cannot change at any other point in the lifecycle.
             if (initialDown) {
@@ -4035,10 +3463,7 @@
                         "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
                         originalKeyCode, fallbackKeyCode, keyEntry->metaState);
 #endif
-
-                dispatchEntry->inProgress = false;
-                startDispatchCycleLocked(now(), connection);
-                return true; // already started next cycle
+                return true; // restart the event
             } else {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
                 ALOGD("Unhandled key event: No fallback key.");
@@ -4080,7 +3505,6 @@
     dumpDispatchStateLocked(dump);
 
     dump.append(INDENT "Configuration:\n");
-    dump.appendFormat(INDENT2 "MaxEventsPerSecond: %d\n", mConfig.maxEventsPerSecond);
     dump.appendFormat(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
     dump.appendFormat(INDENT2 "KeyRepeatTimeout: %0.1fms\n", mConfig.keyRepeatTimeout * 0.000001f);
 }
@@ -4204,17 +3628,6 @@
 }
 
 
-// --- InputDispatcher::MotionSample ---
-
-InputDispatcher::MotionSample::MotionSample(nsecs_t eventTime,
-        const PointerCoords* pointerCoords, uint32_t pointerCount) :
-        next(NULL), eventTime(eventTime), eventTimeBeforeCoalescing(eventTime) {
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        this->pointerCoords[i].copyFrom(pointerCoords[i]);
-    }
-}
-
-
 // --- InputDispatcher::MotionEntry ---
 
 InputDispatcher::MotionEntry::MotionEntry(nsecs_t eventTime,
@@ -4224,66 +3637,31 @@
         nsecs_t downTime, uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) :
         EventEntry(TYPE_MOTION, eventTime, policyFlags),
+        eventTime(eventTime),
         deviceId(deviceId), source(source), action(action), flags(flags),
         metaState(metaState), buttonState(buttonState), edgeFlags(edgeFlags),
         xPrecision(xPrecision), yPrecision(yPrecision),
-        downTime(downTime), pointerCount(pointerCount),
-        firstSample(eventTime, pointerCoords, pointerCount),
-        lastSample(&firstSample) {
+        downTime(downTime), pointerCount(pointerCount) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
+        this->pointerCoords[i].copyFrom(pointerCoords[i]);
     }
 }
 
 InputDispatcher::MotionEntry::~MotionEntry() {
-    for (MotionSample* sample = firstSample.next; sample != NULL; ) {
-        MotionSample* next = sample->next;
-        delete sample;
-        sample = next;
-    }
-}
-
-uint32_t InputDispatcher::MotionEntry::countSamples() const {
-    uint32_t count = 1;
-    for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
-        count += 1;
-    }
-    return count;
-}
-
-bool InputDispatcher::MotionEntry::canAppendSamples(int32_t action, uint32_t pointerCount,
-        const PointerProperties* pointerProperties) const {
-    if (this->action != action
-            || this->pointerCount != pointerCount
-            || this->isInjected()) {
-        return false;
-    }
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        if (this->pointerProperties[i] != pointerProperties[i]) {
-            return false;
-        }
-    }
-    return true;
-}
-
-void InputDispatcher::MotionEntry::appendSample(
-        nsecs_t eventTime, const PointerCoords* pointerCoords) {
-    MotionSample* sample = new MotionSample(eventTime, pointerCoords, pointerCount);
-
-    lastSample->next = sample;
-    lastSample = sample;
 }
 
 
 // --- InputDispatcher::DispatchEntry ---
 
+volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
+
 InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
         int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :
+        seq(nextSeq()),
         eventEntry(eventEntry), targetFlags(targetFlags),
         xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor),
-        inProgress(false),
-        resolvedAction(0), resolvedFlags(0),
-        headMotionSample(NULL), tailMotionSample(NULL) {
+        resolvedAction(0), resolvedFlags(0) {
     eventEntry->refCount += 1;
 }
 
@@ -4291,6 +3669,15 @@
     eventEntry->release();
 }
 
+uint32_t InputDispatcher::DispatchEntry::nextSeq() {
+    // Sequence number 0 is reserved and will never be returned.
+    uint32_t seq;
+    do {
+        seq = android_atomic_inc(&sNextSeqAtomic);
+    } while (!seq);
+    return seq;
+}
+
 
 // --- InputDispatcher::InputState ---
 
@@ -4501,7 +3888,7 @@
     pointerCount = entry->pointerCount;
     for (uint32_t i = 0; i < entry->pointerCount; i++) {
         pointerProperties[i].copyFrom(entry->pointerProperties[i]);
-        pointerCoords[i].copyFrom(entry->lastSample->pointerCoords[i]);
+        pointerCoords[i].copyFrom(entry->pointerCoords[i]);
     }
 }
 
@@ -4621,17 +4008,12 @@
         const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
         status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
         monitor(monitor),
-        inputPublisher(inputChannel),
-        lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
+        inputPublisher(inputChannel), inputPublisherBlocked(false) {
 }
 
 InputDispatcher::Connection::~Connection() {
 }
 
-status_t InputDispatcher::Connection::initialize() {
-    return inputPublisher.initialize();
-}
-
 const char* InputDispatcher::Connection::getStatusLabel() const {
     switch (status) {
     case STATUS_NORMAL:
@@ -4648,12 +4030,10 @@
     }
 }
 
-InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
-        const EventEntry* eventEntry) const {
-    for (DispatchEntry* dispatchEntry = outboundQueue.tail; dispatchEntry;
-            dispatchEntry = dispatchEntry->prev) {
-        if (dispatchEntry->eventEntry == eventEntry) {
-            return dispatchEntry;
+InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) {
+    for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) {
+        if (entry->seq == seq) {
+            return entry;
         }
     }
     return NULL;
@@ -4663,7 +4043,8 @@
 // --- InputDispatcher::CommandEntry ---
 
 InputDispatcher::CommandEntry::CommandEntry(Command command) :
-    command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0), handled(false) {
+    command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0),
+    seq(0), handled(false) {
 }
 
 InputDispatcher::CommandEntry::~CommandEntry() {
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index a1d42e1..4b36480 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -17,8 +17,8 @@
 #ifndef _UI_INPUT_DISPATCHER_H
 #define _UI_INPUT_DISPATCHER_H
 
-#include <ui/Input.h>
-#include <ui/InputTransport.h>
+#include <androidfw/Input.h>
+#include <androidfw/InputTransport.h>
 #include <utils/KeyedVector.h>
 #include <utils/Vector.h>
 #include <utils/threads.h>
@@ -27,6 +27,7 @@
 #include <utils/String8.h>
 #include <utils/Looper.h>
 #include <utils/BitSet.h>
+#include <cutils/atomic.h>
 
 #include <stddef.h>
 #include <unistd.h>
@@ -172,15 +173,9 @@
     // The key repeat inter-key delay.
     nsecs_t keyRepeatDelay;
 
-    // The maximum suggested event delivery rate per second.
-    // This value is used to throttle motion event movement actions on a per-device
-    // basis.  It is not intended to be a hard limit.
-    int32_t maxEventsPerSecond;
-
     InputDispatcherConfiguration() :
             keyRepeatTimeout(500 * 1000000LL),
-            keyRepeatDelay(50 * 1000000LL),
-            maxEventsPerSecond(60) { }
+            keyRepeatDelay(50 * 1000000LL) { }
 };
 
 
@@ -404,6 +399,9 @@
     struct Link {
         T* next;
         T* prev;
+
+    protected:
+        inline Link() : next(NULL), prev(NULL) { }
     };
 
     struct InjectionState {
@@ -496,18 +494,8 @@
         virtual ~KeyEntry();
     };
 
-    struct MotionSample {
-        MotionSample* next;
-
-        nsecs_t eventTime; // may be updated during coalescing
-        nsecs_t eventTimeBeforeCoalescing; // not updated during coalescing
-        PointerCoords pointerCoords[MAX_POINTERS];
-
-        MotionSample(nsecs_t eventTime, const PointerCoords* pointerCoords,
-                uint32_t pointerCount);
-    };
-
     struct MotionEntry : EventEntry {
+        nsecs_t eventTime;
         int32_t deviceId;
         uint32_t source;
         int32_t action;
@@ -520,10 +508,7 @@
         nsecs_t downTime;
         uint32_t pointerCount;
         PointerProperties pointerProperties[MAX_POINTERS];
-
-        // Linked list of motion samples associated with this motion event.
-        MotionSample firstSample;
-        MotionSample* lastSample;
+        PointerCoords pointerCoords[MAX_POINTERS];
 
         MotionEntry(nsecs_t eventTime,
                 int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
@@ -532,46 +517,24 @@
                 nsecs_t downTime, uint32_t pointerCount,
                 const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
 
-        uint32_t countSamples() const;
-
-        // Checks whether we can append samples, assuming the device id and source are the same.
-        bool canAppendSamples(int32_t action, uint32_t pointerCount,
-                const PointerProperties* pointerProperties) const;
-
-        void appendSample(nsecs_t eventTime, const PointerCoords* pointerCoords);
-
     protected:
         virtual ~MotionEntry();
     };
 
     // Tracks the progress of dispatching a particular event to a particular connection.
     struct DispatchEntry : Link<DispatchEntry> {
+        const uint32_t seq; // unique sequence number, never 0
+
         EventEntry* eventEntry; // the event to dispatch
         int32_t targetFlags;
         float xOffset;
         float yOffset;
         float scaleFactor;
 
-        // True if dispatch has started.
-        bool inProgress;
-
         // Set to the resolved action and flags when the event is enqueued.
         int32_t resolvedAction;
         int32_t resolvedFlags;
 
-        // For motion events:
-        //   Pointer to the first motion sample to dispatch in this cycle.
-        //   Usually NULL to indicate that the list of motion samples begins at
-        //   MotionEntry::firstSample.  Otherwise, some samples were dispatched in a previous
-        //   cycle and this pointer indicates the location of the first remainining sample
-        //   to dispatch during the current cycle.
-        MotionSample* headMotionSample;
-        //   Pointer to a motion sample to dispatch in the next cycle if the dispatcher was
-        //   unable to send all motion samples during this cycle.  On the next cycle,
-        //   headMotionSample will be initialized to tailMotionSample and tailMotionSample
-        //   will be set to NULL.
-        MotionSample* tailMotionSample;
-
         DispatchEntry(EventEntry* eventEntry,
                 int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
         ~DispatchEntry();
@@ -583,6 +546,11 @@
         inline bool isSplit() const {
             return targetFlags & InputTarget::FLAG_SPLIT;
         }
+
+    private:
+        static volatile int32_t sNextSeqAtomic;
+
+        static uint32_t nextSeq();
     };
 
     // A command entry captures state and behavior for an action to be performed in the
@@ -618,6 +586,7 @@
         sp<InputApplicationHandle> inputApplicationHandle;
         sp<InputWindowHandle> inputWindowHandle;
         int32_t userActivityEventType;
+        uint32_t seq;
         bool handled;
     };
 
@@ -819,10 +788,17 @@
         bool monitor;
         InputPublisher inputPublisher;
         InputState inputState;
+
+        // True if the socket is full and no further events can be published until
+        // the application consumes some of the input.
+        bool inputPublisherBlocked;
+
+        // Queue of events that need to be published to the connection.
         Queue<DispatchEntry> outboundQueue;
 
-        nsecs_t lastEventTime; // the time when the event was originally captured
-        nsecs_t lastDispatchTime; // the time when the last event was dispatched
+        // Queue of events that have been published to the connection but that have not
+        // yet received a "finished" response from the application.
+        Queue<DispatchEntry> waitQueue;
 
         explicit Connection(const sp<InputChannel>& inputChannel,
                 const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
@@ -831,21 +807,7 @@
 
         const char* getStatusLabel() const;
 
-        // Finds a DispatchEntry in the outbound queue associated with the specified event.
-        // Returns NULL if not found.
-        DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const;
-
-        // Gets the time since the current event was originally obtained from the input driver.
-        inline double getEventLatencyMillis(nsecs_t currentTime) const {
-            return (currentTime - lastEventTime) / 1000000.0;
-        }
-
-        // Gets the time since the current event entered the outbound dispatch queue.
-        inline double getDispatchLatencyMillis(nsecs_t currentTime) const {
-            return (currentTime - lastDispatchTime) / 1000000.0;
-        }
-
-        status_t initialize();
+        DispatchEntry* findWaitQueueEntry(uint32_t seq);
     };
 
     enum DropReason {
@@ -870,15 +832,7 @@
     Queue<EventEntry> mInboundQueue;
     Queue<CommandEntry> mCommandQueue;
 
-    Vector<EventEntry*> mTempCancelationEvents;
-
     void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime);
-    void dispatchIdleLocked();
-
-    // Batches a new sample onto a motion entry.
-    // Assumes that the we have already checked that we can append samples.
-    void batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState,
-            const PointerCoords* pointerCoords, const char* eventDescription);
 
     // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
     bool enqueueInboundEventLocked(EventEntry* entry);
@@ -904,17 +858,11 @@
 
     sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t x, int32_t y);
 
-    // All registered connections mapped by receive pipe file descriptor.
-    KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
+    // All registered connections mapped by channel file descriptor.
+    KeyedVector<int, sp<Connection> > mConnectionsByFd;
 
     ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
 
-    // Active connections are connections that have a non-empty outbound queue.
-    // We don't use a ref-counted pointer here because we explicitly abort connections
-    // during unregistration which causes the connection's outbound queue to be cleared
-    // and the connection itself to be deactivated.
-    Vector<Connection*> mActiveConnections;
-
     // Input channels that will receive a copy of all input events.
     Vector<sp<InputChannel> > mMonitoringChannels;
 
@@ -927,17 +875,6 @@
     void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
     void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
 
-    // Throttling state.
-    struct ThrottleState {
-        nsecs_t minTimeBetweenEvents;
-
-        nsecs_t lastEventTime;
-        int32_t lastDeviceId;
-        uint32_t lastSource;
-
-        uint32_t originalSampleCount; // only collected during debugging
-    } mThrottleState;
-
     // Key repeat tracking.
     struct KeyRepeatState {
         KeyEntry* lastKeyEntry; // or null if no repeat
@@ -1010,16 +947,13 @@
     bool dispatchMotionLocked(
             nsecs_t currentTime, MotionEntry* entry,
             DropReason* dropReason, nsecs_t* nextWakeupTime);
-    void dispatchEventToCurrentInputTargetsLocked(
-            nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
+    void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
+            const Vector<InputTarget>& inputTargets);
 
     void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry);
     void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
 
-    // The input targets that were most recently identified for dispatch.
-    bool mCurrentInputTargetsValid; // false while targets are being recomputed
-    Vector<InputTarget> mCurrentInputTargets;
-
+    // Keeping track of ANR timeouts.
     enum InputTargetWaitCause {
         INPUT_TARGET_WAIT_CAUSE_NONE,
         INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
@@ -1036,8 +970,6 @@
     sp<InputWindowHandle> mLastHoverWindowHandle;
 
     // Finding targets for input events.
-    void resetTargetsLocked();
-    void commitTargetsLocked();
     int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
             const sp<InputApplicationHandle>& applicationHandle,
             const sp<InputWindowHandle>& windowHandle,
@@ -1048,20 +980,22 @@
     void resetANRTimeoutsLocked();
 
     int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
-            nsecs_t* nextWakeupTime);
+            Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime);
     int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
-            nsecs_t* nextWakeupTime, bool* outConflictingPointerActions,
-            const MotionSample** outSplitBatchAfterSample);
+            Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
+            bool* outConflictingPointerActions);
 
     void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
-            int32_t targetFlags, BitSet32 pointerIds);
-    void addMonitoringTargetsLocked();
+            int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
+    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets);
+
     void pokeUserActivityLocked(const EventEntry* eventEntry);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
             const InjectionState* injectionState);
     bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
             int32_t x, int32_t y) const;
-    bool isWindowFinishedWithPreviousInputLocked(const sp<InputWindowHandle>& windowHandle);
+    bool isWindowReadyForMoreInputLocked(nsecs_t currentTime,
+            const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry);
     String8 getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
             const sp<InputWindowHandle>& windowHandle);
 
@@ -1070,22 +1004,19 @@
     // with the mutex held makes it easier to ensure that connection invariants are maintained.
     // If needed, the methods post commands to run later once the critical bits are done.
     void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-            EventEntry* eventEntry, const InputTarget* inputTarget,
-            bool resumeWithAppendedMotionSample);
+            EventEntry* eventEntry, const InputTarget* inputTarget);
     void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
-            EventEntry* eventEntry, const InputTarget* inputTarget,
-            bool resumeWithAppendedMotionSample);
+            EventEntry* eventEntry, const InputTarget* inputTarget);
     void enqueueDispatchEntryLocked(const sp<Connection>& connection,
-            EventEntry* eventEntry, const InputTarget* inputTarget,
-            bool resumeWithAppendedMotionSample, int32_t dispatchMode);
+            EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
     void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
     void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-            bool handled);
-    void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+            uint32_t seq, bool handled);
     void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             bool notify);
-    void drainOutboundQueueLocked(Connection* connection);
-    static int handleReceiveCallback(int receiveFd, int events, void* data);
+    void drainDispatchQueueLocked(Queue<DispatchEntry>* queue);
+    void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry);
+    static int handleReceiveCallback(int fd, int events, void* data);
 
     void synthesizeCancelationEventsForAllConnectionsLocked(
             const CancelationOptions& options);
@@ -1113,10 +1044,8 @@
     void deactivateConnectionLocked(Connection* connection);
 
     // Interesting events that we might like to log or tell the framework about.
-    void onDispatchCycleStartedLocked(
-            nsecs_t currentTime, const sp<Connection>& connection);
     void onDispatchCycleFinishedLocked(
-            nsecs_t currentTime, const sp<Connection>& connection, bool handled);
+            nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
     void onDispatchCycleBrokenLocked(
             nsecs_t currentTime, const sp<Connection>& connection);
     void onANRLocked(
diff --git a/services/input/InputListener.h b/services/input/InputListener.h
index f920cd1..b1dc0b8 100644
--- a/services/input/InputListener.h
+++ b/services/input/InputListener.h
@@ -17,7 +17,7 @@
 #ifndef _UI_INPUT_LISTENER_H
 #define _UI_INPUT_LISTENER_H
 
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
 
diff --git a/services/input/InputManager.h b/services/input/InputManager.h
index df4d299e..29584c9 100644
--- a/services/input/InputManager.h
+++ b/services/input/InputManager.h
@@ -25,8 +25,8 @@
 #include "InputReader.h"
 #include "InputDispatcher.h"
 
-#include <ui/Input.h>
-#include <ui/InputTransport.h>
+#include <androidfw/Input.h>
+#include <androidfw/InputTransport.h>
 #include <utils/Errors.h>
 #include <utils/Vector.h>
 #include <utils/Timers.h>
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 4be06e4..eccce29 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -39,8 +39,8 @@
 #include "InputReader.h"
 
 #include <cutils/log.h>
-#include <ui/Keyboard.h>
-#include <ui/VirtualKeyMap.h>
+#include <androidfw/Keyboard.h>
+#include <androidfw/VirtualKeyMap.h>
 
 #include <stddef.h>
 #include <stdlib.h>
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index ad89a22..9bbe49c 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -21,7 +21,7 @@
 #include "PointerController.h"
 #include "InputListener.h"
 
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include <ui/DisplayInfo.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 38968f9..824a64b 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -17,8 +17,8 @@
 #ifndef _UI_INPUT_WINDOW_H
 #define _UI_INPUT_WINDOW_H
 
-#include <ui/Input.h>
-#include <ui/InputTransport.h>
+#include <androidfw/Input.h>
+#include <androidfw/InputTransport.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 #include <utils/String8.h>
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index 700ef72..39dbf6b 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -20,7 +20,7 @@
 #include "SpriteController.h"
 
 #include <ui/DisplayInfo.h>
-#include <ui/Input.h>
+#include <androidfw/Input.h>
 #include <utils/RefBase.h>
 #include <utils/Looper.h>
 #include <utils/String8.h>
diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk
index 171db3c..8f8c34b 100644
--- a/services/input/tests/Android.mk
+++ b/services/input/tests/Android.mk
@@ -9,6 +9,7 @@
 
 shared_libraries := \
     libcutils \
+    libandroidfw \
     libutils \
     libhardware \
     libhardware_legacy \
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 250386f..9c408c4 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -463,7 +463,7 @@
                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
                 intent.setComponent(p.info.provider);
                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
-                mContext.sendBroadcast(intent);
+                mContext.sendBroadcast(intent, mUserId);
                 if (p.instances.size() == 0) {
                     // cancel the future updates
                     cancelBroadcasts(p);
@@ -471,7 +471,7 @@
                     // send the broacast saying that the provider is not in use any more
                     intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
                     intent.setComponent(p.info.provider);
-                    mContext.sendBroadcast(intent);
+                    mContext.sendBroadcast(intent, mUserId);
                 }
             }
         }
@@ -515,8 +515,6 @@
                             + " safe mode: " + provider);
                 }
 
-                Binder.restoreCallingIdentity(ident);
-
                 id.provider = p;
                 p.instances.add(id);
                 int instancesSize = p.instances.size();
@@ -573,12 +571,13 @@
                 mBoundRemoteViewsServices.remove(key);
             }
 
+            int userId = UserId.getUserId(id.provider.uid);
             // Bind to the RemoteViewsService (which will trigger a callback to the
             // RemoteViewsAdapter.onServiceConnected())
             final long token = Binder.clearCallingIdentity();
             try {
                 conn = new ServiceConnectionProxy(key, connection);
-                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
                 mBoundRemoteViewsServices.put(key, conn);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -638,11 +637,11 @@
 
         // Check if we need to destroy any services (if no other app widgets are
         // referencing the same service)
-        decrementAppWidgetServiceRefCount(appWidgetId);
+        decrementAppWidgetServiceRefCount(id);
     }
 
     // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
-    private void destroyRemoteViewsService(final Intent intent) {
+    private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
         final ServiceConnection conn = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
@@ -663,11 +662,12 @@
             }
         };
 
+        int userId = UserId.getUserId(id.provider.uid);
         // Bind to the service and remove the static intent->factory mapping in the
         // RemoteViewsService.
         final long token = Binder.clearCallingIdentity();
         try {
-            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -687,16 +687,16 @@
 
     // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
     // the ref-count reaches zero.
-    private void decrementAppWidgetServiceRefCount(int appWidgetId) {
+    private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
         Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
         while (it.hasNext()) {
             final FilterComparison key = it.next();
             final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
-            if (ids.remove(appWidgetId)) {
+            if (ids.remove(id.appWidgetId)) {
                 // If we have removed the last app widget referencing this service, then we
                 // should destroy it and remove it from this set
                 if (ids.isEmpty()) {
-                    destroyRemoteViewsService(key.getIntent());
+                    destroyRemoteViewsService(key.getIntent(), id);
                     it.remove();
                 }
             }
@@ -888,10 +888,11 @@
                             }
                         };
 
+                        int userId = UserId.getUserId(id.provider.uid);
                         // Bind to the service and call onDataSetChanged()
                         final long token = Binder.clearCallingIdentity();
                         try {
-                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
                         } finally {
                             Binder.restoreCallingIdentity(token);
                         }
@@ -1063,7 +1064,7 @@
     void sendEnableIntentLocked(Provider p) {
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
         intent.setComponent(p.info.provider);
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcast(intent, mUserId);
     }
 
     void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
@@ -1071,7 +1072,7 @@
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
             intent.setComponent(p.info.provider);
-            mContext.sendBroadcast(intent);
+            mContext.sendBroadcast(intent, mUserId);
         }
     }
 
@@ -1343,7 +1344,6 @@
 
     void readStateFromFileLocked(FileInputStream stream) {
         boolean success = false;
-
         try {
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(stream, null);
@@ -1475,12 +1475,11 @@
     }
 
     AtomicFile savedStateFile() {
-        int userId = Binder.getOrigCallingUser();
-        File dir = new File("/data/system/users/" + userId);
+        File dir = new File("/data/system/users/" + mUserId);
         File settingsFile = new File(dir, SETTINGS_FILENAME);
         if (!dir.exists()) {
             dir.mkdirs();
-            if (userId == 0) {
+            if (mUserId == 0) {
                 // Migrate old data
                 File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
                 // Method doesn't throw an exception on failure. Ignore any errors
diff --git a/services/java/com/android/server/CommonTimeManagementService.java b/services/java/com/android/server/CommonTimeManagementService.java
new file mode 100644
index 0000000..9a25d2e
--- /dev/null
+++ b/services/java/com/android/server/CommonTimeManagementService.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2012 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.server;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.INetworkManagementEventObserver;
+import android.net.InterfaceConfiguration;
+import android.net.NetworkInfo;
+import android.os.Binder;
+import android.os.CommonTimeConfig;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Log;
+
+/**
+ * @hide
+ * <p>CommonTimeManagementService manages the configuration of the native Common Time service,
+ * reconfiguring the native service as appropriate in response to changes in network configuration.
+ */
+class CommonTimeManagementService extends Binder {
+    /*
+     * Constants and globals.
+     */
+    private static final String TAG = CommonTimeManagementService.class.getSimpleName();
+    private static final int NATIVE_SERVICE_RECONNECT_TIMEOUT = 5000;
+    private static final String AUTO_DISABLE_PROP = "ro.common_time.auto_disable";
+    private static final String ALLOW_WIFI_PROP = "ro.common_time.allow_wifi";
+    private static final String SERVER_PRIO_PROP = "ro.common_time.server_prio";
+    private static final String NO_INTERFACE_TIMEOUT_PROP = "ro.common_time.no_iface_timeout";
+    private static final boolean AUTO_DISABLE;
+    private static final boolean ALLOW_WIFI;
+    private static final byte BASE_SERVER_PRIO;
+    private static final int NO_INTERFACE_TIMEOUT;
+    private static final InterfaceScoreRule[] IFACE_SCORE_RULES;
+
+    static {
+        int tmp;
+        AUTO_DISABLE         = (0 != SystemProperties.getInt(AUTO_DISABLE_PROP, 1));
+        ALLOW_WIFI           = (0 != SystemProperties.getInt(ALLOW_WIFI_PROP, 0));
+        tmp                  = SystemProperties.getInt(SERVER_PRIO_PROP, 1);
+        NO_INTERFACE_TIMEOUT = SystemProperties.getInt(NO_INTERFACE_TIMEOUT_PROP, 60000);
+
+        if (tmp < 1)
+            BASE_SERVER_PRIO = 1;
+        else
+        if (tmp > 30)
+            BASE_SERVER_PRIO = 30;
+        else
+            BASE_SERVER_PRIO = (byte)tmp;
+
+        if (ALLOW_WIFI) {
+            IFACE_SCORE_RULES = new InterfaceScoreRule[] {
+                new InterfaceScoreRule("wlan", (byte)1),
+                new InterfaceScoreRule("eth", (byte)2),
+            };
+        } else {
+            IFACE_SCORE_RULES = new InterfaceScoreRule[] {
+                new InterfaceScoreRule("eth", (byte)2),
+            };
+        }
+    };
+
+    /*
+     * Internal state
+     */
+    private final Context mContext;
+    private INetworkManagementService mNetMgr;
+    private CommonTimeConfig mCTConfig;
+    private String mCurIface;
+    private Handler mReconnectHandler = new Handler();
+    private Handler mNoInterfaceHandler = new Handler();
+    private Object mLock = new Object();
+    private boolean mDetectedAtStartup = false;
+    private byte mEffectivePrio = BASE_SERVER_PRIO;
+
+    /*
+     * Callback handler implementations.
+     */
+    private INetworkManagementEventObserver mIfaceObserver =
+        new INetworkManagementEventObserver.Stub() {
+
+        public void interfaceStatusChanged(String iface, boolean up) {
+            reevaluateServiceState();
+        }
+        public void interfaceLinkStateChanged(String iface, boolean up) {
+            reevaluateServiceState();
+        }
+        public void interfaceAdded(String iface) {
+            reevaluateServiceState();
+        }
+        public void interfaceRemoved(String iface) {
+            reevaluateServiceState();
+        }
+        public void limitReached(String limitName, String iface) { }
+    };
+
+    private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            reevaluateServiceState();
+        }
+    };
+
+    private CommonTimeConfig.OnServerDiedListener mCTServerDiedListener =
+        new CommonTimeConfig.OnServerDiedListener() {
+            public void onServerDied() {
+                scheduleTimeConfigReconnect();
+            }
+        };
+
+    private Runnable mReconnectRunnable = new Runnable() {
+        public void run() { connectToTimeConfig(); }
+    };
+
+    private Runnable mNoInterfaceRunnable = new Runnable() {
+        public void run() { handleNoInterfaceTimeout(); }
+    };
+
+    /*
+     * Public interface (constructor, systemReady and dump)
+     */
+    public CommonTimeManagementService(Context context) {
+        mContext = context;
+    }
+
+    void systemReady() {
+        if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
+            Log.i(TAG, "No common time service detected on this platform.  " +
+                       "Common time services will be unavailable.");
+            return;
+        }
+
+        mDetectedAtStartup = true;
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        mNetMgr = INetworkManagementService.Stub.asInterface(b);
+
+        // Network manager is running along-side us, so we should never receiver a remote exception
+        // while trying to register this observer.
+        try {
+            mNetMgr.registerObserver(mIfaceObserver);
+        }
+        catch (RemoteException e) { }
+
+        // Register with the connectivity manager for connectivity changed intents.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mContext.registerReceiver(mConnectivityMangerObserver, filter);
+
+        // Connect to the common time config service and apply the initial configuration.
+        connectToTimeConfig();
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println(String.format(
+                        "Permission Denial: can't dump CommonTimeManagement service from from " +
+                        "pid=%d, uid=%d", Binder.getCallingPid(), Binder.getCallingUid()));
+            return;
+        }
+
+        if (!mDetectedAtStartup) {
+            pw.println("Native Common Time service was not detected at startup.  " +
+                       "Service is unavailable");
+            return;
+        }
+
+        synchronized (mLock) {
+            pw.println("Current Common Time Management Service Config:");
+            pw.println(String.format("  Native service     : %s",
+                                     (null == mCTConfig) ? "reconnecting"
+                                                         : "alive"));
+            pw.println(String.format("  Bound interface    : %s",
+                                     (null == mCurIface ? "unbound" : mCurIface)));
+            pw.println(String.format("  Allow WiFi         : %s", ALLOW_WIFI ? "yes" : "no"));
+            pw.println(String.format("  Allow Auto Disable : %s", AUTO_DISABLE ? "yes" : "no"));
+            pw.println(String.format("  Server Priority    : %d", mEffectivePrio));
+            pw.println(String.format("  No iface timeout   : %d", NO_INTERFACE_TIMEOUT));
+        }
+    }
+
+    /*
+     * Inner helper classes
+     */
+    private static class InterfaceScoreRule {
+        public final String mPrefix;
+        public final byte mScore;
+        public InterfaceScoreRule(String prefix, byte score) {
+            mPrefix = prefix;
+            mScore = score;
+        }
+    };
+
+    /*
+     * Internal implementation
+     */
+    private void cleanupTimeConfig() {
+        mReconnectHandler.removeCallbacks(mReconnectRunnable);
+        mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
+        if (null != mCTConfig) {
+            mCTConfig.release();
+            mCTConfig = null;
+        }
+    }
+
+    private void connectToTimeConfig() {
+        // Get access to the common time service configuration interface.  If we catch a remote
+        // exception in the process (service crashed or no running for w/e reason), schedule an
+        // attempt to reconnect in the future.
+        cleanupTimeConfig();
+        try {
+            synchronized (mLock) {
+                mCTConfig = new CommonTimeConfig();
+                mCTConfig.setServerDiedListener(mCTServerDiedListener);
+                mCurIface = mCTConfig.getInterfaceBinding();
+                mCTConfig.setAutoDisable(AUTO_DISABLE);
+                mCTConfig.setMasterElectionPriority(mEffectivePrio);
+            }
+
+            if (NO_INTERFACE_TIMEOUT >= 0)
+                mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
+
+            reevaluateServiceState();
+        }
+        catch (RemoteException e) {
+            scheduleTimeConfigReconnect();
+        }
+    }
+
+    private void scheduleTimeConfigReconnect() {
+        cleanupTimeConfig();
+        Log.w(TAG, String.format("Native service died, will reconnect in %d mSec",
+                                 NATIVE_SERVICE_RECONNECT_TIMEOUT));
+        mReconnectHandler.postDelayed(mReconnectRunnable,
+                                      NATIVE_SERVICE_RECONNECT_TIMEOUT);
+    }
+
+    private void handleNoInterfaceTimeout() {
+        if (null != mCTConfig) {
+            Log.i(TAG, "Timeout waiting for interface to come up.  " +
+                       "Forcing networkless master mode.");
+            if (CommonTimeConfig.ERROR_DEAD_OBJECT == mCTConfig.forceNetworklessMasterMode())
+                scheduleTimeConfigReconnect();
+        }
+    }
+
+    private void reevaluateServiceState() {
+        String bindIface = null;
+        byte bestScore = -1;
+        try {
+            // Check to see if this interface is suitable to use for time synchronization.
+            //
+            // TODO : This selection algorithm needs to be enhanced for use with mobile devices.  In
+            // particular, the choice of whether to a wireless interface or not should not be an all
+            // or nothing thing controlled by properties.  It would probably be better if the
+            // platform had some concept of public wireless networks vs. home or friendly wireless
+            // networks (something a user would configure in settings or when a new interface is
+            // added).  Then this algorithm could pick only wireless interfaces which were flagged
+            // as friendly, and be dormant when on public wireless networks.
+            //
+            // Another issue which needs to be dealt with is the use of driver supplied interface
+            // name to determine the network type.  The fact that the wireless interface on a device
+            // is named "wlan0" is just a matter of convention; its not a 100% rule.  For example,
+            // there are devices out there where the wireless is name "tiwlan0", not "wlan0".  The
+            // internal network management interfaces in Android have all of the information needed
+            // to make a proper classification, there is just no way (currently) to fetch an
+            // interface's type (available from the ConnectionManager) as well as its address
+            // (available from either the java.net interfaces or from the NetworkManagment service).
+            // Both can enumerate interfaces, but that is no way to correlate their results (no
+            // common shared key; although using the interface name in the connection manager would
+            // be a good start).  Until this gets resolved, we resort to substring searching for
+            // tags like wlan and eth.
+            //
+            String ifaceList[] = mNetMgr.listInterfaces();
+            if (null != ifaceList) {
+                for (String iface : ifaceList) {
+
+                    byte thisScore = -1;
+                    for (InterfaceScoreRule r : IFACE_SCORE_RULES) {
+                        if (iface.contains(r.mPrefix)) {
+                            thisScore = r.mScore;
+                            break;
+                        }
+                    }
+
+                    if (thisScore <= bestScore)
+                        continue;
+
+                    InterfaceConfiguration config = mNetMgr.getInterfaceConfig(iface);
+                    if (null == config)
+                        continue;
+
+                    if (config.isActive()) {
+                        bindIface = iface;
+                        bestScore = thisScore;
+                    }
+                }
+            }
+        }
+        catch (RemoteException e) {
+            // Bad news; we should not be getting remote exceptions from the connectivity manager
+            // since it is running in SystemServer along side of us.  It probably does not matter
+            // what we do here, but go ahead and unbind the common time service in this case, just
+            // so we have some defined behavior.
+            bindIface = null;
+        }
+
+        boolean doRebind = true;
+        synchronized (mLock) {
+            if ((null != bindIface) && (null == mCurIface)) {
+                Log.e(TAG, String.format("Binding common time service to %s.", bindIface));
+                mCurIface = bindIface;
+            } else
+            if ((null == bindIface) && (null != mCurIface)) {
+                Log.e(TAG, "Unbinding common time service.");
+                mCurIface = null;
+            } else
+            if ((null != bindIface) && (null != mCurIface) && !bindIface.equals(mCurIface)) {
+                Log.e(TAG, String.format("Switching common time service binding from %s to %s.",
+                                         mCurIface, bindIface));
+                mCurIface = bindIface;
+            } else {
+                doRebind = false;
+            }
+        }
+
+        if (doRebind && (null != mCTConfig)) {
+            byte newPrio = (bestScore > 0)
+                         ? (byte)(bestScore * BASE_SERVER_PRIO)
+                         : BASE_SERVER_PRIO;
+            if (newPrio != mEffectivePrio) {
+                mEffectivePrio = newPrio;
+                mCTConfig.setMasterElectionPriority(mEffectivePrio);
+            }
+
+            int res = mCTConfig.setNetworkBinding(mCurIface);
+            if (res != CommonTimeConfig.SUCCESS)
+                scheduleTimeConfigReconnect();
+
+            else if (NO_INTERFACE_TIMEOUT >= 0) {
+                mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
+                if (null == mCurIface)
+                    mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index a372fb8..eab60a7 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1394,9 +1394,7 @@
     private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
-            // only someone like NPMS should only be calling us
-            mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-
+            // caller is NPMS, since we only register with them
             if (LOGD_RULES) {
                 log("onUidRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
             }
@@ -1415,9 +1413,7 @@
 
         @Override
         public void onMeteredIfacesChanged(String[] meteredIfaces) {
-            // only someone like NPMS should only be calling us
-            mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-
+            // caller is NPMS, since we only register with them
             if (LOGD_RULES) {
                 log("onMeteredIfacesChanged(ifaces=" + Arrays.toString(meteredIfaces) + ")");
             }
@@ -1429,6 +1425,27 @@
                 }
             }
         }
+
+        @Override
+        public void onRestrictBackgroundChanged(boolean restrictBackground) {
+            // caller is NPMS, since we only register with them
+            if (LOGD_RULES) {
+                log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
+            }
+
+            // kick off connectivity change broadcast for active network, since
+            // global background policy change is radical.
+            final int networkType = mActiveDefaultNetwork;
+            if (isNetworkTypeValid(networkType)) {
+                final NetworkStateTracker tracker = mNetTrackers[networkType];
+                if (tracker != null) {
+                    final NetworkInfo info = tracker.getNetworkInfo();
+                    if (info != null && info.isConnected()) {
+                        sendConnectedBroadcast(info);
+                    }
+                }
+            }
+        }
     };
 
     /**
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index aba1bc6..86669f8 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -101,6 +101,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -154,6 +155,7 @@
     final IWindowManager mIWindowManager;
     final HandlerCaller mCaller;
     private final InputMethodFileManager mFileManager;
+    private final InputMethodAndSubtypeListManager mImListManager;
 
     final InputBindResult mNoBinding = new InputBindResult(null, null, -1);
 
@@ -555,6 +557,7 @@
         synchronized (mMethodMap) {
             mFileManager = new InputMethodFileManager(mMethodMap);
         }
+        mImListManager = new InputMethodAndSubtypeListManager(context, this);
 
         (new MyPackageMonitor()).register(mContext, true);
 
@@ -1693,6 +1696,19 @@
     }
 
     @Override
+    public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
+        synchronized (mMethodMap) {
+            final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod(
+                    onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
+            if (nextSubtype == null) {
+                return false;
+            }
+            setInputMethodWithSubtypeId(token, nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
+            return true;
+        }
+    }
+
+    @Override
     public InputMethodSubtype getLastInputMethodSubtype() {
         synchronized (mMethodMap) {
             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
@@ -2109,62 +2125,9 @@
 
             hideInputMethodMenuLocked();
 
-            final TreeMap<InputMethodInfo, List<InputMethodSubtype>> sortedImmis =
-                    new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
-                            new Comparator<InputMethodInfo>() {
-                                @Override
-                                public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
-                                    if (imi2 == null) return 0;
-                                    if (imi1 == null) return 1;
-                                    if (pm == null) {
-                                        return imi1.getId().compareTo(imi2.getId());
-                                    }
-                                    CharSequence imiId1 = imi1.loadLabel(pm) + "/" + imi1.getId();
-                                    CharSequence imiId2 = imi2.loadLabel(pm) + "/" + imi2.getId();
-                                    return imiId1.toString().compareTo(imiId2.toString());
-                                }
-                            });
-
-            sortedImmis.putAll(immis);
-
-            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>();
-
-            for (InputMethodInfo imi : sortedImmis.keySet()) {
-                if (imi == null) continue;
-                List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
-                HashSet<String> enabledSubtypeSet = new HashSet<String>();
-                for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
-                    enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
-                }
-                ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi);
-                final CharSequence imeLabel = imi.loadLabel(pm);
-                if (showSubtypes && enabledSubtypeSet.size() > 0) {
-                    final int subtypeCount = imi.getSubtypeCount();
-                    if (DEBUG) {
-                        Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
-                    }
-                    for (int j = 0; j < subtypeCount; ++j) {
-                        final InputMethodSubtype subtype = imi.getSubtypeAt(j);
-                        final String subtypeHashCode = String.valueOf(subtype.hashCode());
-                        // We show all enabled IMEs and subtypes when an IME is shown.
-                        if (enabledSubtypeSet.contains(subtypeHashCode)
-                                && ((mInputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
-                            final CharSequence subtypeLabel =
-                                    subtype.overridesImplicitlyEnabledSubtype() ? null
-                                            : subtype.getDisplayName(context, imi.getPackageName(),
-                                                    imi.getServiceInfo().applicationInfo);
-                            imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j));
-
-                            // Removing this subtype from enabledSubtypeSet because we no longer
-                            // need to add an entry of this subtype to imList to avoid duplicated
-                            // entries.
-                            enabledSubtypeSet.remove(subtypeHashCode);
-                        }
-                    }
-                } else {
-                    imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID));
-                }
-            }
+            final List<ImeSubtypeListItem> imList =
+                    mImListManager.getSortedInputMethodAndSubtypeList(
+                            showSubtypes, mInputShown, isScreenLocked);
 
             if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
                 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtype();
@@ -2786,6 +2749,117 @@
         }
     }
 
+    private static class InputMethodAndSubtypeListManager {
+        private final Context mContext;
+        private final PackageManager mPm;
+        private final InputMethodManagerService mImms;
+        public InputMethodAndSubtypeListManager(Context context, InputMethodManagerService imms) {
+            mContext = context;
+            mPm = context.getPackageManager();
+            mImms = imms;
+        }
+
+        private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
+                new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
+                        new Comparator<InputMethodInfo>() {
+                            @Override
+                            public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
+                                if (imi2 == null) return 0;
+                                if (imi1 == null) return 1;
+                                if (mPm == null) {
+                                    return imi1.getId().compareTo(imi2.getId());
+                                }
+                                CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId();
+                                CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId();
+                                return imiId1.toString().compareTo(imiId2.toString());
+                            }
+                        });
+
+        public ImeSubtypeListItem getNextInputMethod(
+                boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
+            if (imi == null) {
+                return null;
+            }
+            final List<ImeSubtypeListItem> imList = getSortedInputMethodAndSubtypeList();
+            if (imList.size() <= 1) {
+                return null;
+            }
+            final int N = imList.size();
+            final int currentSubtypeId = subtype != null
+                    ? mImms.getSubtypeIdFromHashCode(imi, subtype.hashCode())
+                    : NOT_A_SUBTYPE_ID;
+            for (int i = 0; i < N; ++i) {
+                final ImeSubtypeListItem isli = imList.get(i);
+                if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) {
+                    if (!onlyCurrentIme) {
+                        return imList.get((i + 1) % N);
+                    }
+                    for (int j = 0; j < N - 1; ++j) {
+                        final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
+                        if (candidate.mImi.equals(imi)) {
+                            return candidate;
+                        }
+                    }
+                    return null;
+                }
+            }
+            return null;
+        }
+
+        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
+            return getSortedInputMethodAndSubtypeList(true, false, false);
+        }
+
+        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes,
+                boolean inputShown, boolean isScreenLocked) {
+            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>();
+            final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
+                    mImms.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked();
+            if (immis == null || immis.size() == 0) {
+                return Collections.emptyList();
+            }
+            mSortedImmis.clear();
+            mSortedImmis.putAll(immis);
+            for (InputMethodInfo imi : mSortedImmis.keySet()) {
+                if (imi == null) continue;
+                List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
+                HashSet<String> enabledSubtypeSet = new HashSet<String>();
+                for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
+                    enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
+                }
+                ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi);
+                final CharSequence imeLabel = imi.loadLabel(mPm);
+                if (showSubtypes && enabledSubtypeSet.size() > 0) {
+                    final int subtypeCount = imi.getSubtypeCount();
+                    if (DEBUG) {
+                        Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
+                    }
+                    for (int j = 0; j < subtypeCount; ++j) {
+                        final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+                        final String subtypeHashCode = String.valueOf(subtype.hashCode());
+                        // We show all enabled IMEs and subtypes when an IME is shown.
+                        if (enabledSubtypeSet.contains(subtypeHashCode)
+                                && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
+                            final CharSequence subtypeLabel =
+                                    subtype.overridesImplicitlyEnabledSubtype() ? null
+                                            : subtype.getDisplayName(mContext, imi.getPackageName(),
+                                                    imi.getServiceInfo().applicationInfo);
+                            imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j));
+
+                            // Removing this subtype from enabledSubtypeSet because we no longer
+                            // need to add an entry of this subtype to imList to avoid duplicated
+                            // entries.
+                            enabledSubtypeSet.remove(subtypeHashCode);
+                        }
+                    }
+                } else {
+                    imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID));
+                }
+            }
+            return imList;
+        }
+    }
+
     /**
      * Utility class for putting and getting settings for InputMethod
      * TODO: Move all putters and getters of settings to this class.
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index a7d1992..1ff914f 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -55,7 +55,7 @@
 
     private static final int EVENT_AUTO_TIME_CHANGED = 1;
     private static final int EVENT_POLL_NETWORK_TIME = 2;
-    private static final int EVENT_WIFI_CONNECTED = 3;
+    private static final int EVENT_NETWORK_CONNECTED = 3;
 
     /** Normal polling frequency */
     private static final long POLLING_INTERVAL_MS = 24L * 60 * 60 * 1000; // 24 hrs
@@ -240,8 +240,9 @@
                 if (netInfo != null) {
                     // Verify that it's a WIFI connection
                     if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
-                            netInfo.getType() == ConnectivityManager.TYPE_WIFI ) {
-                        mHandler.obtainMessage(EVENT_WIFI_CONNECTED).sendToTarget();
+                            (netInfo.getType() == ConnectivityManager.TYPE_WIFI ||
+                                netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) ) {
+                        mHandler.obtainMessage(EVENT_NETWORK_CONNECTED).sendToTarget();
                     }
                 }
             }
@@ -260,7 +261,7 @@
             switch (msg.what) {
                 case EVENT_AUTO_TIME_CHANGED:
                 case EVENT_POLL_NETWORK_TIME:
-                case EVENT_WIFI_CONNECTED:
+                case EVENT_NETWORK_CONNECTED:
                     onPollNetworkTime(msg.what);
                     break;
             }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 34a8a02..624ee98 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -146,19 +146,18 @@
         final int id;
         final int uid;
         final int initialPid;
-        final int priority;
         final Notification notification;
+        final int score;
         IBinder statusBarKey;
 
-        NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int priority,
-                Notification notification)
+        NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int score, Notification notification)
         {
             this.pkg = pkg;
             this.tag = tag;
             this.id = id;
             this.uid = uid;
             this.initialPid = initialPid;
-            this.priority = priority;
+            this.score = score;
             this.notification = notification;
         }
 
@@ -166,6 +165,8 @@
             pw.println(prefix + this);
             pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
                     + " / " + idDebugString(baseContext, this.pkg, notification.icon));
+            pw.println(prefix + "  pri=" + notification.priority);
+            pw.println(prefix + "  score=" + this.score);
             pw.println(prefix + "  contentIntent=" + notification.contentIntent);
             pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
             pw.println(prefix + "  tickerText=" + notification.tickerText);
@@ -187,7 +188,7 @@
                 + " pkg=" + pkg
                 + " id=" + Integer.toHexString(id)
                 + " tag=" + tag 
-                + " pri=" + priority 
+                + " score=" + score
                 + "}";
         }
     }
@@ -660,28 +661,17 @@
         enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
                 tag, id, notification, idOut);
     }
-
-    public void enqueueNotificationWithTagPriority(String pkg, String tag, int id, int priority,
-            Notification notification, int[] idOut)
-    {
-        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
-                tag, id, priority, notification, idOut);
+    
+    private final static int clamp(int x, int low, int high) {
+        return (x < low) ? low : ((x > high) ? high : x);
     }
 
+    
     // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
     // uid/pid of another application)
     public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
             String tag, int id, Notification notification, int[] idOut)
     {
-        enqueueNotificationInternal(pkg, callingUid, callingPid, tag, id, 
-                ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0)
-                    ? StatusBarNotification.PRIORITY_ONGOING
-                    : StatusBarNotification.PRIORITY_NORMAL,
-                notification, idOut);
-    }
-    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
-            String tag, int id, int priority, Notification notification, int[] idOut)
-    {
         checkIncomingCall(pkg);
 
         // Limit the number of notifications that any given package except the android
@@ -723,10 +713,35 @@
             }
         }
 
+        // === Scoring ===
+        
+        // 0. Sanitize inputs
+        notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
+        // Migrate notification flags to scores
+        if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
+            if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
+        } else if (0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
+            if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
+        }
+        
+        // 1. initial score: buckets of 10, around the app 
+        int score = notification.priority * 10; //[-20..20]
+
+        // 2. Consult oracles (external heuristics)
+        // TODO(dsandler): oracles
+
+        // 3. Apply local heuristics & overrides
+
+        // blocked apps
+        // TODO(dsandler): add block db
+        if (pkg.startsWith("com.test.spammer.")) {
+            score = -1000;
+        }
+
         synchronized (mNotificationList) {
             NotificationRecord r = new NotificationRecord(pkg, tag, id, 
                     callingUid, callingPid, 
-                    priority,
+                    score,
                     notification);
             NotificationRecord old = null;
 
@@ -752,9 +767,7 @@
 
             if (notification.icon != 0) {
                 StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
-                        r.uid, r.initialPid, notification);
-                n.priority = r.priority;
-
+                        r.uid, r.initialPid, score, notification);
                 if (old != null && old.statusBarKey != null) {
                     r.statusBarKey = old.statusBarKey;
                     long identity = Binder.clearCallingIdentity();
@@ -768,7 +781,9 @@
                     long identity = Binder.clearCallingIdentity();
                     try {
                         r.statusBarKey = mStatusBar.addNotification(n);
-                        mAttentionLight.pulse();
+                        if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
+                            mAttentionLight.pulse();
+                        }
                     }
                     finally {
                         Binder.restoreCallingIdentity(identity);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index bb0ac3e..7b4372f 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -50,6 +50,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.util.EventLog;
@@ -176,6 +177,7 @@
 
     private boolean mDoneBooting = false;
     private boolean mBootCompleted = false;
+    private boolean mHeadless = false;
     private int mStayOnConditions = 0;
     private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
     private final int[] mBroadcastWhy = new int[3];
@@ -530,6 +532,7 @@
         mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS);
         mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD);
         mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
+        mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
 
         nativeInit();
         synchronized (mLocks) {
@@ -580,6 +583,7 @@
         }
         
         nativeInit();
+        Power.powerInitNative();
         synchronized (mLocks) {
             updateNativePowerStateLocked();
             // We make sure to start out with the screen on due to user activity.
@@ -1894,9 +1898,11 @@
     }
 
     private void updateNativePowerStateLocked() {
-        nativeSetPowerState(
-                (mPowerState & SCREEN_ON_BIT) != 0,
-                (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
+        if (!mHeadless) {
+            nativeSetPowerState(
+                    (mPowerState & SCREEN_ON_BIT) != 0,
+                    (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
+        }
     }
 
     private int screenOffFinishedAnimatingLocked(int reason) {
@@ -2240,11 +2246,13 @@
                         mScreenOffHandler.postAtTime(this, now+(1000/60));
                     }
                 } else {
-                    // It's pretty scary to hold mLocks for this long, and we should
-                    // redesign this, but it works for now.
-                    nativeStartSurfaceFlingerAnimation(
-                            mScreenOffReason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR
-                            ? 0 : mAnimationSetting);
+                    if (!mHeadless) {
+                        // It's pretty scary to hold mLocks for this long, and we should
+                        // redesign this, but it works for now.
+                        nativeStartSurfaceFlingerAnimation(
+                                mScreenOffReason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR
+                                ? 0 : mAnimationSetting);
+                    }
                     mScreenBrightness.jumpToTargetLocked();
                 }
             }
diff --git a/services/java/com/android/server/SerialService.java b/services/java/com/android/server/SerialService.java
new file mode 100644
index 0000000..5d2b2a0
--- /dev/null
+++ b/services/java/com/android/server/SerialService.java
@@ -0,0 +1,58 @@
+/*
+ * 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 an
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.hardware.ISerialManager;
+import android.os.ParcelFileDescriptor;
+
+import java.io.File;
+import java.util.ArrayList;
+
+public class SerialService extends ISerialManager.Stub {
+
+    private final Context mContext;
+    private final String[] mSerialPorts;
+
+    public SerialService(Context context) {
+        mContext = context;
+        mSerialPorts = context.getResources().getStringArray(
+                com.android.internal.R.array.config_serialPorts);
+    }
+
+    public String[] getSerialPorts() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);
+
+        ArrayList<String> ports = new ArrayList<String>();
+        for (int i = 0; i < mSerialPorts.length; i++) {
+            String path = mSerialPorts[i];
+            if (new File(path).exists()) {
+                ports.add(path);
+            }
+        }
+        String[] result = new String[ports.size()];
+        ports.toArray(result);
+        return result;
+    }
+
+    public ParcelFileDescriptor openSerialPort(String path) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);
+        return native_open(path);
+    }
+
+    private native ParcelFileDescriptor native_open(String path);
+}
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index da97089..a7a583c 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -44,12 +44,16 @@
     private static final String WALLPAPER_IMAGE_FILENAME = "wallpaper";
     private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml";
 
-    private static final String WALLPAPER_IMAGE_DIR = "/data/data/com.android.settings/files";
-    private static final String WALLPAPER_IMAGE = WALLPAPER_IMAGE_DIR + "/" + WALLPAPER_IMAGE_FILENAME;
+    // TODO: Will need to change if backing up non-primary user's wallpaper
+    private static final String WALLPAPER_IMAGE_DIR = "/data/system/users/0";
+    private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE;
 
-    private static final String WALLPAPER_INFO_DIR = "/data/system";
-    private static final String WALLPAPER_INFO = WALLPAPER_INFO_DIR + "/" +  WALLPAPER_INFO_FILENAME;
-
+    // TODO: Will need to change if backing up non-primary user's wallpaper
+    private static final String WALLPAPER_INFO_DIR = "/data/system/users/0";
+    private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO;
+    // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
+    private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
+    private static final String WALLPAPER_INFO_KEY = WallpaperBackupHelper.WALLPAPER_INFO_KEY;
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
@@ -58,13 +62,15 @@
         WallpaperManagerService wallpaper = (WallpaperManagerService)ServiceManager.getService(
                 Context.WALLPAPER_SERVICE);
         String[] files = new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO };
-        if (wallpaper != null && wallpaper.mName != null && wallpaper.mName.length() > 0) {
+        String[] keys = new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY };
+        if (wallpaper != null && wallpaper.getName() != null && wallpaper.getName().length() > 0) {
             // When the wallpaper has a name, back up the info by itself.
             // TODO: Don't rely on the innards of the service object like this!
             // TODO: Send a delete for any stored wallpaper image in this case?
             files = new String[] { WALLPAPER_INFO };
+            keys = new String[] { WALLPAPER_INFO_KEY };
         }
-        addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files));
+        addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys));
         super.onBackup(oldState, data, newState);
     }
 
@@ -90,9 +96,11 @@
             throws IOException {
         // On restore, we also support a previous data schema "system_files"
         addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this,
-                new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }));
+                new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO },
+                new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} ));
         addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this,
-                new String[] { WALLPAPER_IMAGE }));
+                new String[] { WALLPAPER_IMAGE },
+                new String[] { WALLPAPER_IMAGE_KEY} ));
 
         try {
             super.onRestore(data, appVersionCode, newState);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 762acbb..c9b5997 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -108,6 +108,7 @@
         String factoryTestStr = SystemProperties.get("ro.factorytest");
         int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF
                 : Integer.parseInt(factoryTestStr);
+        final boolean headless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
 
         LightsService lights = null;
         PowerManagerService power = null;
@@ -126,10 +127,12 @@
         BluetoothA2dpService bluetoothA2dp = null;
         DockObserver dock = null;
         UsbService usb = null;
+        SerialService serial = null;
         UiModeManagerService uiMode = null;
         RecognitionManagerService recognition = null;
         ThrottleService throttle = null;
         NetworkTimeUpdateService networkTimeUpdater = null;
+        CommonTimeManagementService commonTimeMgmtService = null;
 
         // Critical services...
         try {
@@ -231,10 +234,13 @@
                 bluetooth = new BluetoothService(context);
                 ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth);
                 bluetooth.initAfterRegistration();
-                bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
-                ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
-                                          bluetoothA2dp);
-                bluetooth.initAfterA2dpRegistration();
+
+                if (!"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
+                    bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
+                    ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
+                                              bluetoothA2dp);
+                    bluetooth.initAfterA2dpRegistration();
+                }
 
                 int airplaneModeOn = Settings.System.getInt(mContentResolver,
                         Settings.System.AIRPLANE_MODE_ON, 0);
@@ -396,15 +402,17 @@
                 reportWtf("starting ThrottleService", e);
             }
 
-            try {
-                /*
-                 * NotificationManagerService is dependant on MountService,
-                 * (for media / usb notifications) so we must start MountService first.
-                 */
-                Slog.i(TAG, "Mount Service");
-                ServiceManager.addService("mount", new MountService(context));
-            } catch (Throwable e) {
-                reportWtf("starting Mount Service", e);
+            if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+                try {
+                    /*
+                     * NotificationManagerService is dependant on MountService,
+                     * (for media / usb notifications) so we must start MountService first.
+                     */
+                    Slog.i(TAG, "Mount Service");
+                    ServiceManager.addService("mount", new MountService(context));
+                } catch (Throwable e) {
+                    reportWtf("starting Mount Service", e);
+                }
             }
 
             try {
@@ -456,19 +464,26 @@
                 reportWtf("starting DropBoxManagerService", e);
             }
 
-            try {
-                Slog.i(TAG, "Wallpaper Service");
-                wallpaper = new WallpaperManagerService(context);
-                ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
-            } catch (Throwable e) {
-                reportWtf("starting Wallpaper Service", e);
+            if (context.getResources().getBoolean(
+                        com.android.internal.R.bool.config_enableWallpaperService)) {
+                try {
+                    Slog.i(TAG, "Wallpaper Service");
+                    if (!headless) {
+                        wallpaper = new WallpaperManagerService(context);
+                        ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
+                    }
+                } catch (Throwable e) {
+                    reportWtf("starting Wallpaper Service", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "Audio Service");
-                ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
-            } catch (Throwable e) {
-                reportWtf("starting Audio Service", e);
+            if (!"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
+                try {
+                    Slog.i(TAG, "Audio Service");
+                    ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
+                } catch (Throwable e) {
+                    reportWtf("starting Audio Service", e);
+                }
             }
 
             try {
@@ -497,6 +512,15 @@
             }
 
             try {
+                Slog.i(TAG, "Serial Service");
+                // Serial port support
+                serial = new SerialService(context);
+                ServiceManager.addService(Context.SERIAL_SERVICE, serial);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting SerialService", e);
+            }
+
+            try {
                 Slog.i(TAG, "UI Mode Manager Service");
                 // Listen for UI mode changes
                 uiMode = new UiModeManagerService(context);
@@ -552,6 +576,14 @@
             } catch (Throwable e) {
                 reportWtf("starting NetworkTimeUpdate service", e);
             }
+
+            try {
+                Slog.i(TAG, "CommonTimeManagementService");
+                commonTimeMgmtService = new CommonTimeManagementService(context);
+                ServiceManager.addService("commontime_management", commonTimeMgmtService);
+            } catch (Throwable e) {
+                reportWtf("starting CommonTimeManagementService service", e);
+            }
         }
 
         // Before things start rolling, be sure we have decided whether
@@ -630,6 +662,7 @@
         final LocationManagerService locationF = location;
         final CountryDetectorService countryDetectorF = countryDetector;
         final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
+        final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService;
         final TextServicesManagerService textServiceManagerServiceF = tsms;
         final StatusBarManagerService statusBarF = statusBar;
 
@@ -642,7 +675,7 @@
             public void run() {
                 Slog.i(TAG, "Making services ready");
 
-                startSystemUi(contextF);
+                if (!headless) startSystemUi(contextF);
                 try {
                     if (batteryF != null) batteryF.systemReady();
                 } catch (Throwable e) {
@@ -729,6 +762,11 @@
                     reportWtf("making Network Time Service ready", e);
                 }
                 try {
+                    if (commonTimeMgmtServiceF != null) commonTimeMgmtServiceF.systemReady();
+                } catch (Throwable e) {
+                    reportWtf("making Common time management service ready", e);
+                }
+                try {
                     if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady();
                 } catch (Throwable e) {
                     reportWtf("making Text Services Manager Service ready", e);
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 4925a4e..8ee12bc 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -24,6 +24,7 @@
 import android.app.PendingIntent;
 import android.app.WallpaperInfo;
 import android.app.backup.BackupManager;
+import android.app.backup.WallpaperBackupHelper;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -43,11 +44,13 @@
 import android.os.RemoteCallbackList;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
 import android.service.wallpaper.WallpaperService;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.Xml;
 import android.view.Display;
 import android.view.IWindowManager;
@@ -70,6 +73,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
+import com.android.server.am.ActivityManagerService;
 
 class WallpaperManagerService extends IWallpaperManager.Stub {
     static final String TAG = "WallpaperService";
@@ -83,17 +87,9 @@
      */
     static final long MIN_WALLPAPER_CRASH_TIME = 10000;
     
-    static final File WALLPAPER_DIR = new File(
-            "/data/data/com.android.settings/files");
+    static final File WALLPAPER_BASE_DIR = new File("/data/system/users");
     static final String WALLPAPER = "wallpaper";
-    static final File WALLPAPER_FILE = new File(WALLPAPER_DIR, WALLPAPER);
-
-    /**
-     * List of callbacks registered they should each be notified
-     * when the wallpaper is changed.
-     */
-    private final RemoteCallbackList<IWallpaperManagerCallback> mCallbacks
-            = new RemoteCallbackList<IWallpaperManagerCallback>();
+    static final String WALLPAPER_INFO = "wallpaper_info.xml";
 
     /**
      * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
@@ -101,97 +97,135 @@
      * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
      * everytime the wallpaper is changed.
      */
-    private final FileObserver mWallpaperObserver = new FileObserver(
-            WALLPAPER_DIR.getAbsolutePath(), CLOSE_WRITE | DELETE | DELETE_SELF) {
-                @Override
-                public void onEvent(int event, String path) {
-                    if (path == null) {
-                        return;
-                    }
-                    synchronized (mLock) {
-                        // changing the wallpaper means we'll need to back up the new one
-                        long origId = Binder.clearCallingIdentity();
-                        BackupManager bm = new BackupManager(mContext);
-                        bm.dataChanged();
-                        Binder.restoreCallingIdentity(origId);
+    private class WallpaperObserver extends FileObserver {
 
-                        File changedFile = new File(WALLPAPER_DIR, path);
-                        if (WALLPAPER_FILE.equals(changedFile)) {
-                            notifyCallbacksLocked();
-                            if (mWallpaperComponent == null || event != CLOSE_WRITE
-                                    || mImageWallpaperPending) {
-                                if (event == CLOSE_WRITE) {
-                                    mImageWallpaperPending = false;
-                                }
-                                bindWallpaperComponentLocked(mImageWallpaperComponent,
-                                        true, false);
-                                saveSettingsLocked();
-                            }
+        final WallpaperData mWallpaper;
+        final File mWallpaperDir;
+        final File mWallpaperFile;
+
+        public WallpaperObserver(WallpaperData wallpaper) {
+            super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
+                    CLOSE_WRITE | DELETE | DELETE_SELF);
+            mWallpaperDir = getWallpaperDir(wallpaper.userId);
+            mWallpaper = wallpaper;
+            mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
+        }
+
+        @Override
+        public void onEvent(int event, String path) {
+            if (path == null) {
+                return;
+            }
+            synchronized (mLock) {
+                // changing the wallpaper means we'll need to back up the new one
+                long origId = Binder.clearCallingIdentity();
+                BackupManager bm = new BackupManager(mContext);
+                bm.dataChanged();
+                Binder.restoreCallingIdentity(origId);
+
+                File changedFile = new File(mWallpaperDir, path);
+                if (mWallpaperFile.equals(changedFile)) {
+                    notifyCallbacksLocked(mWallpaper);
+                    if (mWallpaper.wallpaperComponent == null || event != CLOSE_WRITE
+                            || mWallpaper.imageWallpaperPending) {
+                        if (event == CLOSE_WRITE) {
+                            mWallpaper.imageWallpaperPending = false;
                         }
+                        bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true,
+                                false, mWallpaper);
+                        saveSettingsLocked(mWallpaper);
                     }
                 }
-            };
-    
+            }
+        }
+    }
+
     final Context mContext;
     final IWindowManager mIWindowManager;
     final MyPackageMonitor mMonitor;
+    WallpaperData mLastWallpaper;
 
-    int mWidth = -1;
-    int mHeight = -1;
+    SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
 
-    /**
-     * Client is currently writing a new image wallpaper.
-     */
-    boolean mImageWallpaperPending;
+    int mCurrentUserId;
 
-    /**
-     * Resource name if using a picture from the wallpaper gallery
-     */
-    String mName = "";
-    
-    /**
-     * The component name of the currently set live wallpaper.
-     */
-    ComponentName mWallpaperComponent;
-    
-    /**
-     * The component name of the wallpaper that should be set next.
-     */
-    ComponentName mNextWallpaperComponent;
-    
-    /**
-     * Name of the component used to display bitmap wallpapers from either the gallery or
-     * built-in wallpapers.
-     */
-    ComponentName mImageWallpaperComponent = new ComponentName("com.android.systemui",
-            "com.android.systemui.ImageWallpaper");
-    
-    WallpaperConnection mWallpaperConnection;
-    long mLastDiedTime;
-    boolean mWallpaperUpdating;
-    
+    static class WallpaperData {
+
+        int userId;
+
+        File wallpaperFile;
+
+        /**
+         * Client is currently writing a new image wallpaper.
+         */
+        boolean imageWallpaperPending;
+
+        /**
+         * Resource name if using a picture from the wallpaper gallery
+         */
+        String name = "";
+
+        /**
+         * The component name of the currently set live wallpaper.
+         */
+        ComponentName wallpaperComponent;
+
+        /**
+         * The component name of the wallpaper that should be set next.
+         */
+        ComponentName nextWallpaperComponent;
+
+        /**
+         * Name of the component used to display bitmap wallpapers from either the gallery or
+         * built-in wallpapers.
+         */
+        ComponentName imageWallpaperComponent = new ComponentName("com.android.systemui",
+                "com.android.systemui.ImageWallpaper");
+
+        WallpaperConnection connection;
+        long lastDiedTime;
+        boolean wallpaperUpdating;
+        WallpaperObserver wallpaperObserver;
+
+        /**
+         * List of callbacks registered they should each be notified when the wallpaper is changed.
+         */
+        private RemoteCallbackList<IWallpaperManagerCallback> callbacks
+                = new RemoteCallbackList<IWallpaperManagerCallback>();
+
+        int width = -1;
+        int height = -1;
+
+        WallpaperData(int userId) {
+            this.userId = userId;
+            wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
+        }
+    }
+
     class WallpaperConnection extends IWallpaperConnection.Stub
             implements ServiceConnection {
         final WallpaperInfo mInfo;
         final Binder mToken = new Binder();
         IWallpaperService mService;
         IWallpaperEngine mEngine;
+        WallpaperData mWallpaper;
 
-        public WallpaperConnection(WallpaperInfo info) {
+        public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
             mInfo = info;
+            mWallpaper = wallpaper;
         }
         
         public void onServiceConnected(ComponentName name, IBinder service) {
             synchronized (mLock) {
-                if (mWallpaperConnection == this) {
-                    mLastDiedTime = SystemClock.uptimeMillis();
+                if (mWallpaper.connection == this) {
+                    mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
                     mService = IWallpaperService.Stub.asInterface(service);
-                    attachServiceLocked(this);
+                    attachServiceLocked(this, mWallpaper);
                     // XXX should probably do saveSettingsLocked() later
                     // when we have an engine, but I'm not sure about
                     // locking there and anyway we always need to be able to
                     // recover if there is something wrong.
-                    saveSettingsLocked();
+                    saveSettingsLocked(mWallpaper);
                 }
             }
         }
@@ -200,43 +234,50 @@
             synchronized (mLock) {
                 mService = null;
                 mEngine = null;
-                if (mWallpaperConnection == this) {
-                    Slog.w(TAG, "Wallpaper service gone: " + mWallpaperComponent);
-                    if (!mWallpaperUpdating && (mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
-                                > SystemClock.uptimeMillis()) {
+                if (mWallpaper.connection == this) {
+                    Slog.w(TAG, "Wallpaper service gone: " + mWallpaper.wallpaperComponent);
+                    if (!mWallpaper.wallpaperUpdating
+                            && (mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME)
+                                > SystemClock.uptimeMillis()
+                            && mWallpaper.userId == mCurrentUserId) {
                         Slog.w(TAG, "Reverting to built-in wallpaper!");
-                        clearWallpaperLocked(true);
+                        clearWallpaperLocked(true, mWallpaper.userId);
                     }
                 }
             }
         }
-        
+
         public void attachEngine(IWallpaperEngine engine) {
             mEngine = engine;
         }
-        
+
         public ParcelFileDescriptor setWallpaper(String name) {
             synchronized (mLock) {
-                if (mWallpaperConnection == this) {
-                    return updateWallpaperBitmapLocked(name);
+                if (mWallpaper.connection == this) {
+                    return updateWallpaperBitmapLocked(name, mWallpaper);
                 }
                 return null;
             }
         }
     }
-    
+
     class MyPackageMonitor extends PackageMonitor {
         @Override
         public void onPackageUpdateFinished(String packageName, int uid) {
             synchronized (mLock) {
-                if (mWallpaperComponent != null &&
-                        mWallpaperComponent.getPackageName().equals(packageName)) {
-                    mWallpaperUpdating = false;
-                    ComponentName comp = mWallpaperComponent;
-                    clearWallpaperComponentLocked();
-                    if (!bindWallpaperComponentLocked(comp, false, false)) {
-                        Slog.w(TAG, "Wallpaper no longer available; reverting to default");
-                        clearWallpaperLocked(false);
+                for (int i = 0; i < mWallpaperMap.size(); i++) {
+                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                    if (wallpaper.wallpaperComponent != null
+                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+                        wallpaper.wallpaperUpdating = false;
+                        ComponentName comp = wallpaper.wallpaperComponent;
+                        clearWallpaperComponentLocked(wallpaper);
+                        // Do this only for the current user's wallpaper
+                        if (wallpaper.userId == mCurrentUserId
+                                && !bindWallpaperComponentLocked(comp, false, false, wallpaper)) {
+                            Slog.w(TAG, "Wallpaper no longer available; reverting to default");
+                            clearWallpaperLocked(false, wallpaper.userId);
+                        }
                     }
                 }
             }
@@ -245,72 +286,91 @@
         @Override
         public void onPackageModified(String packageName) {
             synchronized (mLock) {
-                if (mWallpaperComponent == null ||
-                        !mWallpaperComponent.getPackageName().equals(packageName)) {
-                    return;
+                for (int i = 0; i < mWallpaperMap.size(); i++) {
+                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                    if (wallpaper.wallpaperComponent == null
+                            || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+                        continue;
+                    }
+                    doPackagesChanged(true, wallpaper);
                 }
             }
-            doPackagesChanged(true);
         }
 
         @Override
         public void onPackageUpdateStarted(String packageName, int uid) {
             synchronized (mLock) {
-                if (mWallpaperComponent != null &&
-                        mWallpaperComponent.getPackageName().equals(packageName)) {
-                    mWallpaperUpdating = true;
+                for (int i = 0; i < mWallpaperMap.size(); i++) {
+                    WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                    if (wallpaper.wallpaperComponent != null
+                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+                        wallpaper.wallpaperUpdating = true;
+                    }
                 }
             }
         }
 
         @Override
         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
-            return doPackagesChanged(doit);
+            boolean changed = false;
+            for (int i = 0; i < mWallpaperMap.size(); i++) {
+                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                boolean res = doPackagesChanged(doit, wallpaper);
+                changed |= res;
+            }
+            return changed;
         }
 
         @Override
         public void onSomePackagesChanged() {
-            doPackagesChanged(true);
+            for (int i = 0; i < mWallpaperMap.size(); i++) {
+                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                doPackagesChanged(true, wallpaper);
+            }
         }
-        
-        boolean doPackagesChanged(boolean doit) {
+
+        boolean doPackagesChanged(boolean doit, WallpaperData wallpaper) {
             boolean changed = false;
             synchronized (mLock) {
-                if (mWallpaperComponent != null) {
-                    int change = isPackageDisappearing(mWallpaperComponent.getPackageName());
+                if (wallpaper.wallpaperComponent != null) {
+                    int change = isPackageDisappearing(wallpaper.wallpaperComponent
+                            .getPackageName());
                     if (change == PACKAGE_PERMANENT_CHANGE
                             || change == PACKAGE_TEMPORARY_CHANGE) {
                         changed = true;
                         if (doit) {
-                            Slog.w(TAG, "Wallpaper uninstalled, removing: " + mWallpaperComponent);
-                            clearWallpaperLocked(false);
+                            Slog.w(TAG, "Wallpaper uninstalled, removing: "
+                                    + wallpaper.wallpaperComponent);
+                            clearWallpaperLocked(false, wallpaper.userId);
                         }
                     }
                 }
-                if (mNextWallpaperComponent != null) {
-                    int change = isPackageDisappearing(mNextWallpaperComponent.getPackageName());
+                if (wallpaper.nextWallpaperComponent != null) {
+                    int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
+                            .getPackageName());
                     if (change == PACKAGE_PERMANENT_CHANGE
                             || change == PACKAGE_TEMPORARY_CHANGE) {
-                        mNextWallpaperComponent = null;
+                        wallpaper.nextWallpaperComponent = null;
                     }
                 }
-                if (mWallpaperComponent != null
-                        && isPackageModified(mWallpaperComponent.getPackageName())) {
+                if (wallpaper.wallpaperComponent != null
+                        && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
                     try {
                         mContext.getPackageManager().getServiceInfo(
-                                mWallpaperComponent, 0);
+                                wallpaper.wallpaperComponent, 0);
                     } catch (NameNotFoundException e) {
-                        Slog.w(TAG, "Wallpaper component gone, removing: " + mWallpaperComponent);
-                        clearWallpaperLocked(false);
+                        Slog.w(TAG, "Wallpaper component gone, removing: "
+                                + wallpaper.wallpaperComponent);
+                        clearWallpaperLocked(false, wallpaper.userId);
                     }
                 }
-                if (mNextWallpaperComponent != null
-                        && isPackageModified(mNextWallpaperComponent.getPackageName())) {
+                if (wallpaper.nextWallpaperComponent != null
+                        && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
                     try {
                         mContext.getPackageManager().getServiceInfo(
-                                mNextWallpaperComponent, 0);
+                                wallpaper.nextWallpaperComponent, 0);
                     } catch (NameNotFoundException e) {
-                        mNextWallpaperComponent = null;
+                        wallpaper.nextWallpaperComponent = null;
                     }
                 }
             }
@@ -325,51 +385,110 @@
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mMonitor = new MyPackageMonitor();
         mMonitor.register(context, true);
-        WALLPAPER_DIR.mkdirs();
-        loadSettingsLocked();
-        mWallpaperObserver.startWatching();
+        WALLPAPER_BASE_DIR.mkdirs();
+        loadSettingsLocked(0);
     }
     
+    private static File getWallpaperDir(int userId) {
+        return new File(WALLPAPER_BASE_DIR + "/" + userId);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         super.finalize();
-        mWallpaperObserver.stopWatching();
+        for (int i = 0; i < mWallpaperMap.size(); i++) {
+            WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+            wallpaper.wallpaperObserver.stopWatching();
+        }
     }
     
     public void systemReady() {
         if (DEBUG) Slog.v(TAG, "systemReady");
+        WallpaperData wallpaper = mWallpaperMap.get(0);
+        switchWallpaper(wallpaper);
+        wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
+        wallpaper.wallpaperObserver.startWatching();
+        ActivityManagerService ams = (ActivityManagerService) ServiceManager
+                .getService(Context.ACTIVITY_SERVICE);
+        ams.addUserListener(new ActivityManagerService.UserListener() {
+
+            @Override
+            public void onUserChanged(int userId) {
+                switchUser(userId);
+            }
+
+            @Override
+            public void onUserAdded(int userId) {
+            }
+
+            @Override
+            public void onUserRemoved(int userId) {
+            }
+
+            @Override
+            public void onUserLoggedOut(int userId) {
+            }
+
+        });
+    }
+
+    String getName() {
+        return mWallpaperMap.get(0).name;
+    }
+
+    void switchUser(int userId) {
+        synchronized (mLock) {
+            mCurrentUserId = userId;
+            WallpaperData wallpaper = mWallpaperMap.get(userId);
+            if (wallpaper == null) {
+                wallpaper = new WallpaperData(userId);
+                mWallpaperMap.put(userId, wallpaper);
+                loadSettingsLocked(userId);
+                wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
+                wallpaper.wallpaperObserver.startWatching();
+            }
+            switchWallpaper(wallpaper);
+        }
+    }
+
+    void switchWallpaper(WallpaperData wallpaper) {
         synchronized (mLock) {
             RuntimeException e = null;
             try {
-                if (bindWallpaperComponentLocked(mNextWallpaperComponent, false, false)) {
+                ComponentName cname = wallpaper.wallpaperComponent != null ?
+                        wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
+                if (bindWallpaperComponentLocked(cname, true, false, wallpaper)) {
                     return;
                 }
             } catch (RuntimeException e1) {
                 e = e1;
             }
             Slog.w(TAG, "Failure starting previous wallpaper", e);
-            clearWallpaperLocked(false);
-        }
-    }
-    
-    public void clearWallpaper() {
-        if (DEBUG) Slog.v(TAG, "clearWallpaper");
-        synchronized (mLock) {
-            clearWallpaperLocked(false);
+            clearWallpaperLocked(false, wallpaper.userId);
         }
     }
 
-    public void clearWallpaperLocked(boolean defaultFailed) {
-        File f = WALLPAPER_FILE;
+    public void clearWallpaper() {
+        if (DEBUG) Slog.v(TAG, "clearWallpaper");
+        synchronized (mLock) {
+            clearWallpaperLocked(false, UserId.getCallingUserId());
+        }
+    }
+
+    void clearWallpaperLocked(boolean defaultFailed, int userId) {
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        File f = new File(getWallpaperDir(userId), WALLPAPER);
         if (f.exists()) {
             f.delete();
         }
         final long ident = Binder.clearCallingIdentity();
         RuntimeException e = null;
         try {
-            mImageWallpaperPending = false;
+            wallpaper.imageWallpaperPending = false;
+            if (userId != mCurrentUserId) return;
             if (bindWallpaperComponentLocked(defaultFailed
-                    ? mImageWallpaperComponent : null, true, false)) {
+                    ? wallpaper.imageWallpaperComponent
+                    : null, true, false, wallpaper)) {
                 return;
             }
         } catch (IllegalArgumentException e1) {
@@ -383,29 +502,35 @@
         // let's not let it crash the system and just live with no
         // wallpaper.
         Slog.e(TAG, "Default wallpaper component not found!", e);
-        clearWallpaperComponentLocked();
+        clearWallpaperComponentLocked(wallpaper);
     }
 
     public void setDimensionHints(int width, int height) throws RemoteException {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
 
+        int userId = UserId.getCallingUserId();
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        if (wallpaper == null) {
+            throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+        }
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
         }
 
         synchronized (mLock) {
-            if (width != mWidth || height != mHeight) {
-                mWidth = width;
-                mHeight = height;
-                saveSettingsLocked();
-                if (mWallpaperConnection != null) {
-                    if (mWallpaperConnection.mEngine != null) {
+            if (width != wallpaper.width || height != wallpaper.height) {
+                wallpaper.width = width;
+                wallpaper.height = height;
+                saveSettingsLocked(wallpaper);
+                if (mCurrentUserId != userId) return; // Don't change the properties now
+                if (wallpaper.connection != null) {
+                    if (wallpaper.connection.mEngine != null) {
                         try {
-                            mWallpaperConnection.mEngine.setDesiredSize(
+                            wallpaper.connection.mEngine.setDesiredSize(
                                     width, height);
                         } catch (RemoteException e) {
                         }
-                        notifyCallbacksLocked();
+                        notifyCallbacksLocked(wallpaper);
                     }
                 }
             }
@@ -414,26 +539,38 @@
 
     public int getWidthHint() throws RemoteException {
         synchronized (mLock) {
-            return mWidth;
+            WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+            return wallpaper.width;
         }
     }
 
     public int getHeightHint() throws RemoteException {
         synchronized (mLock) {
-            return mHeight;
+            WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+            return wallpaper.height;
         }
     }
 
     public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
             Bundle outParams) {
         synchronized (mLock) {
+            // This returns the current user's wallpaper, if called by a system service. Else it
+            // returns the wallpaper for the calling user.
+            int callingUid = Binder.getCallingUid();
+            int wallpaperUserId = 0;
+            if (callingUid == android.os.Process.SYSTEM_UID) {
+                wallpaperUserId = mCurrentUserId;
+            } else {
+                wallpaperUserId = UserId.getUserId(callingUid);
+            }
+            WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId);
             try {
                 if (outParams != null) {
-                    outParams.putInt("width", mWidth);
-                    outParams.putInt("height", mHeight);
+                    outParams.putInt("width", wallpaper.width);
+                    outParams.putInt("height", wallpaper.height);
                 }
-                mCallbacks.register(cb);
-                File f = WALLPAPER_FILE;
+                wallpaper.callbacks.register(cb);
+                File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER);
                 if (!f.exists()) {
                     return null;
                 }
@@ -447,24 +584,30 @@
     }
 
     public WallpaperInfo getWallpaperInfo() {
+        int userId = UserId.getCallingUserId();
         synchronized (mLock) {
-            if (mWallpaperConnection != null) {
-                return mWallpaperConnection.mInfo;
+            WallpaperData wallpaper = mWallpaperMap.get(userId);
+            if (wallpaper.connection != null) {
+                return wallpaper.connection.mInfo;
             }
             return null;
         }
     }
-    
+
     public ParcelFileDescriptor setWallpaper(String name) {
         if (DEBUG) Slog.v(TAG, "setWallpaper");
-        
+        int userId = UserId.getCallingUserId();
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        if (wallpaper == null) {
+            throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+        }
         checkPermission(android.Manifest.permission.SET_WALLPAPER);
         synchronized (mLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name);
+                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper);
                 if (pfd != null) {
-                    mImageWallpaperPending = true;
+                    wallpaper.imageWallpaperPending = true;
                 }
                 return pfd;
             } finally {
@@ -473,19 +616,20 @@
         }
     }
 
-    ParcelFileDescriptor updateWallpaperBitmapLocked(String name) {
+    ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper) {
         if (name == null) name = "";
         try {
-            if (!WALLPAPER_DIR.exists()) {
-                WALLPAPER_DIR.mkdir();
+            File dir = getWallpaperDir(wallpaper.userId);
+            if (!dir.exists()) {
+                dir.mkdir();
                 FileUtils.setPermissions(
-                        WALLPAPER_DIR.getPath(),
+                        dir.getPath(),
                         FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
                         -1, -1);
             }
-            ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+            ParcelFileDescriptor fd = ParcelFileDescriptor.open(new File(dir, WALLPAPER),
                     MODE_CREATE|MODE_READ_WRITE);
-            mName = name;
+            wallpaper.name = name;
             return fd;
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "Error setting wallpaper", e);
@@ -495,31 +639,36 @@
 
     public void setWallpaperComponent(ComponentName name) {
         if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
+        int userId = UserId.getCallingUserId();
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        if (wallpaper == null) {
+            throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
+        }
         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
         synchronized (mLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mImageWallpaperPending = false;
-                bindWallpaperComponentLocked(name, false, true);
+                wallpaper.imageWallpaperPending = false;
+                bindWallpaperComponentLocked(name, false, true, wallpaper);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
     }
     
-    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser) {
+    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
+            boolean fromUser, WallpaperData wallpaper) {
         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
-        
         // Has the component changed?
         if (!force) {
-            if (mWallpaperConnection != null) {
-                if (mWallpaperComponent == null) {
+            if (wallpaper.connection != null) {
+                if (wallpaper.wallpaperComponent == null) {
                     if (componentName == null) {
                         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
                         // Still using default wallpaper.
                         return true;
                     }
-                } else if (mWallpaperComponent.equals(componentName)) {
+                } else if (wallpaper.wallpaperComponent.equals(componentName)) {
                     // Changing to same wallpaper.
                     if (DEBUG) Slog.v(TAG, "same wallpaper");
                     return true;
@@ -538,7 +687,7 @@
                 }
                 if (componentName == null) {
                     // Fall back to static image wallpaper
-                    componentName = mImageWallpaperComponent;
+                    componentName = wallpaper.imageWallpaperComponent;
                     //clearWallpaperComponentLocked();
                     //return;
                     if (DEBUG) Slog.v(TAG, "Using image wallpaper");
@@ -560,7 +709,7 @@
             WallpaperInfo wi = null;
             
             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
-            if (componentName != null && !componentName.equals(mImageWallpaperComponent)) {
+            if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) {
                 // Make sure the selected service is actually a wallpaper service.
                 List<ResolveInfo> ris = mContext.getPackageManager()
                         .queryIntentServices(intent, PackageManager.GET_META_DATA);
@@ -599,8 +748,13 @@
             
             // Bind the service!
             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
-            WallpaperConnection newConn = new WallpaperConnection(wi);
+            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
             intent.setComponent(componentName);
+            int serviceUserId = wallpaper.userId;
+            // Because the image wallpaper is running in the system ui
+            if (componentName.equals(wallpaper.imageWallpaperComponent)) {
+                serviceUserId = 0;
+            }
             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                     com.android.internal.R.string.wallpaper_binding_label);
             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
@@ -608,8 +762,7 @@
                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
                             0));
-            if (!mContext.bindService(intent, newConn,
-                    Context.BIND_AUTO_CREATE)) {
+            if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
                 String msg = "Unable to bind service: "
                         + componentName;
                 if (fromUser) {
@@ -618,18 +771,22 @@
                 Slog.w(TAG, msg);
                 return false;
             }
-            
-            clearWallpaperComponentLocked();
-            mWallpaperComponent = componentName;
-            mWallpaperConnection = newConn;
-            mLastDiedTime = SystemClock.uptimeMillis();
+            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
+                detachWallpaperLocked(mLastWallpaper);
+            }
+            wallpaper.wallpaperComponent = componentName;
+            wallpaper.connection = newConn;
+            wallpaper.lastDiedTime = SystemClock.uptimeMillis();
             try {
-                if (DEBUG) Slog.v(TAG, "Adding window token: " + newConn.mToken);
-                mIWindowManager.addWindowToken(newConn.mToken,
-                        WindowManager.LayoutParams.TYPE_WALLPAPER);
+                if (wallpaper.userId == mCurrentUserId) {
+                    if (DEBUG)
+                        Slog.v(TAG, "Adding window token: " + newConn.mToken);
+                    mIWindowManager.addWindowToken(newConn.mToken,
+                            WindowManager.LayoutParams.TYPE_WALLPAPER);
+                    mLastWallpaper = wallpaper;
+                }
             } catch (RemoteException e) {
             }
-            
         } catch (PackageManager.NameNotFoundException e) {
             String msg = "Unknown component " + componentName;
             if (fromUser) {
@@ -640,54 +797,58 @@
         }
         return true;
     }
-    
-    void clearWallpaperComponentLocked() {
-        mWallpaperComponent = null;
-        if (mWallpaperConnection != null) {
-            if (mWallpaperConnection.mEngine != null) {
+
+    void detachWallpaperLocked(WallpaperData wallpaper) {
+        if (wallpaper.connection != null) {
+            if (wallpaper.connection.mEngine != null) {
                 try {
-                    mWallpaperConnection.mEngine.destroy();
+                    wallpaper.connection.mEngine.destroy();
                 } catch (RemoteException e) {
                 }
             }
-            mContext.unbindService(mWallpaperConnection);
+            mContext.unbindService(wallpaper.connection);
             try {
-                if (DEBUG) Slog.v(TAG, "Removing window token: "
-                        + mWallpaperConnection.mToken);
-                mIWindowManager.removeWindowToken(mWallpaperConnection.mToken);
+                if (DEBUG)
+                    Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
+                mIWindowManager.removeWindowToken(wallpaper.connection.mToken);
             } catch (RemoteException e) {
             }
-            mWallpaperConnection.mService = null;
-            mWallpaperConnection.mEngine = null;
-            mWallpaperConnection = null;
+            wallpaper.connection.mService = null;
+            wallpaper.connection.mEngine = null;
+            wallpaper.connection = null;
         }
     }
-    
-    void attachServiceLocked(WallpaperConnection conn) {
+
+    void clearWallpaperComponentLocked(WallpaperData wallpaper) {
+        wallpaper.wallpaperComponent = null;
+        detachWallpaperLocked(wallpaper);
+    }
+
+    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
         try {
             conn.mService.attach(conn, conn.mToken,
                     WindowManager.LayoutParams.TYPE_WALLPAPER, false,
-                    mWidth, mHeight);
+                    wallpaper.width, wallpaper.height);
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
-            if (!mWallpaperUpdating) {
-                bindWallpaperComponentLocked(null, false, false);
+            if (!wallpaper.wallpaperUpdating) {
+                bindWallpaperComponentLocked(null, false, false, wallpaper);
             }
         }
     }
-    
-    private void notifyCallbacksLocked() {
-        final int n = mCallbacks.beginBroadcast();
+
+    private void notifyCallbacksLocked(WallpaperData wallpaper) {
+        final int n = wallpaper.callbacks.beginBroadcast();
         for (int i = 0; i < n; i++) {
             try {
-                mCallbacks.getBroadcastItem(i).onWallpaperChanged();
+                wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
             } catch (RemoteException e) {
 
                 // The RemoteCallbackList will take care of removing
                 // the dead object for us.
             }
         }
-        mCallbacks.finishBroadcast();
+        wallpaper.callbacks.finishBroadcast();
         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
         mContext.sendBroadcast(intent);
     }
@@ -699,13 +860,13 @@
         }
     }
 
-    private static JournaledFile makeJournaledFile() {
-        final String base = "/data/system/wallpaper_info.xml";
+    private static JournaledFile makeJournaledFile(int userId) {
+        final String base = "/data/system/users/" + userId + "/" + WALLPAPER_INFO;
         return new JournaledFile(new File(base), new File(base + ".tmp"));
     }
 
-    private void saveSettingsLocked() {
-        JournaledFile journal = makeJournaledFile();
+    private void saveSettingsLocked(WallpaperData wallpaper) {
+        JournaledFile journal = makeJournaledFile(wallpaper.userId);
         FileOutputStream stream = null;
         try {
             stream = new FileOutputStream(journal.chooseForWrite(), false);
@@ -714,13 +875,13 @@
             out.startDocument(null, true);
 
             out.startTag(null, "wp");
-            out.attribute(null, "width", Integer.toString(mWidth));
-            out.attribute(null, "height", Integer.toString(mHeight));
-            out.attribute(null, "name", mName);
-            if (mWallpaperComponent != null &&
-                    !mWallpaperComponent.equals(mImageWallpaperComponent)) {
+            out.attribute(null, "width", Integer.toString(wallpaper.width));
+            out.attribute(null, "height", Integer.toString(wallpaper.height));
+            out.attribute(null, "name", wallpaper.name);
+            if (wallpaper.wallpaperComponent != null
+                    && !wallpaper.wallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
                 out.attribute(null, "component",
-                        mWallpaperComponent.flattenToShortString());
+                        wallpaper.wallpaperComponent.flattenToShortString());
             }
             out.endTag(null, "wp");
 
@@ -739,12 +900,34 @@
         }
     }
 
-    private void loadSettingsLocked() {
+    private void migrateFromOld() {
+        File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
+        File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
+        if (oldWallpaper.exists()) {
+            File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
+            oldWallpaper.renameTo(newWallpaper);
+        }
+        if (oldInfo.exists()) {
+            File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
+            oldInfo.renameTo(newInfo);
+        }
+    }
+
+    private void loadSettingsLocked(int userId) {
         if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
         
-        JournaledFile journal = makeJournaledFile();
+        JournaledFile journal = makeJournaledFile(userId);
         FileInputStream stream = null;
         File file = journal.chooseForRead();
+        if (!file.exists()) {
+            // This should only happen one time, when upgrading from a legacy system
+            migrateFromOld();
+        }
+        WallpaperData wallpaper = mWallpaperMap.get(userId);
+        if (wallpaper == null) {
+            wallpaper = new WallpaperData(userId);
+            mWallpaperMap.put(userId, wallpaper);
+        }
         boolean success = false;
         try {
             stream = new FileInputStream(file);
@@ -757,23 +940,26 @@
                 if (type == XmlPullParser.START_TAG) {
                     String tag = parser.getName();
                     if ("wp".equals(tag)) {
-                        mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
-                        mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
-                        mName = parser.getAttributeValue(null, "name");
+                        wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
+                        wallpaper.height = Integer.parseInt(parser
+                                .getAttributeValue(null, "height"));
+                        wallpaper.name = parser.getAttributeValue(null, "name");
                         String comp = parser.getAttributeValue(null, "component");
-                        mNextWallpaperComponent = comp != null
+                        wallpaper.nextWallpaperComponent = comp != null
                                 ? ComponentName.unflattenFromString(comp)
                                 : null;
-                        if (mNextWallpaperComponent == null ||
-                                "android".equals(mNextWallpaperComponent.getPackageName())) {
-                            mNextWallpaperComponent = mImageWallpaperComponent;
+                        if (wallpaper.nextWallpaperComponent == null
+                                || "android".equals(wallpaper.nextWallpaperComponent
+                                        .getPackageName())) {
+                            wallpaper.nextWallpaperComponent = wallpaper.imageWallpaperComponent;
                         }
                           
                         if (DEBUG) {
-                            Slog.v(TAG, "mWidth:" + mWidth);
-                            Slog.v(TAG, "mHeight:" + mHeight);
-                            Slog.v(TAG, "mName:" + mName);
-                            Slog.v(TAG, "mNextWallpaperComponent:" + mNextWallpaperComponent);
+                            Slog.v(TAG, "mWidth:" + wallpaper.width);
+                            Slog.v(TAG, "mHeight:" + wallpaper.height);
+                            Slog.v(TAG, "mName:" + wallpaper.name);
+                            Slog.v(TAG, "mNextWallpaperComponent:"
+                                    + wallpaper.nextWallpaperComponent);
                         }
                     }
                 }
@@ -799,70 +985,75 @@
         }
 
         if (!success) {
-            mWidth = -1;
-            mHeight = -1;
-            mName = "";
+            wallpaper.width = -1;
+            wallpaper.height = -1;
+            wallpaper.name = "";
         }
 
         // We always want to have some reasonable width hint.
         WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
         Display d = wm.getDefaultDisplay();
         int baseSize = d.getMaximumSizeDimension();
-        if (mWidth < baseSize) {
-            mWidth = baseSize;
+        if (wallpaper.width < baseSize) {
+            wallpaper.width = baseSize;
         }
-        if (mHeight < baseSize) {
-            mHeight = baseSize;
+        if (wallpaper.height < baseSize) {
+            wallpaper.height = baseSize;
         }
     }
 
     // Called by SystemBackupAgent after files are restored to disk.
     void settingsRestored() {
+        // TODO: If necessary, make it work for secondary users as well. This currently assumes
+        // restores only to the primary user
         if (DEBUG) Slog.v(TAG, "settingsRestored");
-
+        WallpaperData wallpaper = null;
         boolean success = false;
         synchronized (mLock) {
-            loadSettingsLocked();
-            if (mNextWallpaperComponent != null && 
-                    !mNextWallpaperComponent.equals(mImageWallpaperComponent)) {
-                if (!bindWallpaperComponentLocked(mNextWallpaperComponent, false, false)) {
+            loadSettingsLocked(0);
+            wallpaper = mWallpaperMap.get(0);
+            if (wallpaper.nextWallpaperComponent != null
+                    && !wallpaper.nextWallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
+                if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
+                        wallpaper)) {
                     // No such live wallpaper or other failure; fall back to the default
                     // live wallpaper (since the profile being restored indicated that the
                     // user had selected a live rather than static one).
-                    bindWallpaperComponentLocked(null, false, false);
+                    bindWallpaperComponentLocked(null, false, false, wallpaper);
                 }
                 success = true;
             } else {
                 // If there's a wallpaper name, we use that.  If that can't be loaded, then we
                 // use the default.
-                if ("".equals(mName)) {
+                if ("".equals(wallpaper.name)) {
                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
                     success = true;
                 } else {
                     if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
-                    success = restoreNamedResourceLocked();
+                    success = restoreNamedResourceLocked(wallpaper);
                 }
                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success);
                 if (success) {
-                    bindWallpaperComponentLocked(mNextWallpaperComponent, false, false);
+                    bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
+                            wallpaper);
                 }
             }
         }
 
         if (!success) {
-            Slog.e(TAG, "Failed to restore wallpaper: '" + mName + "'");
-            mName = "";
-            WALLPAPER_FILE.delete();
+            Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
+            wallpaper.name = "";
+            getWallpaperDir(0).delete();
         }
 
         synchronized (mLock) {
-            saveSettingsLocked();
+            saveSettingsLocked(wallpaper);
         }
     }
 
-    boolean restoreNamedResourceLocked() {
-        if (mName.length() > 4 && "res:".equals(mName.substring(0, 4))) {
-            String resName = mName.substring(4);
+    boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
+        if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
+            String resName = wallpaper.name.substring(4);
 
             String pkg = null;
             int colon = resName.indexOf(':');
@@ -896,10 +1087,10 @@
                     }
 
                     res = r.openRawResource(resId);
-                    if (WALLPAPER_FILE.exists()) {
-                        WALLPAPER_FILE.delete();
+                    if (wallpaper.wallpaperFile.exists()) {
+                        wallpaper.wallpaperFile.delete();
                     }
-                    fos = new FileOutputStream(WALLPAPER_FILE);
+                    fos = new FileOutputStream(wallpaper.wallpaperFile);
 
                     byte[] buffer = new byte[32768];
                     int amt;
@@ -933,7 +1124,7 @@
         }
         return false;
     }
-    
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -947,20 +1138,35 @@
 
         synchronized (mLock) {
             pw.println("Current Wallpaper Service state:");
-            pw.print("  mWidth="); pw.print(mWidth);
-                    pw.print(" mHeight="); pw.println(mHeight);
-            pw.print("  mName="); pw.println(mName);
-            pw.print("  mWallpaperComponent="); pw.println(mWallpaperComponent);
-            if (mWallpaperConnection != null) {
-                WallpaperConnection conn = mWallpaperConnection;
-                pw.print("  Wallpaper connection ");
-                        pw.print(conn); pw.println(":");
-                pw.print("    mInfo.component="); pw.println(conn.mInfo.getComponent());
-                pw.print("    mToken="); pw.println(conn.mToken);
-                pw.print("    mService="); pw.println(conn.mService);
-                pw.print("    mEngine="); pw.println(conn.mEngine);
-                pw.print("    mLastDiedTime=");
-                        pw.println(mLastDiedTime - SystemClock.uptimeMillis());
+            for (int i = 0; i < mWallpaperMap.size(); i++) {
+                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
+                pw.println(" User " + wallpaper.userId + ":");
+                pw.print("  mWidth=");
+                pw.print(wallpaper.width);
+                pw.print(" mHeight=");
+                pw.println(wallpaper.height);
+                pw.print("  mName=");
+                pw.println(wallpaper.name);
+                pw.print("  mWallpaperComponent=");
+                pw.println(wallpaper.wallpaperComponent);
+                if (wallpaper.connection != null) {
+                    WallpaperConnection conn = wallpaper.connection;
+                    pw.print("  Wallpaper connection ");
+                    pw.print(conn);
+                    pw.println(":");
+                    if (conn.mInfo != null) {
+                        pw.print("    mInfo.component=");
+                        pw.println(conn.mInfo.getComponent());
+                    }
+                    pw.print("    mToken=");
+                    pw.println(conn.mToken);
+                    pw.print("    mService=");
+                    pw.println(conn.mService);
+                    pw.print("    mEngine=");
+                    pw.println(conn.mEngine);
+                    pw.print("    mLastDiedTime=");
+                    pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
index 6a63eac..326b940 100644
--- a/services/java/com/android/server/WiredAccessoryObserver.java
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -191,8 +191,12 @@
         mHeadsetState = headsetState;
 
         if (headsetState == 0) {
-            Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
-            mContext.sendBroadcast(intent);
+            if (mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_sendAudioBecomingNoisy)) {
+                Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+                mContext.sendBroadcast(intent);
+            }
+
             // It can take hundreds of ms flush the audio pipeline after
             // apps pause audio playback, but audio route changes are
             // immediate, so delay the route change by 1000ms.
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8bda755..c99aa02 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -86,8 +86,8 @@
 
     private static final String LOG_TAG = "AccessibilityManagerService";
 
-    private static final String FUNCTION_REGISTER_EVENT_LISTENER =
-        "registerEventListener";
+    private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE =
+        "registerUiTestAutomationService";
 
     private static int sIdCounter = 0;
 
@@ -95,10 +95,6 @@
 
     private static final int DO_SET_SERVICE_INFO = 10;
 
-    public static final int ACTIVE_WINDOW_ID = -1;
-
-    public static final long ROOT_NODE_ID = -1;
-
     private static int sNextWindowId;
 
     final HandlerCaller mCaller;
@@ -143,6 +139,8 @@
 
     private final SecurityPolicy mSecurityPolicy;
 
+    private Service mUiAutomationService;
+
     /**
      * Handler for delayed event dispatch.
      */
@@ -239,19 +237,9 @@
                 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
                     synchronized (mLock) {
                         populateAccessibilityServiceListLocked();
-                        // get accessibility enabled setting on boot
-                        mIsAccessibilityEnabled = Settings.Secure.getInt(
-                                mContext.getContentResolver(),
-                                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
-
-                        manageServicesLocked();
-
-                        // get touch exploration enabled setting on boot
-                        mIsTouchExplorationEnabled = Settings.Secure.getInt(
-                                mContext.getContentResolver(),
-                                Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
+                        handleAccessibilityEnabledSettingChangedLocked();
+                        handleTouchExplorationEnabledSettingChangedLocked();
                         updateInputFilterLocked();
-
                         sendStateToClientsLocked();
                     }
                     
@@ -299,9 +287,10 @@
                 @Override
                 public void onChange(boolean selfChange) {
                     super.onChange(selfChange);
-
                     synchronized (mLock) {
                         handleAccessibilityEnabledSettingChangedLocked();
+                        updateInputFilterLocked();
+                        sendStateToClientsLocked();
                     }
                 }
             });
@@ -313,11 +302,8 @@
                     @Override
                     public void onChange(boolean selfChange) {
                         super.onChange(selfChange);
-
                         synchronized (mLock) {
-                            mIsTouchExplorationEnabled = Settings.Secure.getInt(
-                                    mContext.getContentResolver(),
-                                    Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
+                            handleTouchExplorationEnabledSettingChangedLocked();
                             updateInputFilterLocked();
                             sendStateToClientsLocked();
                         }
@@ -331,7 +317,6 @@
                 @Override
                 public void onChange(boolean selfChange) {
                     super.onChange(selfChange);
-
                     synchronized (mLock) {
                         manageServicesLocked();
                     }
@@ -473,7 +458,7 @@
     public void registerUiTestAutomationService(IEventListener listener,
             AccessibilityServiceInfo accessibilityServiceInfo) {
         mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
-                FUNCTION_REGISTER_EVENT_LISTENER);
+                FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE);
         ComponentName componentName = new ComponentName("foo.bar",
                 "AutomationAccessibilityService");
         synchronized (mLock) {
@@ -494,19 +479,15 @@
             }
         }
         // Hook the automation service up.
-        Service service = new Service(componentName, accessibilityServiceInfo, true);
-        service.onServiceConnected(componentName, listener.asBinder());
+        mUiAutomationService = new Service(componentName, accessibilityServiceInfo, true);
+        mUiAutomationService.onServiceConnected(componentName, listener.asBinder());
     }
 
     public void unregisterUiTestAutomationService(IEventListener listener) {
         synchronized (mLock) {
-            final int serviceCount = mServices.size();
-            for (int i = 0; i < serviceCount; i++) {
-                Service service = mServices.get(i);
-                if (service.mServiceInterface == listener && service.mIsAutomation) {
-                    // Automation service is not bound, so pretend it died to perform clean up.
-                    service.binderDied();
-                }
+            // Automation service is not bound, so pretend it died to perform clean up.
+            if (mUiAutomationService != null) {
+                mUiAutomationService.binderDied();
             }
         }
     }
@@ -741,7 +722,10 @@
      * Manages services by starting enabled ones and stopping disabled ones.
      */
     private void manageServicesLocked() {
-        unbindAutomationService();
+        // While the UI automation service is running it takes over.
+        if (mUiAutomationService != null) {
+            return;
+        }
         populateEnabledServicesLocked(mEnabledServices);
         final int enabledInstalledServicesCount = updateServicesStateLocked(mInstalledServices,
                 mEnabledServices);
@@ -769,21 +753,6 @@
     }
 
     /**
-     * Unbinds the automation service if such is running.
-     */
-    private void unbindAutomationService() {
-        List<Service> runningServices = mServices;
-        int runningServiceCount = mServices.size();
-        for (int i = 0; i < runningServiceCount; i++) {
-            Service service = runningServices.get(i);
-            if (service.mIsAutomation) {
-                 service.unbind();
-                 return;
-            }
-        }
-    }
-
-    /**
      * Populates a list with the {@link ComponentName}s of all enabled
      * {@link AccessibilityService}s.
      *
@@ -919,8 +888,15 @@
         } else {
             unbindAllServicesLocked();
         }
-        updateInputFilterLocked();
-        sendStateToClientsLocked();
+    }
+
+    /**
+     * Updates the state based on the touch exploration enabled setting.
+     */
+    private void handleTouchExplorationEnabledSettingChangedLocked() {
+        mIsTouchExplorationEnabled = Settings.Secure.getInt(
+                mContext.getContentResolver(),
+                Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
     }
 
     private class AccessibilityConnectionWrapper implements DeathRecipient {
@@ -1153,7 +1129,8 @@
 
         public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
                 long accessibilityNodeId, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
+                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid,
+                int prefetchFlags)
                 throws RemoteException {
             final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
             IAccessibilityInteractionConnection connection = null;
@@ -1174,7 +1151,7 @@
             final long identityToken = Binder.clearCallingIdentity();
             try {
                 connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
-                        interactionId, callback, interrogatingPid, interrogatingTid);
+                        interactionId, callback, prefetchFlags, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
@@ -1243,12 +1220,16 @@
 
         public void binderDied() {
             synchronized (mLock) {
-                unlinkToOwnDeath();
+                // The death recipient is unregistered in tryRemoveServiceLocked
                 tryRemoveServiceLocked(this);
                 // We no longer have an automation service, so restore
                 // the state based on values in the settings database.
                 if (mIsAutomation) {
+                    mUiAutomationService = null;
                     handleAccessibilityEnabledSettingChangedLocked();
+                    handleTouchExplorationEnabledSettingChangedLocked();
+                    updateInputFilterLocked();
+                    sendStateToClientsLocked();
                 }
             }
         }
@@ -1269,7 +1250,7 @@
         }
 
         private int resolveAccessibilityWindowId(int accessibilityWindowId) {
-            if (accessibilityWindowId == ACTIVE_WINDOW_ID) {
+            if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
                 return mSecurityPolicy.mRetrievalAlowingWindowId;
             }
             return accessibilityWindowId;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e6a1e68..8a5e7fc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -38,7 +38,6 @@
 import android.app.ApplicationErrorReport;
 import android.app.Dialog;
 import android.app.IActivityController;
-import android.app.IActivityWatcher;
 import android.app.IApplicationThread;
 import android.app.IInstrumentationWatcher;
 import android.app.INotificationManager;
@@ -50,6 +49,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
+import android.app.WallpaperManager;
 import android.app.backup.IBackupManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -268,7 +268,14 @@
     static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     public ActivityStack mMainStack;
-    
+
+    private final boolean mHeadless;
+
+    // Whether we should show our dialogs (ANR, crash, etc) or just perform their
+    // default actuion automatically.  Important for devices without direct input
+    // devices.
+    private boolean mShowDialogs = true;
+
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -284,813 +291,12 @@
     final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
             = new ArrayList<PendingActivityLaunch>();
     
-    /**
-     * BROADCASTS
-     *
-     * We keep two broadcast queues and associated bookkeeping, one for those at
-     * foreground priority, and one for normal (background-priority) broadcasts.
-     */
-    public class BroadcastQueue {
-        static final String TAG = "BroadcastQueue";
 
-        static final int MAX_BROADCAST_HISTORY = 25;
-
-        /**
-         * Recognizable moniker for this queue
-         */
-        String mQueueName;
-
-        /**
-         * Timeout period for this queue's broadcasts
-         */
-        long mTimeoutPeriod;
-
-        /**
-         * Lists of all active broadcasts that are to be executed immediately
-         * (without waiting for another broadcast to finish).  Currently this only
-         * contains broadcasts to registered receivers, to avoid spinning up
-         * a bunch of processes to execute IntentReceiver components.  Background-
-         * and foreground-priority broadcasts are queued separately.
-         */
-        final ArrayList<BroadcastRecord> mParallelBroadcasts
-                = new ArrayList<BroadcastRecord>();
-        /**
-         * List of all active broadcasts that are to be executed one at a time.
-         * The object at the top of the list is the currently activity broadcasts;
-         * those after it are waiting for the top to finish.  As with parallel
-         * broadcasts, separate background- and foreground-priority queues are
-         * maintained.
-         */
-        final ArrayList<BroadcastRecord> mOrderedBroadcasts
-                = new ArrayList<BroadcastRecord>();
-
-        /**
-         * Historical data of past broadcasts, for debugging.
-         */
-        final BroadcastRecord[] mBroadcastHistory
-                = new BroadcastRecord[MAX_BROADCAST_HISTORY];
-
-        /**
-         * Set when we current have a BROADCAST_INTENT_MSG in flight.
-         */
-        boolean mBroadcastsScheduled = false;
-
-        /**
-         * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
-         */
-        boolean mPendingBroadcastTimeoutMessage;
-
-        /**
-         * Intent broadcasts that we have tried to start, but are
-         * waiting for the application's process to be created.  We only
-         * need one per scheduling class (instead of a list) because we always
-         * process broadcasts one at a time, so no others can be started while
-         * waiting for this one.
-         */
-        BroadcastRecord mPendingBroadcast = null;
-
-        /**
-         * The receiver index that is pending, to restart the broadcast if needed.
-         */
-        int mPendingBroadcastRecvIndex;
-
-        BroadcastQueue(String name, long timeoutPeriod) {
-            mQueueName = name;
-            mTimeoutPeriod = timeoutPeriod;
-        }
-
-        public boolean isPendingBroadcastProcessLocked(int pid) {
-            return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
-        }
-
-        public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
-            mParallelBroadcasts.add(r);
-        }
-
-        public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
-            mOrderedBroadcasts.add(r);
-        }
-
-        public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) {
-            for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
-                if (r.intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
-                    if (DEBUG_BROADCAST) Slog.v(TAG,
-                            "***** DROPPING PARALLEL ["
-                    + mQueueName + "]: " + r.intent);
-                    mParallelBroadcasts.set(i, r);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public final boolean replaceOrderedBroadcastLocked(BroadcastRecord r) {
-            for (int i=mOrderedBroadcasts.size()-1; i>0; i--) {
-                if (r.intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
-                    if (DEBUG_BROADCAST) Slog.v(TAG,
-                            "***** DROPPING ORDERED ["
-                            + mQueueName + "]: " + r.intent);
-                    mOrderedBroadcasts.set(i, r);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
-            boolean didSomething = false;
-            final BroadcastRecord br = mPendingBroadcast;
-            if (br != null && br.curApp.pid == app.pid) {
-                try {
-                    mPendingBroadcast = null;
-                    processCurBroadcastLocked(br, app);
-                    didSomething = true;
-                } catch (Exception e) {
-                    Slog.w(TAG, "Exception in new application when starting receiver "
-                            + br.curComponent.flattenToShortString(), e);
-                    logBroadcastReceiverDiscardLocked(br);
-                    finishReceiverLocked(br, br.resultCode, br.resultData,
-                            br.resultExtras, br.resultAbort, true);
-                    scheduleBroadcastsLocked();
-                    // We need to reset the state if we fails to start the receiver.
-                    br.state = BroadcastRecord.IDLE;
-                    throw new RuntimeException(e.getMessage());
-                }
-            }
-            return didSomething;
-        }
-
-        public void skipPendingBroadcastLocked(int pid) {
-            final BroadcastRecord br = mPendingBroadcast;
-            if (br != null && br.curApp.pid == pid) {
-                br.state = BroadcastRecord.IDLE;
-                br.nextReceiver = mPendingBroadcastRecvIndex;
-                mPendingBroadcast = null;
-                scheduleBroadcastsLocked();
-            }
-        }
-
-        public void skipCurrentReceiverLocked(ProcessRecord app) {
-            boolean reschedule = false;
-            BroadcastRecord r = app.curReceiver;
-            if (r != null) {
-                // The current broadcast is waiting for this app's receiver
-                // to be finished.  Looks like that's not going to happen, so
-                // let the broadcast continue.
-                logBroadcastReceiverDiscardLocked(r);
-                finishReceiverLocked(r, r.resultCode, r.resultData,
-                        r.resultExtras, r.resultAbort, true);
-                reschedule = true;
-            }
-
-            r = mPendingBroadcast;
-            if (r != null && r.curApp == app) {
-                if (DEBUG_BROADCAST) Slog.v(TAG,
-                        "[" + mQueueName + "] skip & discard pending app " + r);
-                logBroadcastReceiverDiscardLocked(r);
-                finishReceiverLocked(r, r.resultCode, r.resultData,
-                        r.resultExtras, r.resultAbort, true);
-                reschedule = true;
-            }
-            if (reschedule) {
-                scheduleBroadcastsLocked();
-            }
-        }
-
-        public void scheduleBroadcastsLocked() {
-            if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
-                    + mQueueName + "]: current="
-                    + mBroadcastsScheduled);
-
-            if (mBroadcastsScheduled) {
-                return;
-            }
-            mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
-            mBroadcastsScheduled = true;
-        }
-
-        public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
-            if (mOrderedBroadcasts.size() > 0) {
-                final BroadcastRecord r = mOrderedBroadcasts.get(0);
-                if (r != null && r.receiver == receiver) {
-                    return r;
-                }
-            }
-            return null;
-        }
-
-        public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
-                String resultData, Bundle resultExtras, boolean resultAbort,
-                boolean explicit) {
-            int state = r.state;
-            r.state = BroadcastRecord.IDLE;
-            if (state == BroadcastRecord.IDLE) {
-                if (explicit) {
-                    Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
-                }
-            }
-            r.receiver = null;
-            r.intent.setComponent(null);
-            if (r.curApp != null) {
-                r.curApp.curReceiver = null;
-            }
-            if (r.curFilter != null) {
-                r.curFilter.receiverList.curBroadcast = null;
-            }
-            r.curFilter = null;
-            r.curApp = null;
-            r.curComponent = null;
-            r.curReceiver = null;
-            mPendingBroadcast = null;
-
-            r.resultCode = resultCode;
-            r.resultData = resultData;
-            r.resultExtras = resultExtras;
-            r.resultAbort = resultAbort;
-
-            // We will process the next receiver right now if this is finishing
-            // an app receiver (which is always asynchronous) or after we have
-            // come back from calling a receiver.
-            return state == BroadcastRecord.APP_RECEIVE
-                    || state == BroadcastRecord.CALL_DONE_RECEIVE;
-        }
-
-        private final void processNextBroadcast(boolean fromMsg) {
-            synchronized(ActivityManagerService.this) {
-                BroadcastRecord r;
-
-                if (DEBUG_BROADCAST) Slog.v(TAG, "processNextBroadcast ["
-                        + mQueueName + "]: "
-                        + mParallelBroadcasts.size() + " broadcasts, "
-                        + mOrderedBroadcasts.size() + " ordered broadcasts");
-
-                updateCpuStats();
-
-                if (fromMsg) {
-                    mBroadcastsScheduled = false;
-                }
-
-                // First, deliver any non-serialized broadcasts right away.
-                while (mParallelBroadcasts.size() > 0) {
-                    r = mParallelBroadcasts.remove(0);
-                    r.dispatchTime = SystemClock.uptimeMillis();
-                    r.dispatchClockTime = System.currentTimeMillis();
-                    final int N = r.receivers.size();
-                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
-                            + mQueueName + "] " + r);
-                    for (int i=0; i<N; i++) {
-                        Object target = r.receivers.get(i);
-                        if (DEBUG_BROADCAST)  Slog.v(TAG,
-                                "Delivering non-ordered on [" + mQueueName + "] to registered "
-                                + target + ": " + r);
-                        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
-                    }
-                    addBroadcastToHistoryLocked(r);
-                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
-                            + mQueueName + "] " + r);
-                }
-
-                // Now take care of the next serialized one...
-
-                // If we are waiting for a process to come up to handle the next
-                // broadcast, then do nothing at this point.  Just in case, we
-                // check that the process we're waiting for still exists.
-                if (mPendingBroadcast != null) {
-                    if (DEBUG_BROADCAST_LIGHT) {
-                        Slog.v(TAG, "processNextBroadcast ["
-                                + mQueueName + "]: waiting for "
-                                + mPendingBroadcast.curApp);
-                    }
-
-                    boolean isDead;
-                    synchronized (mPidsSelfLocked) {
-                        isDead = (mPidsSelfLocked.get(mPendingBroadcast.curApp.pid) == null);
-                    }
-                    if (!isDead) {
-                        // It's still alive, so keep waiting
-                        return;
-                    } else {
-                        Slog.w(TAG, "pending app  ["
-                                + mQueueName + "]" + mPendingBroadcast.curApp
-                                + " died before responding to broadcast");
-                        mPendingBroadcast.state = BroadcastRecord.IDLE;
-                        mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
-                        mPendingBroadcast = null;
-                    }
-                }
-
-                boolean looped = false;
-                
-                do {
-                    if (mOrderedBroadcasts.size() == 0) {
-                        // No more broadcasts pending, so all done!
-                        scheduleAppGcsLocked();
-                        if (looped) {
-                            // If we had finished the last ordered broadcast, then
-                            // make sure all processes have correct oom and sched
-                            // adjustments.
-                            updateOomAdjLocked();
-                        }
-                        return;
-                    }
-                    r = mOrderedBroadcasts.get(0);
-                    boolean forceReceive = false;
-
-                    // Ensure that even if something goes awry with the timeout
-                    // detection, we catch "hung" broadcasts here, discard them,
-                    // and continue to make progress.
-                    //
-                    // This is only done if the system is ready so that PRE_BOOT_COMPLETED
-                    // receivers don't get executed with timeouts. They're intended for
-                    // one time heavy lifting after system upgrades and can take
-                    // significant amounts of time.
-                    int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
-                    if (mProcessesReady && r.dispatchTime > 0) {
-                        long now = SystemClock.uptimeMillis();
-                        if ((numReceivers > 0) &&
-                                (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
-                            Slog.w(TAG, "Hung broadcast ["
-                                    + mQueueName + "] discarded after timeout failure:"
-                                    + " now=" + now
-                                    + " dispatchTime=" + r.dispatchTime
-                                    + " startTime=" + r.receiverTime
-                                    + " intent=" + r.intent
-                                    + " numReceivers=" + numReceivers
-                                    + " nextReceiver=" + r.nextReceiver
-                                    + " state=" + r.state);
-                            broadcastTimeoutLocked(false); // forcibly finish this broadcast
-                            forceReceive = true;
-                            r.state = BroadcastRecord.IDLE;
-                        }
-                    }
-
-                    if (r.state != BroadcastRecord.IDLE) {
-                        if (DEBUG_BROADCAST) Slog.d(TAG,
-                                "processNextBroadcast("
-                                + mQueueName + ") called when not idle (state="
-                                + r.state + ")");
-                        return;
-                    }
-
-                    if (r.receivers == null || r.nextReceiver >= numReceivers
-                            || r.resultAbort || forceReceive) {
-                        // No more receivers for this broadcast!  Send the final
-                        // result if requested...
-                        if (r.resultTo != null) {
-                            try {
-                                if (DEBUG_BROADCAST) {
-                                    int seq = r.intent.getIntExtra("seq", -1);
-                                    Slog.i(TAG, "Finishing broadcast ["
-                                            + mQueueName + "] " + r.intent.getAction()
-                                            + " seq=" + seq + " app=" + r.callerApp);
-                                }
-                                performReceiveLocked(r.callerApp, r.resultTo,
-                                    new Intent(r.intent), r.resultCode,
-                                    r.resultData, r.resultExtras, false, false);
-                                // Set this to null so that the reference
-                                // (local and remote) isnt kept in the mBroadcastHistory.
-                                r.resultTo = null;
-                            } catch (RemoteException e) {
-                                Slog.w(TAG, "Failure ["
-                                        + mQueueName + "] sending broadcast result of "
-                                        + r.intent, e);
-                            }
-                        }
-
-                        if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
-                        cancelBroadcastTimeoutLocked();
-
-                        if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
-                                + r);
-
-                        // ... and on to the next...
-                        addBroadcastToHistoryLocked(r);
-                        mOrderedBroadcasts.remove(0);
-                        r = null;
-                        looped = true;
-                        continue;
-                    }
-                } while (r == null);
-
-                // Get the next receiver...
-                int recIdx = r.nextReceiver++;
-
-                // Keep track of when this receiver started, and make sure there
-                // is a timeout message pending to kill it if need be.
-                r.receiverTime = SystemClock.uptimeMillis();
-                if (recIdx == 0) {
-                    r.dispatchTime = r.receiverTime;
-                    r.dispatchClockTime = System.currentTimeMillis();
-                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast ["
-                            + mQueueName + "] " + r);
-                }
-                if (! mPendingBroadcastTimeoutMessage) {
-                    long timeoutTime = r.receiverTime + mTimeoutPeriod;
-                    if (DEBUG_BROADCAST) Slog.v(TAG,
-                            "Submitting BROADCAST_TIMEOUT_MSG ["
-                            + mQueueName + "] for " + r + " at " + timeoutTime);
-                    setBroadcastTimeoutLocked(timeoutTime);
-                }
-
-                Object nextReceiver = r.receivers.get(recIdx);
-                if (nextReceiver instanceof BroadcastFilter) {
-                    // Simple case: this is a registered receiver who gets
-                    // a direct call.
-                    BroadcastFilter filter = (BroadcastFilter)nextReceiver;
-                    if (DEBUG_BROADCAST)  Slog.v(TAG,
-                            "Delivering ordered ["
-                            + mQueueName + "] to registered "
-                            + filter + ": " + r);
-                    deliverToRegisteredReceiverLocked(r, filter, r.ordered);
-                    if (r.receiver == null || !r.ordered) {
-                        // The receiver has already finished, so schedule to
-                        // process the next one.
-                        if (DEBUG_BROADCAST) Slog.v(TAG, "Quick finishing ["
-                                + mQueueName + "]: ordered="
-                                + r.ordered + " receiver=" + r.receiver);
-                        r.state = BroadcastRecord.IDLE;
-                        scheduleBroadcastsLocked();
-                    }
-                    return;
-                }
-
-                // Hard case: need to instantiate the receiver, possibly
-                // starting its application process to host it.
-
-                ResolveInfo info =
-                    (ResolveInfo)nextReceiver;
-
-                boolean skip = false;
-                int perm = checkComponentPermission(info.activityInfo.permission,
-                        r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
-                        info.activityInfo.exported);
-                if (perm != PackageManager.PERMISSION_GRANTED) {
-                    if (!info.activityInfo.exported) {
-                        Slog.w(TAG, "Permission Denial: broadcasting "
-                                + r.intent.toString()
-                                + " from " + r.callerPackage + " (pid=" + r.callingPid
-                                + ", uid=" + r.callingUid + ")"
-                                + " is not exported from uid " + info.activityInfo.applicationInfo.uid
-                                + " due to receiver " + info.activityInfo.packageName
-                                + "/" + info.activityInfo.name);
-                    } else {
-                        Slog.w(TAG, "Permission Denial: broadcasting "
-                                + r.intent.toString()
-                                + " from " + r.callerPackage + " (pid=" + r.callingPid
-                                + ", uid=" + r.callingUid + ")"
-                                + " requires " + info.activityInfo.permission
-                                + " due to receiver " + info.activityInfo.packageName
-                                + "/" + info.activityInfo.name);
-                    }
-                    skip = true;
-                }
-                if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
-                    r.requiredPermission != null) {
-                    try {
-                        perm = AppGlobals.getPackageManager().
-                                checkPermission(r.requiredPermission,
-                                        info.activityInfo.applicationInfo.packageName);
-                    } catch (RemoteException e) {
-                        perm = PackageManager.PERMISSION_DENIED;
-                    }
-                    if (perm != PackageManager.PERMISSION_GRANTED) {
-                        Slog.w(TAG, "Permission Denial: receiving "
-                                + r.intent + " to "
-                                + info.activityInfo.applicationInfo.packageName
-                                + " requires " + r.requiredPermission
-                                + " due to sender " + r.callerPackage
-                                + " (uid " + r.callingUid + ")");
-                        skip = true;
-                    }
-                }
-                if (r.curApp != null && r.curApp.crashing) {
-                    // If the target process is crashing, just skip it.
-                    if (DEBUG_BROADCAST)  Slog.v(TAG,
-                            "Skipping deliver ordered ["
-                            + mQueueName + "] " + r + " to " + r.curApp
-                            + ": process crashing");
-                    skip = true;
-                }
-
-                if (skip) {
-                    if (DEBUG_BROADCAST)  Slog.v(TAG,
-                            "Skipping delivery of ordered ["
-                            + mQueueName + "] " + r + " for whatever reason");
-                    r.receiver = null;
-                    r.curFilter = null;
-                    r.state = BroadcastRecord.IDLE;
-                    scheduleBroadcastsLocked();
-                    return;
-                }
-
-                r.state = BroadcastRecord.APP_RECEIVE;
-                String targetProcess = info.activityInfo.processName;
-                r.curComponent = new ComponentName(
-                        info.activityInfo.applicationInfo.packageName,
-                        info.activityInfo.name);
-                if (r.callingUid != Process.SYSTEM_UID) {
-                    info.activityInfo = getActivityInfoForUser(info.activityInfo, UserId
-                            .getUserId(r.callingUid));
-                }
-                r.curReceiver = info.activityInfo;
-                if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
-                    Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
-                            + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
-                            + info.activityInfo.applicationInfo.uid);
-                }
-
-                // Broadcast is being executed, its package can't be stopped.
-                try {
-                    AppGlobals.getPackageManager().setPackageStoppedState(
-                            r.curComponent.getPackageName(), false);
-                } catch (RemoteException e) {
-                } catch (IllegalArgumentException e) {
-                    Slog.w(TAG, "Failed trying to unstop package "
-                            + r.curComponent.getPackageName() + ": " + e);
-                }
-
-                // Is this receiver's application already running?
-                ProcessRecord app = getProcessRecordLocked(targetProcess,
-                        info.activityInfo.applicationInfo.uid);
-                if (app != null && app.thread != null) {
-                    try {
-                        app.addPackage(info.activityInfo.packageName);
-                        processCurBroadcastLocked(r, app);
-                        return;
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Exception when sending broadcast to "
-                              + r.curComponent, e);
-                    }
-
-                    // If a dead object exception was thrown -- fall through to
-                    // restart the application.
-                }
-
-                // Not running -- get it started, to be executed when the app comes up.
-                if (DEBUG_BROADCAST)  Slog.v(TAG,
-                        "Need to start app ["
-                        + mQueueName + "] " + targetProcess + " for broadcast " + r);
-                if ((r.curApp=startProcessLocked(targetProcess,
-                        info.activityInfo.applicationInfo, true,
-                        r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
-                        "broadcast", r.curComponent,
-                        (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0))
-                                == null) {
-                    // Ah, this recipient is unavailable.  Finish it if necessary,
-                    // and mark the broadcast record as ready for the next.
-                    Slog.w(TAG, "Unable to launch app "
-                            + info.activityInfo.applicationInfo.packageName + "/"
-                            + info.activityInfo.applicationInfo.uid + " for broadcast "
-                            + r.intent + ": process is bad");
-                    logBroadcastReceiverDiscardLocked(r);
-                    finishReceiverLocked(r, r.resultCode, r.resultData,
-                            r.resultExtras, r.resultAbort, true);
-                    scheduleBroadcastsLocked();
-                    r.state = BroadcastRecord.IDLE;
-                    return;
-                }
-
-                mPendingBroadcast = r;
-                mPendingBroadcastRecvIndex = recIdx;
-            }
-        }
-
-        final void setBroadcastTimeoutLocked(long timeoutTime) {
-            if (! mPendingBroadcastTimeoutMessage) {
-                Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
-                mHandler.sendMessageAtTime(msg, timeoutTime);
-                mPendingBroadcastTimeoutMessage = true;
-            }
-        }
-
-        final void cancelBroadcastTimeoutLocked() {
-            if (mPendingBroadcastTimeoutMessage) {
-                mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
-                mPendingBroadcastTimeoutMessage = false;
-            }
-        }
-
-        final void broadcastTimeoutLocked(boolean fromMsg) {
-            if (fromMsg) {
-                mPendingBroadcastTimeoutMessage = false;
-            }
-
-            if (mOrderedBroadcasts.size() == 0) {
-                return;
-            }
-
-            long now = SystemClock.uptimeMillis();
-            BroadcastRecord r = mOrderedBroadcasts.get(0);
-            if (fromMsg) {
-                if (mDidDexOpt) {
-                    // Delay timeouts until dexopt finishes.
-                    mDidDexOpt = false;
-                    long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
-                    setBroadcastTimeoutLocked(timeoutTime);
-                    return;
-                }
-                if (! mProcessesReady) {
-                    // Only process broadcast timeouts if the system is ready. That way
-                    // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
-                    // to do heavy lifting for system up.
-                    return;
-                }
-
-                long timeoutTime = r.receiverTime + mTimeoutPeriod;
-                if (timeoutTime > now) {
-                    // We can observe premature timeouts because we do not cancel and reset the
-                    // broadcast timeout message after each receiver finishes.  Instead, we set up
-                    // an initial timeout then kick it down the road a little further as needed
-                    // when it expires.
-                    if (DEBUG_BROADCAST) Slog.v(TAG,
-                            "Premature timeout ["
-                            + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
-                            + timeoutTime);
-                    setBroadcastTimeoutLocked(timeoutTime);
-                    return;
-                }
-            }
-
-            Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
-                    + ", started " + (now - r.receiverTime) + "ms ago");
-            r.receiverTime = now;
-            r.anrCount++;
-
-            // Current receiver has passed its expiration date.
-            if (r.nextReceiver <= 0) {
-                Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
-                return;
-            }
-
-            ProcessRecord app = null;
-            String anrMessage = null;
-
-            Object curReceiver = r.receivers.get(r.nextReceiver-1);
-            Slog.w(TAG, "Receiver during timeout: " + curReceiver);
-            logBroadcastReceiverDiscardLocked(r);
-            if (curReceiver instanceof BroadcastFilter) {
-                BroadcastFilter bf = (BroadcastFilter)curReceiver;
-                if (bf.receiverList.pid != 0
-                        && bf.receiverList.pid != MY_PID) {
-                    synchronized (ActivityManagerService.this.mPidsSelfLocked) {
-                        app = ActivityManagerService.this.mPidsSelfLocked.get(
-                                bf.receiverList.pid);
-                    }
-                }
-            } else {
-                app = r.curApp;
-            }
-
-            if (app != null) {
-                anrMessage = "Broadcast of " + r.intent.toString();
-            }
-
-            if (mPendingBroadcast == r) {
-                mPendingBroadcast = null;
-            }
-
-            // Move on to the next receiver.
-            finishReceiverLocked(r, r.resultCode, r.resultData,
-                    r.resultExtras, r.resultAbort, true);
-            scheduleBroadcastsLocked();
-
-            if (anrMessage != null) {
-                // Post the ANR to the handler since we do not want to process ANRs while
-                // potentially holding our lock.
-                mHandler.post(new AppNotResponding(app, anrMessage));
-            }
-        }
-
-        private final void addBroadcastToHistoryLocked(BroadcastRecord r) {
-            if (r.callingUid < 0) {
-                // This was from a registerReceiver() call; ignore it.
-                return;
-            }
-            System.arraycopy(mBroadcastHistory, 0, mBroadcastHistory, 1,
-                    MAX_BROADCAST_HISTORY-1);
-            r.finishTime = SystemClock.uptimeMillis();
-            mBroadcastHistory[0] = r;
-        }
-
-        final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
-            if (r.nextReceiver > 0) {
-                Object curReceiver = r.receivers.get(r.nextReceiver-1);
-                if (curReceiver instanceof BroadcastFilter) {
-                    BroadcastFilter bf = (BroadcastFilter) curReceiver;
-                    EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
-                            System.identityHashCode(r),
-                            r.intent.getAction(),
-                            r.nextReceiver - 1,
-                            System.identityHashCode(bf));
-                } else {
-                    EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-                            System.identityHashCode(r),
-                            r.intent.getAction(),
-                            r.nextReceiver - 1,
-                            ((ResolveInfo)curReceiver).toString());
-                }
-            } else {
-                Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
-                        + r);
-                EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-                        System.identityHashCode(r),
-                        r.intent.getAction(),
-                        r.nextReceiver,
-                        "NONE");
-            }
-        }
-
-        final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-                int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
-            if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
-                    || mPendingBroadcast != null) {
-                boolean printed = false;
-                for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
-                    BroadcastRecord br = mParallelBroadcasts.get(i);
-                    if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (needSep) {
-                            pw.println();
-                            needSep = false;
-                        }
-                        printed = true;
-                        pw.println("  Active broadcasts [" + mQueueName + "]:");
-                    }
-                    pw.println("  Broadcast #" + i + ":");
-                    br.dump(pw, "    ");
-                }
-                printed = false;
-                needSep = true;
-                for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) {
-                    BroadcastRecord br = mOrderedBroadcasts.get(i);
-                    if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (needSep) {
-                            pw.println();
-                        }
-                        needSep = true;
-                        pw.println("  Active ordered broadcasts [" + mQueueName + "]:");
-                    }
-                    pw.println("  Ordered Broadcast #" + i + ":");
-                    mOrderedBroadcasts.get(i).dump(pw, "    ");
-                }
-                if (dumpPackage == null || (mPendingBroadcast != null
-                        && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
-                    if (needSep) {
-                        pw.println();
-                    }
-                    pw.println("  Pending broadcast [" + mQueueName + "]:");
-                    if (mPendingBroadcast != null) {
-                        mPendingBroadcast.dump(pw, "    ");
-                    } else {
-                        pw.println("    (null)");
-                    }
-                    needSep = true;
-                }
-            }
-
-            boolean printed = false;
-            for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
-                BroadcastRecord r = mBroadcastHistory[i];
-                if (r == null) {
-                    break;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
-                    continue;
-                }
-                if (!printed) {
-                    if (needSep) {
-                        pw.println();
-                    }
-                    needSep = true;
-                    pw.println("  Historical broadcasts [" + mQueueName + "]:");
-                    printed = true;
-                }
-                if (dumpAll) {
-                    pw.print("  Historical Broadcast #"); pw.print(i); pw.println(":");
-                    r.dump(pw, "    ");
-                } else {
-                    if (i >= 50) {
-                        pw.println("  ...");
-                        break;
-                    }
-                    pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
-                }
-            }
-
-            return needSep;
-        }
-    }
-
-    final BroadcastQueue mFgBroadcastQueue = new BroadcastQueue("foreground", BROADCAST_FG_TIMEOUT);
-    final BroadcastQueue mBgBroadcastQueue = new BroadcastQueue("background", BROADCAST_BG_TIMEOUT);
+    BroadcastQueue mFgBroadcastQueue;
+    BroadcastQueue mBgBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
     // so that dispatch of foreground broadcasts gets precedence.
-    final BroadcastQueue[] mBroadcastQueues = { mFgBroadcastQueue, mBgBroadcastQueue };
+    final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];
 
     BroadcastQueue broadcastQueueForIntent(Intent intent) {
         final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
@@ -1135,6 +341,17 @@
     final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
 
     /**
+     * The currently running isolated processes.
+     */
+    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>();
+
+    /**
+     * Counter for assigning isolated process uids, to avoid frequently reusing the
+     * same ones.
+     */
+    int mNextIsolatedProcessUid = 0;
+
+    /**
      * The currently running heavy-weight process, if any.
      */
     ProcessRecord mHeavyWeightProcess = null;
@@ -1350,24 +567,6 @@
      */
     final ArrayList mCancelledThumbnails = new ArrayList();
 
-    /**
-     * All of the currently running global content providers.  Keys are a
-     * string containing the provider name and values are a
-     * ContentProviderRecord object containing the data about it.  Note
-     * that a single provider may be published under multiple names, so
-     * there may be multiple entries here for a single one in mProvidersByClass.
-     */
-    final HashMap<String, ContentProviderRecord> mProvidersByName
-            = new HashMap<String, ContentProviderRecord>();
-
-    /**
-     * All of the currently running global content providers.  Keys are a
-     * string containing the provider's implementation class and values are a
-     * ContentProviderRecord object containing the data about it.
-     */
-    final HashMap<ComponentName, ContentProviderRecord> mProvidersByClass
-            = new HashMap<ComponentName, ContentProviderRecord>();
-
     final ProviderMap mProviderMap = new ProviderMap();
 
     /**
@@ -1548,9 +747,6 @@
     int mProfileType = 0;
     boolean mAutoStopProfiler = false;
 
-    final RemoteCallbackList<IActivityWatcher> mWatchers
-            = new RemoteCallbackList<IActivityWatcher>();
-
     final RemoteCallbackList<IProcessObserver> mProcessObservers
             = new RemoteCallbackList<IProcessObserver>();
 
@@ -1627,8 +823,6 @@
     static final int UPDATE_CONFIGURATION_MSG = 4;
     static final int GC_BACKGROUND_PROCESSES_MSG = 5;
     static final int WAIT_FOR_DEBUGGER_MSG = 6;
-    static final int BROADCAST_INTENT_MSG = 7;
-    static final int BROADCAST_TIMEOUT_MSG = 8;
     static final int SERVICE_TIMEOUT_MSG = 12;
     static final int UPDATE_TIME_ZONE = 13;
     static final int SHOW_UID_ERROR_MSG = 14;
@@ -1648,6 +842,10 @@
     static final int DISPATCH_PROCESS_DIED = 32;
     static final int REPORT_MEM_USAGE = 33;
 
+    static final int FIRST_ACTIVITY_STACK_MSG = 100;
+    static final int FIRST_BROADCAST_QUEUE_MSG = 200;
+    static final int FIRST_COMPAT_MODE_MSG = 300;
+
     AlertDialog mUidAlert;
     CompatModeDialog mCompatModeDialog;
     long mLastMemUsageReportTime = 0;
@@ -1668,7 +866,7 @@
                         return;
                     }
                     AppErrorResult res = (AppErrorResult) data.get("result");
-                    if (!mSleeping && !mShuttingDown) {
+                    if (mShowDialogs && !mSleeping && !mShuttingDown) {
                         Dialog d = new AppErrorDialog(mContext, res, proc);
                         d.show();
                         proc.crashDialog = d;
@@ -1720,7 +918,7 @@
                         return;
                     }
                     AppErrorResult res = (AppErrorResult) data.get("result");
-                    if (!mSleeping && !mShuttingDown) {
+                    if (mShowDialogs && !mSleeping && !mShuttingDown) {
                         Dialog d = new StrictModeViolationDialog(mContext, res, proc);
                         d.show();
                         proc.crashDialog = d;
@@ -1767,18 +965,6 @@
                     }
                 }
             } break;
-            case BROADCAST_INTENT_MSG: {
-                if (DEBUG_BROADCAST) Slog.v(
-                        TAG, "Received BROADCAST_INTENT_MSG");
-                BroadcastQueue queue = (BroadcastQueue) msg.obj;
-                queue.processNextBroadcast(true);
-            } break;
-            case BROADCAST_TIMEOUT_MSG: {
-                final BroadcastQueue queue = (BroadcastQueue) msg.obj;
-                synchronized (ActivityManagerService.this) {
-                    queue.broadcastTimeoutLocked(true);
-                }
-            } break;
             case SERVICE_TIMEOUT_MSG: {
                 if (mDidDexOpt) {
                     mDidDexOpt = false;
@@ -1842,16 +1028,22 @@
                 }
             } break;
             case SHOW_UID_ERROR_MSG: {
-                // XXX This is a temporary dialog, no need to localize.
-                AlertDialog d = new BaseErrorDialog(mContext);
-                d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
-                d.setCancelable(false);
-                d.setTitle("System UIDs Inconsistent");
-                d.setMessage("UIDs on the system are inconsistent, you need to wipe your data partition or your device will be unstable.");
-                d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky",
-                        mHandler.obtainMessage(IM_FEELING_LUCKY_MSG));
-                mUidAlert = d;
-                d.show();
+                String title = "System UIDs Inconsistent";
+                String text = "UIDs on the system are inconsistent, you need to wipe your"
+                        + " data partition or your device will be unstable.";
+                Log.e(TAG, title + ": " + text);
+                if (mShowDialogs) {
+                    // XXX This is a temporary dialog, no need to localize.
+                    AlertDialog d = new BaseErrorDialog(mContext);
+                    d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+                    d.setCancelable(false);
+                    d.setTitle(title);
+                    d.setMessage(text);
+                    d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky",
+                            mHandler.obtainMessage(IM_FEELING_LUCKY_MSG));
+                    mUidAlert = d;
+                    d.show();
+                }
             } break;
             case IM_FEELING_LUCKY_MSG: {
                 if (mUidAlert != null) {
@@ -2081,7 +1273,7 @@
         try {
             ActivityManagerService m = mSelf;
             
-            ServiceManager.addService("activity", m);
+            ServiceManager.addService("activity", m, true);
             ServiceManager.addService("meminfo", new MemBinder(m));
             ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
             ServiceManager.addService("dbinfo", new DbBinder(m));
@@ -2098,11 +1290,11 @@
             synchronized (mSelf) {
                 ProcessRecord app = mSelf.newProcessRecordLocked(
                         mSystemThread.getApplicationThread(), info,
-                        info.processName);
+                        info.processName, false);
                 app.persistent = true;
                 app.pid = MY_PID;
                 app.maxAdj = ProcessList.SYSTEM_ADJ;
-                mSelf.mProcessNames.put(app.processName, app.info.uid, app);
+                mSelf.mProcessNames.put(app.processName, app.uid, app);
                 synchronized (mSelf.mPidsSelfLocked) {
                     mSelf.mPidsSelfLocked.put(app.pid, app);
                 }
@@ -2286,6 +1478,11 @@
     private ActivityManagerService() {
         Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
         
+        mFgBroadcastQueue = new BroadcastQueue(this, "foreground", BROADCAST_FG_TIMEOUT);
+        mBgBroadcastQueue = new BroadcastQueue(this, "background", BROADCAST_BG_TIMEOUT);
+        mBroadcastQueues[0] = mFgBroadcastQueue;
+        mBroadcastQueues[1] = mBgBroadcastQueue;
+
         File dataDir = Environment.getDataDirectory();
         File systemDir = new File(dataDir, "system");
         systemDir.mkdirs();
@@ -2299,6 +1496,7 @@
         
         mUsageStatsService = new UsageStatsService(new File(
                 systemDir, "usagestats").toString());
+        mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
 
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -2620,8 +1818,15 @@
     
     final ProcessRecord startProcessLocked(String processName,
             ApplicationInfo info, boolean knownToBeDead, int intentFlags,
-            String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
-        ProcessRecord app = getProcessRecordLocked(processName, info.uid);
+            String hostingType, ComponentName hostingName, boolean allowWhileBooting,
+            boolean isolated) {
+        ProcessRecord app;
+        if (!isolated) {
+            app = getProcessRecordLocked(processName, info.uid);
+        } else {
+            // If this is an isolated process, it can't re-use an existing process.
+            app = null;
+        }
         // We don't have to do anything more if:
         // (1) There is an existing application record; and
         // (2) The caller doesn't think it is dead, OR there is no thread
@@ -2650,36 +1855,46 @@
 
         String hostingNameStr = hostingName != null
                 ? hostingName.flattenToShortString() : null;
-        
-        if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) {
-            // If we are in the background, then check to see if this process
-            // is bad.  If so, we will just silently fail.
-            if (mBadProcesses.get(info.processName, info.uid) != null) {
-                if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+
+        if (!isolated) {
+            if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) {
+                // If we are in the background, then check to see if this process
+                // is bad.  If so, we will just silently fail.
+                if (mBadProcesses.get(info.processName, info.uid) != null) {
+                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+                            + "/" + info.processName);
+                    return null;
+                }
+            } else {
+                // When the user is explicitly starting a process, then clear its
+                // crash count so that we won't make it bad until they see at
+                // least one crash dialog again, and make the process good again
+                // if it had been bad.
+                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
                         + "/" + info.processName);
-                return null;
-            }
-        } else {
-            // When the user is explicitly starting a process, then clear its
-            // crash count so that we won't make it bad until they see at
-            // least one crash dialog again, and make the process good again
-            // if it had been bad.
-            if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
-                    + "/" + info.processName);
-            mProcessCrashTimes.remove(info.processName, info.uid);
-            if (mBadProcesses.get(info.processName, info.uid) != null) {
-                EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid,
-                        info.processName);
-                mBadProcesses.remove(info.processName, info.uid);
-                if (app != null) {
-                    app.bad = false;
+                mProcessCrashTimes.remove(info.processName, info.uid);
+                if (mBadProcesses.get(info.processName, info.uid) != null) {
+                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid,
+                            info.processName);
+                    mBadProcesses.remove(info.processName, info.uid);
+                    if (app != null) {
+                        app.bad = false;
+                    }
                 }
             }
         }
-        
+
         if (app == null) {
-            app = newProcessRecordLocked(null, info, processName);
-            mProcessNames.put(processName, info.uid, app);
+            app = newProcessRecordLocked(null, info, processName, isolated);
+            if (app == null) {
+                Slog.w(TAG, "Failed making new process record for "
+                        + processName + "/" + info.uid + " isolated=" + isolated);
+                return null;
+            }
+            mProcessNames.put(processName, app.uid, app);
+            if (isolated) {
+                mIsolatedProcesses.put(app.uid, app);
+            }
         } else {
             // If this is a new package in the process, add the package to the list
             app.addPackage(info.packageName);
@@ -2725,14 +1940,16 @@
         mProcDeaths[0] = 0;
         
         try {
-            int uid = app.info.uid;
+            int uid = app.uid;
 
             int[] gids = null;
-            try {
-                gids = mContext.getPackageManager().getPackageGids(
-                        app.info.packageName);
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.w(TAG, "Unable to retrieve gids", e);
+            if (!app.isolated) {
+                try {
+                    gids = mContext.getPackageManager().getPackageGids(
+                            app.info.packageName);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Slog.w(TAG, "Unable to retrieve gids", e);
+                }
             }
             if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) {
                 if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
@@ -2839,6 +2056,13 @@
     }
 
     boolean startHomeActivityLocked(int userId) {
+        if (mHeadless) {
+            // Added because none of the other calls to ensureBootCompleted seem to fire
+            // when running headless.
+            ensureBootCompleted();
+            return false;
+        }
+
         if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
                 && mTopAction == null) {
             // We are running in factory test mode, but unable to find
@@ -2933,37 +2157,52 @@
         return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
     }
 
+    void enforceNotIsolatedCaller(String caller) {
+        if (UserId.isIsolated(Binder.getCallingUid())) {
+            throw new SecurityException("Isolated process not allowed to call " + caller);
+        }
+    }
+
     public int getFrontActivityScreenCompatMode() {
+        enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
         synchronized (this) {
             return mCompatModePackages.getFrontActivityScreenCompatModeLocked();
         }
     }
 
     public void setFrontActivityScreenCompatMode(int mode) {
+        enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setFrontActivityScreenCompatMode");
         synchronized (this) {
             mCompatModePackages.setFrontActivityScreenCompatModeLocked(mode);
         }
     }
 
     public int getPackageScreenCompatMode(String packageName) {
+        enforceNotIsolatedCaller("getPackageScreenCompatMode");
         synchronized (this) {
             return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
         }
     }
 
     public void setPackageScreenCompatMode(String packageName, int mode) {
+        enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setPackageScreenCompatMode");
         synchronized (this) {
             mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
         }
     }
 
     public boolean getPackageAskScreenCompat(String packageName) {
+        enforceNotIsolatedCaller("getPackageAskScreenCompat");
         synchronized (this) {
             return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
         }
     }
 
     public void setPackageAskScreenCompat(String packageName, boolean ask) {
+        enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setPackageAskScreenCompat");
         synchronized (this) {
             mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
         }
@@ -2974,19 +2213,6 @@
         
         final int identHash = System.identityHashCode(r);
         updateUsageStats(r, true);
-        
-        int i = mWatchers.beginBroadcast();
-        while (i > 0) {
-            i--;
-            IActivityWatcher w = mWatchers.getBroadcastItem(i);
-            if (w != null) {
-                try {
-                    w.activityResuming(identHash);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-        mWatchers.finishBroadcast();
     }
 
     private void dispatchForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
@@ -3038,6 +2264,7 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
             String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+        enforceNotIsolatedCaller("startActivity");
         int userId = 0;
         if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
             // Requesting home, set the identity to the current user
@@ -3062,6 +2289,7 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
             String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+        enforceNotIsolatedCaller("startActivityAndWait");
         WaitResult res = new WaitResult();
         int userId = Binder.getOrigCallingUser();
         mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
@@ -3076,6 +2304,7 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug, Configuration config) {
+        enforceNotIsolatedCaller("startActivityWithConfig");
         int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded,
@@ -3087,6 +2316,7 @@
             IntentSender intent, Intent fillInIntent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode,
             int flagsMask, int flagsValues) {
+        enforceNotIsolatedCaller("startActivityIntentSender");
         // Refuse possible leaked file descriptors
         if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -3228,6 +2458,7 @@
 
     public final int startActivities(IApplicationThread caller,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+        enforceNotIsolatedCaller("startActivities");
         int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
                 Binder.getOrigCallingUser());
         return ret;
@@ -3404,7 +2635,7 @@
             synchronized (mPidsSelfLocked) {
                 for (int i=0; i<mPidsSelfLocked.size(); i++) {
                     ProcessRecord p = mPidsSelfLocked.valueAt(i);
-                    if (p.info.uid != uid) {
+                    if (p.uid != uid) {
                         continue;
                     }
                     if (p.pid == initialPid) {
@@ -3803,21 +3034,6 @@
         }
     }
 
-    private final class AppNotResponding implements Runnable {
-        private final ProcessRecord mApp;
-        private final String mAnnotation;
-
-        public AppNotResponding(ProcessRecord app, String annotation) {
-            mApp = app;
-            mAnnotation = annotation;
-        }
-
-        @Override
-        public void run() {
-            appNotResponding(mApp, null, null, mAnnotation);
-        }
-    }
-
     final void appNotResponding(ProcessRecord app, ActivityRecord activity,
             ActivityRecord parent, final String annotation) {
         ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
@@ -3995,6 +3211,7 @@
     
     public boolean clearApplicationUserData(final String packageName,
             final IPackageDataObserver observer, final int userId) {
+        enforceNotIsolatedCaller("clearApplicationUserData");
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
         long callingId = Binder.clearCallingIdentity();
@@ -4183,6 +3400,7 @@
     }
 
     public void closeSystemDialogs(String reason) {
+        enforceNotIsolatedCaller("closeSystemDialogs");
         Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         if (reason != null) {
@@ -4192,22 +3410,9 @@
         final int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         synchronized (this) {
-            int i = mWatchers.beginBroadcast();
-            while (i > 0) {
-                i--;
-                IActivityWatcher w = mWatchers.getBroadcastItem(i);
-                if (w != null) {
-                    try {
-                        w.closingSystemDialogs(reason);
-                    } catch (RemoteException e) {
-                    }
-                }
-            }
-            mWatchers.finishBroadcast();
-            
             mWindowManager.closeSystemDialogs(reason);
             
-            for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+            for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
                 ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
                     r.stack.finishActivityLocked(r, i,
@@ -4223,6 +3428,7 @@
     
     public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
             throws RemoteException {
+        enforceNotIsolatedCaller("getProcessMemoryInfo");
         Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
         for (int i=pids.length-1; i>=0; i--) {
             infos[i] = new Debug.MemoryInfo();
@@ -4232,6 +3438,7 @@
     }
 
     public long[] getProcessPss(int[] pids) throws RemoteException {
+        enforceNotIsolatedCaller("getProcessPss");
         long[] pss = new long[pids.length];
         for (int i=pids.length-1; i>=0; i--) {
             pss[i] = Debug.getPss(pids[i]);
@@ -4394,6 +3601,7 @@
                     service.app.removed = true;
                 }
                 service.app = null;
+                service.isolatedProc = null;
                 services.add(service);
             }
         }
@@ -4404,7 +3612,7 @@
         }
 
         ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
-        for (ContentProviderRecord provider : mProvidersByClass.values()) {
+        for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(-1).values()) {
             if (provider.info.packageName.equals(name)
                     && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
                 if (!doit) {
@@ -4439,12 +3647,13 @@
     private final boolean removeProcessLocked(ProcessRecord app,
             boolean callerWillRestart, boolean allowRestart, String reason) {
         final String name = app.processName;
-        final int uid = app.info.uid;
+        final int uid = app.uid;
         if (DEBUG_PROCESSES) Slog.d(
             TAG, "Force removing proc " + app.toShortString() + " (" + name
             + "/" + uid + ")");
 
         mProcessNames.remove(name, uid);
+        mIsolatedProcesses.remove(app.uid);
         if (mHeavyWeightProcess == app) {
             mHeavyWeightProcess = null;
             mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
@@ -4461,9 +3670,9 @@
             mLruProcesses.remove(app);
             Process.killProcessQuiet(pid);
             
-            if (app.persistent) {
+            if (app.persistent && !app.isolated) {
                 if (!callerWillRestart) {
-                    addAppLocked(app.info);
+                    addAppLocked(app.info, false);
                 } else {
                     needRestart = true;
                 }
@@ -4488,9 +3697,10 @@
         
         if (gone) {
             Slog.w(TAG, "Process " + app + " failed to attach");
-            EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.info.uid,
+            EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.uid,
                     app.processName);
-            mProcessNames.remove(app.processName, app.info.uid);
+            mProcessNames.remove(app.processName, app.uid);
+            mIsolatedProcesses.remove(app.uid);
             if (mHeavyWeightProcess == app) {
                 mHeavyWeightProcess = null;
                 mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
@@ -4500,9 +3710,11 @@
             // Take care of any services that are waiting for the process.
             for (int i=0; i<mPendingServices.size(); i++) {
                 ServiceRecord sr = mPendingServices.get(i);
-                if (app.info.uid == sr.appInfo.uid
-                        && app.processName.equals(sr.processName)) {
+                if ((app.uid == sr.appInfo.uid
+                        && app.processName.equals(sr.processName))
+                        || sr.isolatedProc == app) {
                     Slog.w(TAG, "Forcing bringing down service: " + sr);
+                    sr.isolatedProc = null;
                     mPendingServices.remove(i);
                     i--;
                     bringDownServiceLocked(sr, true);
@@ -4683,10 +3895,12 @@
         // See if the top visible activity is waiting to run in this process...
         ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
         if (hr != null && normalMode) {
-            if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
+            if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                     && processName.equals(hr.processName)) {
                 try {
-                    if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
+                    if (mHeadless) {
+                        Slog.e(TAG, "Starting activities not supported on headless device: " + hr);
+                    } else if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
                         didSomething = true;
                     }
                 } catch (Exception e) {
@@ -4705,8 +3919,8 @@
             try {
                 for (int i=0; i<mPendingServices.size(); i++) {
                     sr = mPendingServices.get(i);
-                    if (app.info.uid != sr.appInfo.uid
-                            || !processName.equals(sr.processName)) {
+                    if (app != sr.isolatedProc && (app.uid != sr.appInfo.uid
+                            || !processName.equals(sr.processName))) {
                         continue;
                     }
 
@@ -4733,7 +3947,7 @@
         }
 
         // Check whether the next backup agent is in this process...
-        if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) {
+        if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {
             if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app);
             ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
             try {
@@ -4795,10 +4009,12 @@
     }
 
     public void showBootMessage(final CharSequence msg, final boolean always) {
+        enforceNotIsolatedCaller("showBootMessage");
         mWindowManager.showBootMessage(msg, always);
     }
 
     public void dismissKeyguardOnNextActivity() {
+        enforceNotIsolatedCaller("dismissKeyguardOnNextActivity");
         synchronized (this) {
             mMainStack.dismissKeyguardOnNextActivityLocked();
         }
@@ -4961,6 +4177,7 @@
     public IIntentSender getIntentSender(int type,
             String packageName, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
+        enforceNotIsolatedCaller("getIntentSender");
         // Refuse possible leaked file descriptors
         if (intents != null) {
             if (intents.length < 1) {
@@ -5188,7 +4405,7 @@
             
             synchronized (mPidsSelfLocked) {
                 ProcessRecord pr = mPidsSelfLocked.get(pid);
-                if (pr == null) {
+                if (pr == null && isForeground) {
                     Slog.w(TAG, "setProcessForeground called on unknown pid: " + pid);
                     return;
                 }
@@ -5196,7 +4413,9 @@
                 if (oldToken != null) {
                     oldToken.token.unlinkToDeath(oldToken, 0);
                     mForegroundProcesses.remove(pid);
-                    pr.forcingToForeground = null;
+                    if (pr != null) {
+                        pr.forcingToForeground = null;
+                    }
                     changed = true;
                 }
                 if (isForeground && token != null) {
@@ -5261,6 +4480,10 @@
         if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
+        // Isolated processes don't get any permissions.
+        if (UserId.isIsolated(uid)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
         // If there is a uid that owns whatever is being accessed, it has
         // blanket access to it regardless of the permissions it requires.
         if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
@@ -5413,6 +4636,8 @@
     }
 
     public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+        enforceNotIsolatedCaller("checkUriPermission");
+
         // Another redirected-binder-call permissions check as in
         // {@link checkComponentPermission}.
         Identity tlsIdentity = sCallerIdentity.get();
@@ -5563,6 +4788,7 @@
 
     public int checkGrantUriPermission(int callingUid, String targetPkg,
             Uri uri, int modeFlags) {
+        enforceNotIsolatedCaller("checkGrantUriPermission");
         synchronized(this) {
             return checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
         }
@@ -5671,6 +4897,7 @@
 
     public void grantUriPermission(IApplicationThread caller, String targetPkg,
             Uri uri, int modeFlags) {
+        enforceNotIsolatedCaller("grantUriPermission");
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (r == null) {
@@ -5685,7 +4912,7 @@
                 throw new IllegalArgumentException("null uri");
             }
 
-            grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags,
+            grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags,
                     null);
         }
     }
@@ -5794,6 +5021,7 @@
 
     public void revokeUriPermission(IApplicationThread caller, Uri uri,
             int modeFlags) {
+        enforceNotIsolatedCaller("revokeUriPermission");
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (r == null) {
@@ -5816,8 +5044,7 @@
 
             final String authority = uri.getAuthority();
             ProviderInfo pi = null;
-            ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
-                    UserId.getUserId(r.info.uid));
+            ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, r.userId);
             if (cpr != null) {
                 pi = cpr.info;
             } else {
@@ -5833,12 +5060,13 @@
                 return;
             }
 
-            revokeUriPermissionLocked(r.info.uid, uri, modeFlags);
+            revokeUriPermissionLocked(r.uid, uri, modeFlags);
         }
     }
 
     @Override
     public IBinder newUriPermissionOwner(String name) {
+        enforceNotIsolatedCaller("newUriPermissionOwner");
         synchronized(this) {
             UriPermissionOwner owner = new UriPermissionOwner(this, name);
             return owner.getExternalTokenLocked();
@@ -6375,6 +5603,7 @@
      * @return Returns true if the move completed, false if not.
      */
     public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
+        enforceNotIsolatedCaller("moveActivityTaskToBack");
         synchronized(this) {
             final long origId = Binder.clearCallingIdentity();
             int taskId = getTaskForActivityLocked(token, !nonRoot);
@@ -6428,29 +5657,6 @@
         return -1;
     }
 
-    public void finishOtherInstances(IBinder token, ComponentName className) {
-        synchronized(this) {
-            final long origId = Binder.clearCallingIdentity();
-
-            int N = mMainStack.mHistory.size();
-            TaskRecord lastTask = null;
-            for (int i=0; i<N; i++) {
-                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
-                if (r.realActivity.equals(className)
-                        && r.appToken != token && lastTask != r.task) {
-                    if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
-                            null, "others")) {
-                        i--;
-                        N--;
-                    }
-                }
-                lastTask = r.task;
-            }
-
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
     // =========================================================
     // THUMBNAILS
     // =========================================================
@@ -6537,13 +5743,13 @@
         List<ProviderInfo> providers = null;
         try {
             providers = AppGlobals.getPackageManager().
-                queryContentProviders(app.processName, app.info.uid,
+                queryContentProviders(app.processName, app.uid,
                         STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
         } catch (RemoteException ex) {
         }
         if (DEBUG_MU)
-            Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.info.uid);
-        int userId = UserId.getUserId(app.info.uid);
+            Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
+        int userId = app.userId;
         if (providers != null) {
             final int N = providers.size();
             for (int i=0; i<N; i++) {
@@ -6553,7 +5759,7 @@
                 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                 ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
                 if (cpr == null) {
-                    cpr = new ContentProviderRecord(cpi, app.info, comp);
+                    cpr = new ContentProviderRecord(this, cpi, app.info, comp);
                     mProviderMap.putProviderByClass(comp, cpr);
                 }
                 if (DEBUG_MU)
@@ -6569,7 +5775,7 @@
     private final String checkContentProviderPermissionLocked(
             ProviderInfo cpi, ProcessRecord r) {
         final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
-        final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
+        final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.applicationInfo.uid, cpi.exported)
                 == PackageManager.PERMISSION_GRANTED) {
@@ -6625,7 +5831,8 @@
         return msg;
     }
 
-    boolean incProviderCount(ProcessRecord r, ContentProviderRecord cpr) {
+    boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
+            IBinder externalProcessToken) {
         if (r != null) {
             Integer cnt = r.conProviders.get(cpr);
             if (DEBUG_PROVIDER) Slog.v(TAG,
@@ -6641,12 +5848,13 @@
                 r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
             }
         } else {
-            cpr.externals++;
+            cpr.addExternalProcessHandleLocked(externalProcessToken);
         }
         return false;
     }
 
-    boolean decProviderCount(ProcessRecord r, ContentProviderRecord cpr) {
+    boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
+            IBinder externalProcessToken) {
         if (r != null) {
             Integer cnt = r.conProviders.get(cpr);
             if (DEBUG_PROVIDER) Slog.v(TAG,
@@ -6662,13 +5870,13 @@
                 r.conProviders.put(cpr, new Integer(cnt.intValue()-1));
             }
         } else {
-            cpr.externals++;
+            cpr.removeExternalProcessHandleLocked(externalProcessToken);
         }
         return false;
     }
 
     private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
-            String name) {
+            String name, IBinder token) {
         ContentProviderRecord cpr;
         ProviderInfo cpi = null;
 
@@ -6685,7 +5893,7 @@
             }
 
             // First check if this content provider has been published...
-            int userId = UserId.getUserId(r != null ? r.info.uid : Binder.getCallingUid());
+            int userId = UserId.getUserId(r != null ? r.uid : Binder.getCallingUid());
             cpr = mProviderMap.getProviderByName(name, userId);
             boolean providerRunning = cpr != null;
             if (providerRunning) {
@@ -6712,7 +5920,7 @@
 
                 // In this case the provider instance already exists, so we can
                 // return it right away.
-                final boolean countChanged = incProviderCount(r, cpr);
+                final boolean countChanged = incProviderCount(r, cpr, token);
                 if (countChanged) {
                     if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                         // If this is a perceptible app accessing the provider,
@@ -6746,7 +5954,7 @@
                         Slog.i(TAG,
                                 "Existing provider " + cpr.name.flattenToShortString()
                                 + " is crashing; detaching " + r);
-                        boolean lastRef = decProviderCount(r, cpr);
+                        boolean lastRef = decProviderCount(r, cpr, token);
                         appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread);
                         if (!lastRef) {
                             // This wasn't the last ref our process had on
@@ -6804,7 +6012,7 @@
                             return null;
                         }
                         ai = getAppInfoForUser(ai, Binder.getOrigCallingUser());
-                        cpr = new ContentProviderRecord(cpi, ai, comp);
+                        cpr = new ContentProviderRecord(this, cpi, ai, comp);
                     } catch (RemoteException ex) {
                         // pm is in same process, this will never happen.
                     }
@@ -6820,7 +6028,7 @@
 
                 if (DEBUG_PROVIDER) {
                     RuntimeException e = new RuntimeException("here");
-                    Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid
+                    Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.uid
                           + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
                 }
 
@@ -6854,7 +6062,7 @@
                         ProcessRecord proc = startProcessLocked(cpi.processName,
                                 cpr.appInfo, false, 0, "content provider",
                                 new ComponentName(cpi.applicationInfo.packageName,
-                                        cpi.name), false);
+                                        cpi.name), false, false);
                         if (proc == null) {
                             Slog.w(TAG, "Unable to launch app "
                                     + cpi.applicationInfo.packageName + "/"
@@ -6874,8 +6082,9 @@
                 if (firstClass) {
                     mProviderMap.putProviderByClass(comp, cpr);
                 }
+
                 mProviderMap.putProviderByName(name, cpr);
-                incProviderCount(r, cpr);
+                incProviderCount(r, cpr, token);
             }
         }
 
@@ -6907,6 +6116,7 @@
 
     public final ContentProviderHolder getContentProvider(
             IApplicationThread caller, String name) {
+        enforceNotIsolatedCaller("getContentProvider");
         if (caller == null) {
             String msg = "null IApplicationThread when getting content provider "
                     + name;
@@ -6914,12 +6124,17 @@
             throw new SecurityException(msg);
         }
 
-        ContentProviderHolder contentProvider = getContentProviderImpl(caller, name);
-        return contentProvider;
+        return getContentProviderImpl(caller, name, null);
     }
 
-    private ContentProviderHolder getContentProviderExternal(String name) {
-        return getContentProviderImpl(null, name);
+    public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
+        enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
+            "Do not have permission in call getContentProviderExternal()");
+        return getContentProviderExternalUnchecked(name, token);
+    }
+
+    private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
+        return getContentProviderImpl(null, name, token);
     }
 
     /**
@@ -6927,6 +6142,7 @@
      * @param cpr
      */
     public void removeContentProvider(IApplicationThread caller, String name) {
+        enforceNotIsolatedCaller("removeContentProvider");
         synchronized (this) {
             int userId = UserId.getUserId(Binder.getCallingUid());
             ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
@@ -6954,14 +6170,20 @@
                         + cpr.info.name + " in process " + r.processName);
                 return;
             } else {
-                if (decProviderCount(r, localCpr)) {
+                if (decProviderCount(r, localCpr, null)) {
                     updateOomAdjLocked();
                 }
             }
         }
     }
 
-    private void removeContentProviderExternal(String name) {
+    public void removeContentProviderExternal(String name, IBinder token) {
+        enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
+            "Do not have permission in call removeContentProviderExternal()");
+        removeContentProviderExternalUnchecked(name, token);
+    }
+
+    private void removeContentProviderExternalUnchecked(String name, IBinder token) {
         synchronized (this) {
             ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
                     Binder.getOrigCallingUser());
@@ -6975,11 +6197,18 @@
             ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
             ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
                     Binder.getOrigCallingUser());
-            localCpr.externals--;
-            if (localCpr.externals < 0) {
-                Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
+            if (localCpr.hasExternalProcessHandles()) {
+                if (localCpr.removeExternalProcessHandleLocked(token)) {
+                    updateOomAdjLocked();
+                } else {
+                    Slog.e(TAG, "Attmpt to remove content provider " + localCpr
+                            + " with no external reference for token: "
+                            + token + ".");
+                }
+            } else {
+                Slog.e(TAG, "Attmpt to remove content provider: " + localCpr
+                        + " with no external references.");
             }
-            updateOomAdjLocked();
         }
     }
     
@@ -6989,10 +6218,11 @@
             return;
         }
 
+        enforceNotIsolatedCaller("publishContentProviders");
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (DEBUG_MU)
-                Slog.v(TAG_MU, "ProcessRecord uid = " + r.info.uid);
+                Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
             if (r == null) {
                 throw new SecurityException(
                         "Unable to find app for caller " + caller
@@ -7076,12 +6306,13 @@
      *     src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
      */
     public String getProviderMimeType(Uri uri) {
+        enforceNotIsolatedCaller("getProviderMimeType");
         final String name = uri.getAuthority();
         final long ident = Binder.clearCallingIdentity();
         ContentProviderHolder holder = null;
 
         try {
-            holder = getContentProviderExternal(name);
+            holder = getContentProviderExternalUnchecked(name, null);
             if (holder != null) {
                 return holder.provider.getType(uri);
             }
@@ -7090,7 +6321,7 @@
             return null;
         } finally {
             if (holder != null) {
-                removeContentProviderExternal(name);
+                removeContentProviderExternalUnchecked(name, null);
             }
             Binder.restoreCallingIdentity(ident);
         }
@@ -7103,22 +6334,52 @@
     // =========================================================
 
     final ProcessRecord newProcessRecordLocked(IApplicationThread thread,
-            ApplicationInfo info, String customProcess) {
+            ApplicationInfo info, String customProcess, boolean isolated) {
         String proc = customProcess != null ? customProcess : info.processName;
         BatteryStatsImpl.Uid.Proc ps = null;
         BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        int uid = info.uid;
+        if (isolated) {
+            int userId = UserId.getUserId(uid);
+            int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
+            uid = 0;
+            while (true) {
+                if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
+                        || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
+                    mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
+                }
+                uid = UserId.getUid(userId, mNextIsolatedProcessUid);
+                mNextIsolatedProcessUid++;
+                if (mIsolatedProcesses.indexOfKey(uid) < 0) {
+                    // No process for this uid, use it.
+                    break;
+                }
+                stepsLeft--;
+                if (stepsLeft <= 0) {
+                    return null;
+                }
+            }
+        }
         synchronized (stats) {
             ps = stats.getProcessStatsLocked(info.uid, proc);
         }
-        return new ProcessRecord(ps, thread, info, proc);
+        return new ProcessRecord(ps, thread, info, proc, uid);
     }
 
-    final ProcessRecord addAppLocked(ApplicationInfo info) {
-        ProcessRecord app = getProcessRecordLocked(info.processName, info.uid);
+    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
+        ProcessRecord app;
+        if (!isolated) {
+            app = getProcessRecordLocked(info.processName, info.uid);
+        } else {
+            app = null;
+        }
 
         if (app == null) {
-            app = newProcessRecordLocked(null, info, null);
-            mProcessNames.put(info.processName, info.uid, app);
+            app = newProcessRecordLocked(null, info, null, isolated);
+            mProcessNames.put(info.processName, app.uid, app);
+            if (isolated) {
+                mIsolatedProcesses.put(app.uid, app);
+            }
             updateLruProcessLocked(app, true, true);
         }
 
@@ -7163,8 +6424,9 @@
     }
 
     public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException {
+        enforceNotIsolatedCaller("openContentUri");
         String name = uri.getAuthority();
-        ContentProviderHolder cph = getContentProviderExternal(name);
+        ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null);
         ParcelFileDescriptor pfd = null;
         if (cph != null) {
             // We record the binder invoker's uid in thread-local storage before
@@ -7186,7 +6448,7 @@
             }
 
             // We've got the fd now, so we're done with the provider.
-            removeContentProviderExternal(name);
+            removeContentProviderExternalUnchecked(name, null);
         } else {
             Slog.d(TAG, "Failed to get provider for authority '" + name + "'");
         }
@@ -7415,24 +6677,18 @@
         }
     }
     
-    public void registerActivityWatcher(IActivityWatcher watcher) {
-        synchronized (this) {
-            mWatchers.register(watcher);
-        }
-    }
-
-    public void unregisterActivityWatcher(IActivityWatcher watcher) {
-        synchronized (this) {
-            mWatchers.unregister(watcher);
-        }
-    }
-
     public void registerProcessObserver(IProcessObserver observer) {
-        mProcessObservers.register(observer);
+        enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+                "registerProcessObserver()");
+        synchronized (this) {
+            mProcessObservers.register(observer);
+        }
     }
 
     public void unregisterProcessObserver(IProcessObserver observer) {
-        mProcessObservers.unregister(observer);
+        synchronized (this) {
+            mProcessObservers.unregister(observer);
+        }
     }
 
     public void setImmersive(IBinder token, boolean immersive) {
@@ -7456,6 +6712,7 @@
     }
 
     public boolean isTopActivityImmersive() {
+        enforceNotIsolatedCaller("startActivity");
         synchronized (this) {
             ActivityRecord r = mMainStack.topRunningActivityLocked(null);
             return (r != null) ? r.immersive : false;
@@ -7764,7 +7021,7 @@
                         /* TODO: Send this to all users */
                         broadcastIntentLocked(null, null, intent, null, finisher,
                                 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
-                                Process.SYSTEM_UID);
+                                0 /* UserId zero */);
                         if (finisher != null) {
                             mWaitingUpdate = true;
                         }
@@ -7867,7 +7124,7 @@
                                 = (ApplicationInfo)apps.get(i);
                             if (info != null &&
                                     !info.packageName.equals("android")) {
-                                addAppLocked(info);
+                                addAppLocked(info, false);
                             }
                         }
                     }
@@ -7966,14 +7223,18 @@
     private boolean handleAppCrashLocked(ProcessRecord app) {
         long now = SystemClock.uptimeMillis();
 
-        Long crashTime = mProcessCrashTimes.get(app.info.processName,
-                app.info.uid);
+        Long crashTime;
+        if (!app.isolated) {
+            crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
+        } else {
+            crashTime = null;
+        }
         if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
             // This process loses!
             Slog.w(TAG, "Process " + app.info.processName
                     + " has crashed too many times: killing!");
             EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
-                    app.info.processName, app.info.uid);
+                    app.info.processName, app.uid);
             for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
                 ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if (r.app == app) {
@@ -7987,11 +7248,15 @@
                 // explicitly does so...  but for persistent process, we really
                 // need to keep it running.  If a persistent process is actually
                 // repeatedly crashing, then badness for everyone.
-                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.info.uid,
+                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.uid,
                         app.info.processName);
-                mBadProcesses.put(app.info.processName, app.info.uid, now);
+                if (!app.isolated) {
+                    // XXX We don't have a way to mark isolated processes
+                    // as bad, since they don't have a peristent identity.
+                    mBadProcesses.put(app.info.processName, app.uid, now);
+                    mProcessCrashTimes.remove(app.info.processName, app.uid);
+                }
                 app.bad = true;
-                mProcessCrashTimes.remove(app.info.processName, app.info.uid);
                 app.removed = true;
                 // Don't let services in this process be restarted and potentially
                 // annoy the user repeatedly.  Unless it is persistent, since those
@@ -8063,7 +7328,12 @@
             }
         }
 
-        mProcessCrashTimes.put(app.info.processName, app.info.uid, now);
+        if (!app.isolated) {
+            // XXX Can't keep track of crash times for isolated processes,
+            // because they don't have a perisistent identity.
+            mProcessCrashTimes.put(app.info.processName, app.uid, now);
+        }
+
         return true;
     }
 
@@ -8561,8 +7831,10 @@
 
         Intent appErrorIntent = null;
         synchronized (this) {
-            if (r != null) {
-                mProcessCrashTimes.put(r.info.processName, r.info.uid,
+            if (r != null && !r.isolated) {
+                // XXX Can't keep track of crash time for isolated processes,
+                // since they don't have a persistent identity.
+                mProcessCrashTimes.put(r.info.processName, r.uid,
                         SystemClock.uptimeMillis());
             }
             if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
@@ -8625,6 +7897,7 @@
     }
 
     public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
+        enforceNotIsolatedCaller("getProcessesInErrorState");
         // assume our apps are happy - lazy create the list
         List<ActivityManager.ProcessErrorStateInfo> errList = null;
 
@@ -8687,6 +7960,7 @@
     }
 
     public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
+        enforceNotIsolatedCaller("getRunningAppProcesses");
         // Lazy instantiation of list
         List<ActivityManager.RunningAppProcessInfo> runList = null;
         synchronized (this) {
@@ -8732,6 +8006,7 @@
     }
 
     public List<ApplicationInfo> getRunningExternalApplications() {
+        enforceNotIsolatedCaller("getRunningExternalApplications");
         List<ActivityManager.RunningAppProcessInfo> runningApps = getRunningAppProcesses();
         List<ApplicationInfo> retList = new ArrayList<ApplicationInfo>();
         if (runningApps != null && runningApps.size() > 0) {
@@ -9095,7 +8370,21 @@
                 }
             }
         }
-        
+
+        if (mIsolatedProcesses.size() > 0) {
+            if (needSep) pw.println(" ");
+            needSep = true;
+            pw.println("  Isolated process list (sorted by uid):");
+            for (int i=0; i<mIsolatedProcesses.size(); i++) {
+                ProcessRecord r = mIsolatedProcesses.valueAt(i);
+                if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+                    continue;
+                }
+                pw.println(String.format("%sIsolated #%2d: %s",
+                        "    ", i, r.toString()));
+            }
+        }
+
         if (mLruProcesses.size() > 0) {
             if (needSep) pw.println(" ");
             needSep = true;
@@ -9500,98 +8789,7 @@
      */
     protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
             int opti, boolean dumpAll) {
-        ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
-
-        if ("all".equals(name)) {
-            synchronized (this) {
-                for (ContentProviderRecord r1 : mProvidersByClass.values()) {
-                    providers.add(r1);
-                }
-            }
-        } else {
-            ComponentName componentName = name != null
-                    ? ComponentName.unflattenFromString(name) : null;
-            int objectId = 0;
-            if (componentName == null) {
-                // Not a '/' separated full component name; maybe an object ID?
-                try {
-                    objectId = Integer.parseInt(name, 16);
-                    name = null;
-                    componentName = null;
-                } catch (RuntimeException e) {
-                }
-            }
-
-            synchronized (this) {
-                for (ContentProviderRecord r1 : mProvidersByClass.values()) {
-                    if (componentName != null) {
-                        if (r1.name.equals(componentName)) {
-                            providers.add(r1);
-                        }
-                    } else if (name != null) {
-                        if (r1.name.flattenToString().contains(name)) {
-                            providers.add(r1);
-                        }
-                    } else if (System.identityHashCode(r1) == objectId) {
-                        providers.add(r1);
-                    }
-                }
-            }
-        }
-
-        if (providers.size() <= 0) {
-            return false;
-        }
-
-        boolean needSep = false;
-        for (int i=0; i<providers.size(); i++) {
-            if (needSep) {
-                pw.println();
-            }
-            needSep = true;
-            dumpProvider("", fd, pw, providers.get(i), args, dumpAll);
-        }
-        return true;
-    }
-
-    /**
-     * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider if
-     * there is a thread associated with the provider.
-     */
-    private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
-            final ContentProviderRecord r, String[] args, boolean dumpAll) {
-        String innerPrefix = prefix + "  ";
-        synchronized (this) {
-            pw.print(prefix); pw.print("PROVIDER ");
-                    pw.print(r);
-                    pw.print(" pid=");
-                    if (r.proc != null) pw.println(r.proc.pid);
-                    else pw.println("(not running)");
-            if (dumpAll) {
-                r.dump(pw, innerPrefix);
-            }
-        }
-        if (r.proc != null && r.proc.thread != null) {
-            pw.println("    Client:");
-            pw.flush();
-            try {
-                TransferPipe tp = new TransferPipe();
-                try {
-                    r.proc.thread.dumpProvider(
-                            tp.getWriteFd().getFileDescriptor(), r.provider.asBinder(), args);
-                    tp.setBufferPrefix("      ");
-                    // Short timeout, since blocking here can
-                    // deadlock with the application.
-                    tp.go(fd, 2000);
-                } finally {
-                    tp.kill();
-                }
-            } catch (IOException ex) {
-                pw.println("      Failure while dumping the provider: " + ex);
-            } catch (RemoteException ex) {
-                pw.println("      Got a RemoteException while dumping the service");
-            }
-        }
+        return mProviderMap.dumpProvider(fd, pw, name, args, opti, dumpAll);
     }
 
     static class ItemMatcher {
@@ -10880,6 +10078,7 @@
                     sr.stats.stopLaunchedLocked();
                 }
                 sr.app = null;
+                sr.isolatedProc = null;
                 sr.executeNesting = 0;
                 if (mStoppingServices.remove(sr)) {
                     if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
@@ -11075,7 +10274,7 @@
             for (int i=0; i<NL; i++) {
                 ContentProviderRecord cpr = (ContentProviderRecord)
                         mLaunchingProviders.get(i);
-                if (cpr.clients.size() <= 0 && cpr.externals <= 0) {
+                if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) {
                     synchronized (cpr) {
                         cpr.launchingApp = null;
                         cpr.notifyAll();
@@ -11115,10 +10314,11 @@
             return;
         }
 
-        if (!app.persistent) {
+        if (!app.persistent || app.isolated) {
             if (DEBUG_PROCESSES) Slog.v(TAG,
                     "Removing non-persistent process during cleanup: " + app);
-            mProcessNames.remove(app.processName, app.info.uid);
+            mProcessNames.remove(app.processName, app.uid);
+            mIsolatedProcesses.remove(app.uid);
             if (mHeavyWeightProcess == app) {
                 mHeavyWeightProcess = null;
                 mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
@@ -11143,10 +10343,10 @@
             mPreviousProcess = null;
         }
 
-        if (restart) {
+        if (restart && !app.isolated) {
             // We have components that still need to be running in the
             // process, so re-launch it.
-            mProcessNames.put(app.processName, app.info.uid, app);
+            mProcessNames.put(app.processName, app.uid, app);
             startProcessLocked(app, "restart", app.processName);
         } else if (app.pid > 0 && app.pid != MY_PID) {
             // Goodbye!
@@ -11226,6 +10426,7 @@
     
     public List<ActivityManager.RunningServiceInfo> getServices(int maxNum,
             int flags) {
+        enforceNotIsolatedCaller("getServices");
         synchronized (this) {
             ArrayList<ActivityManager.RunningServiceInfo> res
                     = new ArrayList<ActivityManager.RunningServiceInfo>();
@@ -11252,6 +10453,7 @@
     }
 
     public PendingIntent getRunningServiceControlPanel(ComponentName name) {
+        enforceNotIsolatedCaller("getRunningServiceControlPanel");
         synchronized (this) {
             int userId = UserId.getUserId(Binder.getCallingUid());
             ServiceRecord r = mServiceMap.getServiceByName(name, userId);
@@ -11353,18 +10555,18 @@
     }
 
     private ServiceLookupResult retrieveServiceLocked(Intent service,
-            String resolvedType, int callingPid, int callingUid) {
+            String resolvedType, int callingPid, int callingUid, int userId) {
         ServiceRecord r = null;
         if (DEBUG_SERVICE)
             Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType
-                    + " origCallingUid=" + callingUid);
+                    + " callingUid=" + callingUid);
 
         if (service.getComponent() != null) {
-            r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
+            r = mServiceMap.getServiceByName(service.getComponent(), userId);
         }
         if (r == null) {
             Intent.FilterComparison filter = new Intent.FilterComparison(service);
-            r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
+            r = mServiceMap.getServiceByIntent(filter, userId);
         }
         if (r == null) {
             try {
@@ -11378,13 +10580,12 @@
                           ": not found");
                     return null;
                 }
-                if (Binder.getOrigCallingUser() > 0) {
-                    sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo,
-                            Binder.getOrigCallingUser());
+                if (userId > 0) {
+                    sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo, userId);
                 }
                 ComponentName name = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
-                r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
+                r = mServiceMap.getServiceByName(name, userId);
                 if (r == null) {
                     Intent.FilterComparison filter = new Intent.FilterComparison(
                             service.cloneFilter());
@@ -11548,7 +10749,7 @@
         }
         if (DEBUG_MU)
             Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
-                    + ", ProcessRecord.uid = " + app.info.uid);
+                    + ", ProcessRecord.uid = " + app.uid);
         r.app = app;
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
@@ -11743,35 +10944,53 @@
                     + r.packageName + ": " + e);
         }
 
+        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
         final String appName = r.processName;
-        ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
-        if (DEBUG_MU)
-            Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
-        if (app != null && app.thread != null) {
-            try {
-                app.addPackage(r.appInfo.packageName);
-                realStartServiceLocked(r, app);
-                return true;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
-            }
+        ProcessRecord app;
 
-            // If a dead object exception was thrown -- fall through to
-            // restart the application.
+        if (!isolated) {
+            app = getProcessRecordLocked(appName, r.appInfo.uid);
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
+            if (app != null && app.thread != null) {
+                try {
+                    app.addPackage(r.appInfo.packageName);
+                    realStartServiceLocked(r, app);
+                    return true;
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
+                }
+
+                // If a dead object exception was thrown -- fall through to
+                // restart the application.
+            }
+        } else {
+            // If this service runs in an isolated process, then each time
+            // we call startProcessLocked() we will get a new isolated
+            // process, starting another process if we are currently waiting
+            // for a previous process to come up.  To deal with this, we store
+            // in the service any current isolated process it is running in or
+            // waiting to have come up.
+            app = r.isolatedProc;
         }
 
         // Not running -- get it started, and enqueue this service record
         // to be executed when the app comes up.
-        if (startProcessLocked(appName, r.appInfo, true, intentFlags,
-                "service", r.name, false) == null) {
-            Slog.w(TAG, "Unable to launch app "
-                    + r.appInfo.packageName + "/"
-                    + r.appInfo.uid + " for service "
-                    + r.intent.getIntent() + ": process is bad");
-            bringDownServiceLocked(r, true);
-            return false;
+        if (app == null) {
+            if ((app=startProcessLocked(appName, r.appInfo, true, intentFlags,
+                    "service", r.name, false, isolated)) == null) {
+                Slog.w(TAG, "Unable to launch app "
+                        + r.appInfo.packageName + "/"
+                        + r.appInfo.uid + " for service "
+                        + r.intent.getIntent() + ": process is bad");
+                bringDownServiceLocked(r, true);
+                return false;
+            }
+            if (isolated) {
+                r.isolatedProc = app;
+            }
         }
-        
+
         if (!mPendingServices.contains(r)) {
             mPendingServices.add(r);
         }
@@ -11930,7 +11149,7 @@
 
             ServiceLookupResult res =
                 retrieveServiceLocked(service, resolvedType,
-                        callingPid, callingUid);
+                        callingPid, callingUid, UserId.getUserId(callingUid));
             if (res == null) {
                 return null;
             }
@@ -11961,6 +11180,7 @@
 
     public ComponentName startService(IApplicationThread caller, Intent service,
             String resolvedType) {
+        enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -12003,6 +11223,7 @@
 
     public int stopService(IApplicationThread caller, Intent service,
             String resolvedType) {
+        enforceNotIsolatedCaller("stopService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -12040,6 +11261,7 @@
     }
 
     public IBinder peekService(Intent service, String resolvedType) {
+        enforceNotIsolatedCaller("peekService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -12177,12 +11399,15 @@
     
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
-            IServiceConnection connection, int flags) {
+            IServiceConnection connection, int flags, int userId) {
+        enforceNotIsolatedCaller("bindService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        checkValidCaller(Binder.getCallingUid(), userId);
+
         synchronized(this) {
             if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
                     + " type=" + resolvedType + " conn=" + connection.asBinder()
@@ -12232,7 +11457,7 @@
             
             ServiceLookupResult res =
                 retrieveServiceLocked(service, resolvedType,
-                        Binder.getCallingPid(), Binder.getOrigCallingUid());
+                        Binder.getCallingPid(), Binder.getCallingUid(), userId);
             if (res == null) {
                 return 0;
             }
@@ -12687,7 +11912,7 @@
                     : new ComponentName("android", "FullBackupAgent");
             // startProcessLocked() returns existing proc's record if it's already running
             ProcessRecord proc = startProcessLocked(app.processName, app,
-                    false, 0, "backup", hostingName, false);
+                    false, 0, "backup", hostingName, false, false);
             if (proc == null) {
                 Slog.e(TAG, "Unable to start backup agent process " + r);
                 return false;
@@ -12834,6 +12059,7 @@
 
     public Intent registerReceiver(IApplicationThread caller, String callerPackage,
             IIntentReceiver receiver, IntentFilter filter, String permission) {
+        enforceNotIsolatedCaller("registerReceiver");
         synchronized(this) {
             ProcessRecord callerApp = null;
             if (caller != null) {
@@ -13343,6 +12569,7 @@
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
             String requiredPermission, boolean serialized, boolean sticky, int userId) {
+        enforceNotIsolatedCaller("broadcastIntent");
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
             
@@ -13450,138 +12677,7 @@
             Binder.restoreCallingIdentity(origId);
         }
     }
-
-    private final void processCurBroadcastLocked(BroadcastRecord r,
-            ProcessRecord app) throws RemoteException {
-        if (DEBUG_BROADCAST)  Slog.v(TAG,
-                "Process cur broadcast " + r + " for app " + app);
-        if (app.thread == null) {
-            throw new RemoteException();
-        }
-        r.receiver = app.thread.asBinder();
-        r.curApp = app;
-        app.curReceiver = r;
-        updateLruProcessLocked(app, true, true);
-
-        // Tell the application to launch this receiver.
-        r.intent.setComponent(r.curComponent);
-
-        boolean started = false;
-        try {
-            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG,
-                    "Delivering to component " + r.curComponent
-                    + ": " + r);
-            ensurePackageDexOpt(r.intent.getComponent().getPackageName());
-            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
-                    compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
-                    r.resultCode, r.resultData, r.resultExtras, r.ordered);
-            if (DEBUG_BROADCAST)  Slog.v(TAG,
-                    "Process cur broadcast " + r + " DELIVERED for app " + app);
-            started = true;
-        } finally {
-            if (!started) {
-                if (DEBUG_BROADCAST)  Slog.v(TAG,
-                        "Process cur broadcast " + r + ": NOT STARTED!");
-                r.receiver = null;
-                r.curApp = null;
-                app.curReceiver = null;
-            }
-        }
-
-    }
-
-    static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
-            Intent intent, int resultCode, String data, Bundle extras,
-            boolean ordered, boolean sticky) throws RemoteException {
-        // Send the intent to the receiver asynchronously using one-way binder calls.
-        if (app != null && app.thread != null) {
-            // If we have an app thread, do the call through that so it is
-            // correctly ordered with other one-way calls.
-            app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
-                    data, extras, ordered, sticky);
-        } else {
-            receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
-        }
-    }
     
-    private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
-            BroadcastFilter filter, boolean ordered) {
-        boolean skip = false;
-        if (filter.requiredPermission != null) {
-            int perm = checkComponentPermission(filter.requiredPermission,
-                    r.callingPid, r.callingUid, -1, true);
-            if (perm != PackageManager.PERMISSION_GRANTED) {
-                Slog.w(TAG, "Permission Denial: broadcasting "
-                        + r.intent.toString()
-                        + " from " + r.callerPackage + " (pid="
-                        + r.callingPid + ", uid=" + r.callingUid + ")"
-                        + " requires " + filter.requiredPermission
-                        + " due to registered receiver " + filter);
-                skip = true;
-            }
-        }
-        if (r.requiredPermission != null) {
-            int perm = checkComponentPermission(r.requiredPermission,
-                    filter.receiverList.pid, filter.receiverList.uid, -1, true);
-            if (perm != PackageManager.PERMISSION_GRANTED) {
-                Slog.w(TAG, "Permission Denial: receiving "
-                        + r.intent.toString()
-                        + " to " + filter.receiverList.app
-                        + " (pid=" + filter.receiverList.pid
-                        + ", uid=" + filter.receiverList.uid + ")"
-                        + " requires " + r.requiredPermission
-                        + " due to sender " + r.callerPackage
-                        + " (uid " + r.callingUid + ")");
-                skip = true;
-            }
-        }
-
-        if (!skip) {
-            // If this is not being sent as an ordered broadcast, then we
-            // don't want to touch the fields that keep track of the current
-            // state of ordered broadcasts.
-            if (ordered) {
-                r.receiver = filter.receiverList.receiver.asBinder();
-                r.curFilter = filter;
-                filter.receiverList.curBroadcast = r;
-                r.state = BroadcastRecord.CALL_IN_RECEIVE;
-                if (filter.receiverList.app != null) {
-                    // Bump hosting application to no longer be in background
-                    // scheduling class.  Note that we can't do that if there
-                    // isn't an app...  but we can only be in that case for
-                    // things that directly call the IActivityManager API, which
-                    // are already core system stuff so don't matter for this.
-                    r.curApp = filter.receiverList.app;
-                    filter.receiverList.app.curReceiver = r;
-                    updateOomAdjLocked();
-                }
-            }
-            try {
-                if (DEBUG_BROADCAST_LIGHT) {
-                    int seq = r.intent.getIntExtra("seq", -1);
-                    Slog.i(TAG, "Delivering to " + filter
-                            + " (seq=" + seq + "): " + r);
-                }
-                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
-                    new Intent(r.intent), r.resultCode,
-                    r.resultData, r.resultExtras, r.ordered, r.initialSticky);
-                if (ordered) {
-                    r.state = BroadcastRecord.CALL_DONE_RECEIVE;
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
-                if (ordered) {
-                    r.receiver = null;
-                    r.curFilter = null;
-                    filter.receiverList.curBroadcast = null;
-                    if (filter.receiverList.app != null) {
-                        filter.receiverList.app.curReceiver = null;
-                    }
-                }
-            }
-        }
-    }
-
     // =========================================================
     // INSTRUMENTATION
     // =========================================================
@@ -13589,6 +12685,7 @@
     public boolean startInstrumentation(ComponentName className,
             String profileFile, int flags, Bundle arguments,
             IInstrumentationWatcher watcher) {
+        enforceNotIsolatedCaller("startInstrumentation");
         // Refuse possible leaked file descriptors
         if (arguments != null && arguments.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Bundle");
@@ -13632,7 +12729,7 @@
             final long origId = Binder.clearCallingIdentity();
             // Instrumentation can kill and relaunch even persistent processes
             forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true);
-            ProcessRecord app = addAppLocked(ai);
+            ProcessRecord app = addAppLocked(ai, false);
             app.instrumentationClass = className;
             app.instrumentationInfo = ai;
             app.instrumentationProfileFile = profileFile;
@@ -13786,8 +12883,11 @@
      * configuration.
      * @param persistent TODO
      */
-    public boolean updateConfigurationLocked(Configuration values,
+    boolean updateConfigurationLocked(Configuration values,
             ActivityRecord starting, boolean persistent, boolean initLocale) {
+        // do nothing if we are headless
+        if (mHeadless) return true;
+
         int changes = 0;
         
         boolean kept = true;
@@ -13817,6 +12917,10 @@
                 Slog.i(TAG, "Config changed: " + newConfig);
 
                 final Configuration configCopy = new Configuration(mConfiguration);
+                
+                // TODO: If our config changes, should we auto dismiss any currently
+                // showing dialogs?
+                mShowDialogs = shouldShowDialogs(newConfig);
 
                 AttributeCache ac = AttributeCache.instance();
                 if (ac != null) {
@@ -13883,6 +12987,19 @@
         
         return kept;
     }
+
+    /**
+     * Decide based on the configuration whether we should shouw the ANR,
+     * crash, etc dialogs.  The idea is that if there is no affordnace to
+     * press the on-screen buttons, we shouldn't show the dialog.
+     *
+     * A thought: SystemUI might also want to get told about this, the Power
+     * dialog / global actions also might want different behaviors.
+     */
+    private static final boolean shouldShowDialogs(Configuration config) {
+        return !(config.keyboard == Configuration.KEYBOARD_NOKEYS
+                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH);
+    }
     
     /**
      * Save the locale.  You must be inside a synchronized (this) block.
@@ -14359,7 +13476,7 @@
                 // If the provider has external (non-framework) process
                 // dependencies, ensure that its adjustment is at least
                 // FOREGROUND_APP_ADJ.
-                if (cpr.externals != 0) {
+                if (cpr.hasExternalProcessHandles()) {
                     if (adj > ProcessList.FOREGROUND_APP_ADJ) {
                         adj = ProcessList.FOREGROUND_APP_ADJ;
                         schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -14839,6 +13956,20 @@
                         Process.killProcessQuiet(app.pid);
                     }
                 }
+                if (!app.killedBackground && app.isolated && app.services.size() <= 0) {
+                    // If this is an isolated process, and there are no
+                    // services running in it, then the process is no longer
+                    // needed.  We agressively kill these because we can by
+                    // definition not re-use the same process again, and it is
+                    // good to avoid having whatever code was running in them
+                    // left sitting around after no longer needed.
+                    Slog.i(TAG, "Isolated process " + app.processName
+                            + " (pid " + app.pid + ") no longer needed");
+                    EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                            app.processName, app.setAdj, "isolated not needed");
+                    app.killedBackground = true;
+                    Process.killProcessQuiet(app.pid);
+                }
             }
         }
 
@@ -14977,7 +14108,7 @@
                     
                     if (app.persistent) {
                         if (app.persistent) {
-                            addAppLocked(app.info);
+                            addAppLocked(app.info, false);
                         }
                     }
                 }
@@ -15186,7 +14317,7 @@
         synchronized (this) { }
     }
 
-    public void onCoreSettingsChange(Bundle settings) {
+    void onCoreSettingsChange(Bundle settings) {
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord processRecord = mLruProcesses.get(i);
             try {
@@ -15203,6 +14334,25 @@
 
     private int mCurrentUserId;
     private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+    private ArrayList<UserListener> mUserListeners = new ArrayList<UserListener>(3);
+
+    public interface UserListener {
+        public void onUserChanged(int userId);
+
+        public void onUserAdded(int userId);
+
+        public void onUserRemoved(int userId);
+
+        public void onUserLoggedOut(int userId);
+    }
+
+    public void addUserListener(UserListener listener) {
+        synchronized (this) {
+            if (!mUserListeners.contains(listener)) {
+                mUserListeners.add(listener);
+            }
+        }
+    }
 
     public boolean switchUser(int userId) {
         final int callingUid = Binder.getCallingUid();
@@ -15213,6 +14363,8 @@
         if (mCurrentUserId == userId)
             return true;
 
+        ArrayList<UserListener> listeners;
+
         synchronized (this) {
             // Check if user is already logged in, otherwise check if user exists first before
             // adding to the list of logged in users.
@@ -15228,6 +14380,12 @@
             if (!haveActivities) {
                 startHomeActivityLocked(userId);
             }
+
+            listeners = (ArrayList<UserListener>) mUserListeners.clone();
+        }
+        // Inform the listeners
+        for (UserListener listener : listeners) {
+            listener.onUserChanged(userId);
         }
         return true;
     }
@@ -15247,6 +14405,12 @@
         return false;
     }
 
+    private void checkValidCaller(int uid, int userId) {
+        if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return;
+
+        throw new SecurityException("Caller uid=" + uid
+                + " is not privileged to communicate with user=" + userId);
+    }
 
     private int applyUserId(int uid, int userId) {
         return UserId.getUid(userId, uid);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c3ae6a1..7b8bc26 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -277,13 +277,13 @@
 
     private int mCurrentUser;
 
-    static final int SLEEP_TIMEOUT_MSG = 8;
-    static final int PAUSE_TIMEOUT_MSG = 9;
-    static final int IDLE_TIMEOUT_MSG = 10;
-    static final int IDLE_NOW_MSG = 11;
-    static final int LAUNCH_TIMEOUT_MSG = 16;
-    static final int DESTROY_TIMEOUT_MSG = 17;
-    static final int RESUME_TOP_ACTIVITY_MSG = 19;
+    static final int SLEEP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG;
+    static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
+    static final int IDLE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
+    static final int IDLE_NOW_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
+    static final int LAUNCH_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
+    static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
+    static final int RESUME_TOP_ACTIVITY_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
     
     final Handler mHandler = new Handler() {
         //public Handler() {
@@ -755,7 +755,7 @@
         }
 
         mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
-                "activity", r.intent.getComponent(), false);
+                "activity", r.intent.getComponent(), false, false);
     }
     
     void stopIfSleepingLocked() {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
new file mode 100644
index 0000000..39b63db
--- /dev/null
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -0,0 +1,1017 @@
+/*
+ * Copyright (C) 2012 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.server.am;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserId;
+import android.util.EventLog;
+import android.util.Slog;
+
+/**
+ * BROADCASTS
+ *
+ * We keep two broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts.
+ */
+public class BroadcastQueue {
+    static final String TAG = "BroadcastQueue";
+    static final String TAG_MU = ActivityManagerService.TAG_MU;
+    static final boolean DEBUG_BROADCAST = ActivityManagerService.DEBUG_BROADCAST;
+    static final boolean DEBUG_BROADCAST_LIGHT = ActivityManagerService.DEBUG_BROADCAST_LIGHT;
+    static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
+
+    static final int MAX_BROADCAST_HISTORY = 25;
+
+    final ActivityManagerService mService;
+
+    /**
+     * Recognizable moniker for this queue
+     */
+    final String mQueueName;
+
+    /**
+     * Timeout period for this queue's broadcasts
+     */
+    final long mTimeoutPeriod;
+
+    /**
+     * Lists of all active broadcasts that are to be executed immediately
+     * (without waiting for another broadcast to finish).  Currently this only
+     * contains broadcasts to registered receivers, to avoid spinning up
+     * a bunch of processes to execute IntentReceiver components.  Background-
+     * and foreground-priority broadcasts are queued separately.
+     */
+    final ArrayList<BroadcastRecord> mParallelBroadcasts
+            = new ArrayList<BroadcastRecord>();
+    /**
+     * List of all active broadcasts that are to be executed one at a time.
+     * The object at the top of the list is the currently activity broadcasts;
+     * those after it are waiting for the top to finish.  As with parallel
+     * broadcasts, separate background- and foreground-priority queues are
+     * maintained.
+     */
+    final ArrayList<BroadcastRecord> mOrderedBroadcasts
+            = new ArrayList<BroadcastRecord>();
+
+    /**
+     * Historical data of past broadcasts, for debugging.
+     */
+    final BroadcastRecord[] mBroadcastHistory
+            = new BroadcastRecord[MAX_BROADCAST_HISTORY];
+
+    /**
+     * Set when we current have a BROADCAST_INTENT_MSG in flight.
+     */
+    boolean mBroadcastsScheduled = false;
+
+    /**
+     * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
+     */
+    boolean mPendingBroadcastTimeoutMessage;
+
+    /**
+     * Intent broadcasts that we have tried to start, but are
+     * waiting for the application's process to be created.  We only
+     * need one per scheduling class (instead of a list) because we always
+     * process broadcasts one at a time, so no others can be started while
+     * waiting for this one.
+     */
+    BroadcastRecord mPendingBroadcast = null;
+
+    /**
+     * The receiver index that is pending, to restart the broadcast if needed.
+     */
+    int mPendingBroadcastRecvIndex;
+
+    static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
+    static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
+
+    final Handler mHandler = new Handler() {
+        //public Handler() {
+        //    if (localLOGV) Slog.v(TAG, "Handler started!");
+        //}
+
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case BROADCAST_INTENT_MSG: {
+                    if (DEBUG_BROADCAST) Slog.v(
+                            TAG, "Received BROADCAST_INTENT_MSG");
+                    processNextBroadcast(true);
+                } break;
+                case BROADCAST_TIMEOUT_MSG: {
+                    synchronized (mService) {
+                        broadcastTimeoutLocked(true);
+                    }
+                } break;
+            }
+        }
+    };
+
+    private final class AppNotResponding implements Runnable {
+        private final ProcessRecord mApp;
+        private final String mAnnotation;
+
+        public AppNotResponding(ProcessRecord app, String annotation) {
+            mApp = app;
+            mAnnotation = annotation;
+        }
+
+        @Override
+        public void run() {
+            mService.appNotResponding(mApp, null, null, mAnnotation);
+        }
+    }
+
+    BroadcastQueue(ActivityManagerService service, String name, long timeoutPeriod) {
+        mService = service;
+        mQueueName = name;
+        mTimeoutPeriod = timeoutPeriod;
+    }
+
+    public boolean isPendingBroadcastProcessLocked(int pid) {
+        return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
+    }
+
+    public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
+        mParallelBroadcasts.add(r);
+    }
+
+    public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+        mOrderedBroadcasts.add(r);
+    }
+
+    public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) {
+        for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
+            if (r.intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "***** DROPPING PARALLEL ["
+                + mQueueName + "]: " + r.intent);
+                mParallelBroadcasts.set(i, r);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public final boolean replaceOrderedBroadcastLocked(BroadcastRecord r) {
+        for (int i=mOrderedBroadcasts.size()-1; i>0; i--) {
+            if (r.intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "***** DROPPING ORDERED ["
+                        + mQueueName + "]: " + r.intent);
+                mOrderedBroadcasts.set(i, r);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private final void processCurBroadcastLocked(BroadcastRecord r,
+            ProcessRecord app) throws RemoteException {
+        if (DEBUG_BROADCAST)  Slog.v(TAG,
+                "Process cur broadcast " + r + " for app " + app);
+        if (app.thread == null) {
+            throw new RemoteException();
+        }
+        r.receiver = app.thread.asBinder();
+        r.curApp = app;
+        app.curReceiver = r;
+        mService.updateLruProcessLocked(app, true, true);
+
+        // Tell the application to launch this receiver.
+        r.intent.setComponent(r.curComponent);
+
+        boolean started = false;
+        try {
+            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG,
+                    "Delivering to component " + r.curComponent
+                    + ": " + r);
+            mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
+                    mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
+                    r.resultCode, r.resultData, r.resultExtras, r.ordered);
+            if (DEBUG_BROADCAST)  Slog.v(TAG,
+                    "Process cur broadcast " + r + " DELIVERED for app " + app);
+            started = true;
+        } finally {
+            if (!started) {
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Process cur broadcast " + r + ": NOT STARTED!");
+                r.receiver = null;
+                r.curApp = null;
+                app.curReceiver = null;
+            }
+        }
+    }
+
+    public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
+        boolean didSomething = false;
+        final BroadcastRecord br = mPendingBroadcast;
+        if (br != null && br.curApp.pid == app.pid) {
+            try {
+                mPendingBroadcast = null;
+                processCurBroadcastLocked(br, app);
+                didSomething = true;
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception in new application when starting receiver "
+                        + br.curComponent.flattenToShortString(), e);
+                logBroadcastReceiverDiscardLocked(br);
+                finishReceiverLocked(br, br.resultCode, br.resultData,
+                        br.resultExtras, br.resultAbort, true);
+                scheduleBroadcastsLocked();
+                // We need to reset the state if we fails to start the receiver.
+                br.state = BroadcastRecord.IDLE;
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+        return didSomething;
+    }
+
+    public void skipPendingBroadcastLocked(int pid) {
+        final BroadcastRecord br = mPendingBroadcast;
+        if (br != null && br.curApp.pid == pid) {
+            br.state = BroadcastRecord.IDLE;
+            br.nextReceiver = mPendingBroadcastRecvIndex;
+            mPendingBroadcast = null;
+            scheduleBroadcastsLocked();
+        }
+    }
+
+    public void skipCurrentReceiverLocked(ProcessRecord app) {
+        boolean reschedule = false;
+        BroadcastRecord r = app.curReceiver;
+        if (r != null) {
+            // The current broadcast is waiting for this app's receiver
+            // to be finished.  Looks like that's not going to happen, so
+            // let the broadcast continue.
+            logBroadcastReceiverDiscardLocked(r);
+            finishReceiverLocked(r, r.resultCode, r.resultData,
+                    r.resultExtras, r.resultAbort, true);
+            reschedule = true;
+        }
+
+        r = mPendingBroadcast;
+        if (r != null && r.curApp == app) {
+            if (DEBUG_BROADCAST) Slog.v(TAG,
+                    "[" + mQueueName + "] skip & discard pending app " + r);
+            logBroadcastReceiverDiscardLocked(r);
+            finishReceiverLocked(r, r.resultCode, r.resultData,
+                    r.resultExtras, r.resultAbort, true);
+            reschedule = true;
+        }
+        if (reschedule) {
+            scheduleBroadcastsLocked();
+        }
+    }
+
+    public void scheduleBroadcastsLocked() {
+        if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
+                + mQueueName + "]: current="
+                + mBroadcastsScheduled);
+
+        if (mBroadcastsScheduled) {
+            return;
+        }
+        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
+        mBroadcastsScheduled = true;
+    }
+
+    public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
+        if (mOrderedBroadcasts.size() > 0) {
+            final BroadcastRecord r = mOrderedBroadcasts.get(0);
+            if (r != null && r.receiver == receiver) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+    public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
+            String resultData, Bundle resultExtras, boolean resultAbort,
+            boolean explicit) {
+        int state = r.state;
+        r.state = BroadcastRecord.IDLE;
+        if (state == BroadcastRecord.IDLE) {
+            if (explicit) {
+                Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
+            }
+        }
+        r.receiver = null;
+        r.intent.setComponent(null);
+        if (r.curApp != null) {
+            r.curApp.curReceiver = null;
+        }
+        if (r.curFilter != null) {
+            r.curFilter.receiverList.curBroadcast = null;
+        }
+        r.curFilter = null;
+        r.curApp = null;
+        r.curComponent = null;
+        r.curReceiver = null;
+        mPendingBroadcast = null;
+
+        r.resultCode = resultCode;
+        r.resultData = resultData;
+        r.resultExtras = resultExtras;
+        r.resultAbort = resultAbort;
+
+        // We will process the next receiver right now if this is finishing
+        // an app receiver (which is always asynchronous) or after we have
+        // come back from calling a receiver.
+        return state == BroadcastRecord.APP_RECEIVE
+                || state == BroadcastRecord.CALL_DONE_RECEIVE;
+    }
+
+    private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
+            Intent intent, int resultCode, String data, Bundle extras,
+            boolean ordered, boolean sticky) throws RemoteException {
+        // Send the intent to the receiver asynchronously using one-way binder calls.
+        if (app != null && app.thread != null) {
+            // If we have an app thread, do the call through that so it is
+            // correctly ordered with other one-way calls.
+            app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
+                    data, extras, ordered, sticky);
+        } else {
+            receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
+        }
+    }
+
+    private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
+            BroadcastFilter filter, boolean ordered) {
+        boolean skip = false;
+        if (filter.requiredPermission != null) {
+            int perm = mService.checkComponentPermission(filter.requiredPermission,
+                    r.callingPid, r.callingUid, -1, true);
+            if (perm != PackageManager.PERMISSION_GRANTED) {
+                Slog.w(TAG, "Permission Denial: broadcasting "
+                        + r.intent.toString()
+                        + " from " + r.callerPackage + " (pid="
+                        + r.callingPid + ", uid=" + r.callingUid + ")"
+                        + " requires " + filter.requiredPermission
+                        + " due to registered receiver " + filter);
+                skip = true;
+            }
+        }
+        if (r.requiredPermission != null) {
+            int perm = mService.checkComponentPermission(r.requiredPermission,
+                    filter.receiverList.pid, filter.receiverList.uid, -1, true);
+            if (perm != PackageManager.PERMISSION_GRANTED) {
+                Slog.w(TAG, "Permission Denial: receiving "
+                        + r.intent.toString()
+                        + " to " + filter.receiverList.app
+                        + " (pid=" + filter.receiverList.pid
+                        + ", uid=" + filter.receiverList.uid + ")"
+                        + " requires " + r.requiredPermission
+                        + " due to sender " + r.callerPackage
+                        + " (uid " + r.callingUid + ")");
+                skip = true;
+            }
+        }
+
+        if (!skip) {
+            // If this is not being sent as an ordered broadcast, then we
+            // don't want to touch the fields that keep track of the current
+            // state of ordered broadcasts.
+            if (ordered) {
+                r.receiver = filter.receiverList.receiver.asBinder();
+                r.curFilter = filter;
+                filter.receiverList.curBroadcast = r;
+                r.state = BroadcastRecord.CALL_IN_RECEIVE;
+                if (filter.receiverList.app != null) {
+                    // Bump hosting application to no longer be in background
+                    // scheduling class.  Note that we can't do that if there
+                    // isn't an app...  but we can only be in that case for
+                    // things that directly call the IActivityManager API, which
+                    // are already core system stuff so don't matter for this.
+                    r.curApp = filter.receiverList.app;
+                    filter.receiverList.app.curReceiver = r;
+                    mService.updateOomAdjLocked();
+                }
+            }
+            try {
+                if (DEBUG_BROADCAST_LIGHT) {
+                    int seq = r.intent.getIntExtra("seq", -1);
+                    Slog.i(TAG, "Delivering to " + filter
+                            + " (seq=" + seq + "): " + r);
+                }
+                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+                    new Intent(r.intent), r.resultCode,
+                    r.resultData, r.resultExtras, r.ordered, r.initialSticky);
+                if (ordered) {
+                    r.state = BroadcastRecord.CALL_DONE_RECEIVE;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
+                if (ordered) {
+                    r.receiver = null;
+                    r.curFilter = null;
+                    filter.receiverList.curBroadcast = null;
+                    if (filter.receiverList.app != null) {
+                        filter.receiverList.app.curReceiver = null;
+                    }
+                }
+            }
+        }
+    }
+
+    final void processNextBroadcast(boolean fromMsg) {
+        synchronized(mService) {
+            BroadcastRecord r;
+
+            if (DEBUG_BROADCAST) Slog.v(TAG, "processNextBroadcast ["
+                    + mQueueName + "]: "
+                    + mParallelBroadcasts.size() + " broadcasts, "
+                    + mOrderedBroadcasts.size() + " ordered broadcasts");
+
+            mService.updateCpuStats();
+
+            if (fromMsg) {
+                mBroadcastsScheduled = false;
+            }
+
+            // First, deliver any non-serialized broadcasts right away.
+            while (mParallelBroadcasts.size() > 0) {
+                r = mParallelBroadcasts.remove(0);
+                r.dispatchTime = SystemClock.uptimeMillis();
+                r.dispatchClockTime = System.currentTimeMillis();
+                final int N = r.receivers.size();
+                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
+                        + mQueueName + "] " + r);
+                for (int i=0; i<N; i++) {
+                    Object target = r.receivers.get(i);
+                    if (DEBUG_BROADCAST)  Slog.v(TAG,
+                            "Delivering non-ordered on [" + mQueueName + "] to registered "
+                            + target + ": " + r);
+                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
+                }
+                addBroadcastToHistoryLocked(r);
+                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
+                        + mQueueName + "] " + r);
+            }
+
+            // Now take care of the next serialized one...
+
+            // If we are waiting for a process to come up to handle the next
+            // broadcast, then do nothing at this point.  Just in case, we
+            // check that the process we're waiting for still exists.
+            if (mPendingBroadcast != null) {
+                if (DEBUG_BROADCAST_LIGHT) {
+                    Slog.v(TAG, "processNextBroadcast ["
+                            + mQueueName + "]: waiting for "
+                            + mPendingBroadcast.curApp);
+                }
+
+                boolean isDead;
+                synchronized (mService.mPidsSelfLocked) {
+                    isDead = (mService.mPidsSelfLocked.get(
+                            mPendingBroadcast.curApp.pid) == null);
+                }
+                if (!isDead) {
+                    // It's still alive, so keep waiting
+                    return;
+                } else {
+                    Slog.w(TAG, "pending app  ["
+                            + mQueueName + "]" + mPendingBroadcast.curApp
+                            + " died before responding to broadcast");
+                    mPendingBroadcast.state = BroadcastRecord.IDLE;
+                    mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
+                    mPendingBroadcast = null;
+                }
+            }
+
+            boolean looped = false;
+            
+            do {
+                if (mOrderedBroadcasts.size() == 0) {
+                    // No more broadcasts pending, so all done!
+                    mService.scheduleAppGcsLocked();
+                    if (looped) {
+                        // If we had finished the last ordered broadcast, then
+                        // make sure all processes have correct oom and sched
+                        // adjustments.
+                        mService.updateOomAdjLocked();
+                    }
+                    return;
+                }
+                r = mOrderedBroadcasts.get(0);
+                boolean forceReceive = false;
+
+                // Ensure that even if something goes awry with the timeout
+                // detection, we catch "hung" broadcasts here, discard them,
+                // and continue to make progress.
+                //
+                // This is only done if the system is ready so that PRE_BOOT_COMPLETED
+                // receivers don't get executed with timeouts. They're intended for
+                // one time heavy lifting after system upgrades and can take
+                // significant amounts of time.
+                int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+                if (mService.mProcessesReady && r.dispatchTime > 0) {
+                    long now = SystemClock.uptimeMillis();
+                    if ((numReceivers > 0) &&
+                            (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
+                        Slog.w(TAG, "Hung broadcast ["
+                                + mQueueName + "] discarded after timeout failure:"
+                                + " now=" + now
+                                + " dispatchTime=" + r.dispatchTime
+                                + " startTime=" + r.receiverTime
+                                + " intent=" + r.intent
+                                + " numReceivers=" + numReceivers
+                                + " nextReceiver=" + r.nextReceiver
+                                + " state=" + r.state);
+                        broadcastTimeoutLocked(false); // forcibly finish this broadcast
+                        forceReceive = true;
+                        r.state = BroadcastRecord.IDLE;
+                    }
+                }
+
+                if (r.state != BroadcastRecord.IDLE) {
+                    if (DEBUG_BROADCAST) Slog.d(TAG,
+                            "processNextBroadcast("
+                            + mQueueName + ") called when not idle (state="
+                            + r.state + ")");
+                    return;
+                }
+
+                if (r.receivers == null || r.nextReceiver >= numReceivers
+                        || r.resultAbort || forceReceive) {
+                    // No more receivers for this broadcast!  Send the final
+                    // result if requested...
+                    if (r.resultTo != null) {
+                        try {
+                            if (DEBUG_BROADCAST) {
+                                int seq = r.intent.getIntExtra("seq", -1);
+                                Slog.i(TAG, "Finishing broadcast ["
+                                        + mQueueName + "] " + r.intent.getAction()
+                                        + " seq=" + seq + " app=" + r.callerApp);
+                            }
+                            performReceiveLocked(r.callerApp, r.resultTo,
+                                new Intent(r.intent), r.resultCode,
+                                r.resultData, r.resultExtras, false, false);
+                            // Set this to null so that the reference
+                            // (local and remote) isnt kept in the mBroadcastHistory.
+                            r.resultTo = null;
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "Failure ["
+                                    + mQueueName + "] sending broadcast result of "
+                                    + r.intent, e);
+                        }
+                    }
+
+                    if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
+                    cancelBroadcastTimeoutLocked();
+
+                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
+                            + r);
+
+                    // ... and on to the next...
+                    addBroadcastToHistoryLocked(r);
+                    mOrderedBroadcasts.remove(0);
+                    r = null;
+                    looped = true;
+                    continue;
+                }
+            } while (r == null);
+
+            // Get the next receiver...
+            int recIdx = r.nextReceiver++;
+
+            // Keep track of when this receiver started, and make sure there
+            // is a timeout message pending to kill it if need be.
+            r.receiverTime = SystemClock.uptimeMillis();
+            if (recIdx == 0) {
+                r.dispatchTime = r.receiverTime;
+                r.dispatchClockTime = System.currentTimeMillis();
+                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast ["
+                        + mQueueName + "] " + r);
+            }
+            if (! mPendingBroadcastTimeoutMessage) {
+                long timeoutTime = r.receiverTime + mTimeoutPeriod;
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "Submitting BROADCAST_TIMEOUT_MSG ["
+                        + mQueueName + "] for " + r + " at " + timeoutTime);
+                setBroadcastTimeoutLocked(timeoutTime);
+            }
+
+            Object nextReceiver = r.receivers.get(recIdx);
+            if (nextReceiver instanceof BroadcastFilter) {
+                // Simple case: this is a registered receiver who gets
+                // a direct call.
+                BroadcastFilter filter = (BroadcastFilter)nextReceiver;
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Delivering ordered ["
+                        + mQueueName + "] to registered "
+                        + filter + ": " + r);
+                deliverToRegisteredReceiverLocked(r, filter, r.ordered);
+                if (r.receiver == null || !r.ordered) {
+                    // The receiver has already finished, so schedule to
+                    // process the next one.
+                    if (DEBUG_BROADCAST) Slog.v(TAG, "Quick finishing ["
+                            + mQueueName + "]: ordered="
+                            + r.ordered + " receiver=" + r.receiver);
+                    r.state = BroadcastRecord.IDLE;
+                    scheduleBroadcastsLocked();
+                }
+                return;
+            }
+
+            // Hard case: need to instantiate the receiver, possibly
+            // starting its application process to host it.
+
+            ResolveInfo info =
+                (ResolveInfo)nextReceiver;
+
+            boolean skip = false;
+            int perm = mService.checkComponentPermission(info.activityInfo.permission,
+                    r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
+                    info.activityInfo.exported);
+            if (perm != PackageManager.PERMISSION_GRANTED) {
+                if (!info.activityInfo.exported) {
+                    Slog.w(TAG, "Permission Denial: broadcasting "
+                            + r.intent.toString()
+                            + " from " + r.callerPackage + " (pid=" + r.callingPid
+                            + ", uid=" + r.callingUid + ")"
+                            + " is not exported from uid " + info.activityInfo.applicationInfo.uid
+                            + " due to receiver " + info.activityInfo.packageName
+                            + "/" + info.activityInfo.name);
+                } else {
+                    Slog.w(TAG, "Permission Denial: broadcasting "
+                            + r.intent.toString()
+                            + " from " + r.callerPackage + " (pid=" + r.callingPid
+                            + ", uid=" + r.callingUid + ")"
+                            + " requires " + info.activityInfo.permission
+                            + " due to receiver " + info.activityInfo.packageName
+                            + "/" + info.activityInfo.name);
+                }
+                skip = true;
+            }
+            if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
+                r.requiredPermission != null) {
+                try {
+                    perm = AppGlobals.getPackageManager().
+                            checkPermission(r.requiredPermission,
+                                    info.activityInfo.applicationInfo.packageName);
+                } catch (RemoteException e) {
+                    perm = PackageManager.PERMISSION_DENIED;
+                }
+                if (perm != PackageManager.PERMISSION_GRANTED) {
+                    Slog.w(TAG, "Permission Denial: receiving "
+                            + r.intent + " to "
+                            + info.activityInfo.applicationInfo.packageName
+                            + " requires " + r.requiredPermission
+                            + " due to sender " + r.callerPackage
+                            + " (uid " + r.callingUid + ")");
+                    skip = true;
+                }
+            }
+            if (r.curApp != null && r.curApp.crashing) {
+                // If the target process is crashing, just skip it.
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Skipping deliver ordered ["
+                        + mQueueName + "] " + r + " to " + r.curApp
+                        + ": process crashing");
+                skip = true;
+            }
+
+            if (skip) {
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "Skipping delivery of ordered ["
+                        + mQueueName + "] " + r + " for whatever reason");
+                r.receiver = null;
+                r.curFilter = null;
+                r.state = BroadcastRecord.IDLE;
+                scheduleBroadcastsLocked();
+                return;
+            }
+
+            r.state = BroadcastRecord.APP_RECEIVE;
+            String targetProcess = info.activityInfo.processName;
+            r.curComponent = new ComponentName(
+                    info.activityInfo.applicationInfo.packageName,
+                    info.activityInfo.name);
+            if (r.callingUid != Process.SYSTEM_UID) {
+                info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, UserId
+                        .getUserId(r.callingUid));
+            }
+            r.curReceiver = info.activityInfo;
+            if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+                Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+                        + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+                        + info.activityInfo.applicationInfo.uid);
+            }
+
+            // Broadcast is being executed, its package can't be stopped.
+            try {
+                AppGlobals.getPackageManager().setPackageStoppedState(
+                        r.curComponent.getPackageName(), false);
+            } catch (RemoteException e) {
+            } catch (IllegalArgumentException e) {
+                Slog.w(TAG, "Failed trying to unstop package "
+                        + r.curComponent.getPackageName() + ": " + e);
+            }
+
+            // Is this receiver's application already running?
+            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
+                    info.activityInfo.applicationInfo.uid);
+            if (app != null && app.thread != null) {
+                try {
+                    app.addPackage(info.activityInfo.packageName);
+                    processCurBroadcastLocked(r, app);
+                    return;
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Exception when sending broadcast to "
+                          + r.curComponent, e);
+                }
+
+                // If a dead object exception was thrown -- fall through to
+                // restart the application.
+            }
+
+            // Not running -- get it started, to be executed when the app comes up.
+            if (DEBUG_BROADCAST)  Slog.v(TAG,
+                    "Need to start app ["
+                    + mQueueName + "] " + targetProcess + " for broadcast " + r);
+            if ((r.curApp=mService.startProcessLocked(targetProcess,
+                    info.activityInfo.applicationInfo, true,
+                    r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
+                    "broadcast", r.curComponent,
+                    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false))
+                            == null) {
+                // Ah, this recipient is unavailable.  Finish it if necessary,
+                // and mark the broadcast record as ready for the next.
+                Slog.w(TAG, "Unable to launch app "
+                        + info.activityInfo.applicationInfo.packageName + "/"
+                        + info.activityInfo.applicationInfo.uid + " for broadcast "
+                        + r.intent + ": process is bad");
+                logBroadcastReceiverDiscardLocked(r);
+                finishReceiverLocked(r, r.resultCode, r.resultData,
+                        r.resultExtras, r.resultAbort, true);
+                scheduleBroadcastsLocked();
+                r.state = BroadcastRecord.IDLE;
+                return;
+            }
+
+            mPendingBroadcast = r;
+            mPendingBroadcastRecvIndex = recIdx;
+        }
+    }
+
+    final void setBroadcastTimeoutLocked(long timeoutTime) {
+        if (! mPendingBroadcastTimeoutMessage) {
+            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
+            mHandler.sendMessageAtTime(msg, timeoutTime);
+            mPendingBroadcastTimeoutMessage = true;
+        }
+    }
+
+    final void cancelBroadcastTimeoutLocked() {
+        if (mPendingBroadcastTimeoutMessage) {
+            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
+            mPendingBroadcastTimeoutMessage = false;
+        }
+    }
+
+    final void broadcastTimeoutLocked(boolean fromMsg) {
+        if (fromMsg) {
+            mPendingBroadcastTimeoutMessage = false;
+        }
+
+        if (mOrderedBroadcasts.size() == 0) {
+            return;
+        }
+
+        long now = SystemClock.uptimeMillis();
+        BroadcastRecord r = mOrderedBroadcasts.get(0);
+        if (fromMsg) {
+            if (mService.mDidDexOpt) {
+                // Delay timeouts until dexopt finishes.
+                mService.mDidDexOpt = false;
+                long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
+                setBroadcastTimeoutLocked(timeoutTime);
+                return;
+            }
+            if (!mService.mProcessesReady) {
+                // Only process broadcast timeouts if the system is ready. That way
+                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
+                // to do heavy lifting for system up.
+                return;
+            }
+
+            long timeoutTime = r.receiverTime + mTimeoutPeriod;
+            if (timeoutTime > now) {
+                // We can observe premature timeouts because we do not cancel and reset the
+                // broadcast timeout message after each receiver finishes.  Instead, we set up
+                // an initial timeout then kick it down the road a little further as needed
+                // when it expires.
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "Premature timeout ["
+                        + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+                        + timeoutTime);
+                setBroadcastTimeoutLocked(timeoutTime);
+                return;
+            }
+        }
+
+        Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+                + ", started " + (now - r.receiverTime) + "ms ago");
+        r.receiverTime = now;
+        r.anrCount++;
+
+        // Current receiver has passed its expiration date.
+        if (r.nextReceiver <= 0) {
+            Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
+            return;
+        }
+
+        ProcessRecord app = null;
+        String anrMessage = null;
+
+        Object curReceiver = r.receivers.get(r.nextReceiver-1);
+        Slog.w(TAG, "Receiver during timeout: " + curReceiver);
+        logBroadcastReceiverDiscardLocked(r);
+        if (curReceiver instanceof BroadcastFilter) {
+            BroadcastFilter bf = (BroadcastFilter)curReceiver;
+            if (bf.receiverList.pid != 0
+                    && bf.receiverList.pid != ActivityManagerService.MY_PID) {
+                synchronized (mService.mPidsSelfLocked) {
+                    app = mService.mPidsSelfLocked.get(
+                            bf.receiverList.pid);
+                }
+            }
+        } else {
+            app = r.curApp;
+        }
+
+        if (app != null) {
+            anrMessage = "Broadcast of " + r.intent.toString();
+        }
+
+        if (mPendingBroadcast == r) {
+            mPendingBroadcast = null;
+        }
+
+        // Move on to the next receiver.
+        finishReceiverLocked(r, r.resultCode, r.resultData,
+                r.resultExtras, r.resultAbort, true);
+        scheduleBroadcastsLocked();
+
+        if (anrMessage != null) {
+            // Post the ANR to the handler since we do not want to process ANRs while
+            // potentially holding our lock.
+            mHandler.post(new AppNotResponding(app, anrMessage));
+        }
+    }
+
+    private final void addBroadcastToHistoryLocked(BroadcastRecord r) {
+        if (r.callingUid < 0) {
+            // This was from a registerReceiver() call; ignore it.
+            return;
+        }
+        System.arraycopy(mBroadcastHistory, 0, mBroadcastHistory, 1,
+                MAX_BROADCAST_HISTORY-1);
+        r.finishTime = SystemClock.uptimeMillis();
+        mBroadcastHistory[0] = r;
+    }
+
+    final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
+        if (r.nextReceiver > 0) {
+            Object curReceiver = r.receivers.get(r.nextReceiver-1);
+            if (curReceiver instanceof BroadcastFilter) {
+                BroadcastFilter bf = (BroadcastFilter) curReceiver;
+                EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
+                        System.identityHashCode(r),
+                        r.intent.getAction(),
+                        r.nextReceiver - 1,
+                        System.identityHashCode(bf));
+            } else {
+                EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+                        System.identityHashCode(r),
+                        r.intent.getAction(),
+                        r.nextReceiver - 1,
+                        ((ResolveInfo)curReceiver).toString());
+            }
+        } else {
+            Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
+                    + r);
+            EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+                    System.identityHashCode(r),
+                    r.intent.getAction(),
+                    r.nextReceiver,
+                    "NONE");
+        }
+    }
+
+    final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+        if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
+                || mPendingBroadcast != null) {
+            boolean printed = false;
+            for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
+                BroadcastRecord br = mParallelBroadcasts.get(i);
+                if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
+                    continue;
+                }
+                if (!printed) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    printed = true;
+                    pw.println("  Active broadcasts [" + mQueueName + "]:");
+                }
+                pw.println("  Broadcast #" + i + ":");
+                br.dump(pw, "    ");
+            }
+            printed = false;
+            needSep = true;
+            for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) {
+                BroadcastRecord br = mOrderedBroadcasts.get(i);
+                if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
+                    continue;
+                }
+                if (!printed) {
+                    if (needSep) {
+                        pw.println();
+                    }
+                    needSep = true;
+                    pw.println("  Active ordered broadcasts [" + mQueueName + "]:");
+                }
+                pw.println("  Ordered Broadcast #" + i + ":");
+                mOrderedBroadcasts.get(i).dump(pw, "    ");
+            }
+            if (dumpPackage == null || (mPendingBroadcast != null
+                    && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
+                if (needSep) {
+                    pw.println();
+                }
+                pw.println("  Pending broadcast [" + mQueueName + "]:");
+                if (mPendingBroadcast != null) {
+                    mPendingBroadcast.dump(pw, "    ");
+                } else {
+                    pw.println("    (null)");
+                }
+                needSep = true;
+            }
+        }
+
+        boolean printed = false;
+        for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
+            BroadcastRecord r = mBroadcastHistory[i];
+            if (r == null) {
+                break;
+            }
+            if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
+                continue;
+            }
+            if (!printed) {
+                if (needSep) {
+                    pw.println();
+                }
+                needSep = true;
+                pw.println("  Historical broadcasts [" + mQueueName + "]:");
+                printed = true;
+            }
+            if (dumpAll) {
+                pw.print("  Historical Broadcast #"); pw.print(i); pw.println(":");
+                r.dump(pw, "    ");
+            } else {
+                if (i >= 50) {
+                    pw.println("  ...");
+                    break;
+                }
+                pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
+            }
+        }
+
+        return needSep;
+    }
+}
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 6738e4f..dd560fc 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -59,7 +59,7 @@
     IBinder receiver;       // who is currently running, null if none.
     int state;
     int anrCount;           // has this broadcast record hit any ANRs?
-    ActivityManagerService.BroadcastQueue queue;   // the outbound queue handling this broadcast
+    BroadcastQueue queue;   // the outbound queue handling this broadcast
 
     static final int IDLE = 0;
     static final int APP_RECEIVE = 1;
@@ -162,7 +162,7 @@
         }
     }
 
-    BroadcastRecord(ActivityManagerService.BroadcastQueue _queue,
+    BroadcastRecord(BroadcastQueue _queue,
             Intent _intent, ProcessRecord _callerApp, String _callerPackage,
             int _callingPid, int _callingUid, String _requiredPermission,
             List _receivers, IIntentReceiver _resultTo, int _resultCode,
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index f656486..cd72202 100644
--- a/services/java/com/android/server/am/CompatModePackages.java
+++ b/services/java/com/android/server/am/CompatModePackages.java
@@ -41,7 +41,7 @@
 
     private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
 
-    private static final int MSG_WRITE = 1;
+    private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
 
     private final Handler mHandler = new Handler() {
         @Override public void handleMessage(Message msg) {
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 38355537..f338cfc 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -20,24 +20,35 @@
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.Process;
+import android.os.RemoteException;
+import android.util.Slog;
 
 import java.io.PrintWriter;
+import java.util.HashMap;
 import java.util.HashSet;
 
 class ContentProviderRecord extends ContentProviderHolder {
     // All attached clients
     final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
+    // Handles for non-framework processes supported by this provider
+    HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
+    // Count for external process for which we have no handles.
+    int externalProcessNoHandleCount;
+    final ActivityManagerService service;
     final int uid;
     final ApplicationInfo appInfo;
     final ComponentName name;
-    int externals;     // number of non-framework processes supported by this provider
     ProcessRecord proc; // if non-null, hosting process.
     ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
     String stringName;
-    
-    public ContentProviderRecord(ProviderInfo _info, ApplicationInfo ai, ComponentName _name) {
+
+    public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
+            ApplicationInfo ai, ComponentName _name) {
         super(_info);
+        service = _service;
         uid = ai.uid;
         appInfo = ai;
         name = _name;
@@ -50,6 +61,7 @@
         appInfo = cpr.appInfo;
         name = cpr.name;
         noReleaseNeeded = cpr.noReleaseNeeded;
+        service = cpr.service;
     }
 
     public boolean canRunHere(ProcessRecord app) {
@@ -57,6 +69,57 @@
                 && (uid == Process.SYSTEM_UID || uid == app.info.uid);
     }
 
+    public void addExternalProcessHandleLocked(IBinder token) {
+        if (token == null) {
+            externalProcessNoHandleCount++;
+        } else {
+            if (externalProcessTokenToHandle == null) {
+                externalProcessTokenToHandle = new HashMap<IBinder, ExternalProcessHandle>();
+            }
+            ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
+            if (handle == null) {
+                handle = new ExternalProcessHandle(token);
+                externalProcessTokenToHandle.put(token, handle);
+            }
+            handle.mAcquisitionCount++;
+        }
+    }
+
+    public boolean removeExternalProcessHandleLocked(IBinder token) {
+        if (hasExternalProcessHandles()) {
+            boolean hasHandle = false;
+            if (externalProcessTokenToHandle != null) {
+                ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
+                if (handle != null) {
+                    hasHandle = true;
+                    handle.mAcquisitionCount--;
+                    if (handle.mAcquisitionCount == 0) {
+                        removeExternalProcessHandleInternalLocked(token);
+                        return true;
+                    }
+                }
+            }
+            if (!hasHandle) {
+                externalProcessNoHandleCount--;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void removeExternalProcessHandleInternalLocked(IBinder token) {
+        ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
+        handle.unlinkFromOwnDeathLocked();
+        externalProcessTokenToHandle.remove(token);
+        if (externalProcessTokenToHandle.size() == 0) {
+            externalProcessTokenToHandle = null;
+        }
+    }
+
+    public boolean hasExternalProcessHandles() {
+        return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("package=");
                 pw.print(info.applicationInfo.packageName);
@@ -73,8 +136,9 @@
                     pw.print("multiprocess="); pw.print(info.multiprocess);
                     pw.print(" initOrder="); pw.println(info.initOrder);
         }
-        if (externals != 0) {
-            pw.print(prefix); pw.print("externals="); pw.println(externals);
+        if (hasExternalProcessHandles()) {
+            pw.print(prefix); pw.print("externals=");
+            pw.println(externalProcessTokenToHandle.size());
         }
         if (clients.size() > 0) {
             pw.print(prefix); pw.println("Clients:");
@@ -84,6 +148,7 @@
         }
     }
 
+    @Override
     public String toString() {
         if (stringName != null) {
             return stringName;
@@ -96,4 +161,35 @@
         sb.append('}');
         return stringName = sb.toString();
     }
+
+    // This class represents a handle from an external process to a provider.
+    private class ExternalProcessHandle implements DeathRecipient {
+        private static final String LOG_TAG = "ExternalProcessHanldle";
+
+        private final IBinder mToken;
+        private int mAcquisitionCount;
+
+        public ExternalProcessHandle(IBinder token) {
+            mToken = token;
+            try {
+                token.linkToDeath(this, 0);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Couldn't register for death for token: " + mToken, re);
+            }
+        }
+
+        public void unlinkFromOwnDeathLocked() {
+            mToken.unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (service) {
+                if (hasExternalProcessHandles() &&
+                        externalProcessTokenToHandle.get(mToken) != null) {
+                    removeExternalProcessHandleInternalLocked(mToken);
+                }                        
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 72292be..b64261d 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -28,7 +28,9 @@
 import android.content.res.CompatibilityInfo;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.PrintWriterPrinter;
 import android.util.TimeUtils;
 
@@ -44,6 +46,9 @@
 class ProcessRecord {
     final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics
     final ApplicationInfo info; // all about the first app in the process
+    final boolean isolated;     // true if this is a special isolated process
+    final int uid;              // uid of process; may be different from 'info' if isolated
+    final int userId;           // user of process.
     final String processName;   // name of the process
     // List of packages running in the process
     final HashSet<String> pkgList = new HashSet<String>();
@@ -147,6 +152,12 @@
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
 
+        pw.print(prefix); pw.print("user #"); pw.print(userId);
+                pw.print(" uid="); pw.print(info.uid);
+        if (uid != info.uid) {
+            pw.print(" ISOLATED uid="); pw.print(uid);
+        }
+        pw.println();
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
         }
@@ -267,9 +278,12 @@
     }
     
     ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread,
-            ApplicationInfo _info, String _processName) {
+            ApplicationInfo _info, String _processName, int _uid) {
         batteryStats = _batteryStats;
         info = _info;
+        isolated = _info.uid != _uid;
+        uid = _uid;
+        userId = UserId.getUserId(_uid);
         processName = _processName;
         pkgList.add(_info.packageName);
         thread = _thread;
@@ -343,7 +357,18 @@
         sb.append(':');
         sb.append(processName);
         sb.append('/');
-        sb.append(info.uid);
+        if (info.uid < Process.FIRST_APPLICATION_UID) {
+            sb.append(uid);
+        } else {
+            sb.append('u');
+            sb.append(userId);
+            sb.append('a');
+            sb.append(info.uid%Process.FIRST_APPLICATION_UID);
+            if (uid != info.uid) {
+                sb.append('i');
+                sb.append(UserId.getAppId(uid) - Process.FIRST_ISOLATED_UID);
+            }
+        }
     }
     
     public String toString() {
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index 44e7ecc..2021e0d 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -17,17 +17,22 @@
 package com.android.server.am;
 
 import android.content.ComponentName;
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.UserId;
 import android.util.Slog;
 import android.util.SparseArray;
 
+import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * Keeps track of content providers by authority (name) and class. It separates the mapping by
@@ -153,7 +158,7 @@
         }
     }
 
-    private HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
+    HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
         final int userId = optionalUserId >= 0
                 ? optionalUserId : Binder.getOrigCallingUser();
         final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
@@ -241,4 +246,102 @@
             }
         }
     }
+
+    protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+            int opti, boolean dumpAll) {
+        ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
+
+        if ("all".equals(name)) {
+            synchronized (this) {
+                for (ContentProviderRecord r1 : getProvidersByClass(-1).values()) {
+                    providers.add(r1);
+                }
+            }
+        } else {
+            ComponentName componentName = name != null
+                    ? ComponentName.unflattenFromString(name) : null;
+            int objectId = 0;
+            if (componentName == null) {
+                // Not a '/' separated full component name; maybe an object ID?
+                try {
+                    objectId = Integer.parseInt(name, 16);
+                    name = null;
+                    componentName = null;
+                } catch (RuntimeException e) {
+                }
+            }
+
+            synchronized (this) {
+                for (ContentProviderRecord r1 : getProvidersByClass(-1).values()) {
+                    if (componentName != null) {
+                        if (r1.name.equals(componentName)) {
+                            providers.add(r1);
+                        }
+                    } else if (name != null) {
+                        if (r1.name.flattenToString().contains(name)) {
+                            providers.add(r1);
+                        }
+                    } else if (System.identityHashCode(r1) == objectId) {
+                        providers.add(r1);
+                    }
+                }
+            }
+        }
+
+        if (providers.size() <= 0) {
+            return false;
+        }
+
+        boolean needSep = false;
+        for (int i=0; i<providers.size(); i++) {
+            if (needSep) {
+                pw.println();
+            }
+            needSep = true;
+            dumpProvider("", fd, pw, providers.get(i), args, dumpAll);
+        }
+        return true;
+    }
+
+    /**
+     * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider if
+     * there is a thread associated with the provider.
+     */
+    private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
+            final ContentProviderRecord r, String[] args, boolean dumpAll) {
+        String innerPrefix = prefix + "  ";
+        synchronized (this) {
+            pw.print(prefix); pw.print("PROVIDER ");
+                    pw.print(r);
+                    pw.print(" pid=");
+                    if (r.proc != null) pw.println(r.proc.pid);
+                    else pw.println("(not running)");
+            if (dumpAll) {
+                r.dump(pw, innerPrefix);
+            }
+        }
+        if (r.proc != null && r.proc.thread != null) {
+            pw.println("    Client:");
+            pw.flush();
+            try {
+                TransferPipe tp = new TransferPipe();
+                try {
+                    r.proc.thread.dumpProvider(
+                            tp.getWriteFd().getFileDescriptor(), r.provider.asBinder(), args);
+                    tp.setBufferPrefix("      ");
+                    // Short timeout, since blocking here can
+                    // deadlock with the application.
+                    tp.go(fd, 2000);
+                } finally {
+                    tp.kill();
+                }
+            } catch (IOException ex) {
+                pw.println("      Failure while dumping the provider: " + ex);
+            } catch (RemoteException ex) {
+                pw.println("      Got a RemoteException while dumping the service");
+            }
+        }
+    }
+
+
 }
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 75ba9474..daa3653 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -80,6 +80,7 @@
                             // IBinder -> ConnectionRecord of all bound clients
 
     ProcessRecord app;      // where this service is running or null.
+    ProcessRecord isolatedProc; // keep track of isolated process, if requested
     boolean isForeground;   // is service currently in foreground mode?
     int foregroundId;       // Notification ID of last foreground req.
     Notification foregroundNoti; // Notification record of foreground state.
@@ -210,6 +211,9 @@
         }
         pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
         pw.print(prefix); pw.print("app="); pw.println(app);
+        if (isolatedProc != null) {
+            pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
+        }
         if (isForeground || foregroundId != 0) {
             pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
                     pw.print(" foregroundId="); pw.print(foregroundId);
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 9772d6a..a890068 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -195,6 +195,7 @@
     private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 3;
     private static final int MSG_PROCESS_DIED = 4;
     private static final int MSG_LIMIT_REACHED = 5;
+    private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
@@ -1225,6 +1226,9 @@
             updateNotificationsLocked();
             writePolicyLocked();
         }
+
+        mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED, restrictBackground ? 1 : 0, 0)
+                .sendToTarget();
     }
 
     @Override
@@ -1573,6 +1577,20 @@
                     }
                     return true;
                 }
+                case MSG_RESTRICT_BACKGROUND_CHANGED: {
+                    final boolean restrictBackground = msg.arg1 != 0;
+                    final int length = mListeners.beginBroadcast();
+                    for (int i = 0; i < length; i++) {
+                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                        if (listener != null) {
+                            try {
+                                listener.onRestrictBackgroundChanged(restrictBackground);
+                            } catch (RemoteException e) {
+                            }
+                        }
+                    }
+                    mListeners.finishBroadcast();
+                }
                 default: {
                     return false;
                 }
diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java
index e7ba358..240cc1c 100644
--- a/services/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/java/com/android/server/net/NetworkStatsRecorder.java
@@ -51,6 +51,7 @@
 public class NetworkStatsRecorder {
     private static final String TAG = "NetworkStatsRecorder";
     private static final boolean LOGD = true;
+    private static final boolean LOGV = false;
 
     private final FileRotator mRotator;
     private final NonMonotonicObserver<String> mObserver;
@@ -170,7 +171,7 @@
 
         mLastSnapshot = snapshot;
 
-        if (LOGD && unknownIfaces.size() > 0) {
+        if (LOGV && unknownIfaces.size() > 0) {
             Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
         }
     }
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 38c128c..7169015 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -164,13 +164,9 @@
     private static final boolean DEBUG_APP_DIR_OBSERVER = false;
     private static final boolean DEBUG_VERIFY = false;
 
-    static final boolean MULTIPLE_APPLICATION_UIDS = true;
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
-    static final int FIRST_APPLICATION_UID =
-        Process.FIRST_APPLICATION_UID;
-    static final int MAX_APPLICATION_UIDS = 1000;
 
     private static final boolean GET_CERTIFICATES = true;
 
@@ -874,18 +870,9 @@
         mSettings = new Settings();
         mSettings.addSharedUserLPw("android.uid.system",
                 Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
-        mSettings.addSharedUserLPw("android.uid.phone",
-                MULTIPLE_APPLICATION_UIDS
-                        ? RADIO_UID : FIRST_APPLICATION_UID,
-                ApplicationInfo.FLAG_SYSTEM);
-        mSettings.addSharedUserLPw("android.uid.log",
-                MULTIPLE_APPLICATION_UIDS
-                        ? LOG_UID : FIRST_APPLICATION_UID,
-                ApplicationInfo.FLAG_SYSTEM);
-        mSettings.addSharedUserLPw("android.uid.nfc",
-                MULTIPLE_APPLICATION_UIDS
-                        ? NFC_UID : FIRST_APPLICATION_UID,
-                ApplicationInfo.FLAG_SYSTEM);
+        mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM);
+        mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM);
+        mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM);
 
         String separateProcesses = SystemProperties.get("debug.separate_processes");
         if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -2274,8 +2261,6 @@
             }
         }
 
-        final int userId = UserId.getUserId(Binder.getCallingUid());
-
         if (comp != null) {
             final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
             final ActivityInfo ai = getActivityInfo(comp, flags);
@@ -6446,10 +6431,6 @@
             // package that we deleted.
             if(deletedPkg) {
                 File restoreFile = new File(deletedPackage.mPath);
-                if (restoreFile == null) {
-                    Slog.e(TAG, "Failed allocating storage when restoring pkg : " + pkgName);
-                    return;
-                }
                 // Parse old package
                 boolean oldOnSd = isExternal(deletedPackage);
                 int oldParseFlags  = mDefParseFlags | PackageParser.PARSE_CHATTY |
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 3616aa2..ebf954b 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -200,11 +200,7 @@
                 return null;
             }
             s = new SharedUserSetting(name, pkgFlags);
-            if (PackageManagerService.MULTIPLE_APPLICATION_UIDS) {
-                s.userId = newUserIdLPw(s);
-            } else {
-                s.userId = PackageManagerService.FIRST_APPLICATION_UID;
-            }
+            s.userId = newUserIdLPw(s);
             Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId);
             // < 0 means we couldn't assign a userid; fall out and return
             // s, which is currently null
@@ -407,7 +403,7 @@
                 }
                 if (sharedUser != null) {
                     p.userId = sharedUser.userId;
-                } else if (PackageManagerService.MULTIPLE_APPLICATION_UIDS) {
+                } else {
                     // Clone the setting here for disabled system packages
                     PackageSetting dis = mDisabledSysPackages.get(name);
                     if (dis != null) {
@@ -430,8 +426,6 @@
                         // Assign new user id
                         p.userId = newUserIdLPw(p);
                     }
-                } else {
-                    p.userId = PackageManagerService.FIRST_APPLICATION_UID;
                 }
             }
             if (p.userId < 0) {
@@ -598,13 +592,13 @@
     }
 
     private boolean addUserIdLPw(int uid, Object obj, Object name) {
-        if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) {
+        if (uid > Process.LAST_APPLICATION_UID) {
             return false;
         }
 
-        if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+        if (uid >= Process.FIRST_APPLICATION_UID) {
             int N = mUserIds.size();
-            final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+            final int index = uid - Process.FIRST_APPLICATION_UID;
             while (index >= N) {
                 mUserIds.add(null);
                 N++;
@@ -629,9 +623,9 @@
     }
 
     public Object getUserIdLPr(int uid) {
-        if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+        if (uid >= Process.FIRST_APPLICATION_UID) {
             final int N = mUserIds.size();
-            final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+            final int index = uid - Process.FIRST_APPLICATION_UID;
             return index < N ? mUserIds.get(index) : null;
         } else {
             return mOtherUserIds.get(uid);
@@ -639,9 +633,9 @@
     }
 
     private void removeUserIdLPw(int uid) {
-        if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+        if (uid >= Process.FIRST_APPLICATION_UID) {
             final int N = mUserIds.size();
-            final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+            final int index = uid - Process.FIRST_APPLICATION_UID;
             if (index < N) mUserIds.set(index, null);
         } else {
             mOtherUserIds.remove(uid);
@@ -649,9 +643,9 @@
     }
 
     private void replaceUserIdLPw(int uid, Object obj) {
-        if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+        if (uid >= Process.FIRST_APPLICATION_UID) {
             final int N = mUserIds.size();
-            final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+            final int index = uid - Process.FIRST_APPLICATION_UID;
             if (index < N) mUserIds.set(index, obj);
         } else {
             mOtherUserIds.put(uid, obj);
@@ -1898,17 +1892,17 @@
         for (int i = 0; i < N; i++) {
             if (mUserIds.get(i) == null) {
                 mUserIds.set(i, obj);
-                return PackageManagerService.FIRST_APPLICATION_UID + i;
+                return Process.FIRST_APPLICATION_UID + i;
             }
         }
 
         // None left?
-        if (N >= PackageManagerService.MAX_APPLICATION_UIDS) {
+        if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
             return -1;
         }
 
         mUserIds.add(obj);
-        return PackageManagerService.FIRST_APPLICATION_UID + N;
+        return Process.FIRST_APPLICATION_UID + N;
     }
 
     public VerifierDeviceIdentity getVerifierDeviceIdentityLPw() {
@@ -2018,6 +2012,39 @@
         return false;
     }
 
+    static final void printFlags(PrintWriter pw, int val, Object[] spec) {
+        pw.print("[ ");
+        for (int i=0; i<spec.length; i+=2) {
+            int mask = (Integer)spec[i];
+            if ((val & mask) != 0) {
+                pw.print(spec[i+1]);
+                pw.print(" ");
+            }
+        }
+        pw.print("]");
+    }
+
+    static final Object[] FLAG_DUMP_SPEC = new Object[] {
+        ApplicationInfo.FLAG_SYSTEM, "SYSTEM",
+        ApplicationInfo.FLAG_DEBUGGABLE, "DEBUGGABLE",
+        ApplicationInfo.FLAG_HAS_CODE, "HAS_CODE",
+        ApplicationInfo.FLAG_PERSISTENT, "PERSISTENT",
+        ApplicationInfo.FLAG_FACTORY_TEST, "FACTORY_TEST",
+        ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING, "ALLOW_TASK_REPARENTING",
+        ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA, "ALLOW_CLEAR_USER_DATA",
+        ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, "UPDATED_SYSTEM_APP",
+        ApplicationInfo.FLAG_TEST_ONLY, "TEST_ONLY",
+        ApplicationInfo.FLAG_VM_SAFE_MODE, "VM_SAFE_MODE",
+        ApplicationInfo.FLAG_ALLOW_BACKUP, "ALLOW_BACKUP",
+        ApplicationInfo.FLAG_KILL_AFTER_RESTORE, "KILL_AFTER_RESTORE",
+        ApplicationInfo.FLAG_RESTORE_ANY_VERSION, "RESTORE_ANY_VERSION",
+        ApplicationInfo.FLAG_EXTERNAL_STORAGE, "EXTERNAL_STORAGE",
+        ApplicationInfo.FLAG_LARGE_HEAP, "LARGE_HEAP",
+        ApplicationInfo.FLAG_STOPPED, "STOPPED",
+        ApplicationInfo.FLAG_FORWARD_LOCK, "FORWARD_LOCK",
+        ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
+    };
+
     void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState) {
         final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         final Date date = new Date();
@@ -2058,6 +2085,7 @@
             pw.print("    nativeLibraryPath="); pw.println(ps.nativeLibraryPathString);
             pw.print("    versionCode="); pw.println(ps.versionCode);
             if (ps.pkg != null) {
+                pw.print("    flags="); printFlags(pw, ps.pkg.applicationInfo.flags, FLAG_DUMP_SPEC); pw.println();
                 pw.print("    versionName="); pw.println(ps.pkg.mVersionName);
                 pw.print("    dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
                 pw.print("    targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion);
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 8f51466..ed83fbe 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -46,6 +46,7 @@
 import android.os.SystemProperties;
 import android.os.UEventObserver;
 import android.provider.Settings;
+import android.util.Pair;
 import android.util.Slog;
 
 import java.io.File;
@@ -54,7 +55,10 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * UsbDeviceManager manages USB state in device mode.
@@ -88,6 +92,8 @@
     // which need debouncing.
     private static final int UPDATE_DELAY = 1000;
 
+    private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
+
     private UsbHandler mHandler;
     private boolean mBootCompleted;
 
@@ -98,6 +104,7 @@
     private final boolean mHasUsbAccessory;
     private boolean mUseUsbNotification;
     private boolean mAdbEnabled;
+    private Map<String, List<Pair<String, String>>> mOemModeMap;
 
     private class AdbSettingsObserver extends ContentObserver {
         public AdbSettingsObserver() {
@@ -138,6 +145,8 @@
         mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
         initRndisAddress();
 
+        readOemUsbOverrideConfig();
+
         // create a thread for our Handler
         HandlerThread thread = new HandlerThread("UsbDeviceManager",
                 Process.THREAD_PRIORITY_BACKGROUND);
@@ -259,6 +268,10 @@
                 // persist.sys.usb.config should never be unset.  But if it is, set it to "adb"
                 // so we have a chance of debugging what happened.
                 mDefaultFunctions = SystemProperties.get("persist.sys.usb.config", "adb");
+
+                // Check if USB mode needs to be overridden depending on OEM specific bootmode.
+                mDefaultFunctions = processOemUsbOverride(mDefaultFunctions);
+
                 // sanity check the sys.usb.config system property
                 // this may be necessary if we crashed while switching USB configurations
                 String config = SystemProperties.get("sys.usb.config", "none");
@@ -381,7 +394,11 @@
         }
 
         private void setEnabledFunctions(String functions, boolean makeDefault) {
-            if (functions != null && makeDefault) {
+
+            // Do not update persystent.sys.usb.config if the device is booted up
+            // with OEM specific mode.
+            if (functions != null && makeDefault && !needsOemUsbOverride()) {
+
                 if (mAdbEnabled) {
                     functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB);
                 } else {
@@ -410,6 +427,10 @@
                 if (functions == null) {
                     functions = mDefaultFunctions;
                 }
+
+                // Override with bootmode specific usb mode if needed
+                functions = processOemUsbOverride(functions);
+
                 if (mAdbEnabled) {
                     functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB);
                 } else {
@@ -671,6 +692,53 @@
         }
     }
 
+    private void readOemUsbOverrideConfig() {
+        String[] configList = mContext.getResources().getStringArray(
+            com.android.internal.R.array.config_oemUsbModeOverride);
+
+        if (configList != null) {
+            for (String config: configList) {
+                String[] items = config.split(":");
+                if (items.length == 3) {
+                    if (mOemModeMap == null) {
+                        mOemModeMap = new HashMap<String, List<Pair<String, String>>>();
+                    }
+                    List overrideList = mOemModeMap.get(items[0]);
+                    if (overrideList == null) {
+                        overrideList = new LinkedList<Pair<String, String>>();
+                        mOemModeMap.put(items[0], overrideList);
+                    }
+                    overrideList.add(new Pair<String, String>(items[1], items[2]));
+                }
+            }
+        }
+    }
+
+    private boolean needsOemUsbOverride() {
+        if (mOemModeMap == null) return false;
+
+        String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+        return (mOemModeMap.get(bootMode) != null) ? true : false;
+    }
+
+    private String processOemUsbOverride(String usbFunctions) {
+        if ((usbFunctions == null) || (mOemModeMap == null)) return usbFunctions;
+
+        String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+
+        List<Pair<String, String>> overrides = mOemModeMap.get(bootMode);
+        if (overrides != null) {
+            for (Pair<String, String> pair: overrides) {
+                if (pair.first.equals(usbFunctions)) {
+                    Slog.d(TAG, "OEM USB override: " + pair.first + " ==> " + pair.second);
+                    return pair.second;
+                }
+            }
+        }
+        // return passed in functions as is.
+        return usbFunctions;
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw) {
         if (mHandler != null) {
             mHandler.dump(fd, pw);
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index a3293e8..a9d4e01 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -180,7 +180,9 @@
 
     public void printTo(String prefix, PrintWriter pw) {
         pw.print(prefix);
-        pw.print("mDimSurface="); pw.println(mDimSurface);
+        pw.print("mDimSurface="); pw.print(mDimSurface);
+                pw.print(" "); pw.print(mLastDimWidth); pw.print(" x ");
+                pw.println(mLastDimHeight);
         pw.print(prefix);
         pw.print("mDimShown="); pw.print(mDimShown);
         pw.print(" current="); pw.print(mDimCurrentAlpha);
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index a4f0a0c2..56c3519 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -668,25 +668,6 @@
         }
 
         @SuppressWarnings("unused")
-        public int getMaxEventsPerSecond() {
-            int result = 0;
-            try {
-                result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec"));
-            } catch (NumberFormatException e) {
-            }
-            if (result < 1) {
-                // This number equates to the refresh rate * 1.5. The rate should be at least
-                // equal to the screen refresh rate. We increase the rate by 50% to compensate for
-                // the discontinuity between the actual rate that events come in at (they do
-                // not necessarily come in constantly and are not handled synchronously).
-                // Ideally, we would use Display.getRefreshRate(), but as this does not necessarily
-                // return a sensible result, we use '60' as our default assumed refresh rate.
-                result = 90;
-            }
-            return result;
-        }
-
-        @SuppressWarnings("unused")
         public int getPointerLayer() {
             return mWindowManagerService.mPolicy.windowTypeToLayerLw(
                     WindowManager.LayoutParams.TYPE_POINTER)
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 55fb038..04a039f 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import java.io.PrintWriter;
+
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
@@ -96,6 +98,46 @@
     final Matrix mTmpMatrix = new Matrix();
     final float[] mTmpFloats = new float[9];
 
+    public void printTo(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("mSurface="); pw.print(mSurface);
+                pw.print(" mWidth="); pw.print(mWidth);
+                pw.print(" mHeight="); pw.println(mHeight);
+        pw.print(prefix); pw.print("mBlackFrame="); pw.println(mBlackFrame);
+        pw.print(prefix); pw.print("mSnapshotRotation="); pw.print(mSnapshotRotation);
+                pw.print(" mSnapshotDeltaRotation="); pw.print(mSnapshotDeltaRotation);
+                pw.print(" mCurRotation="); pw.println(mCurRotation);
+        pw.print(prefix); pw.print("mOriginalRotation="); pw.print(mOriginalRotation);
+                pw.print(" mOriginalWidth="); pw.print(mOriginalWidth);
+                pw.print(" mOriginalHeight="); pw.println(mOriginalHeight);
+        pw.print(prefix); pw.print("mStarted="); pw.print(mStarted);
+                pw.print(" mAnimRunning="); pw.print(mAnimRunning);
+                pw.print(" mFinishAnimReady="); pw.print(mFinishAnimReady);
+                pw.print(" mFinishAnimStartTime="); pw.println(mFinishAnimStartTime);
+        pw.print(prefix); pw.print("mStartExitAnimation="); pw.print(mStartExitAnimation);
+                pw.print(" "); mStartExitTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mStartEnterAnimation="); pw.print(mStartEnterAnimation);
+                pw.print(" "); mStartEnterTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mFinishExitAnimation="); pw.print(mFinishExitAnimation);
+                pw.print(" "); mFinishExitTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mFinishEnterAnimation="); pw.print(mFinishEnterAnimation);
+                pw.print(" "); mFinishEnterTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mRotateExitAnimation="); pw.print(mRotateExitAnimation);
+                pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
+                pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mLastRotateExitAnimation=");
+                pw.print(mLastRotateExitAnimation);
+                pw.print(" "); mLastRotateExitTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mExitTransformation=");
+                mExitTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mEnterTransformation=");
+                mEnterTransformation.printShortString(pw); pw.println();
+        pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
+                mSnapshotInitialMatrix.printShortString(pw);
+                pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw);
+                pw.println();
+    }
+
     public ScreenRotationAnimation(Context context, SurfaceSession session,
             boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) {
         mContext = context;
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 04c1c98..21cb3e8 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -142,8 +142,7 @@
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
-        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
-        Choreographer.OnAnimateListener {
+        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
     static final String TAG = "WindowManager";
     static final boolean DEBUG = false;
     static final boolean DEBUG_ADD_REMOVE = false;
@@ -250,6 +249,7 @@
 
     private static final String SYSTEM_SECURE = "ro.secure";
     private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+    private static final String SYSTEM_HEADLESS = "ro.config.headless";
 
     /**
      * Condition waited on by {@link #reenableKeyguard} to know the call to
@@ -259,6 +259,8 @@
      */
     private boolean mKeyguardDisabled = false;
 
+    private final boolean mHeadless;
+
     private static final int ALLOW_DISABLE_YES = 1;
     private static final int ALLOW_DISABLE_NO = 0;
     private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager
@@ -266,6 +268,7 @@
 
     final TokenWatcher mKeyguardTokenWatcher = new TokenWatcher(
             new Handler(), "WindowManagerService.mKeyguardTokenWatcher") {
+        @Override
         public void acquired() {
             if (shouldAllowDisableKeyguard()) {
                 mPolicy.enableKeyguard(false);
@@ -274,6 +277,7 @@
                 Log.v(TAG, "Not disabling keyguard since device policy is enforced");
             }
         }
+        @Override
         public void released() {
             mPolicy.enableKeyguard(true);
             synchronized (mKeyguardTokenWatcher) {
@@ -597,9 +601,22 @@
         private boolean mSyswin = false;
         private float mScreenBrightness = -1;
         private float mButtonBrightness = -1;
+        private boolean mUpdateRotation = false;
     }
     private LayoutAndSurfaceFields mInnerFields = new LayoutAndSurfaceFields();
 
+    private final class AnimationRunnable implements Runnable {
+        @Override
+        public void run() {
+            synchronized(mWindowMap) {
+                mAnimationScheduled = false;
+                performLayoutAndPlaceSurfacesLocked();
+            }
+        }
+    }
+    final AnimationRunnable mAnimationRunnable = new AnimationRunnable();
+    boolean mAnimationScheduled;
+
     final class DragInputEventReceiver extends InputEventReceiver {
         public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
@@ -721,7 +738,6 @@
             Looper.prepare();
             WindowManagerService s = new WindowManagerService(mContext, mPM,
                     mHaveInputMethods, mAllowBootMessages);
-            s.mChoreographer.addOnAnimateListener(s);
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_DISPLAY);
             android.os.Process.setCanSelfBackground(false);
@@ -789,6 +805,7 @@
         mAllowBootMessages = showBootMsgs;
         mLimitedAlphaCompositing = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_sf_limitedAlpha);
+        mHeadless = "1".equals(SystemProperties.get(SYSTEM_HEADLESS, "0"));
 
         mPowerManager = pm;
         mPowerManager.setPolicy(mPolicy);
@@ -4889,7 +4906,7 @@
 
     public void performBootTimeout() {
         synchronized(mWindowMap) {
-            if (mDisplayEnabled) {
+            if (mDisplayEnabled || mHeadless) {
                 return;
             }
             Slog.w(TAG, "***** BOOT TIMEOUT: forcing display enabled");
@@ -5057,6 +5074,8 @@
     // TODO: more accounting of which pid(s) turned it on, keep count,
     // only allow disables from pids which have count on, etc.
     public void showStrictModeViolation(boolean on) {
+        if (mHeadless) return;
+
         int pid = Binder.getCallingPid();
         synchronized(mWindowMap) {
             // Ignoring requests to enable the red border from clients
@@ -5435,7 +5454,7 @@
                 if (mScreenRotationAnimation.setRotation(rotation, mFxSession,
                         MAX_ANIMATION_DURATION, mTransitionAnimationScale,
                         mCurDisplayWidth, mCurDisplayHeight)) {
-                    mChoreographer.scheduleAnimation();
+                    scheduleAnimationLocked();
                 }
             }
             Surface.setOrientation(0, rotation);
@@ -6361,7 +6380,7 @@
                 INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
-        return reportInjectionResult(result);
+        return reportInjectionResult(result, pid);
     }
 
     /**
@@ -6391,7 +6410,7 @@
                 INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
-        return reportInjectionResult(result);
+        return reportInjectionResult(result, pid);
     }
 
     /**
@@ -6421,7 +6440,7 @@
                 INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
-        return reportInjectionResult(result);
+        return reportInjectionResult(result, pid);
     }
     
     /**
@@ -6442,24 +6461,23 @@
                 INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
-        return reportInjectionResult(result);
+        return reportInjectionResult(result, pid);
     }
     
-    private boolean reportInjectionResult(int result) {
+    private boolean reportInjectionResult(int result, int pid) {
         switch (result) {
             case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED:
-                Slog.w(TAG, "Input event injection permission denied.");
+                Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
                 throw new SecurityException(
                         "Injecting to another application requires INJECT_EVENTS permission");
             case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
-                //Slog.v(TAG, "Input event injection succeeded.");
                 return true;
             case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
-                Slog.w(TAG, "Input event injection timed out.");
+                Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
                 return false;
             case InputManager.INPUT_EVENT_INJECTION_FAILED:
             default:
-                Slog.w(TAG, "Input event injection failed.");
+                Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
                 return false;
         }
     }
@@ -6876,7 +6894,7 @@
 
                 case FORCE_GC: {
                     synchronized(mWindowMap) {
-                        if (mChoreographer.isAnimationScheduled()) {
+                        if (mAnimationScheduled) {
                             // If we are animating, don't do the gc now but
                             // delay a bit so we don't interrupt the animation.
                             mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
@@ -7605,51 +7623,53 @@
      * @param innerDh Height of app window.
      * @return true if rotation has stopped, false otherwise
      */
-    private boolean updateAppsAndRotationAnimationsLocked(long currentTime,
+    private void updateWindowsAppsAndRotationAnimationsLocked(long currentTime,
                                                           int innerDw, int innerDh) {
         int i;
+        for (i = mWindows.size() - 1; i >= 0; i--) {
+            mInnerFields.mAnimating |= mWindows.get(i).stepAnimationLocked(currentTime);
+        }
+
         final int NAT = mAppTokens.size();
         for (i=0; i<NAT; i++) {
-            if (mAppTokens.get(i).stepAnimationLocked(currentTime,
-                    innerDw, innerDh)) {
-                mInnerFields.mAnimating = true;
-            }
+            mInnerFields.mAnimating |=
+                    mAppTokens.get(i).stepAnimationLocked(currentTime, innerDw, innerDh);
         }
         final int NEAT = mExitingAppTokens.size();
         for (i=0; i<NEAT; i++) {
-            if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime,
-                    innerDw, innerDh)) {
-                mInnerFields.mAnimating = true;
-            }
+            mInnerFields.mAnimating |=
+                    mExitingAppTokens.get(i).stepAnimationLocked(currentTime, innerDw, innerDh);
         }
 
-        boolean updateRotation = false;
         if (mScreenRotationAnimation != null) {
             if (mScreenRotationAnimation.isAnimating()) {
                 if (mScreenRotationAnimation.stepAnimation(currentTime)) {
+                    mInnerFields.mUpdateRotation = false;
                     mInnerFields.mAnimating = true;
                 } else {
-                    updateRotation = true;
+                    mInnerFields.mUpdateRotation = true;
+                    mScreenRotationAnimation.kill();
+                    mScreenRotationAnimation = null;
                 }
             }
         }
-
-        return updateRotation;
     }
 
     /**
      * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
      *
      * @param currentTime The time which animations use for calculating transitions.
+     * @param dw Width of app window.
+     * @param dh Height of app window.
      * @param innerDw Width of app window.
      * @param innerDh Height of app window.
      */
-    private void updateWindowsAndWallpaperLocked(final long currentTime,
-                                                 final int innerDw, final int innerDh) {
-        int i;
-        final int N = mWindows.size();
+    private int updateWindowsAndWallpaperLocked(final long currentTime, final int dw, final int dh,
+                                                final int innerDw, final int innerDh) {
 
-        for (i=N-1; i>=0; i--) {
+        mPolicy.beginAnimationLw(dw, dh);
+
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
             WindowState w = mWindows.get(i);
 
             final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -7667,9 +7687,6 @@
 
                 final boolean wasAnimating = w.mAnimating;
 
-                int animDw = innerDw;
-                int animDh = innerDh;
-
                 // If the window has moved due to its containing
                 // content frame changing, then we'd like to animate
                 // it.  The checks here are ordered by what is least
@@ -7681,13 +7698,15 @@
                     Animation a = AnimationUtils.loadAnimation(mContext,
                             com.android.internal.R.anim.window_move_from_decor);
                     w.setAnimation(a);
-                    animDw = w.mLastFrame.left - w.mFrame.left;
-                    animDh = w.mLastFrame.top - w.mFrame.top;
+                    w.mAnimDw = w.mLastFrame.left - w.mFrame.left;
+                    w.mAnimDh = w.mLastFrame.top - w.mFrame.top;
+                } else {
+                    w.mAnimDw = innerDw;
+                    w.mAnimDh = innerDh;
                 }
 
                 // Execute animation.
-                final boolean nowAnimating = w.stepAnimationLocked(currentTime,
-                        animDw, animDh);
+                final boolean nowAnimating = w.isAnimating();
 
                 // If this window is animating, make a note that we have
                 // an animating window and take care of a request to run
@@ -7829,6 +7848,8 @@
                 w.performShowLocked();
             }
         } // end forall windows
+
+        return mPolicy.finishAnimationLw();
     }
 
     /**
@@ -8099,7 +8120,7 @@
      *
      * @return bitmap indicating if another pass through layout must be made.
      */
-    private int handleAnimatingAndTransitionLocked() {
+    private int handleAnimatingStoppedAndTransitionLocked() {
         int changes = 0;
 
         mAppTransitionRunning = false;
@@ -8549,6 +8570,11 @@
                     if (mDimAnimator == null) {
                         mDimAnimator = new DimAnimator(mFxSession);
                     }
+                    if (attrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
+                        mDimAnimator.show(mCurDisplayWidth, mCurDisplayHeight);
+                    } else {
+                        mDimAnimator.show(innerDw, innerDh);
+                    }
                     mDimAnimator.show(innerDw, innerDh);
                     mDimAnimator.updateParameters(mContext.getResources(),
                             w, currentTime);
@@ -8635,7 +8661,6 @@
         boolean focusDisplayed = false;
         mInnerFields.mAnimating = false;
         boolean createWatermark = false;
-        boolean updateRotation = false;
 
         if (mFxSession == null) {
             mFxSession = new SurfaceSession();
@@ -8689,22 +8714,13 @@
 
                 // FIRST LOOP: Perform a layout, if needed.
                 if (repeats < 4) {
-                    performLayoutLockedInner(repeats == 0, false /*updateInputWindows*/);
+                    performLayoutLockedInner(repeats == 1, false /*updateInputWindows*/);
                 } else {
                     Slog.w(TAG, "Layout repeat skipped after too many iterations");
                 }
 
-                changes = 0;
                 ++mTransactionSequence;
 
-                // Update animations of all applications, including those
-                // associated with exiting/removed apps
-                mInnerFields.mAnimating = false;
-
-                // SECOND LOOP: Execute animations and update visibility of windows.
-                updateRotation =
-                        updateAppsAndRotationAnimationsLocked(currentTime, innerDw, innerDh);
-
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
                         + mTransactionSequence + " mAnimating="
                         + mInnerFields.mAnimating);
@@ -8716,11 +8732,7 @@
                 mInnerFields.mWindowAnimationBackground = null;
                 mInnerFields.mWindowAnimationBackgroundColor = 0;
 
-                mPolicy.beginAnimationLw(dw, dh);
-
-                updateWindowsAndWallpaperLocked(currentTime, innerDw, innerDh);
-
-                changes |= mPolicy.finishAnimationLw();
+                changes = updateWindowsAndWallpaperLocked(currentTime, dw, dh, innerDw, innerDh);
 
                 if (mInnerFields.mTokenMayBeDrawn) {
                     changes |= testTokenMayBeDrawnLocked();
@@ -8742,7 +8754,7 @@
                     // reflects the correct Z-order, but the window list may now
                     // be out of sync with it.  So here we will just rebuild the
                     // entire app window list.  Fun!
-                    changes |= handleAnimatingAndTransitionLocked();
+                    changes |= handleAnimatingStoppedAndTransitionLocked();
                 }
 
                 if (mInnerFields.mWallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
@@ -8765,6 +8777,12 @@
                         + Integer.toHexString(changes));
             } while (changes != 0);
 
+            // Update animations of all applications, including those
+            // associated with exiting/removed apps
+            mInnerFields.mAnimating = false;
+
+            updateWindowsAppsAndRotationAnimationsLocked(currentTime, innerDw, innerDh);
+
             // THIRD LOOP: Update the surfaces of all windows.
 
             final boolean someoneLosingFocus = mLosingFocus.size() != 0;
@@ -8972,7 +8990,7 @@
         if (needRelayout) {
             requestTraversalLocked();
         } else if (mInnerFields.mAnimating) {
-            mChoreographer.scheduleAnimation();
+            scheduleAnimationLocked();
         }
 
         // Finally update all input windows now that the window changes have stabilized.
@@ -9006,21 +9024,17 @@
             mTurnOnScreen = false;
         }
 
-        if (updateRotation && mScreenRotationAnimation != null) {
-            mScreenRotationAnimation.kill();
-            mScreenRotationAnimation = null;
-        }
-
-        if (updateRotation) {
+        if (mInnerFields.mUpdateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
             if (updateRotationUncheckedLocked(false)) {
                 mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
             } else {
-                updateRotation = false;
+                mInnerFields.mUpdateRotation = false;
             }
         }
 
-        if (mInnerFields.mOrientationChangeComplete && !needRelayout && !updateRotation) {
+        if (mInnerFields.mOrientationChangeComplete && !needRelayout &&
+                !mInnerFields.mUpdateRotation) {
             checkDrawnWindowsLocked();
         }
 
@@ -9097,10 +9111,10 @@
         }
     }
 
-    @Override
-    public void onAnimate() {
-        synchronized(mWindowMap) {
-            performLayoutAndPlaceSurfacesLocked();
+    void scheduleAnimationLocked() {
+        if (!mAnimationScheduled) {
+            mChoreographer.postAnimationCallback(mAnimationRunnable);
+            mAnimationScheduled = true;
         }
     }
 
@@ -9381,7 +9395,7 @@
         }
 
         if (CUSTOM_SCREEN_ROTATION) {
-            if (mScreenRotationAnimation != null && mScreenRotationAnimation.isAnimating()) {
+            if (mScreenRotationAnimation != null) {
                 mScreenRotationAnimation.kill();
                 mScreenRotationAnimation = null;
             }
@@ -9420,7 +9434,7 @@
             if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation");
             if (mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
                     mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) {
-                mChoreographer.scheduleAnimation();
+                scheduleAnimationLocked();
             } else {
                 mScreenRotationAnimation = null;
                 updateRotation = true;
@@ -9912,11 +9926,15 @@
             pw.print("  mLastWindowForcedOrientation"); pw.print(mLastWindowForcedOrientation);
                     pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
             pw.print("  mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
-            pw.print("  mTraversalScheduled="); pw.print(mTraversalScheduled);
-                    pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale);
-                    pw.print(" mTransitionWindowAnimationScale="); pw.println(mTransitionAnimationScale);
+            if (mScreenRotationAnimation != null) {
+                pw.println("  mScreenRotationAnimation:");
+                mScreenRotationAnimation.printTo("    ", pw);
+            }
+            pw.print("  mWindowAnimationScale="); pw.print(mWindowAnimationScale);
+                    pw.print(" mTransitionWindowAnimationScale="); pw.print(mTransitionAnimationScale);
                     pw.print(" mAnimatorDurationScale="); pw.println(mAnimatorDurationScale);
-            pw.print("  mNextAppTransition=0x");
+            pw.print("  mTraversalScheduled="); pw.print(mTraversalScheduled);
+                    pw.print("  mNextAppTransition=0x");
                     pw.print(Integer.toHexString(mNextAppTransition));
                     pw.print(" mAppTransitionReady="); pw.println(mAppTransitionReady);
             pw.print("  mAppTransitionRunning="); pw.print(mAppTransitionRunning);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 6868cf6..d7a7cb0 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -297,6 +297,11 @@
     CharSequence mLastTitle;
     boolean mWasPaused;
 
+    // Used to save animation distances between the time they are calculated and when they are 
+    // used.
+    int mAnimDw;
+    int mAnimDh;
+
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState attachedWindow, int seq, WindowManager.LayoutParams a,
            int viewVisibility) {
@@ -973,7 +978,7 @@
 
     // This must be called while inside a transaction.  Returns true if
     // there is more animation to run.
-    boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+    boolean stepAnimationLocked(long currentTime) {
         if (!mService.mDisplayFrozen && mService.mPolicy.isScreenOnFully()) {
             // We will run animations as long as the display isn't frozen.
 
@@ -985,8 +990,9 @@
                         WindowManagerService.TAG, "Starting animation in " + this +
                         " @ " + currentTime + ": ww=" + mFrame.width() +
                         " wh=" + mFrame.height() +
-                        " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale);
-                    mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
+                        " dw=" + mAnimDw + " dh=" + mAnimDh +
+                        " scale=" + mService.mWindowAnimationScale);
+                    mAnimation.initialize(mFrame.width(), mFrame.height(), mAnimDw, mAnimDh);
                     mAnimation.setStartTime(currentTime);
                     mLocalAnimating = true;
                     mAnimating = true;
@@ -1593,7 +1599,7 @@
             mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
         }
         if (requestAnim) {
-            mService.mChoreographer.scheduleAnimation();
+            mService.scheduleAnimationLocked();
         }
         return true;
     }
@@ -1634,7 +1640,7 @@
             }
         }
         if (requestAnim) {
-            mService.mChoreographer.scheduleAnimation();
+            mService.scheduleAnimationLocked();
         }
         return true;
     }
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 6fa5dfa..c02dd36 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -9,6 +9,7 @@
     com_android_server_InputWindowHandle.cpp \
     com_android_server_LightsService.cpp \
     com_android_server_PowerManagerService.cpp \
+    com_android_server_SerialService.cpp \
     com_android_server_SystemServer.cpp \
     com_android_server_UsbDeviceManager.cpp \
     com_android_server_UsbHostManager.cpp \
@@ -25,6 +26,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
+    libandroidfw \
     libcutils \
     libhardware \
     libhardware_legacy \
diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
index 1cadc4e..ca6f206 100644
--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -233,75 +233,75 @@
     DIR* dir = opendir(POWER_SUPPLY_PATH);
     if (dir == NULL) {
         ALOGE("Could not open %s\n", POWER_SUPPLY_PATH);
-        return -1;
-    }
-    while ((entry = readdir(dir))) {
-        const char* name = entry->d_name;
+    } else {
+        while ((entry = readdir(dir))) {
+            const char* name = entry->d_name;
 
-        // ignore "." and ".."
-        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
-            continue;
-        }
-
-        char buf[20];
-        // Look for "type" file in each subdirectory
-        snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name);
-        int length = readFromFile(path, buf, sizeof(buf));
-        if (length > 0) {
-            if (buf[length - 1] == '\n')
-                buf[length - 1] = 0;
-
-            if (strcmp(buf, "Mains") == 0) {
-                snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0)
-                    gPaths.acOnlinePath = strdup(path);
+            // ignore "." and ".."
+            if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+                continue;
             }
-            else if (strcmp(buf, "USB") == 0) {
-                snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0)
-                    gPaths.usbOnlinePath = strdup(path);
-            }
-            else if (strcmp(buf, "Battery") == 0) {
-                snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0)
-                    gPaths.batteryStatusPath = strdup(path);
-                snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0)
-                    gPaths.batteryHealthPath = strdup(path);
-                snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0)
-                    gPaths.batteryPresentPath = strdup(path);
-                snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0)
-                    gPaths.batteryCapacityPath = strdup(path);
 
-                snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0) {
-                    gPaths.batteryVoltagePath = strdup(path);
-                    // voltage_now is in microvolts, not millivolts
-                    gVoltageDivisor = 1000;
-                } else {
-                    snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name);
+            char buf[20];
+            // Look for "type" file in each subdirectory
+            snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name);
+            int length = readFromFile(path, buf, sizeof(buf));
+            if (length > 0) {
+                if (buf[length - 1] == '\n')
+                    buf[length - 1] = 0;
+
+                if (strcmp(buf, "Mains") == 0) {
+                    snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
                     if (access(path, R_OK) == 0)
+                        gPaths.acOnlinePath = strdup(path);
+                }
+                else if (strcmp(buf, "USB") == 0) {
+                    snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        gPaths.usbOnlinePath = strdup(path);
+                }
+                else if (strcmp(buf, "Battery") == 0) {
+                    snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        gPaths.batteryStatusPath = strdup(path);
+                    snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        gPaths.batteryHealthPath = strdup(path);
+                    snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        gPaths.batteryPresentPath = strdup(path);
+                    snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        gPaths.batteryCapacityPath = strdup(path);
+
+                    snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name);
+                    if (access(path, R_OK) == 0) {
                         gPaths.batteryVoltagePath = strdup(path);
-                }
+                        // voltage_now is in microvolts, not millivolts
+                        gVoltageDivisor = 1000;
+                    } else {
+                        snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name);
+                        if (access(path, R_OK) == 0)
+                            gPaths.batteryVoltagePath = strdup(path);
+                    }
 
-                snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0) {
-                    gPaths.batteryTemperaturePath = strdup(path);
-                } else {
-                    snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name);
+                    if (access(path, R_OK) == 0) {
                         gPaths.batteryTemperaturePath = strdup(path);
-                }
+                    } else {
+                        snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name);
+                        if (access(path, R_OK) == 0)
+                            gPaths.batteryTemperaturePath = strdup(path);
+                    }
 
-                snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name);
-                if (access(path, R_OK) == 0)
-                    gPaths.batteryTechnologyPath = strdup(path);
+                    snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        gPaths.batteryTechnologyPath = strdup(path);
+                }
             }
         }
+        closedir(dir);
     }
-    closedir(dir);
 
     if (!gPaths.acOnlinePath)
         ALOGE("acOnlinePath not found");
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index e163826..5c3e002 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -71,7 +71,6 @@
     jmethodID getExcludedDeviceNames;
     jmethodID getKeyRepeatTimeout;
     jmethodID getKeyRepeatDelay;
-    jmethodID getMaxEventsPerSecond;
     jmethodID getHoverTapTimeout;
     jmethodID getHoverTapSlop;
     jmethodID getDoubleTapTimeout;
@@ -586,12 +585,6 @@
     if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatDelay")) {
         outConfig->keyRepeatDelay = milliseconds_to_nanoseconds(keyRepeatDelay);
     }
-
-    jint maxEventsPerSecond = env->CallIntMethod(mCallbacksObj,
-            gCallbacksClassInfo.getMaxEventsPerSecond);
-    if (!checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) {
-        outConfig->maxEventsPerSecond = maxEventsPerSecond;
-    }
 }
 
 bool NativeInputManager::isKeyRepeatEnabled() {
@@ -1480,9 +1473,6 @@
     GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, clazz,
             "getLongPressTimeout", "()I");
 
-    GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz,
-            "getMaxEventsPerSecond", "()I");
-
     GET_METHOD_ID(gCallbacksClassInfo.getPointerLayer, clazz,
             "getPointerLayer", "()I");
 
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
index af10711..cc3b5ef5 100644
--- a/services/jni/com_android_server_PowerManagerService.h
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -20,7 +20,7 @@
 #include "JNIHelp.h"
 #include "jni.h"
 
-#include <ui/PowerManager.h>
+#include <androidfw/PowerManager.h>
 
 namespace android {
 
diff --git a/services/jni/com_android_server_SerialService.cpp b/services/jni/com_android_server_SerialService.cpp
new file mode 100644
index 0000000..b889b78
--- /dev/null
+++ b/services/jni/com_android_server_SerialService.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SerialServiceJNI"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace android
+{
+
+static struct parcel_file_descriptor_offsets_t
+{
+    jclass mClass;
+    jmethodID mConstructor;
+} gParcelFileDescriptorOffsets;
+
+static jobject android_server_SerialService_open(JNIEnv *env, jobject thiz, jstring path)
+{
+    const char *pathStr = env->GetStringUTFChars(path, NULL);
+
+    int fd = open(pathStr, O_RDWR | O_NOCTTY);
+    if (fd < 0) {
+        ALOGE("could not open %s", pathStr);
+        env->ReleaseStringUTFChars(path, pathStr);
+        return NULL;
+    }
+    env->ReleaseStringUTFChars(path, pathStr);
+
+    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
+    if (fileDescriptor == NULL) {
+        return NULL;
+    }
+    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
+        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
+}
+
+
+static JNINativeMethod method_table[] = {
+    { "native_open",                "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
+                                    (void*)android_server_SerialService_open },
+};
+
+int register_android_server_SerialService(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("com/android/server/SerialService");
+    if (clazz == NULL) {
+        ALOGE("Can't find com/android/server/SerialService");
+        return -1;
+    }
+
+    clazz = env->FindClass("android/os/ParcelFileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
+    gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+    gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
+    LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
+                 "Unable to find constructor for android.os.ParcelFileDescriptor");
+
+    return jniRegisterNativeMethods(env, "com/android/server/SerialService",
+            method_table, NELEM(method_table));
+}
+
+};
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index c7beb5f..423ebd1 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -27,6 +27,7 @@
 int register_android_server_InputManager(JNIEnv* env);
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
+int register_android_server_SerialService(JNIEnv* env);
 int register_android_server_UsbDeviceManager(JNIEnv* env);
 int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
@@ -49,6 +50,7 @@
     ALOG_ASSERT(env, "Could not retrieve the env!");
 
     register_android_server_PowerManagerService(env);
+    register_android_server_SerialService(env);
     register_android_server_InputApplicationHandle(env);
     register_android_server_InputWindowHandle(env);
     register_android_server_InputManager(env);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3e6b872..4ee6953 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -42,7 +42,6 @@
 
 #define DEBUG_RESIZE    0
 
-
 namespace android {
 
 // ---------------------------------------------------------------------------
@@ -55,7 +54,7 @@
         mCurrentTransform(0),
         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
         mCurrentOpacity(true),
-        mRefreshPending(0),
+        mRefreshPending(false),
         mFrameLatencyNeeded(false),
         mFrameLatencyOffset(0),
         mFormat(PIXEL_FORMAT_NONE),
@@ -408,15 +407,9 @@
 // pageflip handling...
 // ----------------------------------------------------------------------------
 
-bool Layer::onPreComposition()
-{
-    // if there was more than one pending update, request a refresh
-    if (mRefreshPending >= 2) {
-        mRefreshPending = 0;
-        return true;
-    }
-    mRefreshPending = 0;
-    return false;
+bool Layer::onPreComposition() {
+    mRefreshPending = false;
+    return mQueuedFrames > 0;
 }
 
 void Layer::lockPageFlip(bool& recomputeVisibleRegions)
@@ -428,9 +421,11 @@
         // because we cannot call updateTeximage() without a corresponding
         // compositionComplete() call.
         // we'll trigger an update in onPreComposition().
-        if (mRefreshPending++) {
+        if (mRefreshPending) {
+            mPostedDirtyRegion.clear();
             return;
         }
+        mRefreshPending = true;
 
         // Capture the old state of the layer for comparisons later
         const bool oldOpacity = isOpaque();
@@ -541,25 +536,23 @@
 void Layer::unlockPageFlip(
         const Transform& planeTransform, Region& outDirtyRegion)
 {
-    if (mRefreshPending >= 2) {
-        return;
-    }
-
-    Region dirtyRegion(mPostedDirtyRegion);
-    if (!dirtyRegion.isEmpty()) {
+    Region postedRegion(mPostedDirtyRegion);
+    if (!postedRegion.isEmpty()) {
         mPostedDirtyRegion.clear();
-        // The dirty region is given in the layer's coordinate space
-        // transform the dirty region by the surface's transformation
-        // and the global transformation.
-        const Layer::State& s(drawingState());
-        const Transform tr(planeTransform * s.transform);
-        dirtyRegion = tr.transform(dirtyRegion);
+        if (!visibleRegionScreen.isEmpty()) {
+            // The dirty region is given in the layer's coordinate space
+            // transform the dirty region by the surface's transformation
+            // and the global transformation.
+            const Layer::State& s(drawingState());
+            const Transform tr(planeTransform * s.transform);
+            postedRegion = tr.transform(postedRegion);
 
-        // At this point, the dirty region is in screen space.
-        // Make sure it's constrained by the visible region (which
-        // is in screen space as well).
-        dirtyRegion.andSelf(visibleRegionScreen);
-        outDirtyRegion.orSelf(dirtyRegion);
+            // At this point, the dirty region is in screen space.
+            // Make sure it's constrained by the visible region (which
+            // is in screen space as well).
+            postedRegion.andSelf(visibleRegionScreen);
+            outDirtyRegion.orSelf(postedRegion);
+        }
     }
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index bf30608..39bbb2b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -116,7 +116,7 @@
     uint32_t mCurrentTransform;
     uint32_t mCurrentScalingMode;
     bool mCurrentOpacity;
-    size_t mRefreshPending;
+    bool mRefreshPending;
     bool mFrameLatencyNeeded;
     int mFrameLatencyOffset;
 
diff --git a/services/surfaceflinger/LayerScreenshot.cpp b/services/surfaceflinger/LayerScreenshot.cpp
index c127fa6..c7cf46e 100644
--- a/services/surfaceflinger/LayerScreenshot.cpp
+++ b/services/surfaceflinger/LayerScreenshot.cpp
@@ -70,6 +70,8 @@
     glBindTexture(GL_TEXTURE_2D, mTextureName);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     mTexCoords[0] = 0;         mTexCoords[1] = v;
     mTexCoords[2] = 0;         mTexCoords[3] = 0;
     mTexCoords[4] = u;         mTexCoords[5] = 0;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 40717f4..9d821dc 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1000,6 +1000,12 @@
         drawWormhole();
     }
 
+    // FIXME: workaroud for b/6020860
+    glEnable(GL_SCISSOR_TEST);
+    glScissor(0,0,0,0);
+    glClear(GL_COLOR_BUFFER_BIT);
+    // end-workaround
+
     /*
      * and then, render the layers targeted at the framebuffer
      */
@@ -1776,6 +1782,10 @@
                 setTransactionFlags(eTransactionNeeded|eTraversalNeeded);
                 return NO_ERROR;
             }
+            case 1006:{ // send empty update
+                signalRefresh();
+                return NO_ERROR;
+            }
             case 1008:  // toggle use of hw composer
                 n = data.readInt32();
                 mDebugDisableHWC = n ? 1 : 0;
@@ -1922,6 +1932,8 @@
     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
     glVertexPointer(2, GL_FLOAT, 0, vtx);
@@ -2094,6 +2106,8 @@
     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
     glVertexPointer(2, GL_FLOAT, 0, vtx);
diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
index 56b2a8f..7f3f064 100644
--- a/services/surfaceflinger/tests/resize/resize.cpp
+++ b/services/surfaceflinger/tests/resize/resize.cpp
@@ -39,7 +39,7 @@
     // create a client to surfaceflinger
     sp<SurfaceComposerClient> client = new SurfaceComposerClient();
     
-    sp<Surface> surface = client->createSurface(getpid(), 0, 160, 240, 
+    sp<Surface> surface = client->createSurface(0, 160, 240,
             PIXEL_FORMAT_RGB_565);
 
 
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
index 8e1c3fe..9c15f9b 100644
--- a/services/surfaceflinger/tests/surface/surface.cpp
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -38,7 +38,7 @@
     sp<SurfaceComposerClient> client = new SurfaceComposerClient();
     
     sp<SurfaceControl> surfaceControl = client->createSurface(
-            getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565);
+            0, 160, 240, PIXEL_FORMAT_RGB_565);
     SurfaceComposerClient::openGlobalTransaction();
     surfaceControl->setLayer(100000);
     SurfaceComposerClient::closeGlobalTransaction();
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 5da3d97..8cfdb79 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -292,6 +292,20 @@
     }
 
     /**
+     * Translates keypad letters to actual digits (e.g. 1-800-GOOG-411 will
+     * become 1-800-4664-411), and then strips all separators (e.g. 1-800-4664-411 will become
+     * 18004664411).
+     *
+     * @see #convertKeypadLettersToDigits(String)
+     * @see #stripSeparators(String)
+     *
+     * @hide
+     */
+    public static String convertAndStrip(String phoneNumber) {
+        return stripSeparators(convertKeypadLettersToDigits(phoneNumber));
+    }
+
+    /**
      * Converts pause and tonewait pause characters
      * to Android representation.
      * RFC 3601 says pause is 'p' and tonewait is 'w'.
@@ -1438,7 +1452,8 @@
      *            phoneNumber doesn't have the country code.
      * @param defaultCountryIso
      *            the ISO 3166-1 two letters country code whose convention will
-     *            be used if the phoneNumberE164 is null or invalid.
+     *            be used if the phoneNumberE164 is null or invalid, or if phoneNumber
+     *            contains IDD.
      * @return the formatted number if the given number has been formatted,
      *            otherwise, return the given number.
      *
@@ -1457,9 +1472,13 @@
         if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
                 && phoneNumberE164.charAt(0) == '+') {
             try {
-                PhoneNumber pn = util.parse(phoneNumberE164, defaultCountryIso);
+                // The number to be parsed is in E164 format, so the default region used doesn't
+                // matter.
+                PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
                 String regionCode = util.getRegionCodeForNumber(pn);
-                if (!TextUtils.isEmpty(regionCode)) {
+                if (!TextUtils.isEmpty(regionCode) &&
+                    // This makes sure phoneNumber doesn't contain an IDD
+                    normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
                     defaultCountryIso = regionCode;
                 }
             } catch (NumberParseException e) {
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index 893d7a8..35cdf9bd 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -715,4 +715,7 @@
                 "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'");
         return retVal;
     }
+
+    @Override
+    public void testingEmergencyCall() {}
 }
diff --git a/telephony/java/com/android/internal/telephony/CallTracker.java b/telephony/java/com/android/internal/telephony/CallTracker.java
index 31f9e18..958481c 100644
--- a/telephony/java/com/android/internal/telephony/CallTracker.java
+++ b/telephony/java/com/android/internal/telephony/CallTracker.java
@@ -19,6 +19,8 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.telephony.CommandException;
@@ -116,6 +118,48 @@
         return pendingOperations == 0;
     }
 
+    /**
+     * Routine called from dial to check if the number is a test Emergency number
+     * and if so remap the number. This allows a short emergency number to be remapped
+     * to a regular number for testing how the frameworks handles emergency numbers
+     * without actually calling an emergency number.
+     *
+     * This is not a full test and is not a substitute for testing real emergency
+     * numbers but can be useful.
+     *
+     * To use this feature set a system property ril.test.emergencynumber to a pair of
+     * numbers separated by a colon. If the first number matches the number parameter
+     * this routine returns the second number. Example:
+     *
+     * ril.test.emergencynumber=112:1-123-123-45678
+     *
+     * To test Dial 112 take call then hang up on MO device to enter ECM
+     * see RIL#processSolicited RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND
+     *
+     * @param number to test if it should be remapped
+     * @return the same number or the remapped number.
+     */
+    protected String checkForTestEmergencyNumber(String dialString) {
+        String testEn = SystemProperties.get("ril.test.emergencynumber");
+        if (DBG_POLL) {
+            log("checkForTestEmergencyNumber: dialString=" + dialString +
+                " testEn=" + testEn);
+        }
+        if (!TextUtils.isEmpty(testEn)) {
+            String values[] = testEn.split(":");
+            log("checkForTestEmergencyNumber: values.length=" + values.length);
+            if (values.length == 2) {
+                if (values[0].equals(
+                        android.telephony.PhoneNumberUtils.stripSeparators(dialString))) {
+                    cm.testingEmergencyCall();
+                    log("checkForTestEmergencyNumber: remap " +
+                            dialString + " to " + values[1]);
+                    dialString = values[1];
+                }
+            }
+        }
+        return dialString;
+    }
 
     //***** Overridden from Handler
     public abstract void handleMessage (Message msg);
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index a0efab2..f7757b3 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -1571,4 +1571,9 @@
      * @param response a callback message with the String response in the obj field
      */
     public void requestIsimAuthentication(String nonce, Message response);
+
+    /**
+     * Notifiy that we are testing an emergency call
+     */
+    public void testingEmergencyCall();
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index d4e81b2..fa90f1c 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -226,7 +226,8 @@
     //       having to have different values for GSM and
     //       CDMA. If so we can then remove the need for
     //       getActionIntentReconnectAlarm.
-    protected static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason";
+    protected static final String INTENT_RECONNECT_ALARM_EXTRA_REASON =
+        "reconnect_alarm_extra_reason";
 
     // Used for debugging. Send the INTENT with an optional counter value with the number
     // of times the setup is to fail before succeeding. If the counter isn't passed the
@@ -1040,7 +1041,8 @@
                 }
             }
             if (didDisable) {
-                if (enabledCount == 0) {
+                if ((enabledCount == 0) || (apnId == APN_DUN_ID)) {
+                    mRequestedApnType = Phone.APN_TYPE_DEFAULT;
                     onCleanUpConnection(true, apnId, Phone.REASON_DATA_DISABLED);
                 }
 
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index f587fe1..5afc1f3 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -60,6 +60,7 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * {@hide}
@@ -232,6 +233,9 @@
 
     Object     mLastNITZTimeInfo;
 
+    // When we are testing emergency calls
+    AtomicBoolean mTestingEmergencyCall = new AtomicBoolean(false);
+
     //***** Events
 
     static final int EVENT_SEND                 = 1;
@@ -2213,7 +2217,16 @@
             case RIL_REQUEST_GET_IMSI: ret =  responseString(p); break;
             case RIL_REQUEST_HANGUP: ret =  responseVoid(p); break;
             case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: ret =  responseVoid(p); break;
-            case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: ret =  responseVoid(p); break;
+            case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: {
+                if (mTestingEmergencyCall.getAndSet(false)) {
+                    if (mEmergencyCallbackModeRegistrant != null) {
+                        riljLog("testing emergency call, notify ECM Registrants");
+                        mEmergencyCallbackModeRegistrant.notifyRegistrant();
+                    }
+                }
+                ret =  responseVoid(p);
+                break;
+            }
             case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: ret =  responseVoid(p); break;
             case RIL_REQUEST_CONFERENCE: ret =  responseVoid(p); break;
             case RIL_REQUEST_UDUB: ret =  responseVoid(p); break;
@@ -3014,6 +3027,11 @@
         num = p.readInt();
         response = new ArrayList<DriverCall>(num);
 
+        if (RILJ_LOGV) {
+            riljLog("responseCallList: num=" + num +
+                    " mEmergencyCallbackModeRegistrant=" + mEmergencyCallbackModeRegistrant +
+                    " mTestingEmergencyCall=" + mTestingEmergencyCall.get());
+        }
         for (int i = 0 ; i < num ; i++) {
             dc = new DriverCall();
 
@@ -3065,6 +3083,14 @@
 
         Collections.sort(response);
 
+        if ((num == 0) && mTestingEmergencyCall.getAndSet(false)) {
+            if (mEmergencyCallbackModeRegistrant != null) {
+                riljLog("responseCallList: call ended, testing emergency call," +
+                            " notify ECM Registrants");
+                mEmergencyCallbackModeRegistrant.notifyRegistrant();
+            }
+        }
+
         return response;
     }
 
@@ -3791,4 +3817,13 @@
 
         send(rr);
     }
+
+    /* (non-Javadoc)
+     * @see com.android.internal.telephony.BaseCommands#testingEmergencyCall()
+     */
+    @Override
+    public void testingEmergencyCall() {
+        if (RILJ_LOGD) riljLog("testingEmergencyCall");
+        mTestingEmergencyCall.set(true);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index 83efc51..f918dce 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -210,7 +210,8 @@
             return dialThreeWay(dialString);
         }
 
-        pendingMO = new CdmaConnection(phone.getContext(), dialString, this, foregroundCall);
+        pendingMO = new CdmaConnection(phone.getContext(), checkForTestEmergencyNumber(dialString),
+                this, foregroundCall);
         hangupPendingMO = false;
 
         if (pendingMO.address == null || pendingMO.address.length() == 0
@@ -259,7 +260,7 @@
 
             // Attach the new connection to foregroundCall
             pendingMO = new CdmaConnection(phone.getContext(),
-                                dialString, this, foregroundCall);
+                                checkForTestEmergencyNumber(dialString), this, foregroundCall);
             cm.sendCDMAFeatureCode(pendingMO.address,
                 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
             return pendingMO;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 3fe57ae..6e4dd58 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -95,6 +95,9 @@
             Phone.APN_TYPE_MMS,
             Phone.APN_TYPE_HIPRI };
 
+    private String[] mDunApnTypes = {
+            Phone.APN_TYPE_DUN };
+
     private static final int mDefaultApnId = DataConnectionTracker.APN_DEFAULT_ID;
 
     /* Constructor */
@@ -121,11 +124,26 @@
 
         createAllDataConnectionList();
         broadcastMessenger();
+
+        Context c = mCdmaPhone.getContext();
+        String[] t = c.getResources().getStringArray(
+                com.android.internal.R.array.config_cdma_dun_supported_types);
+        if (t != null && t.length > 0) {
+            ArrayList<String> temp = new ArrayList<String>();
+            for(int i=0; i< t.length; i++) {
+                if (!Phone.APN_TYPE_DUN.equalsIgnoreCase(t[i])) {
+                    temp.add(t[i]);
+                }
+            }
+            temp.add(0, Phone.APN_TYPE_DUN);
+            mDunApnTypes = temp.toArray(t);
+        }
+
     }
 
     @Override
     public void dispose() {
-        cleanUpConnection(false, null);
+        cleanUpConnection(false, null, false);
 
         super.dispose();
 
@@ -282,7 +300,7 @@
      * @param tearDown true if the underlying DataConnection should be disconnected.
      * @param reason for the clean up.
      */
-    private void cleanUpConnection(boolean tearDown, String reason) {
+    private void cleanUpConnection(boolean tearDown, String reason, boolean doAll) {
         if (DBG) log("cleanUpConnection: reason: " + reason);
 
         // Clear the reconnect alarm, if set.
@@ -302,9 +320,15 @@
                 DataConnectionAc dcac =
                     mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
                 if (tearDown) {
-                    if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
-                    conn.tearDown(reason, obtainMessage(EVENT_DISCONNECT_DONE,
-                            conn.getDataConnectionId(), 0, reason));
+                    if (doAll) {
+                        if (DBG) log("cleanUpConnection: teardown, conn.tearDownAll");
+                        conn.tearDownAll(reason, obtainMessage(EVENT_DISCONNECT_DONE,
+                                conn.getDataConnectionId(), 0, reason));
+                    } else {
+                        if (DBG) log("cleanUpConnection: teardown, conn.tearDown");
+                        conn.tearDown(reason, obtainMessage(EVENT_DISCONNECT_DONE,
+                                conn.getDataConnectionId(), 0, reason));
+                    }
                     notificationDeferred = true;
                 } else {
                     if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
@@ -348,8 +372,7 @@
         String[] types;
         int apnId;
         if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) {
-            types = new String[1];
-            types[0] = Phone.APN_TYPE_DUN;
+            types = mDunApnTypes;
             apnId = DataConnectionTracker.APN_DUN_ID;
         } else {
             types = mDefaultApnTypes;
@@ -587,7 +610,7 @@
     @Override
     protected void onEnableNewApn() {
         // No mRequestedApnType check; only one connection is supported
-        cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
+        cleanUpConnection(true, Phone.REASON_APN_SWITCHED, false);
     }
 
     /**
@@ -769,13 +792,13 @@
     @Override
     protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
         // No apnId check; only one connection is supported
-        cleanUpConnection(tearDown, reason);
+        cleanUpConnection(tearDown, reason, (apnId == APN_DUN_ID));
     }
 
     @Override
     protected void onCleanUpAllConnections(String cause) {
         // Only one CDMA connection is supported
-        cleanUpConnection(true, cause);
+        cleanUpConnection(true, cause, false);
     }
 
     private void createAllDataConnectionList() {
@@ -821,7 +844,7 @@
             notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED);
         } else {
             if (mState == State.FAILED) {
-                cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED);
+                cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED, false);
                 mDataConnections.get(0).resetRetryCount();
 
                 CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
@@ -900,7 +923,7 @@
                 log("onDataStateChanged: No active connection"
                         + "state is CONNECTED, disconnecting/cleanup");
                 writeEventLogCdmaDataDrop();
-                cleanUpConnection(true, null);
+                cleanUpConnection(true, null, false);
                 return;
             }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
index 06f310c..425afe6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
@@ -198,7 +198,8 @@
             throw new CallStateException("cannot dial in current state");
         }
 
-        pendingMO = new GsmConnection(phone.getContext(), dialString, this, foregroundCall);
+        pendingMO = new GsmConnection(phone.getContext(), checkForTestEmergencyNumber(dialString),
+                this, foregroundCall);
         hangupPendingMO = false;
 
         if (pendingMO.address == null || pendingMO.address.length() == 0
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 4f439e8..d1873eb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -131,7 +131,7 @@
 
     private static final String INTENT_RECONNECT_ALARM =
         "com.android.internal.telephony.gprs-reconnect";
-    private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type";
+    private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "reconnect_alarm_extra_type";
 
     private static final String INTENT_DATA_STALL_ALARM =
         "com.android.internal.telephony.gprs-data-stall";
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 5b76272..db670f8 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -512,6 +512,9 @@
     public void testFormatNumber() {
         assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "US"));
         assertEquals("223-4567", PhoneNumberUtils.formatNumber("2234567", "US"));
+        assertEquals("011 86 10 8888 0000",
+                     PhoneNumberUtils.formatNumber("011861088880000", "US"));
+        assertEquals("010 8888 0000", PhoneNumberUtils.formatNumber("01088880000", "CN"));
         // formatNumber doesn't format alpha numbers, but keep them as they are.
         assertEquals("800-GOOG-114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US"));
     }
@@ -542,6 +545,16 @@
         // Using the phoneNumberE164's country code
         assertEquals("(650) 291-0000",
                 PhoneNumberUtils.formatNumber("6502910000", "+16502910000", "CN"));
+        // Using the default country code for a phone number containing the IDD
+        assertEquals("011 86 10 8888 0000",
+                PhoneNumberUtils.formatNumber("011861088880000", "+861088880000", "US"));
+        assertEquals("00 86 10 8888 0000",
+                PhoneNumberUtils.formatNumber("00861088880000", "+861088880000", "GB"));
+        assertEquals("+86 10 8888 0000",
+                PhoneNumberUtils.formatNumber("+861088880000", "+861088880000", "GB"));
+        // Wrong default country, so no formatting is done
+        assertEquals("011861088880000",
+                PhoneNumberUtils.formatNumber("011861088880000", "+861088880000", "GB"));
         // The phoneNumberE164 is null
         assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("6502910000", null, "US"));
         // The given number has a country code.
@@ -553,7 +566,11 @@
         // An invalid Polish number should be left as it is. Note Poland doesn't use '0' as a
         // national prefix; therefore, the leading '0' makes the number invalid.
         assertEquals("0506128687", PhoneNumberUtils.formatNumber("0506128687", null, "PL"));
+        // Wrong default country, so no formatting is done
+        assertEquals("011861088880000",
+                PhoneNumberUtils.formatNumber("011861088880000", "", "GB"));
     }
+
     @SmallTest
     public void testIsEmergencyNumber() {
         // There are two parallel sets of tests here: one for the
@@ -606,4 +623,35 @@
         // Brazil.
         assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "BR"));
     }
+
+    @SmallTest
+    public void testStripSeparators() {
+        // Smoke tests which should never fail.
+        assertEquals("1234567890", PhoneNumberUtils.stripSeparators("1234567890"));
+        assertEquals("911", PhoneNumberUtils.stripSeparators("911"));
+        assertEquals("112", PhoneNumberUtils.stripSeparators("112"));
+
+        // Separators should be removed, while '+' or any other digits should not.
+        assertEquals("+16502910000", PhoneNumberUtils.stripSeparators("+1 (650) 291-0000"));
+
+        // WAIT, PAUSE should *not* be stripped
+        assertEquals("+16502910000,300;",
+                PhoneNumberUtils.stripSeparators("+1 (650) 291-0000, 300;"));
+    }
+
+    @SmallTest
+    public void testConvertAndStrip() {
+        // Smoke tests which should never fail.
+        assertEquals("1234567890", PhoneNumberUtils.convertAndStrip("1234567890"));
+        assertEquals("911", PhoneNumberUtils.convertAndStrip("911"));
+        assertEquals("112", PhoneNumberUtils.convertAndStrip("112"));
+
+        // It should convert keypad characters into digits, and strip separators
+        assertEquals("22233344455566677778889999",
+                PhoneNumberUtils.convertAndStrip("ABC DEF GHI JKL MNO PQR STUV WXYZ"));
+
+        // Test real cases.
+        assertEquals("18004664411", PhoneNumberUtils.convertAndStrip("1-800-GOOG-411"));
+        assertEquals("8002223334", PhoneNumberUtils.convertAndStrip("(800) ABC-DEFG"));
+    }
 }
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 0b4fc51..5fab2bb 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -267,6 +267,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void sendBroadcast(Intent intent, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void sendBroadcast(Intent intent, String receiverPermission) {
         throw new UnsupportedOperationException();
@@ -333,6 +339,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void unbindService(ServiceConnection conn) {
         throw new UnsupportedOperationException();
diff --git a/tests/BiDiTests/res/layout/grid_layout_code.xml b/tests/BiDiTests/res/layout/grid_layout_code.xml
new file mode 100644
index 0000000..87a0ec0
--- /dev/null
+++ b/tests/BiDiTests/res/layout/grid_layout_code.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/grid_layout_code"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/grid_layout_ltr.xml b/tests/BiDiTests/res/layout/grid_layout_ltr.xml
index 46ea658..e320809 100644
--- a/tests/BiDiTests/res/layout/grid_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/grid_layout_ltr.xml
@@ -57,6 +57,27 @@
         <EditText
                 android:ems="8"/>
 
+        <TextView
+                android:text="You can configure email in just a few steps:"
+                android:textSize="16dip"
+                android:layout_columnSpan="4"
+                android:layout_gravity="start"/>
+
+        <TextView
+                android:text="Email address:"
+                android:layout_gravity="end"/>
+
+        <EditText
+                android:ems="10"/>
+
+        <TextView
+                android:text="Password:"
+                android:layout_column="0"
+                android:layout_gravity="end"/>
+
+        <EditText
+                android:ems="8"/>
+
         <Space
                 android:layout_row="4"
                 android:layout_column="0"
diff --git a/tests/BiDiTests/res/layout/grid_layout_rtl.xml b/tests/BiDiTests/res/layout/grid_layout_rtl.xml
index 947e13c..6d3aae6 100644
--- a/tests/BiDiTests/res/layout/grid_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/grid_layout_rtl.xml
@@ -57,6 +57,27 @@
         <EditText
                 android:ems="8"/>
 
+        <TextView
+                android:text="You can configure email in just a few steps:"
+                android:textSize="16dip"
+                android:layout_columnSpan="4"
+                android:layout_gravity="end"/>
+
+        <TextView
+                android:text="Email address:"
+                android:layout_gravity="start"/>
+
+        <EditText
+                android:ems="10"/>
+
+        <TextView
+                android:text="Password:"
+                android:layout_column="0"
+                android:layout_gravity="start"/>
+
+        <EditText
+                android:ems="8"/>
+
         <Space
                 android:layout_row="4"
                 android:layout_column="0"
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index 4d7ace1..c5a1235 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -111,6 +111,8 @@
         addItem(result, "Grid LTR", BiDiTestGridLayoutLtr.class, R.id.grid_layout_ltr);
         addItem(result, "Grid RTL", BiDiTestGridLayoutRtl.class, R.id.grid_layout_rtl);
         addItem(result, "Grid LOC", BiDiTestGridLayoutLocale.class, R.id.grid_layout_locale);
+        addItem(result, "Grid C-LTR", BiDiTestGridLayoutCodeLtr.class, R.id.grid_layout_code);
+        addItem(result, "Grid C-RTL", BiDiTestGridLayoutCodeRtl.class, R.id.grid_layout_code);
 
         addItem(result, "Frame LTR", BiDiTestFrameLayoutLtr.class, R.id.frame_layout_ltr);
         addItem(result, "Frame RTL", BiDiTestFrameLayoutRtl.class, R.id.frame_layout_rtl);
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeLtr.java
new file mode 100644
index 0000000..2b5e674
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeLtr.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 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.bidi;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.GridLayout;
+import android.widget.*;
+
+import static android.text.InputType.*;
+import static android.widget.GridLayout.*;
+
+public class BiDiTestGridLayoutCodeLtr extends Fragment {
+
+    private FrameLayout currentView;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        currentView = (FrameLayout) inflater.inflate(R.layout.grid_layout_code, container, false);
+        return currentView;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        currentView.addView(create(currentView.getContext()));
+    }
+
+    public static View create(Context context) {
+        GridLayout layout = new GridLayout(context);
+        layout.setUseDefaultMargins(true);
+        layout.setAlignmentMode(ALIGN_BOUNDS);
+        layout.setRowOrderPreserved(false);
+        layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+
+        Spec row1 = spec(0);
+        Spec row2 = spec(1);
+        Spec row3 = spec(2, BASELINE);
+        Spec row4 = spec(3, BASELINE);
+        Spec row5 = spec(2, 3, FILL); // allow the last two rows to overlap the middle two
+        Spec row6 = spec(5);
+        Spec row7 = spec(6);
+
+        Spec col1a = spec(0, 4, CENTER);
+        Spec col1b = spec(0, 4, LEFT);
+        Spec col1c = spec(0, RIGHT);
+        Spec col2 = spec(1, START);
+        Spec col3 = spec(2, FILL);
+        Spec col4a = spec(3);
+        Spec col4b = spec(3, FILL);
+
+        {
+            TextView c = new TextView(context);
+            c.setTextSize(32);
+            c.setText("Email setup");
+            layout.addView(c, new GridLayout.LayoutParams(row1, col1a));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setTextSize(16);
+            c.setText("You can configure email in just a few steps:");
+            layout.addView(c, new GridLayout.LayoutParams(row2, col1b));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setText("Email address:");
+            layout.addView(c, new GridLayout.LayoutParams(row3, col1c));
+        }
+        {
+            EditText c = new EditText(context);
+            c.setEms(10);
+            c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+            layout.addView(c, new GridLayout.LayoutParams(row3, col2));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setText("Password:");
+            layout.addView(c, new GridLayout.LayoutParams(row4, col1c));
+        }
+        {
+            TextView c = new EditText(context);
+            c.setEms(8);
+            c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
+            layout.addView(c, new GridLayout.LayoutParams(row4, col2));
+        }
+        {
+            Space c = new Space(context);
+            layout.addView(c, new GridLayout.LayoutParams(row5, col3));
+        }
+        {
+            Button c = new Button(context);
+            c.setText("Manual setup");
+            layout.addView(c, new GridLayout.LayoutParams(row6, col4a));
+        }
+        {
+            Button c = new Button(context);
+            c.setText("Next");
+            layout.addView(c, new GridLayout.LayoutParams(row7, col4b));
+        }
+
+        return layout;
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeRtl.java
new file mode 100644
index 0000000..3a03c6c
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeRtl.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 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.bidi;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.GridLayout;
+import android.widget.*;
+
+import static android.text.InputType.*;
+import static android.widget.GridLayout.*;
+
+public class BiDiTestGridLayoutCodeRtl extends Fragment {
+
+    private FrameLayout currentView;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        currentView = (FrameLayout) inflater.inflate(R.layout.grid_layout_code, container, false);
+        return currentView;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        currentView.addView(create(currentView.getContext()));
+    }
+
+    public static View create(Context context) {
+        GridLayout layout = new GridLayout(context);
+        layout.setUseDefaultMargins(true);
+        layout.setAlignmentMode(ALIGN_BOUNDS);
+        layout.setRowOrderPreserved(false);
+        layout.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+
+        Spec row1 = spec(0);
+        Spec row2 = spec(1);
+        Spec row3 = spec(2, BASELINE);
+        Spec row4 = spec(3, BASELINE);
+        Spec row5 = spec(2, 3, FILL); // allow the last two rows to overlap the middle two
+        Spec row6 = spec(5);
+        Spec row7 = spec(6);
+
+        Spec col1a = spec(0, 4, CENTER);
+        Spec col1b = spec(0, 4, LEFT);
+        Spec col1c = spec(0, RIGHT);
+        Spec col2 = spec(1, START);
+        Spec col3 = spec(2, FILL);
+        Spec col4a = spec(3);
+        Spec col4b = spec(3, FILL);
+
+        {
+            TextView c = new TextView(context);
+            c.setTextSize(32);
+            c.setText("Email setup");
+            layout.addView(c, new GridLayout.LayoutParams(row1, col1a));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setTextSize(16);
+            c.setText("You can configure email in just a few steps:");
+            layout.addView(c, new GridLayout.LayoutParams(row2, col1b));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setText("Email address:");
+            layout.addView(c, new GridLayout.LayoutParams(row3, col1c));
+        }
+        {
+            EditText c = new EditText(context);
+            c.setEms(10);
+            c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+            layout.addView(c, new GridLayout.LayoutParams(row3, col2));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setText("Password:");
+            layout.addView(c, new GridLayout.LayoutParams(row4, col1c));
+        }
+        {
+            TextView c = new EditText(context);
+            c.setEms(8);
+            c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
+            layout.addView(c, new GridLayout.LayoutParams(row4, col2));
+        }
+        {
+            Space c = new Space(context);
+            layout.addView(c, new GridLayout.LayoutParams(row5, col3));
+        }
+        {
+            Button c = new Button(context);
+            c.setText("Manual setup");
+            layout.addView(c, new GridLayout.LayoutParams(row6, col4a));
+        }
+        {
+            Button c = new Button(context);
+            c.setText("Next");
+            layout.addView(c, new GridLayout.LayoutParams(row7, col4b));
+        }
+
+        return layout;
+    }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index c0ba8cf..83c9c3d 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -22,12 +22,12 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.Intent;
 import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Bitmap.CompressFormat;
 import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.os.Environment;
@@ -36,7 +36,6 @@
 import android.util.Log;
 import android.view.ViewGroup;
 import android.view.Window;
-import android.webkit.CookieManager;
 import android.webkit.ConsoleMessage;
 import android.webkit.CookieManager;
 import android.webkit.GeolocationPermissions;
@@ -904,9 +903,7 @@
         settings.setWorkersEnabled(false);
         settings.setXSSAuditorEnabled(false);
         settings.setPageCacheCapacity(0);
-        // this enables cpu upload path (as opposed to gpu upload path)
-        // and it's only meant to be a temporary workaround!
-        settings.setProperty("enable_cpu_upload_path", "true");
+        settings.setProperty("use_minimal_memory", "false");
     }
 
     private WebView mWebView;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 643cb8d..ed78daa3 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -40,6 +40,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name="ClipRegionActivity"
+                android:label="_ClipRegion">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         
         <activity
                 android:name="DisplayListLayersActivity"
@@ -559,6 +568,15 @@
         </activity>
 
         <activity
+                android:name="PathsCacheActivity"
+                android:label="_PathsCache">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="PointsActivity"
                 android:label="_Points">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
new file mode 100644
index 0000000..b2a508b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
@@ -0,0 +1,57 @@
+/*
+ * 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.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Region;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ClipRegionActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final RegionView view = new RegionView(this);
+        setContentView(view);
+    }
+
+    public static class RegionView extends View {
+        public RegionView(Context c) {
+            super(c);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.save();
+            canvas.clipRect(100.0f, 100.0f, getWidth() - 100.0f, getHeight() - 100.0f,
+                    Region.Op.DIFFERENCE);
+            canvas.drawARGB(255, 255, 0, 0);
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java
new file mode 100644
index 0000000..9ab2a86
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java
@@ -0,0 +1,116 @@
+/*
+ * 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.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PathsCacheActivity extends Activity {
+    private Path mPath;
+
+    private final Random mRandom = new Random();
+    private final ArrayList<Path> mPathList = new ArrayList<Path>();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mPath = makePath();
+
+        final PathsView view = new PathsView(this);
+        setContentView(view);
+    }
+
+    private Path makePath() {
+        Path path = new Path();
+        buildPath(path);
+        return path;
+    }
+
+    private void buildPath(Path path) {
+        path.moveTo(0.0f, 0.0f);
+        path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
+        path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
+        path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
+    }
+
+    public class PathsView extends View {
+        private final Paint mMediumPaint;
+
+        public PathsView(Context c) {
+            super(c);
+
+            mMediumPaint = new Paint();
+            mMediumPaint.setAntiAlias(true);
+            mMediumPaint.setColor(0xe00000ff);
+            mMediumPaint.setStrokeWidth(10.0f);
+            mMediumPaint.setStyle(Paint.Style.STROKE);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            
+            Log.d("OpenGLRenderer", "Start frame");
+
+            canvas.drawARGB(255, 255, 255, 255);
+
+            canvas.save();
+            canvas.translate(550.0f, 60.0f);
+            canvas.drawPath(mPath, mMediumPaint);
+
+            mPath.reset();
+            buildPath(mPath);
+
+            canvas.translate(30.0f, 30.0f);
+            canvas.drawPath(mPath, mMediumPaint);
+            canvas.drawPath(mPath, mMediumPaint);
+
+            canvas.restore();
+
+            for (int i = 0; i < mRandom.nextInt(20); i++) {
+                Path path = makePath();
+                int r = mRandom.nextInt(10);
+                if (r == 5 || r == 3) {
+                    mPathList.add(path);
+                }
+    
+                canvas.save();
+                canvas.translate(450.0f + mRandom.nextInt(200), mRandom.nextInt(200));
+                canvas.drawPath(path, mMediumPaint);
+                canvas.restore();
+            }
+
+            int r = mRandom.nextInt(100);
+            if (r == 50) {
+                mPathList.clear();
+            }
+
+            invalidate();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
index 4037a69..0a868fa 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
@@ -21,6 +21,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.os.Bundle;
+import android.text.TextPaint;
 import android.view.View;
 
 @SuppressWarnings({"UnusedDeclaration"})
@@ -39,6 +40,7 @@
         private final Paint mScaledPaint;
         private final Paint mSkewPaint;
         private final Paint mHugePaint;
+        private final TextPaint mEventPaint;
 
         CustomTextView(Context c) {
             super(c);
@@ -70,6 +72,11 @@
             mHugePaint.setAntiAlias(true);
             mHugePaint.setColor(0xff000000);
             mHugePaint.setTextSize(300f);
+
+            mEventPaint = new TextPaint();
+            mEventPaint.setFakeBoldText(true);
+            mEventPaint.setAntiAlias(true);
+            mEventPaint.setTextSize(14);
         }
 
         @Override
@@ -77,6 +84,8 @@
             super.onDraw(canvas);
             canvas.drawRGB(255, 255, 255);
 
+            canvas.drawText("Hello OpenGL renderer!", 300, 20, mEventPaint);
+            
             mMediumPaint.setStyle(Paint.Style.FILL_AND_STROKE);
             mMediumPaint.setStrokeWidth(2.0f);
             canvas.drawText("Hello OpenGL renderer!", 100, 20, mMediumPaint);
diff --git a/tests/RenderScriptTests/ComputePerf/src/com/example/android/rs/computeperf/mandelbrot.rs b/tests/RenderScriptTests/ComputePerf/src/com/example/android/rs/computeperf/mandelbrot.rs
index a7987b3..0ffb0e5 100644
--- a/tests/RenderScriptTests/ComputePerf/src/com/example/android/rs/computeperf/mandelbrot.rs
+++ b/tests/RenderScriptTests/ComputePerf/src/com/example/android/rs/computeperf/mandelbrot.rs
@@ -25,13 +25,14 @@
     p.y = -1.f + ((float)y / gDimY) * 2.f;
 
     float2 t = 0;
+    float2 t2 = t * t;
     int iteration = 0;
-    while((t.x*t.x + t.y*t.y < 4.f) && (iteration < gMaxIteration)) {
-        float2 t2 = t * t;
+    while((t2.x + t2.y < 4.f) && (iteration < gMaxIteration)) {
         float xtemp = t2.x - t2.y + p.x;
         t.y = 2 * t.x * t.y + p.y;
         t.x = xtemp;
         iteration++;
+        t2 = t * t;
     }
 
     if(iteration >= gMaxIteration) {
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 3615f60..7368260 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -283,6 +283,9 @@
         mRadius = MAX_RADIUS;
         mScript.set_radius(mRadius);
 
+        mScript.invoke_filter();
+        mRS.finish();
+
         long t = java.lang.System.currentTimeMillis();
 
         mScript.invoke_filter();
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl b/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl
new file mode 100644
index 0000000..656961c
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl
@@ -0,0 +1,8 @@
+varying vec2 varTex0;
+
+void main() {
+   lowp vec3 col0 = texture2D(UNI_Tex0, varTex0).rgb;
+   gl_FragColor.xyz = col0 * UNI_modulate.rgb;
+   gl_FragColor.w = UNI_modulate.a;
+}
+
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java
index ba70c71..41f664a 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java
@@ -35,18 +35,22 @@
     // Custom shaders
     private ProgramFragment mProgFragmentMultitex;
     private ProgramFragment mProgFragmentSingletex;
+    private ProgramFragment mProgFragmentSingletexModulate;
     private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
     int mBenchmarkDimX;
     int mBenchmarkDimY;
 
     private ScriptC_fill_test mFillScript;
     ScriptField_TestScripts_s.Item[] mTests;
+    ScriptField_FillTestFragData_s mFragData;
 
     private final String[] mNames = {
         "Fill screen 10x singletexture",
         "Fill screen 10x 3tex multitexture",
         "Fill screen 10x blended singletexture",
-        "Fill screen 10x blended 3tex multitexture"
+        "Fill screen 10x blended 3tex multitexture",
+        "Fill screen 3x modulate blended singletexture",
+        "Fill screen 1x modulate blended singletexture",
     };
 
     public FillTest() {
@@ -88,6 +92,8 @@
         addTest(index++, 0 /*testId*/, 0 /*blend*/, 10 /*quadCount*/);
         addTest(index++, 1 /*testId*/, 1 /*blend*/, 10 /*quadCount*/);
         addTest(index++, 0 /*testId*/, 1 /*blend*/, 10 /*quadCount*/);
+        addTest(index++, 2 /*testId*/, 1 /*blend*/, 3 /*quadCount*/);
+        addTest(index++, 2 /*testId*/, 1 /*blend*/, 1 /*quadCount*/);
 
         return true;
     }
@@ -112,6 +118,14 @@
         pfbCustom.setShader(mRes, R.raw.singletexf);
         pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
         mProgFragmentSingletex = pfbCustom.create();
+
+        pfbCustom = new ProgramFragment.Builder(mRS);
+        pfbCustom.setShader(mRes, R.raw.singletexfm);
+        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
+        mFragData = new ScriptField_FillTestFragData_s(mRS, 1);
+        pfbCustom.addConstant(mFragData.getType());
+        mProgFragmentSingletexModulate = pfbCustom.create();
+        mProgFragmentSingletexModulate.bindConstants(mFragData.getAllocation(), 0);
     }
 
     private Allocation loadTextureARGB(int id) {
@@ -140,6 +154,7 @@
         mFillScript.set_gProgVertex(progVertex);
 
         mFillScript.set_gProgFragmentTexture(mProgFragmentSingletex);
+        mFillScript.set_gProgFragmentTextureModulate(mProgFragmentSingletexModulate);
         mFillScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
         mFillScript.set_gProgStoreBlendNone(ProgramStore.BLEND_NONE_DEPTH_NONE(mRS));
         mFillScript.set_gProgStoreBlendAlpha(ProgramStore.BLEND_ALPHA_DEPTH_NONE(mRS));
@@ -150,5 +165,7 @@
         mFillScript.set_gTexOpaque(loadTextureRGB(R.drawable.data));
         mFillScript.set_gTexTransparent(loadTextureARGB(R.drawable.leaf));
         mFillScript.set_gTexChecker(loadTextureRGB(R.drawable.checker));
+
+        mFillScript.bind_gFragData(mFragData);
     }
 }
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs
index 23832d3..281f830 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs
@@ -21,6 +21,7 @@
 
 rs_program_vertex gProgVertex;
 rs_program_fragment gProgFragmentTexture;
+rs_program_fragment gProgFragmentTextureModulate;
 rs_program_fragment gProgFragmentMultitex;
 
 rs_program_store gProgStoreBlendNone;
@@ -41,6 +42,11 @@
 } FillTestData;
 FillTestData *gData;
 
+typedef struct FillTestFragData_s {
+    float4 modulate;
+} FillTestFragData;
+FillTestFragData *gFragData;
+
 static float gDt = 0.0f;
 
 void init() {
@@ -58,7 +64,7 @@
     rsgProgramVertexLoadProjectionMatrix(&proj);
 }
 
-static void displaySingletexFill(bool blend, int quadCount) {
+static void displaySingletexFill(bool blend, int quadCount, bool modulate) {
     bindProgramVertexOrtho();
     rs_matrix4x4 matrix;
     rsMatrixLoadIdentity(&matrix);
@@ -70,9 +76,21 @@
     } else {
         rsgBindProgramStore(gProgStoreBlendAlpha);
     }
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+    if (modulate) {
+        rsgBindProgramFragment(gProgFragmentTextureModulate);
+        rsgBindSampler(gProgFragmentTextureModulate, 0, gLinearClamp);
+        rsgBindTexture(gProgFragmentTextureModulate, 0, gTexOpaque);
+
+        gFragData->modulate.r = 0.8f;
+        gFragData->modulate.g = 0.7f;
+        gFragData->modulate.b = 0.8f;
+        gFragData->modulate.a = 0.5f;
+        rsgAllocationSyncAll(rsGetAllocation(gFragData));
+    } else {
+        rsgBindProgramFragment(gProgFragmentTexture);
+        rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+        rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+    }
 
     for (int i = 0; i < quadCount; i ++) {
         float startX = 5 * i, startY = 5 * i;
@@ -128,7 +146,10 @@
             displayMultitextureSample(gData->blend == 1 ? true : false, gData->quadCount);
             break;
         case 1:
-            displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount);
+            displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount, false);
+            break;
+        case 2:
+            displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount, true);
             break;
         default:
             rsDebug("Wrong test number", 0);
diff --git a/tests/RenderScriptTests/SceneGraph/Android.mk b/tests/RenderScriptTests/SceneGraph/Android.mk
new file mode 100644
index 0000000..ba4b3c5
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/Android.mk
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := SceneGraphTest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
new file mode 100644
index 0000000..67af0fa
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testapp">
+    <uses-permission
+        android:name="android.permission.INTERNET" />
+    <application android:label="SceneGraphTest">
+        <activity android:name="TestApp"
+                  android:label="SceneGraphTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="SimpleApp"
+                  android:label="SimpleSceneGraph">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="FileSelector"
+                  android:label="FileSelector"
+                  android:hardwareAccelerated="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/SceneGraph/assets/blue.jpg b/tests/RenderScriptTests/SceneGraph/assets/blue.jpg
new file mode 100644
index 0000000..494e77a
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/blue.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg b/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg
new file mode 100644
index 0000000..2fcecb0
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/green.jpg b/tests/RenderScriptTests/SceneGraph/assets/green.jpg
new file mode 100644
index 0000000..a86a754
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/green.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/grey.jpg b/tests/RenderScriptTests/SceneGraph/assets/grey.jpg
new file mode 100644
index 0000000..5870b1a
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/grey.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orange.jpg b/tests/RenderScriptTests/SceneGraph/assets/orange.jpg
new file mode 100644
index 0000000..7dbe942
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/orange.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d
new file mode 100644
index 0000000..07318ae
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae
new file mode 100644
index 0000000..7eef443f
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae
@@ -0,0 +1,1102 @@
+<?xml version="1.0" ?>
+<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
+    <asset>
+        <contributor>
+            <author>alexst</author>
+            <authoring_tool>OpenCOLLADA2010</authoring_tool>
+            <comments>ColladaMaya export options: bakeTransforms=0;relativePaths=0;copyTextures=0;exportTriangles=1;exportCgfxFileReferences=0; isSampling=0;curveConstrainSampling=0;removeStaticCurves=1;exportPolygonMeshes=1;exportLights=1; exportCameras=1;exportJointsAndSkin=1;exportAnimations=0;exportInvisibleNodes=0;exportDefaultCameras=0; exportTexCoords=1;exportNormals=1;exportNormalsPerVertex=1;exportVertexColors=0;exportVertexColorsPerVertex=0; exportTexTangents=0;exportTangents=0;exportReferencedMaterials=1;exportMaterialsOnly=0; exportXRefs=1;dereferenceXRefs=1;exportCameraAsLookat=0;cameraXFov=0;cameraYFov=1;doublePrecision=0</comments>
+            <source_data>file:///Volumes/Android/art/orientation_test.mb</source_data>
+        </contributor>
+        <created>2011-09-30T15:31:38</created>
+        <modified>2011-09-30T15:31:38</modified>
+        <unit meter="0.01" name="centimeter" />
+        <up_axis>Y_UP</up_axis>
+    </asset>
+    <library_cameras>
+        <camera id="cameraShape1" name="cameraShape1">
+            <optics>
+                <technique_common>
+                    <perspective>
+                        <yfov>37.8493</yfov>
+                        <aspect_ratio>1.5</aspect_ratio>
+                        <znear>1</znear>
+                        <zfar>400</zfar>
+                    </perspective>
+                </technique_common>
+            </optics>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <film_fit>0</film_fit>
+                    <film_fit_offset>0</film_fit_offset>
+                    <film_offsetX>0</film_offsetX>
+                    <film_offsetY>0</film_offsetY>
+                    <horizontal_aperture>3.599993</horizontal_aperture>
+                    <lens_squeeze>1</lens_squeeze>
+                    <originalMayaNodeId>cameraShape1</originalMayaNodeId>
+                    <vertical_aperture>2.399995</vertical_aperture>
+                </technique>
+            </extra>
+        </camera>
+        <camera id="CameraDistShape" name="CameraDistShape">
+            <optics>
+                <technique_common>
+                    <perspective>
+                        <yfov>37.8493</yfov>
+                        <aspect_ratio>1.5</aspect_ratio>
+                        <znear>1</znear>
+                        <zfar>1000</zfar>
+                    </perspective>
+                </technique_common>
+            </optics>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <film_fit>0</film_fit>
+                    <film_fit_offset>0</film_fit_offset>
+                    <film_offsetX>0</film_offsetX>
+                    <film_offsetY>0</film_offsetY>
+                    <horizontal_aperture>3.599993</horizontal_aperture>
+                    <lens_squeeze>1</lens_squeeze>
+                    <originalMayaNodeId>CameraDistShape</originalMayaNodeId>
+                    <vertical_aperture>2.399995</vertical_aperture>
+                </technique>
+            </extra>
+        </camera>
+    </library_cameras>
+    <library_materials>
+        <material id="Paint1" name="Paint1">
+            <instance_effect url="#Paint1-fx" />
+        </material>
+        <material id="lambert2" name="lambert2">
+            <instance_effect url="#lambert2-fx" />
+        </material>
+        <material id="Plastic" name="Plastic">
+            <instance_effect url="#Plastic-fx" />
+        </material>
+        <material id="Metal" name="Metal">
+            <instance_effect url="#Metal-fx" />
+        </material>
+        <material id="PlasticCenter" name="PlasticCenter">
+            <instance_effect url="#PlasticCenter-fx" />
+        </material>
+        <material id="PlasticRed" name="PlasticRed">
+            <instance_effect url="#PlasticRed-fx" />
+        </material>
+        <material id="lambert10" name="lambert10">
+            <instance_effect url="#lambert10-fx" />
+        </material>
+        <material id="lambert11" name="lambert11">
+            <instance_effect url="#lambert11-fx" />
+        </material>
+    </library_materials>
+    <library_effects>
+        <effect id="Metal-fx">
+            <profile_COMMON>
+                <newparam sid="file23-surface">
+                    <surface type="2D">
+                        <init_from>file23</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file23-sampler">
+                    <sampler2D>
+                        <source>file23-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file23-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="Paint1-fx">
+            <profile_COMMON>
+                <newparam sid="file25-surface">
+                    <surface type="2D">
+                        <init_from>file25</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file25-sampler">
+                    <sampler2D>
+                        <source>file25-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file25-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="Plastic-fx">
+            <profile_COMMON>
+                <newparam sid="file24-surface">
+                    <surface type="2D">
+                        <init_from>file24</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file24-sampler">
+                    <sampler2D>
+                        <source>file24-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file24-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="PlasticCenter-fx">
+            <profile_COMMON>
+                <newparam sid="file24-surface">
+                    <surface type="2D">
+                        <init_from>file24</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file24-sampler">
+                    <sampler2D>
+                        <source>file24-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file24-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="PlasticRed-fx">
+            <profile_COMMON>
+                <newparam sid="file23-surface">
+                    <surface type="2D">
+                        <init_from>file23</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file23-sampler">
+                    <sampler2D>
+                        <source>file23-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file23-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="lambert10-fx">
+            <profile_COMMON>
+                <newparam sid="file28-surface">
+                    <surface type="2D">
+                        <init_from>file28</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file28-sampler">
+                    <sampler2D>
+                        <source>file28-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file28-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="lambert11-fx">
+            <profile_COMMON>
+                <newparam sid="file29-surface">
+                    <surface type="2D">
+                        <init_from>file29</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file29-sampler">
+                    <sampler2D>
+                        <source>file29-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file29-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+        <effect id="lambert2-fx">
+            <profile_COMMON>
+                <newparam sid="file22-surface">
+                    <surface type="2D">
+                        <init_from>file22</init_from>
+                    </surface>
+                </newparam>
+                <newparam sid="file22-sampler">
+                    <sampler2D>
+                        <source>file22-surface</source>
+                    </sampler2D>
+                </newparam>
+                <technique sid="common">
+                    <lambert>
+                        <emission>
+                            <color>0 0 0 1</color>
+                        </emission>
+                        <ambient>
+                            <color>0 0 0 1</color>
+                        </ambient>
+                        <diffuse>
+                            <texture texture="file22-sampler" texcoord="TEX0">
+                                <extra>
+                                    <technique profile="OpenCOLLADAMaya">
+                                        <blend_mode>NONE</blend_mode>
+                                        <coverageU>1</coverageU>
+                                        <coverageV>1</coverageV>
+                                        <fast>0</fast>
+                                        <mirrorU>0</mirrorU>
+                                        <mirrorV>0</mirrorV>
+                                        <noiseU>0</noiseU>
+                                        <noiseV>0</noiseV>
+                                        <offsetU>0</offsetU>
+                                        <offsetV>0</offsetV>
+                                        <repeatU>1</repeatU>
+                                        <repeatV>1</repeatV>
+                                        <rotateFrame>0</rotateFrame>
+                                        <rotateUV>0</rotateUV>
+                                        <stagger>0</stagger>
+                                        <translateFrameU>0</translateFrameU>
+                                        <translateFrameV>0</translateFrameV>
+                                        <wrapU>1</wrapU>
+                                        <wrapV>1</wrapV>
+                                    </technique>
+                                </extra>
+                            </texture>
+                        </diffuse>
+                        <transparent opaque="RGB_ZERO">
+                            <color>0 0 0 1</color>
+                        </transparent>
+                        <transparency>
+                            <float>1</float>
+                        </transparency>
+                    </lambert>
+                </technique>
+            </profile_COMMON>
+        </effect>
+    </library_effects>
+    <library_images>
+        <image id="file29" name="file29" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/blue.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file29</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file25" name="file25" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/carbonfiber.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file25</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file28" name="file28" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/green.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file28</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file22" name="file22" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/grey.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file22</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file24" name="file24" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/orange.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file24</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+        <image id="file23" name="file23" height="0" width="0">
+            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/red.jpg</init_from>
+            <extra>
+                <technique profile="OpenCOLLADAMaya">
+                    <dgnode_type>kFile</dgnode_type>
+                    <image_sequence>0</image_sequence>
+                    <originalMayaNodeId>file23</originalMayaNodeId>
+                </technique>
+            </extra>
+        </image>
+    </library_images>
+    <library_visual_scenes>
+        <visual_scene id="VisualSceneNode" name="orientation_test">
+            <node id="camera1" name="camera1">
+                <translate sid="translate">24.5791 14.1321 31.4654</translate>
+                <rotate sid="rotateZ">0 0 1 0</rotate>
+                <rotate sid="rotateY">0 1 0 42</rotate>
+                <rotate sid="rotateX">1 0 0 -16.2</rotate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_camera url="#cameraShape1" />
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>camera1</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="CameraAim" name="CameraAim">
+                <translate sid="translate">0.0209301 3.68542 2.06912</translate>
+                <rotate sid="rotateY">0 1 0 43.2561</rotate>
+                <rotate sid="rotateX">1 0 0 -20</rotate>
+                <scale sid="scale">1 1 1</scale>
+                <node id="CameraDist" name="CameraDist">
+                    <translate sid="translate">0 0 45</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_camera url="#CameraDistShape" />
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>CameraDist</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>CameraAim</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere4" name="pSphere4">
+                <translate sid="translate">-9.69237 0 7.70498</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape4">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere4</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere1" name="pSphere1">
+                <translate sid="translate">13.0966 0 5.76254</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape1">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere1</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere2" name="pSphere2">
+                <translate sid="translate">21.7661 0 -13.6375</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape2">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere2</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere3" name="pSphere3">
+                <translate sid="translate">-13.862 0 -13.6154</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape3">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere3</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pSphere5" name="pSphere5">
+                <translate sid="translate">31.0862 0 18.5992</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pSphereShape5">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert7SG" target="#Paint1">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pSphere5</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube1" name="pCube1">
+                <translate sid="translate">0 0 0</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape1">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert4SG" target="#lambert2">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube1</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="group1" name="group1">
+                <translate sid="translate">0 0 0</translate>
+                <rotate sid="rotateZ">0 0 1 -162.693</rotate>
+                <rotate sid="rotateY">0 1 0 21.3345</rotate>
+                <rotate sid="rotateX">1 0 0 -100.567</rotate>
+                <scale sid="scale">1 1 1</scale>
+                <node id="pSphere6" name="pSphere6">
+                    <translate sid="translate">-13.862 0 -13.6154</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape6">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert6SG" target="#Plastic">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere6</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere7" name="pSphere7">
+                    <translate sid="translate">-9.69237 0 7.70498</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape7">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert6SG" target="#Plastic">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere7</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere8" name="pSphere8">
+                    <translate sid="translate">21.7661 0 -13.6375</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape8">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert6SG" target="#Plastic">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere8</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere9" name="pSphere9">
+                    <translate sid="translate">13.0966 0 5.76254</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape9">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert6SG" target="#Plastic">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere9</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>group1</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="group2" name="group2">
+                <translate sid="translate">0 0 0</translate>
+                <rotate sid="rotateZ">0 0 1 45.4017</rotate>
+                <rotate sid="rotateY">0 1 0 79.393</rotate>
+                <rotate sid="rotateX">1 0 0 5.10889</rotate>
+                <scale sid="scale">1 1 1</scale>
+                <node id="pSphere10" name="pSphere10">
+                    <translate sid="translate">31.0862 0 18.5992</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape10">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere10</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere11" name="pSphere11">
+                    <translate sid="translate">13.0966 0 5.76254</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape11">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere11</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere12" name="pSphere12">
+                    <translate sid="translate">7.4784 16.3496 7.36882</translate>
+                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
+                    <rotate sid="rotateY">0 1 0 158.666</rotate>
+                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape12">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere12</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere13" name="pSphere13">
+                    <translate sid="translate">-9.69237 0 7.70498</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape13">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere13</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere14" name="pSphere14">
+                    <translate sid="translate">11.3635 -4.3926 2.21012</translate>
+                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
+                    <rotate sid="rotateY">0 1 0 158.666</rotate>
+                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape14">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere14</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere15" name="pSphere15">
+                    <translate sid="translate">21.7661 0 -13.6375</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape15">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere15</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere16" name="pSphere16">
+                    <translate sid="translate">-9.5945 -8.92317 -5.74901</translate>
+                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
+                    <rotate sid="rotateY">0 1 0 158.666</rotate>
+                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape16">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere16</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere17" name="pSphere17">
+                    <translate sid="translate">-13.862 0 -13.6154</translate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape17">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere17</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <node id="pSphere18" name="pSphere18">
+                    <translate sid="translate">-24.2135 6.497 -5.58935</translate>
+                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
+                    <rotate sid="rotateY">0 1 0 158.666</rotate>
+                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
+                    <scale sid="scale">1 1 1</scale>
+                    <instance_geometry url="#pSphereShape18">
+                        <bind_material>
+                            <technique_common>
+                                <instance_material symbol="lambert5SG" target="#Metal">
+                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                                </instance_material>
+                            </technique_common>
+                        </bind_material>
+                    </instance_geometry>
+                    <extra>
+                        <technique profile="OpenCOLLADAMaya">
+                            <originalMayaNodeId>pSphere18</originalMayaNodeId>
+                        </technique>
+                    </extra>
+                </node>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>group2</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube2" name="pCube2">
+                <translate sid="translate">0 0 0</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape2">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert8SG" target="#PlasticCenter">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube2</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube3" name="pCube3">
+                <translate sid="translate">15 0 0</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape3">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert9SG" target="#PlasticRed">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube3</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube4" name="pCube4">
+                <translate sid="translate">0 15 0</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape4">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert10SG" target="#lambert10">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube4</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+            <node id="pCube5" name="pCube5">
+                <translate sid="translate">0 0 15</translate>
+                <scale sid="scale">1 1 1</scale>
+                <instance_geometry url="#pCubeShape5">
+                    <bind_material>
+                        <technique_common>
+                            <instance_material symbol="lambert11SG" target="#lambert11">
+                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
+                            </instance_material>
+                        </technique_common>
+                    </bind_material>
+                </instance_geometry>
+                <extra>
+                    <technique profile="OpenCOLLADAMaya">
+                        <originalMayaNodeId>pCube5</originalMayaNodeId>
+                    </technique>
+                </extra>
+            </node>
+        </visual_scene>
+    </library_visual_scenes>
+    <scene>
+        <instance_visual_scene url="#VisualSceneNode" />
+    </scene>
+</COLLADA>
diff --git a/tests/RenderScriptTests/SceneGraph/assets/paint.jpg b/tests/RenderScriptTests/SceneGraph/assets/paint.jpg
new file mode 100644
index 0000000..0791045
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/paint.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/red.jpg b/tests/RenderScriptTests/SceneGraph/assets/red.jpg
new file mode 100644
index 0000000..320a2a6
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/assets/red.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png
new file mode 100644
index 0000000..ff34a7f
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png
new file mode 100644
index 0000000..f7353fd
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml b/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml
new file mode 100644
index 0000000..9ea30107
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml
@@ -0,0 +1,25 @@
+<?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.
+*/
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/load_model"
+          android:title="@string/load_model" />
+    <item android:id="@+id/use_blur"
+          android:title="@string/use_blur" />
+</menu>
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
new file mode 100644
index 0000000..c34adc9
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
@@ -0,0 +1,15 @@
+varying vec2 varTex0;
+
+void main() {
+   vec2 blurCoord = varTex0;
+   blurCoord.x = varTex0.x + UNI_blurOffset0;
+   vec3 col = texture2D(UNI_color, blurCoord).rgb;
+   blurCoord.x = varTex0.x + UNI_blurOffset1;
+   col += texture2D(UNI_color, blurCoord).rgb;
+   blurCoord.x = varTex0.x + UNI_blurOffset2;
+   col += texture2D(UNI_color, blurCoord).rgb;
+   blurCoord.x = varTex0.x + UNI_blurOffset3;
+   col += texture2D(UNI_color, blurCoord).rgb;
+
+   gl_FragColor = vec4(col * 0.25, 0.0);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl
new file mode 100644
index 0000000..ade05a2
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl
@@ -0,0 +1,17 @@
+varying vec2 varTex0;
+
+void main() {
+   vec2 blurCoord = varTex0;
+   blurCoord.y = varTex0.y + UNI_blurOffset0;
+   vec3 col = texture2D(UNI_color, blurCoord).rgb;
+   blurCoord.y = varTex0.y + UNI_blurOffset1;
+   col += texture2D(UNI_color, blurCoord).rgb;
+   blurCoord.y = varTex0.y + UNI_blurOffset2;
+   col += texture2D(UNI_color, blurCoord).rgb;
+   blurCoord.y = varTex0.y + UNI_blurOffset3;
+   col += texture2D(UNI_color, blurCoord).rgb;
+
+   col = col * 0.25;
+
+   gl_FragColor = vec4(col, 0.0);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl
new file mode 100644
index 0000000..bc824b6
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl
@@ -0,0 +1,7 @@
+varying vec2 varTex0;
+
+void main() {
+   gl_Position = ATTRIB_position;
+   varTex0 = ATTRIB_texture0;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
new file mode 100644
index 0000000..2eb1028
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
@@ -0,0 +1,19 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = (varWorldNormal);
+
+   vec3 light0Vec = V;
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = dot(worldNorm, light0Vec);
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
+   col.xyz = col.xyz * light0_Diffuse * 1.2;
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
new file mode 100644
index 0000000..b90a7b2
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
@@ -0,0 +1,23 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = V;
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
+   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, 15.0) * 0.5;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
+   col.xyz = col.xyz * (textureCube(UNI_reflection, worldNorm).rgb * 0.5 + vec3(light0_Diffuse));
+   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
+
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl
new file mode 100644
index 0000000..f3b89ed
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl
@@ -0,0 +1,26 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = V;
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.01, 0.99);
+   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, 150.0) * 0.5;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
+   col.xyz = col.xyz * light0_Diffuse * 1.1;
+   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
+
+   float fresnel = mix(pow(1.0 - light0_Diffuse, 15.0), 1.0, 0.1);
+   col.xyz = mix(col.xyz, textureCube(UNI_reflection, -light0R).rgb * 2.4, fresnel);
+   col.w = 0.8;
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl
new file mode 100644
index 0000000..56f7151f
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl
@@ -0,0 +1,22 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = V;
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
+   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, 10.0) * 0.5;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
+   col.xyz = col.xyz * light0_Diffuse * 1.2;
+   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl
new file mode 100644
index 0000000..b253622
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl
@@ -0,0 +1,29 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+void main() {
+
+   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
+   vec3 worldNorm = normalize(varWorldNormal);
+
+   vec3 light0Vec = normalize(UNI_lightPos_0.xyz - varWorldPos.xyz);
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
+   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
+   float light0_Specular = pow(light0Spec, 10.0) * 0.7;
+
+   vec3 light1Vec = normalize(UNI_lightPos_1.xyz - varWorldPos.xyz);
+   vec3 light1R = reflect(light1Vec, worldNorm);
+   float light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0);
+   float light1Spec = clamp(dot(-light1R, V), 0.001, 1.0);
+   float light1_Specular = pow(light1Spec, 10.0) * 0.7;
+
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = UNI_diffuse;
+   col.xyz = col.xyz * (light0_Diffuse * UNI_lightColor_0.xyz +
+                        light1_Diffuse * UNI_lightColor_1.xyz);
+   col.xyz += (light0_Specular + light1_Specular);
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d b/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d
new file mode 100644
index 0000000..f48895c
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
new file mode 100644
index 0000000..1a927ca
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
@@ -0,0 +1,13 @@
+varying vec2 varTex0;
+
+void main() {
+   vec3 col = texture2D(UNI_color, varTex0).rgb;
+
+   vec3 desat = vec3(0.299, 0.587, 0.114);
+   float lum = dot(desat, col);
+   float stepVal = step(lum, 0.8);
+   col = mix(col, vec3(0.0), stepVal)*0.5;
+
+   gl_FragColor = vec4(col, 0.0);
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl
new file mode 100644
index 0000000..1ea234f
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl
@@ -0,0 +1,17 @@
+varying vec3 varWorldPos;
+varying vec3 varWorldNormal;
+varying vec2 varTex0;
+
+// This is where actual shader code begins
+void main() {
+   vec4 objPos = ATTRIB_position;
+   vec4 worldPos = UNI_model * objPos;
+   gl_Position = UNI_viewProj * worldPos;
+
+   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
+   vec3 worldNorm = model3 * ATTRIB_normal;
+
+   varWorldPos = worldPos.xyz;
+   varWorldNormal = worldNorm;
+   varTex0 = ATTRIB_texture0;
+}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
new file mode 100644
index 0000000..662ecd8
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
@@ -0,0 +1,7 @@
+varying vec2 varTex0;
+
+void main() {
+   lowp vec4 col = texture2D(UNI_color, varTex0).rgba;
+   gl_FragColor = col;
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/unit_obj.a3d b/tests/RenderScriptTests/SceneGraph/res/raw/unit_obj.a3d
new file mode 100644
index 0000000..56eff04
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/unit_obj.a3d
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/values/strings.xml b/tests/RenderScriptTests/SceneGraph/res/values/strings.xml
new file mode 100644
index 0000000..c916d79
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <skip />
+    <string name="load_model">Load Model</string>
+    <string name="use_blur">Use Blur</string>
+</resources>
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java
new file mode 100644
index 0000000..42f2be5
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java
@@ -0,0 +1,118 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.SceneManager;
+
+import android.renderscript.*;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Camera extends SceneGraphBase {
+
+    Transform mTransform;
+
+    ScriptField_Camera_s.Item mData;
+    ScriptField_Camera_s mField;
+
+    public Camera() {
+        mData = new ScriptField_Camera_s.Item();
+        mData.near = 0.1f;
+        mData.far = 1000.0f;
+        mData.horizontalFOV = 60.0f;
+        mData.aspect = 0;
+    }
+
+    public void setTransform(Transform t) {
+        mTransform = t;
+        if (mField != null) {
+            mField.set_transformMatrix(0, mTransform.getRSData().getAllocation(), true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+    public void setFOV(float fov) {
+        mData.horizontalFOV = fov;
+        if (mField != null) {
+            mField.set_horizontalFOV(0, fov, true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+
+    public void setNear(float n) {
+        mData.near = n;
+        if (mField != null) {
+            mField.set_near(0, n, true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+
+    public void setFar(float f) {
+        mData.far = f;
+        if (mField != null) {
+            mField.set_far(0, f, true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+
+    public void setName(String n) {
+        super.setName(n);
+        if (mField != null) {
+            RenderScriptGL rs = SceneManager.getRS();
+            mData.name = getNameAlloc(rs);
+            mField.set_name(0, mData.name, true);
+            mField.set_isDirty(0, 1, true);
+        }
+    }
+
+    ScriptField_Camera_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+
+        if (mTransform == null) {
+            throw new RuntimeException("Cameras without transforms are invalid");
+        }
+
+        mField = new ScriptField_Camera_s(rs, 1);
+
+        mData.transformMatrix = mTransform.getRSData().getAllocation();
+        mData.transformTimestamp = 1;
+        mData.timestamp = 1;
+        mData.isDirty = 1;
+        mData.name = getNameAlloc(rs);
+        mField.set(mData, 0, true);
+
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
new file mode 100644
index 0000000..b4b6fb9
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
@@ -0,0 +1,563 @@
+/*

+ * 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.android.scenegraph;

+import com.android.scenegraph.CompoundTransform.TranslateComponent;

+import com.android.scenegraph.CompoundTransform.RotateComponent;

+import com.android.scenegraph.CompoundTransform.ScaleComponent;

+import java.io.IOException;

+import java.io.InputStream;

+import java.util.ArrayList;

+import java.util.Iterator;

+import java.util.List;

+import java.util.StringTokenizer;

+import java.util.HashMap;

+

+import javax.xml.parsers.DocumentBuilder;

+import javax.xml.parsers.DocumentBuilderFactory;

+import javax.xml.parsers.ParserConfigurationException;

+

+import org.w3c.dom.Document;

+import org.w3c.dom.Element;

+import org.w3c.dom.Node;

+import org.w3c.dom.NodeList;

+import org.xml.sax.SAXException;

+

+import android.renderscript.*;

+import android.util.Log;

+

+public class ColladaParser {

+    static final String TAG = "ColladaParser";

+    Document mDom;

+

+    HashMap<String, LightBase> mLights;

+    HashMap<String, Camera> mCameras;

+    HashMap<String, ArrayList<ShaderParam> > mEffectsParams;

+    HashMap<String, Texture2D> mImages;

+    HashMap<String, Texture2D> mSamplerImageMap;

+    HashMap<String, String> mMeshIdNameMap;

+    Scene mScene;

+

+    String mRootDir;

+

+    String toString(Float3 v) {

+        String valueStr = v.x + " " + v.y + " " + v.z;

+        return valueStr;

+    }

+

+    String toString(Float4 v) {

+        String valueStr = v.x + " " + v.y + " " + v.z + " " + v.w;

+        return valueStr;

+    }

+

+    public ColladaParser(){

+        mLights = new HashMap<String, LightBase>();

+        mCameras = new HashMap<String, Camera>();

+        mEffectsParams = new HashMap<String, ArrayList<ShaderParam> >();

+        mImages = new HashMap<String, Texture2D>();

+        mMeshIdNameMap = new HashMap<String, String>();

+    }

+

+    public void init(InputStream is, String rootDir) {

+        mLights.clear();

+        mCameras.clear();

+        mEffectsParams.clear();

+

+        mRootDir = rootDir;

+

+        long start = System.currentTimeMillis();

+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

+        try {

+            DocumentBuilder db = dbf.newDocumentBuilder();

+            mDom = db.parse(is);

+        } catch(ParserConfigurationException e) {

+            e.printStackTrace();

+        } catch(SAXException e) {

+            e.printStackTrace();

+        } catch(IOException e) {

+            e.printStackTrace();

+        }

+        long end = System.currentTimeMillis();

+        Log.v("TIMER", "    Parse time: " + (end - start));

+        exportSceneData();

+    }

+

+    Scene getScene() {

+        return mScene;

+    }

+

+    private void exportSceneData(){

+        mScene = new Scene();

+

+        Element docEle = mDom.getDocumentElement();

+        NodeList nl = docEle.getElementsByTagName("light");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element l = (Element)nl.item(i);

+                convertLight(l);

+            }

+        }

+

+        nl = docEle.getElementsByTagName("camera");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element c = (Element)nl.item(i);

+                convertCamera(c);

+            }

+        }

+

+        nl = docEle.getElementsByTagName("image");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element img = (Element)nl.item(i);

+                convertImage(img);

+            }

+        }

+

+        nl = docEle.getElementsByTagName("effect");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element e = (Element)nl.item(i);

+                convertEffects(e);

+            }

+        }

+

+        // Material is just a link to the effect

+        nl = docEle.getElementsByTagName("material");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element m = (Element)nl.item(i);

+                convertMaterials(m);

+            }

+        }

+

+        // Look through the geometry list and build up a correlation between id's and names

+        nl = docEle.getElementsByTagName("geometry");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element m = (Element)nl.item(i);

+                convertGeometries(m);

+            }

+        }

+

+

+        nl = docEle.getElementsByTagName("visual_scene");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element s = (Element)nl.item(i);

+                getScene(s);

+            }

+        }

+    }

+

+    private void getRenderable(Element shape, Transform t) {

+        String geoURL = shape.getAttribute("url").substring(1);

+        String geoName = mMeshIdNameMap.get(geoURL);

+        if (geoName != null) {

+            geoURL = geoName;

+        }

+        //RenderableGroup group = new RenderableGroup();

+        //group.setName(geoURL.substring(1));

+        //mScene.appendRenderable(group);

+        NodeList nl = shape.getElementsByTagName("instance_material");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element materialRef = (Element)nl.item(i);

+                String meshIndexName = materialRef.getAttribute("symbol");

+                String materialName = materialRef.getAttribute("target");

+

+                Renderable d = new Renderable();

+                d.setMesh(geoURL, meshIndexName);

+                d.setMaterialName(materialName.substring(1));

+                d.setName(geoURL);

+

+                //Log.v(TAG, "Created drawable geo " + geoURL + " index " + meshIndexName + " material " + materialName);

+

+                d.setTransform(t);

+                //Log.v(TAG, "Set source param " + t.getName());

+

+                // Now find all the parameters that exist on the material

+                ArrayList<ShaderParam> materialParams;

+                materialParams = mEffectsParams.get(materialName.substring(1));

+                for (int pI = 0; pI < materialParams.size(); pI ++) {

+                    d.appendSourceParams(materialParams.get(pI));

+                    //Log.v(TAG, "Set source param i: " + pI + " name " + materialParams.get(pI).getParamName());

+                }

+                mScene.appendRenderable(d);

+                //group.appendChildren(d);

+            }

+        }

+    }

+

+    private void updateLight(Element shape, Transform t) {

+        String lightURL = shape.getAttribute("url");

+        // collada uses a uri structure to link things,

+        // but we ignore it for now and do a simple search

+        LightBase light = mLights.get(lightURL.substring(1));

+        if (light != null) {

+            light.setTransform(t);

+            //Log.v(TAG, "Set Light " + light.getName() + " " + t.getName());

+        }

+    }

+

+    private void updateCamera(Element shape, Transform t) {

+        String camURL = shape.getAttribute("url");

+        // collada uses a uri structure to link things,

+        // but we ignore it for now and do a simple search

+        Camera cam = mCameras.get(camURL.substring(1));

+        if (cam != null) {

+            cam.setTransform(t);

+            //Log.v(TAG, "Set Camera " + cam.getName() + " " + t.getName());

+        }

+    }

+

+    private void getNode(Element node, Transform parent, String indent) {

+        String name = node.getAttribute("name");

+        String id = node.getAttribute("id");

+        CompoundTransform current = new CompoundTransform();

+        current.setName(name);

+        if (parent != null) {

+            parent.appendChild(current);

+        } else {

+            mScene.appendTransform(current);

+        }

+

+        mScene.addToTransformMap(current);

+

+        //Log.v(TAG, indent + "|");

+        //Log.v(TAG, indent + "[" + name + "]");

+

+        Node childNode = node.getFirstChild();

+        while (childNode != null) {

+            if (childNode.getNodeType() == Node.ELEMENT_NODE) {

+                Element field = (Element)childNode;

+                String fieldName = field.getTagName();

+                String description = field.getAttribute("sid");

+                if (fieldName.equals("translate")) {

+                    Float3 value = getFloat3(field);

+                    current.addTranslate(description, value);

+                    //Log.v(TAG, indent + " translate " + description + toString(value));

+                } else if (fieldName.equals("rotate")) {

+                    Float4 value = getFloat4(field);

+                    //Log.v(TAG, indent + " rotate " + description + toString(value));

+                    Float3 axis = new Float3(value.x, value.y, value.z);

+                    current.addRotate(description, axis, value.w);

+                } else if (fieldName.equals("scale")) {

+                    Float3 value = getFloat3(field);

+                    //Log.v(TAG, indent + " scale " + description + toString(value));

+                    current.addScale(description, value);

+                } else if (fieldName.equals("instance_geometry")) {

+                    getRenderable(field, current);

+                } else if (fieldName.equals("instance_light")) {

+                    updateLight(field, current);

+                } else if (fieldName.equals("instance_camera")) {

+                    updateCamera(field, current);

+                } else if (fieldName.equals("node")) {

+                    getNode(field, current, indent + "   ");

+                }

+            }

+            childNode = childNode.getNextSibling();

+        }

+    }

+

+    // This will find the actual texture node, which is sometimes hidden behind a sampler

+    // and sometimes referenced directly

+    Texture2D getTexture(String samplerName) {

+        String texName = samplerName;

+

+        // Check to see if the image file is hidden by a sampler surface link combo

+        Element sampler = mDom.getElementById(samplerName);

+        if (sampler != null) {

+            NodeList nl = sampler.getElementsByTagName("source");

+            if (nl != null && nl.getLength() == 1) {

+                Element ref = (Element)nl.item(0);

+                String surfaceName = getString(ref);

+                if (surfaceName == null) {

+                    return null;

+                }

+

+                Element surface = mDom.getElementById(surfaceName);

+                if (surface == null) {

+                    return null;

+                }

+                nl = surface.getElementsByTagName("init_from");

+                if (nl != null && nl.getLength() == 1) {

+                    ref = (Element)nl.item(0);

+                    texName = getString(ref);

+                }

+            }

+        }

+

+        //Log.v(TAG, "Extracted texture name " + texName);

+        return mImages.get(texName);

+    }

+

+    void extractParams(Element fx, ArrayList<ShaderParam> params) {

+        Node paramNode = fx.getFirstChild();

+        while (paramNode != null) {

+            if (paramNode.getNodeType() == Node.ELEMENT_NODE) {

+                String name = paramNode.getNodeName();

+                // Now find what type it is

+                Node typeNode = paramNode.getFirstChild();

+                while (typeNode != null && typeNode.getNodeType() != Node.ELEMENT_NODE) {

+                    typeNode = typeNode.getNextSibling();

+                }

+                String paramType = typeNode.getNodeName();

+                Element typeElem = (Element)typeNode;

+                ShaderParam sceneParam = null;

+                if (paramType.equals("color")) {

+                    Float4Param f4p = new Float4Param(name);

+                    Float4 value = getFloat4(typeElem);

+                    f4p.setValue(value);

+                    sceneParam = f4p;

+                    //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + toString(value));

+                } else if (paramType.equals("float")) {

+                    Float4Param f4p = new Float4Param(name);

+                    float value = getFloat(typeElem);

+                    f4p.setValue(new Float4(value, value, value, value));

+                    sceneParam = f4p;

+                    //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + value);

+                }  else if (paramType.equals("texture")) {

+                    String samplerName = typeElem.getAttribute("texture");

+                    Texture2D tex = getTexture(samplerName);

+                    TextureParam texP = new TextureParam(name);

+                    texP.setTexture(tex);

+                    sceneParam = texP;

+                    //Log.v(TAG, "Extracted texture " + tex);

+                }

+                if (sceneParam != null) {

+                    params.add(sceneParam);

+                }

+            }

+            paramNode = paramNode.getNextSibling();

+        }

+    }

+

+    private void convertMaterials(Element mat) {

+        String id = mat.getAttribute("id");

+        NodeList nl = mat.getElementsByTagName("instance_effect");

+        if (nl != null && nl.getLength() == 1) {

+            Element ref = (Element)nl.item(0);

+            String url = ref.getAttribute("url");

+            ArrayList<ShaderParam> params = mEffectsParams.get(url.substring(1));

+            mEffectsParams.put(id, params);

+        }

+    }

+

+    private void convertGeometries(Element geo) {

+        String id = geo.getAttribute("id");

+        String name = geo.getAttribute("name");

+        if (!id.equals(name)) {

+            mMeshIdNameMap.put(id, name);

+        }

+    }

+

+    private void convertEffects(Element fx) {

+        String id = fx.getAttribute("id");

+        ArrayList<ShaderParam> params = new ArrayList<ShaderParam>();

+

+        NodeList nl = fx.getElementsByTagName("newparam");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element field = (Element)nl.item(i);

+                field.setIdAttribute("sid", true);

+            }

+        }

+

+        nl = fx.getElementsByTagName("blinn");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element field = (Element)nl.item(i);

+                //Log.v(TAG, "blinn");

+                extractParams(field, params);

+            }

+        }

+        nl = fx.getElementsByTagName("lambert");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element field = (Element)nl.item(i);

+                //Log.v(TAG, "lambert");

+                extractParams(field, params);

+            }

+        }

+        nl = fx.getElementsByTagName("phong");

+        if (nl != null) {

+            for(int i = 0; i < nl.getLength(); i++) {

+                Element field = (Element)nl.item(i);

+                //Log.v(TAG, "phong");

+                extractParams(field, params);

+            }

+        }

+        mEffectsParams.put(id, params);

+    }

+

+    private void convertLight(Element light) {

+        String name = light.getAttribute("name");

+        String id = light.getAttribute("id");

+

+        // Determine type

+        String[] knownTypes = { "point", "spot", "directional" };

+        final int POINT_LIGHT = 0;

+        final int SPOT_LIGHT = 1;

+        final int DIR_LIGHT = 2;

+        int type = -1;

+        for (int i = 0; i < knownTypes.length; i ++) {

+            NodeList nl = light.getElementsByTagName(knownTypes[i]);

+            if (nl != null && nl.getLength() != 0) {

+                type = i;

+                break;

+            }

+        }

+

+        //Log.v(TAG, "Found Light Type " + type);

+

+        LightBase sceneLight = null;

+        switch (type) {

+        case POINT_LIGHT:

+            sceneLight = new PointLight();

+            break;

+        case SPOT_LIGHT: // TODO: finish light types

+            break;

+        case DIR_LIGHT: // TODO: finish light types

+            break;

+        }

+

+        if (sceneLight == null) {

+            return;

+        }

+

+        Float3 color = getFloat3(light, "color");

+        sceneLight.setColor(color.x, color.y, color.z);

+        sceneLight.setName(name);

+        mScene.appendLight(sceneLight);

+        mLights.put(id, sceneLight);

+

+        //Log.v(TAG, "Light " + name + " color " + toString(color));

+    }

+

+    private void convertCamera(Element camera) {

+        String name = camera.getAttribute("name");

+        String id = camera.getAttribute("id");

+        float fov = 30.0f;

+        if (getString(camera, "yfov") != null) {

+            fov = getFloat(camera, "yfov");

+        } else if(getString(camera, "xfov") != null) {

+            float aspect = getFloat(camera, "aspect_ratio");

+            fov = getFloat(camera, "xfov") / aspect;

+        }

+

+        float near = getFloat(camera, "znear");

+        float far = getFloat(camera, "zfar");

+

+        Camera sceneCamera = new Camera();

+        sceneCamera.setFOV(fov);

+        sceneCamera.setNear(near);

+        sceneCamera.setFar(far);

+        sceneCamera.setName(name);

+        mScene.appendCamera(sceneCamera);

+        mCameras.put(id, sceneCamera);

+    }

+

+    private void convertImage(Element img) {

+        String name = img.getAttribute("name");

+        String id = img.getAttribute("id");

+        String file = getString(img, "init_from");

+

+        Texture2D tex = new Texture2D();

+        tex.setFileName(file);

+        tex.setFileDir(mRootDir);

+        mScene.appendTextures(tex);

+        mImages.put(id, tex);

+    }

+

+    private void getScene(Element scene) {

+        String name = scene.getAttribute("name");

+        String id = scene.getAttribute("id");

+

+        Node childNode = scene.getFirstChild();

+        while (childNode != null) {

+            if (childNode.getNodeType() == Node.ELEMENT_NODE) {

+                String indent = "";

+                getNode((Element)childNode, null, indent);

+            }

+            childNode = childNode.getNextSibling();

+        }

+    }

+

+    private String getString(Element elem, String name) {

+        String text = null;

+        NodeList nl = elem.getElementsByTagName(name);

+        if (nl != null && nl.getLength() != 0) {

+            text = ((Element)nl.item(0)).getFirstChild().getNodeValue();

+        }

+        return text;

+    }

+

+    private String getString(Element elem) {

+        String text = null;

+        text = elem.getFirstChild().getNodeValue();

+        return text;

+    }

+

+    private int getInt(Element elem, String name) {

+        return Integer.parseInt(getString(elem, name));

+    }

+

+    private float getFloat(Element elem, String name) {

+        return Float.parseFloat(getString(elem, name));

+    }

+

+    private float getFloat(Element elem) {

+        return Float.parseFloat(getString(elem));

+    }

+

+    private Float3 parseFloat3(String valueString) {

+        StringTokenizer st = new StringTokenizer(valueString);

+        float x = Float.parseFloat(st.nextToken());

+        float y = Float.parseFloat(st.nextToken());

+        float z = Float.parseFloat(st.nextToken());

+        return new Float3(x, y, z);

+    }

+

+    private Float4 parseFloat4(String valueString) {

+        StringTokenizer st = new StringTokenizer(valueString);

+        float x = Float.parseFloat(st.nextToken());

+        float y = Float.parseFloat(st.nextToken());

+        float z = Float.parseFloat(st.nextToken());

+        float w = Float.parseFloat(st.nextToken());

+        return new Float4(x, y, z, w);

+    }

+

+    private Float3 getFloat3(Element elem, String name) {

+        String valueString = getString(elem, name);

+        return parseFloat3(valueString);

+    }

+

+    private Float4 getFloat4(Element elem, String name) {

+        String valueString = getString(elem, name);

+        return parseFloat4(valueString);

+    }

+

+    private Float3 getFloat3(Element elem) {

+        String valueString = getString(elem);

+        return parseFloat3(valueString);

+    }

+

+    private Float4 getFloat4(Element elem) {

+        String valueString = getString(elem);

+        return parseFloat4(valueString);

+    }

+}

diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java
new file mode 100644
index 0000000..301075e
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java
@@ -0,0 +1,139 @@
+/*
+ * 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.android.scenegraph;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Allocation.MipmapControl;
+import android.renderscript.Element.Builder;
+import android.renderscript.Font.Style;
+import android.renderscript.Program.TextureType;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+import com.android.scenegraph.SceneManager.SceneLoadedCallback;
+
+
+public class ColladaScene {
+
+    private String modelName;
+    private static String TAG = "ColladaScene";
+    private final int STATE_LAST_FOCUS = 1;
+    boolean mLoadFromSD = false;
+
+    SceneLoadedCallback mCallback;
+
+    public ColladaScene(String name, SceneLoadedCallback cb) {
+        modelName = name;
+        mCallback = cb;
+    }
+
+    public void init(RenderScriptGL rs, Resources res) {
+        mRS = rs;
+        mRes = res;
+
+        mLoadFromSD = SceneManager.isSDCardPath(modelName);
+
+        new ColladaLoaderTask().execute(modelName);
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    Scene mActiveScene;
+
+    private class ColladaLoaderTask extends AsyncTask<String, Void, Boolean> {
+        ColladaParser sceneSource;
+        protected Boolean doInBackground(String... names) {
+            String rootDir = names[0].substring(0, names[0].lastIndexOf('/') + 1);
+            long start = System.currentTimeMillis();
+            sceneSource = new ColladaParser();
+            InputStream is = null;
+            try {
+                if (!mLoadFromSD) {
+                    is = mRes.getAssets().open(names[0]);
+                } else {
+                    File f = new File(names[0]);
+                    is = new BufferedInputStream(new FileInputStream(f));
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Could not open collada file");
+                return new Boolean(false);
+            }
+            long end = System.currentTimeMillis();
+            Log.v("TIMER", "Stream load time: " + (end - start));
+
+            start = System.currentTimeMillis();
+            sceneSource.init(is, rootDir);
+            end = System.currentTimeMillis();
+            Log.v("TIMER", "Collada parse time: " + (end - start));
+            return new Boolean(true);
+        }
+
+        protected void onPostExecute(Boolean result) {
+            mActiveScene = sceneSource.getScene();
+            if (mCallback != null) {
+                mCallback.mLoadedScene = mActiveScene;
+                mCallback.run();
+            }
+
+            String shortName = modelName.substring(0, modelName.lastIndexOf('.'));
+            new A3DLoaderTask().execute(shortName + ".a3d");
+        }
+    }
+
+    private class A3DLoaderTask extends AsyncTask<String, Void, Boolean> {
+        protected Boolean doInBackground(String... names) {
+            long start = System.currentTimeMillis();
+            FileA3D model;
+            if (!mLoadFromSD) {
+                model = FileA3D.createFromAsset(mRS, mRes.getAssets(), names[0]);
+            } else {
+                model = FileA3D.createFromFile(mRS, names[0]);
+            }
+            int numModels = model.getIndexEntryCount();
+            for (int i = 0; i < numModels; i ++) {
+                FileA3D.IndexEntry entry = model.getIndexEntry(i);
+                if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
+                    mActiveScene.meshLoaded(entry.getMesh());
+                }
+            }
+            long end = System.currentTimeMillis();
+            Log.v("TIMER", "A3D load time: " + (end - start));
+            return new Boolean(true);
+        }
+
+        protected void onPostExecute(Boolean result) {
+        }
+    }
+
+}
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
new file mode 100644
index 0000000..9274b17
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
@@ -0,0 +1,215 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.SceneManager;
+
+import android.renderscript.*;
+import android.renderscript.Float3;
+import android.renderscript.Matrix4f;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class CompoundTransform extends Transform {
+
+    public static abstract class Component {
+        String mName;
+        CompoundTransform mParent;
+        int mParentIndex;
+        protected ScriptField_TransformComponent_s.Item mData;
+
+        Component(int type, String name) {
+            mData = new ScriptField_TransformComponent_s.Item();
+            mData.type = type;
+            mName = name;
+        }
+
+        void setNameAlloc() {
+            RenderScriptGL rs = SceneManager.getRS();
+            if (mData.name != null)  {
+                return;
+            }
+            mData.name = SceneManager.getCachedAlloc(getName());
+            if (mData.name == null) {
+                mData.name = SceneManager.getStringAsAllocation(rs, getName());
+                SceneManager.cacheAlloc(getName(), mData.name);
+            }
+        }
+
+        ScriptField_TransformComponent_s.Item getRSData() {
+            setNameAlloc();
+            return mData;
+        }
+
+        protected void update() {
+            if (mParent != null) {
+                mParent.updateRSComponent(this);
+            }
+        }
+
+        public String getName() {
+            return mName;
+        }
+    }
+
+    public static class TranslateComponent extends Component {
+        public TranslateComponent(String name, Float3 translate) {
+            super(ScriptC_export.const_Transform_TRANSLATE, name);
+            setValue(translate);
+        }
+        public Float3 getValue() {
+            return new Float3(mData.value.x, mData.value.y, mData.value.z);
+        }
+        public void setValue(Float3 val) {
+            mData.value.x = val.x;
+            mData.value.y = val.y;
+            mData.value.z = val.z;
+            update();
+        }
+    }
+
+    public static class RotateComponent extends Component {
+        public RotateComponent(String name, Float3 axis, float angle) {
+            super(ScriptC_export.const_Transform_ROTATE, name);
+            setAxis(axis);
+            setAngle(angle);
+        }
+        public Float3 getAxis() {
+            return new Float3(mData.value.x, mData.value.y, mData.value.z);
+        }
+        public float getAngle() {
+            return mData.value.w;
+        }
+        public void setAxis(Float3 val) {
+            mData.value.x = val.x;
+            mData.value.y = val.y;
+            mData.value.z = val.z;
+            update();
+        }
+        public void setAngle(float val) {
+            mData.value.w = val;
+            update();
+        }
+    }
+
+    public static class ScaleComponent extends Component {
+        public ScaleComponent(String name, Float3 scale) {
+            super(ScriptC_export.const_Transform_SCALE, name);
+            setValue(scale);
+        }
+        public Float3 getValue() {
+            return new Float3(mData.value.x, mData.value.y, mData.value.z);
+        }
+        public void setValue(Float3 val) {
+            mData.value.x = val.x;
+            mData.value.y = val.y;
+            mData.value.z = val.z;
+            update();
+        }
+    }
+
+    ScriptField_TransformComponent_s mComponentField;
+    public ArrayList<Component> mTransformComponents;
+
+    public CompoundTransform() {
+        mTransformComponents = new ArrayList<Component>();
+    }
+
+    public TranslateComponent addTranslate(String name, Float3 translate) {
+        TranslateComponent c = new TranslateComponent(name, translate);
+        addComponent(c);
+        return c;
+    }
+
+    public RotateComponent addRotate(String name, Float3 axis, float angle) {
+        RotateComponent c = new RotateComponent(name, axis, angle);
+        addComponent(c);
+        return c;
+    }
+
+    public ScaleComponent addScale(String name, Float3 scale) {
+        ScaleComponent c = new ScaleComponent(name, scale);
+        addComponent(c);
+        return c;
+    }
+
+    public void addComponent(Component c) {
+        if (c.mParent != null) {
+            throw new IllegalArgumentException("Transform components may not be shared");
+        }
+        c.mParent = this;
+        c.mParentIndex = mTransformComponents.size();
+        mTransformComponents.add(c);
+        updateRSComponentAllocation();
+    }
+
+    public void setComponent(int index, Component c) {
+        if (c.mParent != null) {
+            throw new IllegalArgumentException("Transform components may not be shared");
+        }
+        if (index >= mTransformComponents.size()) {
+            throw new IllegalArgumentException("Invalid component index");
+        }
+        c.mParent = this;
+        c.mParentIndex = index;
+        mTransformComponents.set(index, c);
+        updateRSComponent(c);
+    }
+
+    void updateRSComponent(Component c) {
+        if (mField == null || mComponentField == null) {
+            return;
+        }
+        mComponentField.set(c.getRSData(), c.mParentIndex, true);
+        mField.set_isDirty(0, 1, true);
+    }
+
+    void updateRSComponentAllocation() {
+        if (mField == null) {
+            return;
+        }
+        initLocalData();
+
+        mField.set_components(0, mTransformData.components, false);
+        mField.set_isDirty(0, 1, true);
+    }
+
+    void initLocalData() {
+        RenderScriptGL rs = SceneManager.getRS();
+        int numComponenets = mTransformComponents.size();
+        if (numComponenets > 0) {
+            mComponentField = new ScriptField_TransformComponent_s(rs, numComponenets);
+            for (int i = 0; i < numComponenets; i ++) {
+                Component ith = mTransformComponents.get(i);
+                mComponentField.set(ith.getRSData(), i, false);
+            }
+            mComponentField.copyAll();
+
+            mTransformData.components = mComponentField.getAllocation();
+        }
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java
new file mode 100644
index 0000000..1502458
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java
@@ -0,0 +1,146 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.Scene;
+import com.android.scenegraph.SceneManager;
+
+import android.renderscript.Element;
+import android.renderscript.Float4;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Float4Param extends ShaderParam {
+    private static String TAG = "Float4Param";
+
+    LightBase mLight;
+
+    public Float4Param(String name) {
+        super(name);
+    }
+
+    public Float4Param(String name, float x) {
+        super(name);
+        set(x, 0, 0, 0);
+    }
+
+    public Float4Param(String name, float x, float y) {
+        super(name);
+        set(x, y, 0, 0);
+    }
+
+    public Float4Param(String name, float x, float y, float z) {
+        super(name);
+        set(x, y, z, 0);
+    }
+
+    public Float4Param(String name, float x, float y, float z, float w) {
+        super(name);
+        set(x, y, z, w);
+    }
+
+    void set(float x, float y, float z, float w) {
+        mData.float_value.x = x;
+        mData.float_value.y = y;
+        mData.float_value.z = z;
+        mData.float_value.w = w;
+        if (mField != null) {
+            mField.set_float_value(0, mData.float_value, true);
+        }
+        incTimestamp();
+    }
+
+    public void setValue(Float4 v) {
+        set(v.x, v.y, v.z, v.w);
+    }
+
+    public Float4 getValue() {
+        return mData.float_value;
+    }
+
+    public void setLight(LightBase l) {
+        mLight = l;
+        if (mField != null) {
+            mData.light = mLight.getRSData().getAllocation();
+            mField.set_light(0, mData.light, true);
+        }
+        incTimestamp();
+    }
+
+    boolean findLight(String property) {
+        String indexStr = mParamName.substring(property.length() + 1);
+        if (indexStr == null) {
+            Log.e(TAG, "Invalid light index.");
+            return false;
+        }
+        int index = Integer.parseInt(indexStr);
+        if (index == -1) {
+            return false;
+        }
+        Scene parentScene = SceneManager.getInstance().getActiveScene();
+        ArrayList<LightBase> allLights = parentScene.getLights();
+        if (index >= allLights.size()) {
+            return false;
+        }
+        mLight = allLights.get(index);
+        if (mLight == null) {
+            return false;
+        }
+        return true;
+    }
+
+    int getTypeFromName() {
+        int paramType = ScriptC_export.const_ShaderParam_FLOAT4_DATA;
+        if (mParamName.equalsIgnoreCase(cameraPos)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_CAMERA_POS;
+        } else if(mParamName.equalsIgnoreCase(cameraDir)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_CAMERA_DIR;
+        } else if(mParamName.startsWith(lightColor) && findLight(lightColor)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_COLOR;
+        } else if(mParamName.startsWith(lightPos) && findLight(lightPos)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_POS;
+        } else if(mParamName.startsWith(lightDir) && findLight(lightDir)) {
+            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_DIR;
+        }
+        return paramType;
+    }
+
+    void initLocalData() {
+        mData.type = getTypeFromName();
+        if (mCamera != null) {
+            mData.camera = mCamera.getRSData().getAllocation();
+        }
+        if (mLight != null) {
+            mData.light = mLight.getRSData().getAllocation();
+        }
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
new file mode 100644
index 0000000..8a468db
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
@@ -0,0 +1,174 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.TextureBase;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramFragment.Builder;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class FragmentShader extends Shader {
+    ProgramFragment mProgram;
+    ScriptField_FragmentShader_s mField;
+
+    public static class Builder {
+
+        FragmentShader mShader;
+        ProgramFragment.Builder mBuilder;
+
+        public Builder(RenderScriptGL rs) {
+            mShader = new FragmentShader();
+            mBuilder = new ProgramFragment.Builder(rs);
+        }
+
+        public Builder setShader(Resources resources, int resourceID) {
+            mBuilder.setShader(resources, resourceID);
+            return this;
+        }
+
+        public Builder setShader(String code) {
+            mBuilder.setShader(code);
+            return this;
+        }
+
+        public Builder setObjectConst(Type type) {
+            mShader.mPerObjConstants = type;
+            return this;
+        }
+
+        public Builder setShaderConst(Type type) {
+            mShader.mPerShaderConstants = type;
+            return this;
+        }
+
+        public Builder addShaderTexture(Program.TextureType texType, String name) {
+            mShader.mShaderTextureNames.add(name);
+            mShader.mShaderTextureTypes.add(texType);
+            return this;
+        }
+
+        public Builder addTexture(Program.TextureType texType, String name) {
+            mShader.mTextureNames.add(name);
+            mShader.mTextureTypes.add(texType);
+            return this;
+        }
+
+        public FragmentShader create() {
+            if (mShader.mPerShaderConstants != null) {
+                mBuilder.addConstant(mShader.mPerShaderConstants);
+            }
+            if (mShader.mPerObjConstants != null) {
+                mBuilder.addConstant(mShader.mPerObjConstants);
+            }
+            for (int i = 0; i < mShader.mTextureTypes.size(); i ++) {
+                mBuilder.addTexture(mShader.mTextureTypes.get(i),
+                                    mShader.mTextureNames.get(i));
+            }
+            for (int i = 0; i < mShader.mShaderTextureTypes.size(); i ++) {
+                mBuilder.addTexture(mShader.mShaderTextureTypes.get(i),
+                                    mShader.mShaderTextureNames.get(i));
+            }
+
+            mShader.mProgram = mBuilder.create();
+            return mShader;
+        }
+    }
+
+    public ProgramFragment getProgram() {
+        return mProgram;
+    }
+
+    ScriptField_ShaderParam_s getTextureParams() {
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        ArrayList<ScriptField_ShaderParam_s.Item> paramList;
+        paramList = new ArrayList<ScriptField_ShaderParam_s.Item>();
+
+        int shaderTextureStart = mTextureTypes.size();
+        for (int i = 0; i < mShaderTextureNames.size(); i ++) {
+            ShaderParam sp = mSourceParams.get(mShaderTextureNames.get(i));
+            if (sp != null && sp instanceof TextureParam) {
+                TextureParam p = (TextureParam)sp;
+                ScriptField_ShaderParam_s.Item paramRS = new ScriptField_ShaderParam_s.Item();
+                paramRS.bufferOffset = shaderTextureStart + i;
+                paramRS.transformTimestamp = 0;
+                paramRS.dataTimestamp = 0;
+                paramRS.data = p.getRSData().getAllocation();
+                paramList.add(paramRS);
+            }
+        }
+
+        ScriptField_ShaderParam_s rsParams = null;
+        int paramCount = paramList.size();
+        if (paramCount != 0) {
+            rsParams = new ScriptField_ShaderParam_s(rs, paramCount);
+            for (int i = 0; i < paramCount; i++) {
+                rsParams.set(paramList.get(i), i, false);
+            }
+            rsParams.copyAll();
+        }
+        return rsParams;
+    }
+
+    ScriptField_FragmentShader_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        ScriptField_FragmentShader_s.Item item = new ScriptField_FragmentShader_s.Item();
+        item.program = mProgram;
+
+        ScriptField_ShaderParam_s texParams = getTextureParams();
+        if (texParams != null) {
+            item.shaderTextureParams = texParams.getAllocation();
+        }
+
+        linkConstants(rs);
+        if (mPerShaderConstants != null) {
+            item.shaderConst = mConstantBuffer;
+            item.shaderConstParams = mConstantBufferParams.getAllocation();
+            mProgram.bindConstants(item.shaderConst, 0);
+        }
+
+        item.objectConstIndex = -1;
+        if (mPerObjConstants != null) {
+            item.objectConstIndex = mPerShaderConstants != null ? 1 : 0;
+        }
+
+        mField = new ScriptField_FragmentShader_s(rs, 1);
+        mField.set(item, 0, true);
+        return mField;
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java
new file mode 100644
index 0000000..8f5e2e7
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java
@@ -0,0 +1,111 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class LightBase extends SceneGraphBase {
+    static final int RS_LIGHT_POINT = 0;
+    static final int RS_LIGHT_DIRECTIONAL = 1;
+
+    ScriptField_Light_s mField;
+    ScriptField_Light_s.Item mFieldData;
+    Transform mTransform;
+    Float4 mColor;
+    float mIntensity;
+    public LightBase() {
+        mColor = new Float4(0.0f, 0.0f, 0.0f, 0.0f);
+        mIntensity = 1.0f;
+    }
+
+    public void setTransform(Transform t) {
+        mTransform = t;
+        updateRSData();
+    }
+
+    public void setColor(float r, float g, float b) {
+        mColor.x = r;
+        mColor.y = g;
+        mColor.z = b;
+        updateRSData();
+    }
+
+    public void setColor(Float3 c) {
+        setColor(c.x, c.y, c.z);
+    }
+
+    public void setIntensity(float i) {
+        mIntensity = i;
+        updateRSData();
+    }
+
+    public void setName(String n) {
+        super.setName(n);
+        updateRSData();
+    }
+
+    protected void updateRSData() {
+        if (mField == null) {
+            return;
+        }
+        RenderScriptGL rs = SceneManager.getRS();
+        mFieldData.transformMatrix = mTransform.getRSData().getAllocation();
+        mFieldData.name = getNameAlloc(rs);
+        mFieldData.color = mColor;
+        mFieldData.intensity = mIntensity;
+
+        initLocalData();
+
+        mField.set(mFieldData, 0, true);
+    }
+
+    abstract void initLocalData();
+
+    ScriptField_Light_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+        if (mField == null) {
+            mField = new ScriptField_Light_s(rs, 1);
+            mFieldData = new ScriptField_Light_s.Item();
+        }
+
+        updateRSData();
+
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java
new file mode 100644
index 0000000..6d70bc9
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java
@@ -0,0 +1,60 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Matrix4f;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class MatrixTransform extends Transform {
+
+    Matrix4f mLocalMatrix;
+    public MatrixTransform() {
+        mLocalMatrix = new Matrix4f();
+    }
+
+    public void setMatrix(Matrix4f matrix) {
+        mLocalMatrix = matrix;
+        updateRSData();
+    }
+
+    public Matrix4f getMatrix() {
+        return new Matrix4f(mLocalMatrix.getArray());
+    }
+
+    void initLocalData() {
+        mTransformData.localMat = mLocalMatrix;
+    }
+
+    void updateRSData() {
+        if (mField == null) {
+            return;
+        }
+        mField.set_localMat(0, mLocalMatrix, false);
+        mField.set_isDirty(0, 1, true);
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java
new file mode 100644
index 0000000..574bafc
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java
@@ -0,0 +1,43 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class PointLight extends LightBase {
+    public PointLight() {
+    }
+
+     void initLocalData() {
+        mFieldData.type = RS_LIGHT_POINT;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java
new file mode 100644
index 0000000..02fd69d
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java
@@ -0,0 +1,124 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.util.Log;
+
+import android.renderscript.*;
+import android.content.res.Resources;
+
+/**
+ * @hide
+ */
+public class RenderPass extends SceneGraphBase {
+
+    TextureRenderTarget mColorTarget;
+    Float4 mClearColor;
+    boolean mShouldClearColor;
+
+    TextureRenderTarget mDepthTarget;
+    float mClearDepth;
+    boolean mShouldClearDepth;
+
+    ArrayList<RenderableBase> mObjectsToDraw;
+
+    Camera mCamera;
+
+    ScriptField_RenderPass_s.Item mRsField;
+
+    public RenderPass() {
+        mObjectsToDraw = new ArrayList<RenderableBase>();
+        mClearColor = new Float4(0.0f, 0.0f, 0.0f, 0.0f);
+        mShouldClearColor = true;
+        mClearDepth = 1.0f;
+        mShouldClearDepth = true;
+    }
+
+    public void appendRenderable(Renderable d) {
+        mObjectsToDraw.add(d);
+    }
+
+    public void setCamera(Camera c) {
+        mCamera = c;
+    }
+
+    public void setColorTarget(TextureRenderTarget colorTarget) {
+        mColorTarget = colorTarget;
+    }
+    public void setClearColor(Float4 clearColor) {
+        mClearColor = clearColor;
+    }
+    public void setShouldClearColor(boolean shouldClearColor) {
+        mShouldClearColor = shouldClearColor;
+    }
+
+    public void setDepthTarget(TextureRenderTarget depthTarget) {
+        mDepthTarget = depthTarget;
+    }
+    public void setClearDepth(float clearDepth) {
+        mClearDepth = clearDepth;
+    }
+    public void setShouldClearDepth(boolean shouldClearDepth) {
+        mShouldClearDepth = shouldClearDepth;
+    }
+
+    public ArrayList<RenderableBase> getRenderables() {
+        return mObjectsToDraw;
+    }
+
+    ScriptField_RenderPass_s.Item getRsField(RenderScriptGL rs, Resources res) {
+        if (mRsField != null) {
+            return mRsField;
+        }
+
+        mRsField = new ScriptField_RenderPass_s.Item();
+        if (mColorTarget != null) {
+            mRsField.color_target = mColorTarget.getRsData(true).get_texture(0);
+        }
+        if (mColorTarget != null) {
+            mRsField.depth_target = mDepthTarget.getRsData(true).get_texture(0);
+        }
+        mRsField.camera = mCamera != null ? mCamera.getRSData().getAllocation() : null;
+
+        if (mObjectsToDraw.size() != 0) {
+            Allocation drawableData = Allocation.createSized(rs,
+                                                              Element.ALLOCATION(rs),
+                                                              mObjectsToDraw.size());
+            Allocation[] drawableAllocs = new Allocation[mObjectsToDraw.size()];
+            for (int i = 0; i < mObjectsToDraw.size(); i ++) {
+                Renderable dI = (Renderable)mObjectsToDraw.get(i);
+                drawableAllocs[i] = dI.getRsField(rs, res).getAllocation();
+            }
+            drawableData.copyFrom(drawableAllocs);
+            mRsField.objects = drawableData;
+        }
+
+        mRsField.clear_color = mClearColor;
+        mRsField.clear_depth = mClearDepth;
+        mRsField.should_clear_color = mShouldClearColor;
+        mRsField.should_clear_depth = mShouldClearDepth;
+        return mRsField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java
new file mode 100644
index 0000000..c08a722
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java
@@ -0,0 +1,111 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import android.content.res.Resources;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramRaster;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RenderState extends SceneGraphBase {
+    VertexShader mVertex;
+    FragmentShader mFragment;
+    ProgramStore mStore;
+    ProgramRaster mRaster;
+
+    ScriptField_RenderState_s mField;
+
+    public RenderState(VertexShader pv,
+                       FragmentShader pf,
+                       ProgramStore ps,
+                       ProgramRaster pr) {
+        mVertex = pv;
+        mFragment = pf;
+        mStore = ps;
+        mRaster = pr;
+    }
+
+    public RenderState(RenderState r) {
+        mVertex = r.mVertex;
+        mFragment = r.mFragment;
+        mStore = r.mStore;
+        mRaster = r.mRaster;
+    }
+
+    public void setProgramVertex(VertexShader pv) {
+        mVertex = pv;
+        updateRSData();
+    }
+
+    public void setProgramFragment(FragmentShader pf) {
+        mFragment = pf;
+        updateRSData();
+    }
+
+    public void setProgramStore(ProgramStore ps) {
+        mStore = ps;
+        updateRSData();
+    }
+
+    public void setProgramRaster(ProgramRaster pr) {
+        mRaster = pr;
+        updateRSData();
+    }
+
+    void updateRSData() {
+        if (mField == null) {
+            return;
+        }
+        ScriptField_RenderState_s.Item item = new ScriptField_RenderState_s.Item();
+        item.pv = mVertex.getRSData().getAllocation();
+        item.pf = mFragment.getRSData().getAllocation();
+        item.ps = mStore;
+        item.pr = mRaster;
+
+        mField.set(item, 0, true);
+    }
+
+    public ScriptField_RenderState_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+
+        mField = new ScriptField_RenderState_s(rs, 1);
+        updateRSData();
+
+        return mField;
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
new file mode 100644
index 0000000..9266f30
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
@@ -0,0 +1,224 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import com.android.scenegraph.Float4Param;
+import com.android.scenegraph.MatrixTransform;
+import com.android.scenegraph.SceneManager;
+import com.android.scenegraph.ShaderParam;
+import com.android.scenegraph.TransformParam;
+
+import android.content.res.Resources;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Element.DataType;
+import android.renderscript.Matrix4f;
+import android.renderscript.Mesh;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Renderable extends RenderableBase {
+    HashMap<String, ShaderParam> mSourceParams;
+
+    RenderState mRenderState;
+    Transform mTransform;
+
+    String mMeshName;
+    String mMeshIndexName;
+
+    public String mMaterialName;
+
+    ScriptField_Renderable_s mField;
+    ScriptField_Renderable_s.Item mData;
+
+    public Renderable() {
+        mSourceParams = new HashMap<String, ShaderParam>();
+        mData = new ScriptField_Renderable_s.Item();
+    }
+
+    public void setCullType(int cull) {
+        mData.cullType = cull;
+    }
+
+    public void setRenderState(RenderState renderState) {
+        mRenderState = renderState;
+        if (mField != null) {
+            RenderScriptGL rs = SceneManager.getRS();
+            updateFieldItem(rs);
+            mField.set(mData, 0, true);
+        }
+    }
+
+    public void setMesh(Mesh mesh) {
+        mData.mesh = mesh;
+        if (mField != null) {
+            mField.set_mesh(0, mData.mesh, true);
+        }
+    }
+
+    public void setMesh(String mesh, String indexName) {
+        mMeshName = mesh;
+        mMeshIndexName = indexName;
+    }
+
+    public void setMaterialName(String name) {
+        mMaterialName = name;
+    }
+
+    public Transform getTransform() {
+        return mTransform;
+    }
+
+    public void setTransform(Transform t) {
+        mTransform = t;
+        if (mField != null) {
+            RenderScriptGL rs = SceneManager.getRS();
+            updateFieldItem(rs);
+            mField.set(mData, 0, true);
+        }
+    }
+
+    public void appendSourceParams(ShaderParam p) {
+        mSourceParams.put(p.getParamName(), p);
+        // Possibly lift this restriction later
+        if (mField != null) {
+            throw new RuntimeException("Can't add source params to objects that are rendering");
+        }
+    }
+
+    public void resolveMeshData(Mesh mesh) {
+        mData.mesh = mesh;
+        if (mData.mesh == null) {
+            Log.v("DRAWABLE: ", "*** NO MESH *** " + mMeshName);
+            return;
+        }
+        int subIndexCount = mData.mesh.getPrimitiveCount();
+        if (subIndexCount == 1 || mMeshIndexName == null) {
+            mData.meshIndex = 0;
+        } else {
+            for (int i = 0; i < subIndexCount; i ++) {
+                if (mData.mesh.getIndexSetAllocation(i).getName().equals(mMeshIndexName)) {
+                    mData.meshIndex = i;
+                    break;
+                }
+            }
+        }
+        if (mField != null) {
+            mField.set(mData, 0, true);
+        }
+    }
+
+    void updateTextures(RenderScriptGL rs) {
+        Iterator<ShaderParam> allParamsIter = mSourceParams.values().iterator();
+        int paramIndex = 0;
+        while (allParamsIter.hasNext()) {
+            ShaderParam sp = allParamsIter.next();
+            if (sp instanceof TextureParam) {
+                TextureParam p = (TextureParam)sp;
+                TextureBase tex = p.getTexture();
+                if (tex != null) {
+                    mData.pf_textures[paramIndex++] = tex.getRsData(false).getAllocation();
+                }
+            }
+        }
+        ProgramFragment pf = mRenderState.mFragment.mProgram;
+        mData.pf_num_textures = pf != null ? Math.min(pf.getTextureCount(), paramIndex) : 0;
+        if (mField != null) {
+            mField.set_pf_textures(0, mData.pf_textures, true);
+            mField.set_pf_num_textures(0, mData.pf_num_textures, true);
+        }
+    }
+
+    public void setVisible(boolean vis) {
+        mData.cullType = vis ? 0 : 2;
+        if (mField != null) {
+            mField.set_cullType(0, mData.cullType, true);
+        }
+    }
+
+    ScriptField_Renderable_s getRsField(RenderScriptGL rs, Resources res) {
+        if (mField != null) {
+            return mField;
+        }
+        updateFieldItem(rs);
+        updateTextures(rs);
+
+        mField = new ScriptField_Renderable_s(rs, 1);
+        mField.set(mData, 0, true);
+
+        return mField;
+    }
+
+    void updateVertexConstants(RenderScriptGL rs) {
+        Allocation pvParams = null, vertexConstants = null;
+        VertexShader pv = mRenderState.mVertex;
+        if (pv != null && pv.getObjectConstants() != null) {
+            vertexConstants = Allocation.createTyped(rs, pv.getObjectConstants());
+            Element vertexConst = vertexConstants.getType().getElement();
+            pvParams = ShaderParam.fillInParams(vertexConst, mSourceParams,
+                                                mTransform).getAllocation();
+        }
+        mData.pv_const = vertexConstants;
+        mData.pv_constParams = pvParams;
+    }
+
+    void updateFragmentConstants(RenderScriptGL rs) {
+        Allocation pfParams = null, fragmentConstants = null;
+        FragmentShader pf = mRenderState.mFragment;
+        if (pf != null && pf.getObjectConstants() != null) {
+            fragmentConstants = Allocation.createTyped(rs, pf.getObjectConstants());
+            Element fragmentConst = fragmentConstants.getType().getElement();
+            pfParams = ShaderParam.fillInParams(fragmentConst, mSourceParams,
+                                                mTransform).getAllocation();
+        }
+        mData.pf_const = fragmentConstants;
+        mData.pf_constParams = pfParams;
+    }
+
+    void updateFieldItem(RenderScriptGL rs) {
+        if (mRenderState == null) {
+            mRenderState = SceneManager.getDefaultState();
+        }
+        if (mTransform == null) {
+            mTransform = SceneManager.getDefaultTransform();
+        }
+        updateVertexConstants(rs);
+        updateFragmentConstants(rs);
+
+        mData.transformMatrix = mTransform.getRSData().getAllocation();
+
+        mData.name = getNameAlloc(rs);
+        mData.render_state = mRenderState.getRSData().getAllocation();
+        mData.bVolInitialized = 0;
+    }
+}
+
+
+
+
+
diff --git a/libs/rs/rsFifo.cpp b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java
similarity index 61%
copy from libs/rs/rsFifo.cpp
copy to tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java
index 3d5d8c4..74535dd 100644
--- a/libs/rs/rsFifo.cpp
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java
@@ -14,18 +14,26 @@
  * limitations under the License.
  */
 
-#include "rsFifoSocket.h"
-#include "utils/Timers.h"
-#include "utils/StopWatch.h"
+package com.android.scenegraph;
 
-using namespace android;
-using namespace android::renderscript;
+import java.lang.Math;
+import java.util.ArrayList;
 
-Fifo::Fifo() {
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.util.Log;
 
+/**
+ * @hide
+ */
+public class RenderableBase extends SceneGraphBase {
+    public RenderableBase() {
+    }
 }
 
-Fifo::~Fifo() {
 
-}
+
+
 
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java
new file mode 100644
index 0000000..590bbab
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java
@@ -0,0 +1,47 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RenderableGroup extends RenderableBase {
+
+    ArrayList<RenderableBase> mChildren;
+
+    public RenderableGroup() {
+        mChildren = new ArrayList<RenderableBase>();
+    }
+
+    public void appendChildren(RenderableBase d) {
+        mChildren.add(d);
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
new file mode 100644
index 0000000..27336ab
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
@@ -0,0 +1,373 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.android.scenegraph.Camera;
+import com.android.scenegraph.CompoundTransform;
+import com.android.scenegraph.RenderPass;
+import com.android.scenegraph.Renderable;
+import com.android.scenegraph.SceneManager;
+import com.android.scenegraph.TextureBase;
+
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Mesh;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Scene extends SceneGraphBase {
+    private static String TIMER_TAG = "TIMER";
+
+    CompoundTransform mRootTransforms;
+    HashMap<String, Transform> mTransformMap;
+    ArrayList<RenderPass> mRenderPasses;
+    ArrayList<LightBase> mLights;
+    ArrayList<Camera> mCameras;
+    ArrayList<FragmentShader> mFragmentShaders;
+    ArrayList<VertexShader> mVertexShaders;
+    ArrayList<RenderableBase> mRenderables;
+    HashMap<String, RenderableBase> mRenderableMap;
+    ArrayList<Texture2D> mTextures;
+
+    HashMap<String, ArrayList<Renderable> > mRenderableMeshMap;
+
+    // RS Specific stuff
+    ScriptField_SgTransform mTransformRSData;
+
+    RenderScriptGL mRS;
+    Resources mRes;
+
+    ScriptField_RenderPass_s mRenderPassAlloc;
+
+    public Scene() {
+        mRenderPasses = new ArrayList<RenderPass>();
+        mLights = new ArrayList<LightBase>();
+        mCameras = new ArrayList<Camera>();
+        mFragmentShaders = new ArrayList<FragmentShader>();
+        mVertexShaders = new ArrayList<VertexShader>();
+        mRenderables = new ArrayList<RenderableBase>();
+        mRenderableMap = new HashMap<String, RenderableBase>();
+        mRenderableMeshMap = new HashMap<String, ArrayList<Renderable> >();
+        mTextures = new ArrayList<Texture2D>();
+        mRootTransforms = new CompoundTransform();
+        mRootTransforms.setName("_scene_root_");
+        mTransformMap = new HashMap<String, Transform>();
+    }
+
+    public void appendTransform(Transform t) {
+        if (t == null) {
+            throw new RuntimeException("Adding null object");
+        }
+        mRootTransforms.appendChild(t);
+    }
+
+    public CompoundTransform appendNewCompoundTransform() {
+        CompoundTransform t = new CompoundTransform();
+        appendTransform(t);
+        return t;
+    }
+
+    public MatrixTransform appendNewMatrixTransform() {
+        MatrixTransform t = new MatrixTransform();
+        appendTransform(t);
+        return t;
+    }
+
+    // temporary
+    public void addToTransformMap(Transform t) {
+        mTransformMap.put(t.getName(), t);
+    }
+
+    public Transform getTransformByName(String name) {
+        return mTransformMap.get(name);
+    }
+
+    public void appendRenderPass(RenderPass p) {
+        if (p == null) {
+            throw new RuntimeException("Adding null object");
+        }
+        mRenderPasses.add(p);
+    }
+
+    public RenderPass appendNewRenderPass() {
+        RenderPass p = new RenderPass();
+        appendRenderPass(p);
+        return p;
+    }
+
+    public void clearRenderPasses() {
+        mRenderPasses.clear();
+    }
+
+    public void appendLight(LightBase l) {
+        if (l == null) {
+            throw new RuntimeException("Adding null object");
+        }
+        mLights.add(l);
+    }
+
+    public void appendCamera(Camera c) {
+        if (c == null) {
+            throw new RuntimeException("Adding null object");
+        }
+        mCameras.add(c);
+    }
+
+    public Camera appendNewCamera() {
+        Camera c = new Camera();
+        appendCamera(c);
+        return c;
+    }
+
+    public void appendShader(FragmentShader f) {
+        if (f == null) {
+            throw new RuntimeException("Adding null object");
+        }
+        mFragmentShaders.add(f);
+    }
+
+    public void appendShader(VertexShader v) {
+        if (v == null) {
+            throw new RuntimeException("Adding null object");
+        }
+        mVertexShaders.add(v);
+    }
+
+    public ArrayList<Camera> getCameras() {
+        return mCameras;
+    }
+
+    public ArrayList<LightBase> getLights() {
+        return mLights;
+    }
+
+    public void appendRenderable(RenderableBase d) {
+        if (d == null) {
+            throw new RuntimeException("Adding null object");
+        }
+        mRenderables.add(d);
+        if (d.getName() != null) {
+            mRenderableMap.put(d.getName(), d);
+        }
+    }
+
+    public Renderable appendNewRenderable() {
+        Renderable r = new Renderable();
+        appendRenderable(r);
+        return r;
+    }
+
+    public ArrayList<RenderableBase> getRenderables() {
+        return mRenderables;
+    }
+
+    public RenderableBase getRenderableByName(String name) {
+        return mRenderableMap.get(name);
+    }
+
+    public void appendTextures(Texture2D tex) {
+        if (tex == null) {
+            throw new RuntimeException("Adding null object");
+        }
+        mTextures.add(tex);
+    }
+
+    public void assignRenderStateToMaterial(RenderState renderState, String regex) {
+        Pattern pattern = Pattern.compile(regex);
+        int numRenderables = mRenderables.size();
+        for (int i = 0; i < numRenderables; i ++) {
+            Renderable shape = (Renderable)mRenderables.get(i);
+            Matcher m = pattern.matcher(shape.mMaterialName);
+            if (m.find()) {
+                shape.setRenderState(renderState);
+            }
+        }
+    }
+
+    public void assignRenderState(RenderState renderState) {
+        int numRenderables = mRenderables.size();
+        for (int i = 0; i < numRenderables; i ++) {
+            Renderable shape = (Renderable)mRenderables.get(i);
+            shape.setRenderState(renderState);
+        }
+    }
+
+    public void meshLoaded(Mesh m) {
+        ArrayList<Renderable> entries = mRenderableMeshMap.get(m.getName());
+        int numEntries = entries.size();
+        for (int i = 0; i < numEntries; i++) {
+            Renderable d = entries.get(i);
+            d.resolveMeshData(m);
+        }
+    }
+
+    void addToMeshMap(Renderable d) {
+        ArrayList<Renderable> entries = mRenderableMeshMap.get(d.mMeshName);
+        if (entries == null) {
+            entries = new ArrayList<Renderable>();
+            mRenderableMeshMap.put(d.mMeshName, entries);
+        }
+        entries.add(d);
+    }
+
+    public void destroyRS() {
+        SceneManager sceneManager = SceneManager.getInstance();
+        mTransformRSData = null;
+        sceneManager.mRenderLoop.bind_gRootNode(mTransformRSData);
+        sceneManager.mRenderLoop.set_gRenderableObjects(null);
+        mRenderPassAlloc = null;
+        sceneManager.mRenderLoop.set_gRenderPasses(null);
+        sceneManager.mRenderLoop.bind_gFrontToBack(null);
+        sceneManager.mRenderLoop.bind_gBackToFront(null);
+        sceneManager.mRenderLoop.set_gCameras(null);
+
+        mTransformMap = null;
+        mRenderPasses = null;
+        mLights = null;
+        mCameras = null;
+        mRenderables = null;
+        mRenderableMap = null;
+        mTextures = null;
+        mRenderableMeshMap = null;
+        mRootTransforms = null;
+    }
+
+    public void initRenderPassRS(RenderScriptGL rs, SceneManager sceneManager) {
+        if (mRenderPasses.size() != 0) {
+            mRenderPassAlloc = new ScriptField_RenderPass_s(mRS, mRenderPasses.size());
+            for (int i = 0; i < mRenderPasses.size(); i ++) {
+                mRenderPassAlloc.set(mRenderPasses.get(i).getRsField(mRS, mRes), i, false);
+            }
+            mRenderPassAlloc.copyAll();
+            sceneManager.mRenderLoop.set_gRenderPasses(mRenderPassAlloc.getAllocation());
+        }
+    }
+
+    private void addDrawables(RenderScriptGL rs, Resources res, SceneManager sceneManager) {
+        Allocation drawableData = Allocation.createSized(rs,
+                                                         Element.ALLOCATION(rs),
+                                                         mRenderables.size());
+        Allocation[] drawableAllocs = new Allocation[mRenderables.size()];
+        for (int i = 0; i < mRenderables.size(); i ++) {
+            Renderable dI = (Renderable)mRenderables.get(i);
+            addToMeshMap(dI);
+            drawableAllocs[i] = dI.getRsField(rs, res).getAllocation();
+        }
+        drawableData.copyFrom(drawableAllocs);
+        sceneManager.mRenderLoop.set_gRenderableObjects(drawableData);
+
+        initRenderPassRS(rs, sceneManager);
+    }
+
+    private void addShaders(RenderScriptGL rs, Resources res, SceneManager sceneManager) {
+        if (mVertexShaders.size() > 0) {
+            Allocation shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs),
+                                                           mVertexShaders.size());
+            Allocation[] shaderAllocs = new Allocation[mVertexShaders.size()];
+            for (int i = 0; i < mVertexShaders.size(); i ++) {
+                VertexShader sI = mVertexShaders.get(i);
+                shaderAllocs[i] = sI.getRSData().getAllocation();
+            }
+            shaderData.copyFrom(shaderAllocs);
+            sceneManager.mRenderLoop.set_gVertexShaders(shaderData);
+        }
+
+        if (mFragmentShaders.size() > 0) {
+            Allocation shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs),
+                                                           mFragmentShaders.size());
+            Allocation[] shaderAllocs = new Allocation[mFragmentShaders.size()];
+            for (int i = 0; i < mFragmentShaders.size(); i ++) {
+                FragmentShader sI = mFragmentShaders.get(i);
+                shaderAllocs[i] = sI.getRSData().getAllocation();
+            }
+            shaderData.copyFrom(shaderAllocs);
+            sceneManager.mRenderLoop.set_gFragmentShaders(shaderData);
+        }
+    }
+
+    public void initRS() {
+        SceneManager sceneManager = SceneManager.getInstance();
+        mRS = SceneManager.getRS();
+        mRes = SceneManager.getRes();
+        long start = System.currentTimeMillis();
+        mTransformRSData = mRootTransforms.getRSData();
+        long end = System.currentTimeMillis();
+        Log.v(TIMER_TAG, "Transform init time: " + (end - start));
+
+        start = System.currentTimeMillis();
+
+        sceneManager.mRenderLoop.bind_gRootNode(mTransformRSData);
+        end = System.currentTimeMillis();
+        Log.v(TIMER_TAG, "Script init time: " + (end - start));
+
+        start = System.currentTimeMillis();
+        addDrawables(mRS, mRes, sceneManager);
+        end = System.currentTimeMillis();
+        Log.v(TIMER_TAG, "Renderable init time: " + (end - start));
+
+        addShaders(mRS, mRes, sceneManager);
+
+        Allocation opaqueBuffer = null;
+        if (mRenderables.size() > 0) {
+            opaqueBuffer = Allocation.createSized(mRS, Element.U32(mRS), mRenderables.size());
+        }
+        Allocation transparentBuffer = null;
+        if (mRenderables.size() > 0) {
+            transparentBuffer = Allocation.createSized(mRS, Element.U32(mRS), mRenderables.size());
+        }
+
+        sceneManager.mRenderLoop.bind_gFrontToBack(opaqueBuffer);
+        sceneManager.mRenderLoop.bind_gBackToFront(transparentBuffer);
+
+        if (mCameras.size() > 0) {
+            Allocation cameraData;
+            cameraData = Allocation.createSized(mRS, Element.ALLOCATION(mRS), mCameras.size());
+            Allocation[] cameraAllocs = new Allocation[mCameras.size()];
+            for (int i = 0; i < mCameras.size(); i ++) {
+                cameraAllocs[i] = mCameras.get(i).getRSData().getAllocation();
+            }
+            cameraData.copyFrom(cameraAllocs);
+            sceneManager.mRenderLoop.set_gCameras(cameraData);
+        }
+
+        if (mLights.size() > 0) {
+            Allocation lightData = Allocation.createSized(mRS,
+                                                          Element.ALLOCATION(mRS),
+                                                          mLights.size());
+            Allocation[] lightAllocs = new Allocation[mLights.size()];
+            for (int i = 0; i < mLights.size(); i ++) {
+                lightAllocs[i] = mLights.get(i).getRSData().getAllocation();
+            }
+            lightData.copyFrom(lightAllocs);
+            sceneManager.mRenderLoop.set_gLights(lightData);
+        }
+    }
+}
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java
new file mode 100644
index 0000000..412ffbf
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java
@@ -0,0 +1,61 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import com.android.scenegraph.SceneManager;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class SceneGraphBase {
+    String mName;
+    Allocation mNameAlloc;
+    public void setName(String n) {
+        mName = n;
+        mNameAlloc = null;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    Allocation getNameAlloc(RenderScriptGL rs) {
+        if (mNameAlloc == null)  {
+            mNameAlloc = SceneManager.getStringAsAllocation(rs, getName());
+        }
+        return mNameAlloc;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
new file mode 100644
index 0000000..4ff2c8b
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
@@ -0,0 +1,503 @@
+/*
+ * 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.android.scenegraph;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.android.scenegraph.Camera;
+import com.android.scenegraph.FragmentShader;
+import com.android.scenegraph.MatrixTransform;
+import com.android.scenegraph.Scene;
+import com.android.scenegraph.VertexShader;
+import com.android.testapp.R;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Allocation.MipmapControl;
+import android.renderscript.Mesh;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+/**
+ * @hide
+ */
+public class SceneManager extends SceneGraphBase {
+
+    HashMap<String, Allocation> mAllocationMap;
+
+    ScriptC_render mRenderLoop;
+    ScriptC mCameraScript;
+    ScriptC mLightScript;
+    ScriptC mObjectParamsScript;
+    ScriptC mFragmentParamsScript;
+    ScriptC mVertexParamsScript;
+    ScriptC mCullScript;
+    ScriptC_transform mTransformScript;
+    ScriptC_export mExportScript;
+
+    RenderScriptGL mRS;
+    Resources mRes;
+    Mesh mQuad;
+    int mWidth;
+    int mHeight;
+
+    Scene mActiveScene;
+    private static SceneManager sSceneManager;
+
+    private Allocation mDefault2D;
+    private Allocation mDefaultCube;
+
+    private FragmentShader mColor;
+    private FragmentShader mTexture;
+    private VertexShader mDefaultVertex;
+
+    private RenderState mDefaultState;
+    private Transform mDefaultTransform;
+
+    private static Allocation getDefault(boolean isCube) {
+        final int dimension = 4;
+        final int bytesPerPixel = 4;
+        int arraySize = dimension * dimension * bytesPerPixel;
+
+        RenderScriptGL rs = sSceneManager.mRS;
+        Type.Builder b = new Type.Builder(rs, Element.RGBA_8888(rs));
+        b.setX(dimension).setY(dimension);
+        if (isCube) {
+            b.setFaces(true);
+            arraySize *= 6;
+        }
+        Type bitmapType = b.create();
+
+        Allocation.MipmapControl mip = Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
+        int usage =  Allocation.USAGE_GRAPHICS_TEXTURE;
+        Allocation defaultImage = Allocation.createTyped(rs, bitmapType, mip, usage);
+
+        byte imageData[] = new byte[arraySize];
+        defaultImage.copyFrom(imageData);
+        return defaultImage;
+    }
+
+    static Allocation getDefaultTex2D() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        if (sSceneManager.mDefault2D == null) {
+            sSceneManager.mDefault2D = getDefault(false);
+        }
+        return sSceneManager.mDefault2D;
+    }
+
+    static Allocation getDefaultTexCube() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        if (sSceneManager.mDefaultCube == null) {
+            sSceneManager.mDefaultCube = getDefault(true);
+        }
+        return sSceneManager.mDefaultCube;
+    }
+
+    public static boolean isSDCardPath(String path) {
+        int sdCardIndex = path.indexOf("sdcard/");
+        // We are looking for /sdcard/ or sdcard/
+        if (sdCardIndex == 0 || sdCardIndex == 1) {
+            return true;
+        }
+        sdCardIndex = path.indexOf("mnt/sdcard/");
+        if (sdCardIndex == 0 || sdCardIndex == 1) {
+            return true;
+        }
+        return false;
+    }
+
+    static Bitmap loadBitmap(String name, Resources res) {
+        InputStream is = null;
+        boolean loadFromSD = isSDCardPath(name);
+        try {
+            if (!loadFromSD) {
+                is = res.getAssets().open(name);
+            } else {
+                File f = new File(name);
+                is = new BufferedInputStream(new FileInputStream(f));
+            }
+        } catch (IOException e) {
+            Log.e("ImageLoaderTask", " Message: " + e.getMessage());
+            return null;
+        }
+
+        Bitmap b = BitmapFactory.decodeStream(is);
+        try {
+            is.close();
+        } catch (IOException e) {
+            Log.e("ImageLoaderTask", " Message: " + e.getMessage());
+        }
+        return b;
+    }
+
+    static Allocation createFromBitmap(Bitmap b, RenderScriptGL rs, boolean isCube) {
+        if (b == null) {
+            return null;
+        }
+        MipmapControl mip = MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
+        int usage = Allocation.USAGE_GRAPHICS_TEXTURE;
+        if (isCube) {
+            return Allocation.createCubemapFromBitmap(rs, b, mip, usage);
+        }
+        return Allocation.createFromBitmap(rs, b, mip, usage);
+    }
+
+    public static Allocation loadCubemap(String name, RenderScriptGL rs, Resources res) {
+        return createFromBitmap(loadBitmap(name, res), rs, true);
+    }
+
+    public static Allocation loadCubemap(int id, RenderScriptGL rs, Resources res) {
+        return createFromBitmap(BitmapFactory.decodeResource(res, id), rs, true);
+    }
+
+    public static Allocation loadTexture2D(String name, RenderScriptGL rs, Resources res) {
+        return createFromBitmap(loadBitmap(name, res), rs, false);
+    }
+
+    public static Allocation loadTexture2D(int id, RenderScriptGL rs, Resources res) {
+        return createFromBitmap(BitmapFactory.decodeResource(res, id), rs, false);
+    }
+
+    public static ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) {
+        ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        builder.setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ONE);
+        builder.setDitherEnabled(false);
+        builder.setDepthMaskEnabled(false);
+        return builder.create();
+    }
+
+    static Allocation getStringAsAllocation(RenderScript rs, String str) {
+        if (str == null) {
+            return null;
+        }
+        if (str.length() == 0) {
+            return null;
+        }
+        byte[] allocArray = null;
+        byte[] nullChar = new byte[1];
+        nullChar[0] = 0;
+        try {
+            allocArray = str.getBytes("UTF-8");
+            Allocation alloc = Allocation.createSized(rs, Element.U8(rs),
+                                                      allocArray.length + 1,
+                                                      Allocation.USAGE_SCRIPT);
+            alloc.copy1DRangeFrom(0, allocArray.length, allocArray);
+            alloc.copy1DRangeFrom(allocArray.length, 1, nullChar);
+            return alloc;
+        }
+        catch (Exception e) {
+            throw new RSRuntimeException("Could not convert string to utf-8.");
+        }
+    }
+
+    static Allocation getCachedAlloc(String str) {
+        if (sSceneManager == null) {
+            throw new RuntimeException("Scene manager not initialized");
+        }
+        return sSceneManager.mAllocationMap.get(str);
+    }
+
+    static void cacheAlloc(String str, Allocation alloc) {
+        if (sSceneManager == null) {
+            throw new RuntimeException("Scene manager not initialized");
+        }
+        sSceneManager.mAllocationMap.put(str, alloc);
+    }
+
+    public static class SceneLoadedCallback implements Runnable {
+        public Scene mLoadedScene;
+        public String mName;
+        public void run() {
+        }
+    }
+
+    public Scene getActiveScene() {
+        return mActiveScene;
+    }
+
+    public void setActiveScene(Scene s) {
+        mActiveScene = s;
+
+        if (mActiveScene == null) {
+            return;
+        }
+
+        // Do some sanity checking
+        if (mActiveScene.getCameras().size() == 0) {
+            Matrix4f camPos = new Matrix4f();
+            camPos.translate(0, 0, 10);
+            MatrixTransform cameraTransform = new MatrixTransform();
+            cameraTransform.setName("_DefaultCameraTransform");
+            cameraTransform.setMatrix(camPos);
+            mActiveScene.appendTransform(cameraTransform);
+            Camera cam = new Camera();
+            cam.setName("_DefaultCamera");
+            cam.setTransform(cameraTransform);
+            mActiveScene.appendCamera(cam);
+        }
+
+        mActiveScene.appendShader(getDefaultVS());
+        mActiveScene.appendTransform(getDefaultTransform());
+    }
+
+    static RenderScriptGL getRS() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        return sSceneManager.mRS;
+    }
+
+    static Resources getRes() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        return sSceneManager.mRes;
+    }
+
+    // Provides the folowing inputs to fragment shader
+    // Assigned by default if nothing is present
+    // vec3 varWorldPos;
+    // vec3 varWorldNormal;
+    // vec2 varTex0;
+    public static VertexShader getDefaultVS() {
+        if (sSceneManager == null) {
+            return null;
+        }
+
+        if (sSceneManager.mDefaultVertex == null) {
+            RenderScriptGL rs = getRS();
+            Element.Builder b = new Element.Builder(rs);
+            b.add(Element.MATRIX_4X4(rs), "model");
+            Type.Builder objConstBuilder = new Type.Builder(rs, b.create());
+
+            b = new Element.Builder(rs);
+            b.add(Element.MATRIX_4X4(rs), "viewProj");
+            Type.Builder shaderConstBuilder = new Type.Builder(rs, b.create());
+
+            b = new Element.Builder(rs);
+            b.add(Element.F32_4(rs), "position");
+            b.add(Element.F32_2(rs), "texture0");
+            b.add(Element.F32_3(rs), "normal");
+            Element defaultIn = b.create();
+
+            final String code = "\n" +
+                "varying vec3 varWorldPos;\n" +
+                "varying vec3 varWorldNormal;\n" +
+                "varying vec2 varTex0;\n" +
+                "void main() {" +
+                "   vec4 objPos = ATTRIB_position;\n" +
+                "   vec4 worldPos = UNI_model * objPos;\n" +
+                "   gl_Position = UNI_viewProj * worldPos;\n" +
+                "   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);\n" +
+                "   vec3 worldNorm = model3 * ATTRIB_normal;\n" +
+                "   varWorldPos = worldPos.xyz;\n" +
+                "   varWorldNormal = worldNorm;\n" +
+                "   varTex0 = ATTRIB_texture0;\n" +
+                "}\n";
+
+            VertexShader.Builder sb = new VertexShader.Builder(rs);
+            sb.addInput(defaultIn);
+            sb.setObjectConst(objConstBuilder.setX(1).create());
+            sb.setShaderConst(shaderConstBuilder.setX(1).create());
+            sb.setShader(code);
+            sSceneManager.mDefaultVertex = sb.create();
+        }
+
+        return sSceneManager.mDefaultVertex;
+    }
+
+    public static FragmentShader getColorFS() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        if (sSceneManager.mColor == null) {
+            RenderScriptGL rs = getRS();
+            Element.Builder b = new Element.Builder(rs);
+            b.add(Element.F32_4(rs), "color");
+            Type.Builder objConstBuilder = new Type.Builder(rs, b.create());
+
+            final String code = "\n" +
+                "varying vec2 varTex0;\n" +
+                "void main() {\n" +
+                "   lowp vec4 col = UNI_color;\n" +
+                "   gl_FragColor = col;\n" +
+                "}\n";
+            FragmentShader.Builder fb = new FragmentShader.Builder(rs);
+            fb.setShader(code);
+            fb.setObjectConst(objConstBuilder.create());
+            sSceneManager.mColor = fb.create();
+        }
+
+        return sSceneManager.mColor;
+    }
+
+    public static FragmentShader getTextureFS() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        if (sSceneManager.mTexture == null) {
+            RenderScriptGL rs = getRS();
+
+            final String code = "\n" +
+                "varying vec2 varTex0;\n" +
+                "void main() {\n" +
+                "   lowp vec4 col = texture2D(UNI_color, varTex0).rgba;\n" +
+                "   gl_FragColor = col;\n" +
+                "}\n";
+
+            FragmentShader.Builder fb = new FragmentShader.Builder(rs);
+            fb.setShader(code);
+            fb.addTexture(Program.TextureType.TEXTURE_2D, "color");
+            sSceneManager.mTexture = fb.create();
+            sSceneManager.mTexture.mProgram.bindSampler(Sampler.CLAMP_LINEAR_MIP_LINEAR(rs), 0);
+        }
+
+        return sSceneManager.mTexture;
+    }
+
+    static RenderState getDefaultState() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        if (sSceneManager.mDefaultState == null) {
+            sSceneManager.mDefaultState = new RenderState(getDefaultVS(), getColorFS(), null, null);
+            sSceneManager.mDefaultState.setName("__DefaultState");
+        }
+        return sSceneManager.mDefaultState;
+    }
+
+    static Transform getDefaultTransform() {
+        if (sSceneManager == null) {
+            return null;
+        }
+        if (sSceneManager.mDefaultTransform == null) {
+            sSceneManager.mDefaultTransform = new MatrixTransform();
+            sSceneManager.mDefaultTransform.setName("__DefaultTransform");
+        }
+        return sSceneManager.mDefaultTransform;
+    }
+
+    public static SceneManager getInstance() {
+        if (sSceneManager == null) {
+            sSceneManager = new SceneManager();
+        }
+        return sSceneManager;
+    }
+
+    protected SceneManager() {
+    }
+
+    public void loadModel(String name, SceneLoadedCallback cb) {
+        ColladaScene scene = new ColladaScene(name, cb);
+        scene.init(mRS, mRes);
+    }
+
+    public Mesh getScreenAlignedQuad() {
+        if (mQuad != null) {
+            return mQuad;
+        }
+
+        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
+                                           3, Mesh.TriangleMeshBuilder.TEXTURE_0);
+
+        tmb.setTexture(0.0f, 1.0f).addVertex(-1.0f, 1.0f, 1.0f);
+        tmb.setTexture(0.0f, 0.0f).addVertex(-1.0f, -1.0f, 1.0f);
+        tmb.setTexture(1.0f, 0.0f).addVertex(1.0f, -1.0f, 1.0f);
+        tmb.setTexture(1.0f, 1.0f).addVertex(1.0f, 1.0f, 1.0f);
+
+        tmb.addTriangle(0, 1, 2);
+        tmb.addTriangle(2, 3, 0);
+
+        mQuad = tmb.create(true);
+        return mQuad;
+    }
+
+    public Renderable getRenderableQuad(String name, RenderState state) {
+        Renderable quad = new Renderable();
+        quad.setTransform(new MatrixTransform());
+        quad.setMesh(getScreenAlignedQuad());
+        quad.setName(name);
+        quad.setRenderState(state);
+        quad.setCullType(1);
+        return quad;
+    }
+
+    public void initRS(RenderScriptGL rs, Resources res, int w, int h) {
+        mRS = rs;
+        mRes = res;
+        mAllocationMap = new HashMap<String, Allocation>();
+
+        mQuad = null;
+        mDefault2D = null;
+        mDefaultCube = null;
+        mDefaultVertex = null;
+        mColor = null;
+        mTexture = null;
+        mDefaultState = null;
+        mDefaultTransform = null;
+
+        mExportScript = new ScriptC_export(rs, res, R.raw.export);
+
+        mTransformScript = new ScriptC_transform(rs, res, R.raw.transform);
+        mTransformScript.set_gTransformScript(mTransformScript);
+
+        mCameraScript = new ScriptC_camera(rs, res, R.raw.camera);
+        mLightScript = new ScriptC_light(rs, res, R.raw.light);
+        mObjectParamsScript = new ScriptC_object_params(rs, res, R.raw.object_params);
+        mFragmentParamsScript = new ScriptC_object_params(rs, res, R.raw.fragment_params);
+        mVertexParamsScript = new ScriptC_object_params(rs, res, R.raw.vertex_params);
+        mCullScript = new ScriptC_cull(rs, res, R.raw.cull);
+
+        mRenderLoop = new ScriptC_render(rs, res, R.raw.render);
+        mRenderLoop.set_gTransformScript(mTransformScript);
+        mRenderLoop.set_gCameraScript(mCameraScript);
+        mRenderLoop.set_gLightScript(mLightScript);
+        mRenderLoop.set_gObjectParamsScript(mObjectParamsScript);
+        mRenderLoop.set_gFragmentParamsScript(mFragmentParamsScript);
+        mRenderLoop.set_gVertexParamsScript(mVertexParamsScript);
+        mRenderLoop.set_gCullScript(mCullScript);
+
+        mRenderLoop.set_gPFSBackground(ProgramStore.BLEND_NONE_DEPTH_TEST(mRS));
+    }
+
+    public ScriptC getRenderLoop() {
+        return mRenderLoop;
+    }
+}
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java
new file mode 100644
index 0000000..4975114
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java
@@ -0,0 +1,76 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import com.android.scenegraph.SceneGraphBase;
+import com.android.scenegraph.ShaderParam;
+
+import android.renderscript.*;
+import android.renderscript.ProgramFragment.Builder;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class Shader extends SceneGraphBase {
+    protected Type mPerObjConstants;
+    protected Type mPerShaderConstants;
+
+    protected HashMap<String, ShaderParam> mSourceParams;
+    protected ArrayList<String> mShaderTextureNames;
+    protected ArrayList<Program.TextureType > mShaderTextureTypes;
+    protected ArrayList<String> mTextureNames;
+    protected ArrayList<Program.TextureType > mTextureTypes;
+
+    protected Allocation mConstantBuffer;
+    protected ScriptField_ShaderParam_s mConstantBufferParams;
+
+    public Shader() {
+        mSourceParams = new HashMap<String, ShaderParam>();
+        mShaderTextureNames = new ArrayList<String>();
+        mShaderTextureTypes = new ArrayList<Program.TextureType>();
+        mTextureNames = new ArrayList<String>();
+        mTextureTypes = new ArrayList<Program.TextureType>();
+    }
+
+    public void appendSourceParams(ShaderParam p) {
+        mSourceParams.put(p.getParamName(), p);
+    }
+
+    public Type getObjectConstants() {
+        return mPerObjConstants;
+    }
+
+    public Type getShaderConstants() {
+        return mPerObjConstants;
+    }
+
+    void linkConstants(RenderScriptGL rs) {
+        if (mPerShaderConstants == null) {
+            return;
+        }
+
+        Element constElem = mPerShaderConstants.getElement();
+        mConstantBufferParams  = ShaderParam.fillInParams(constElem, mSourceParams, null);
+
+        mConstantBuffer = Allocation.createTyped(rs, mPerShaderConstants);
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
new file mode 100644
index 0000000..3dd41ca
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
@@ -0,0 +1,162 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import com.android.scenegraph.SceneManager;
+import com.android.scenegraph.Transform;
+
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.RenderScriptGL;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class ShaderParam extends SceneGraphBase {
+
+    static final String cameraPos        = "cameraPos";
+    static final String cameraDir        = "cameraDir";
+
+    static final String lightColor       = "lightColor";
+    static final String lightPos         = "lightPos";
+    static final String lightDir         = "lightDir";
+
+    static final String view             = "view";
+    static final String proj             = "proj";
+    static final String viewProj         = "viewProj";
+    static final String model            = "model";
+    static final String modelView        = "modelView";
+    static final String modelViewProj    = "modelViewProj";
+
+    static final long sMaxTimeStamp = 0xffffffffL;
+
+    ScriptField_ShaderParamData_s.Item mData;
+    ScriptField_ShaderParamData_s mField;
+
+    String mParamName;
+    Camera mCamera;
+
+    static ScriptField_ShaderParam_s fillInParams(Element constantElem,
+                                                  HashMap<String, ShaderParam> sourceParams,
+                                                  Transform transform) {
+        RenderScriptGL rs = SceneManager.getRS();
+        ArrayList<ScriptField_ShaderParam_s.Item> paramList;
+        paramList = new ArrayList<ScriptField_ShaderParam_s.Item>();
+
+        int subElemCount = constantElem.getSubElementCount();
+        for (int i = 0; i < subElemCount; i ++) {
+            String inputName = constantElem.getSubElementName(i);
+            int offset = constantElem.getSubElementOffsetBytes(i);
+
+            ShaderParam matchingParam = sourceParams.get(inputName);
+            Element subElem = constantElem.getSubElement(i);
+            // Make one if it's not there
+            if (matchingParam == null) {
+                if (subElem.getDataType() == Element.DataType.FLOAT_32) {
+                    matchingParam = new Float4Param(inputName, 0.5f, 0.5f, 0.5f, 0.5f);
+                } else if (subElem.getDataType() == Element.DataType.MATRIX_4X4) {
+                    TransformParam trParam = new TransformParam(inputName);
+                    trParam.setTransform(transform);
+                    matchingParam = trParam;
+                }
+            }
+            ScriptField_ShaderParam_s.Item paramRS = new ScriptField_ShaderParam_s.Item();
+            paramRS.bufferOffset = offset;
+            paramRS.transformTimestamp = 0;
+            paramRS.dataTimestamp = 0;
+            paramRS.data = matchingParam.getRSData().getAllocation();
+            if (subElem.getDataType() == Element.DataType.FLOAT_32) {
+                paramRS.float_vecSize = subElem.getVectorSize();
+            }
+
+            paramList.add(paramRS);
+        }
+
+        ScriptField_ShaderParam_s rsParams = null;
+        int paramCount = paramList.size();
+        if (paramCount != 0) {
+            rsParams = new ScriptField_ShaderParam_s(rs, paramCount);
+            for (int i = 0; i < paramCount; i++) {
+                rsParams.set(paramList.get(i), i, false);
+            }
+            rsParams.copyAll();
+        }
+        return rsParams;
+    }
+
+    public ShaderParam(String name) {
+        mParamName = name;
+        mData = new ScriptField_ShaderParamData_s.Item();
+    }
+
+    public String getParamName() {
+        return mParamName;
+    }
+
+    public void setCamera(Camera c) {
+        mCamera = c;
+        if (mField != null) {
+            mData.camera = mCamera.getRSData().getAllocation();
+            mField.set_camera(0, mData.camera, true);
+        }
+    }
+
+    protected void incTimestamp() {
+        if (mField != null) {
+            mData.timestamp ++;
+            mData.timestamp %= sMaxTimeStamp;
+            mField.set_timestamp(0, mData.timestamp, true);
+        }
+    }
+
+    abstract void initLocalData();
+
+    public ScriptField_ShaderParamData_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        mField = new ScriptField_ShaderParamData_s(rs, 1);
+
+        if (mParamName != null) {
+            mData.paramName = SceneManager.getCachedAlloc(mParamName);
+            if (mData.paramName == null) {
+                mData.paramName = SceneManager.getStringAsAllocation(rs, mParamName);
+                SceneManager.cacheAlloc(mParamName, mData.paramName);
+            }
+        }
+        initLocalData();
+        mData.timestamp = 1;
+
+        mField.set(mData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
new file mode 100644
index 0000000..b53ab88
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
@@ -0,0 +1,113 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+
+import com.android.scenegraph.SceneManager;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class Texture2D extends TextureBase {
+    String mFileName;
+    String mFileDir;
+    int mResourceID;
+
+    public Texture2D() {
+        super(ScriptC_export.const_TextureType_TEXTURE_2D);
+    }
+
+    public Texture2D(Allocation tex) {
+        super(ScriptC_export.const_TextureType_TEXTURE_2D);
+        setTexture(tex);
+    }
+
+    public Texture2D(String dir, String file) {
+        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
+        setFileDir(dir);
+        setFileName(file);
+    }
+
+    public Texture2D(int resourceID) {
+        super(ScriptC_export.const_TextureType_TEXTURE_2D);
+        mResourceID = resourceID;
+    }
+
+    public void setFileDir(String dir) {
+        mFileDir = dir;
+    }
+
+    public void setFileName(String file) {
+        mFileName = file;
+    }
+
+    public String getFileName() {
+        return mFileName;
+    }
+
+    public void setTexture(Allocation tex) {
+        mData.texture = tex != null ? tex : SceneManager.getDefaultTex2D();
+        if (mField != null) {
+            mField.set_texture(0, mData.texture, true);
+        }
+    }
+
+    void load() {
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (mFileName != null && mFileName.length() > 0) {
+            String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1);
+            setTexture(SceneManager.loadTexture2D(mFileDir + shortName, rs, res));
+        } else if (mResourceID != 0) {
+            setTexture(SceneManager.loadTexture2D(mResourceID, rs, res));
+        }
+    }
+
+    ScriptField_Texture_s getRsData(boolean loadNow) {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        mField = new ScriptField_Texture_s(rs, 1);
+
+        if (loadNow) {
+            load();
+        } else {
+            mData.texture = SceneManager.getDefaultTex2D();
+            new SingleImageLoaderTask().execute(this);
+        }
+
+        mField.set(mData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java
new file mode 100644
index 0000000..ba49d4e
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 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.scenegraph;
+
+import java.lang.Math;
+
+import com.android.scenegraph.SceneManager;
+import android.os.AsyncTask;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class TextureBase extends SceneGraphBase {
+
+    class SingleImageLoaderTask extends AsyncTask<TextureBase, Void, Boolean> {
+        protected Boolean doInBackground(TextureBase... objects) {
+            TextureBase tex = objects[0];
+            tex.load();
+            return new Boolean(true);
+        }
+        protected void onPostExecute(Boolean result) {
+        }
+    }
+
+    ScriptField_Texture_s.Item mData;
+    ScriptField_Texture_s mField;
+    TextureBase(int type) {
+        mData = new ScriptField_Texture_s.Item();
+        mData.type = type;
+    }
+
+    protected Allocation mRsTexture;
+    abstract ScriptField_Texture_s getRsData(boolean loadNow);
+    abstract void load();
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
new file mode 100644
index 0000000..1269e3c
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
@@ -0,0 +1,114 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+
+import com.android.scenegraph.SceneManager;
+import com.android.scenegraph.TextureBase;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TextureCube extends TextureBase {
+    String mFileName;
+    String mFileDir;
+    int mResourceID;
+
+    public TextureCube() {
+        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
+    }
+
+    public TextureCube(Allocation tex) {
+        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
+        setTexture(tex);
+    }
+
+    public TextureCube(String dir, String file) {
+        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
+        setFileDir(dir);
+        setFileName(file);
+    }
+
+    public TextureCube(int resourceID) {
+        super(ScriptC_export.const_TextureType_TEXTURE_2D);
+        mResourceID = resourceID;
+    }
+
+    public void setFileDir(String dir) {
+        mFileDir = dir;
+    }
+
+    public void setFileName(String file) {
+        mFileName = file;
+    }
+
+    public String getFileName() {
+        return mFileName;
+    }
+
+    public void setTexture(Allocation tex) {
+        mData.texture = tex != null ? tex : SceneManager.getDefaultTexCube();
+        if (mField != null) {
+            mField.set_texture(0, mData.texture, true);
+        }
+    }
+
+    void load() {
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (mFileName != null && mFileName.length() > 0) {
+            String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1);
+            setTexture(SceneManager.loadCubemap(mFileDir + shortName, rs, res));
+        } else if (mResourceID != 0) {
+            setTexture(SceneManager.loadCubemap(mResourceID , rs, res));
+        }
+    }
+
+    ScriptField_Texture_s getRsData(boolean loadNow) {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        mField = new ScriptField_Texture_s(rs, 1);
+
+        if (loadNow) {
+            load();
+        } else {
+            mData.texture = SceneManager.getDefaultTexCube();
+            new SingleImageLoaderTask().execute(this);
+        }
+
+        mField.set(mData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java
new file mode 100644
index 0000000..e656ed2
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java
@@ -0,0 +1,67 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.graphics.Camera;
+import android.renderscript.RenderScriptGL;
+import android.renderscript.Float4;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.Element;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TextureParam extends ShaderParam {
+
+    TextureBase mTexture;
+
+    public TextureParam(String name) {
+        super(name);
+    }
+
+    public TextureParam(String name, TextureBase t) {
+        super(name);
+        setTexture(t);
+    }
+
+    public void setTexture(TextureBase t) {
+        mTexture = t;
+    }
+
+    public TextureBase getTexture() {
+        return mTexture;
+    }
+
+    void initLocalData() {
+        mData.type = ScriptC_export.const_ShaderParam_TEXTURE;
+        if (mTexture != null) {
+            mData.texture = mTexture.getRsData(false).getAllocation();
+        }
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java
new file mode 100644
index 0000000..6aa29a5
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 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.scenegraph;
+
+import java.lang.Math;
+
+import com.android.scenegraph.SceneManager;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TextureRenderTarget extends TextureBase {
+    public TextureRenderTarget() {
+        super(ScriptC_export.const_TextureType_TEXTURE_RENDER_TARGET);
+    }
+
+    public TextureRenderTarget(Allocation tex) {
+        super(ScriptC_export.const_TextureType_TEXTURE_RENDER_TARGET);
+        setTexture(tex);
+    }
+
+    public void setTexture(Allocation tex) {
+        mData.texture = tex;
+        if (mField != null) {
+            mField.set_texture(0, mData.texture, true);
+        }
+    }
+
+    void load() {
+    }
+
+    ScriptField_Texture_s getRsData(boolean loadNow) {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+
+        mField = new ScriptField_Texture_s(rs, 1);
+        mField.set(mData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java
new file mode 100644
index 0000000..8180bd0
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java
@@ -0,0 +1,98 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.*;
+import android.renderscript.Matrix4f;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class Transform extends SceneGraphBase {
+    Transform mParent;
+    ArrayList<Transform> mChildren;
+
+    ScriptField_SgTransform mField;
+    ScriptField_SgTransform.Item mTransformData;
+
+    public Transform() {
+        mChildren = new ArrayList<Transform>();
+        mParent = null;
+    }
+
+    public void appendChild(Transform t) {
+        mChildren.add(t);
+        t.mParent = this;
+        updateRSChildData(true);
+    }
+
+    abstract void initLocalData();
+
+    void updateRSChildData(boolean copyData) {
+        if (mField == null) {
+            return;
+        }
+        RenderScriptGL rs = SceneManager.getRS();
+        if (mChildren.size() != 0) {
+            Allocation childRSData = Allocation.createSized(rs, Element.ALLOCATION(rs),
+                                                            mChildren.size());
+            mTransformData.children = childRSData;
+
+            Allocation[] childrenAllocs = new Allocation[mChildren.size()];
+            for (int i = 0; i < mChildren.size(); i ++) {
+                Transform child = mChildren.get(i);
+                childrenAllocs[i] = child.getRSData().getAllocation();
+            }
+            childRSData.copyFrom(childrenAllocs);
+        }
+        if (copyData) {
+            mField.set(mTransformData, 0, true);
+        }
+    }
+
+    ScriptField_SgTransform getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        if (rs == null) {
+            return null;
+        }
+        mField = new ScriptField_SgTransform(rs, 1);
+
+        mTransformData = new ScriptField_SgTransform.Item();
+        mTransformData.name = getNameAlloc(rs);
+        mTransformData.isDirty = 1;
+        mTransformData.timestamp = 1;
+
+        initLocalData();
+        updateRSChildData(false);
+
+        mField.set(mTransformData, 0, true);
+        return mField;
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java
new file mode 100644
index 0000000..d120d5d
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java
@@ -0,0 +1,85 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.renderscript.RenderScriptGL;
+import android.renderscript.Matrix4f;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.ProgramVertex;
+import android.renderscript.Element;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TransformParam extends ShaderParam {
+
+    Transform mTransform;
+    LightBase mLight;
+
+    public TransformParam(String name) {
+        super(name);
+    }
+
+    public void setTransform(Transform t) {
+        mTransform = t;
+        if (mField != null && mTransform != null) {
+            mData.transform = mTransform.getRSData().getAllocation();
+        }
+        incTimestamp();
+    }
+
+    int getTypeFromName() {
+        int paramType = ScriptC_export.const_ShaderParam_TRANSFORM_DATA;
+        if (mParamName.equalsIgnoreCase(view)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_VIEW;
+        } else if(mParamName.equalsIgnoreCase(proj)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_PROJ;
+        } else if(mParamName.equalsIgnoreCase(viewProj)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_VIEW_PROJ;
+        } else if(mParamName.equalsIgnoreCase(model)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL;
+        } else if(mParamName.equalsIgnoreCase(modelView)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL_VIEW;
+        } else if(mParamName.equalsIgnoreCase(modelViewProj)) {
+            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL_VIEW_PROJ;
+        }
+        return paramType;
+    }
+
+    void initLocalData() {
+        mData.type = getTypeFromName();
+        if (mTransform != null) {
+            mData.transform = mTransform.getRSData().getAllocation();
+        }
+        if (mCamera != null) {
+            mData.camera = mCamera.getRSData().getAllocation();
+        }
+        if (mLight != null) {
+            mData.light = mLight.getRSData().getAllocation();
+        }
+    }
+}
+
+
+
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
new file mode 100644
index 0000000..4efaff7
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
@@ -0,0 +1,113 @@
+/*
+ * 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.android.scenegraph;
+
+import java.lang.Math;
+import java.util.ArrayList;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class VertexShader extends Shader {
+    ProgramVertex mProgram;
+    ScriptField_VertexShader_s mField;
+
+    public static class Builder {
+        VertexShader mShader;
+        ProgramVertex.Builder mBuilder;
+
+        public Builder(RenderScriptGL rs) {
+            mShader = new VertexShader();
+            mBuilder = new ProgramVertex.Builder(rs);
+        }
+
+        public Builder setShader(Resources resources, int resourceID) {
+            mBuilder.setShader(resources, resourceID);
+            return this;
+        }
+
+        public Builder setShader(String code) {
+            mBuilder.setShader(code);
+            return this;
+        }
+
+        public Builder setObjectConst(Type type) {
+            mShader.mPerObjConstants = type;
+            return this;
+        }
+
+        public Builder setShaderConst(Type type) {
+            mShader.mPerShaderConstants = type;
+            return this;
+        }
+
+        public Builder addInput(Element e) {
+            mBuilder.addInput(e);
+            return this;
+        }
+
+        public VertexShader create() {
+            if (mShader.mPerShaderConstants != null) {
+                mBuilder.addConstant(mShader.mPerShaderConstants);
+            }
+            if (mShader.mPerObjConstants != null) {
+                mBuilder.addConstant(mShader.mPerObjConstants);
+            }
+            mShader.mProgram = mBuilder.create();
+            return mShader;
+        }
+    }
+
+    public ProgramVertex getProgram() {
+        return mProgram;
+    }
+
+    ScriptField_VertexShader_s getRSData() {
+        if (mField != null) {
+            return mField;
+        }
+
+        RenderScriptGL rs = SceneManager.getRS();
+        Resources res = SceneManager.getRes();
+        if (rs == null || res == null) {
+            return null;
+        }
+
+        ScriptField_VertexShader_s.Item item = new ScriptField_VertexShader_s.Item();
+        item.program = mProgram;
+
+        linkConstants(rs);
+        if (mPerShaderConstants != null) {
+            item.shaderConst = mConstantBuffer;
+            item.shaderConstParams = mConstantBufferParams.getAllocation();
+            mProgram.bindConstants(item.shaderConst, 0);
+        }
+
+        item.objectConstIndex = -1;
+        if (mPerObjConstants != null) {
+            item.objectConstIndex = mPerShaderConstants != null ? 1 : 0;
+        }
+
+        mField = new ScriptField_VertexShader_s(rs, 1);
+        mField.set(item, 0, true);
+        return mField;
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs
new file mode 100644
index 0000000..dc0a885
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs
@@ -0,0 +1,66 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+//#define DEBUG_CAMERA
+#include "scenegraph_objects.rsh"
+
+void root(const rs_allocation *v_in, rs_allocation *v_out, const float *usrData) {
+
+    SgCamera *cam = (SgCamera *)rsGetElementAt(*v_in, 0);
+    float aspect = *usrData;
+    if (cam->aspect != aspect) {
+        cam->isDirty = 1;
+        cam->aspect = aspect;
+    }
+    if (cam->isDirty) {
+        rsMatrixLoadPerspective(&cam->proj, cam->horizontalFOV, cam->aspect, cam->near, cam->far);
+    }
+
+    const SgTransform *camTransform = (const SgTransform *)rsGetElementAt(cam->transformMatrix, 0);
+    //rsDebug("Camera stamp", cam->transformTimestamp);
+    //rsDebug("Transform stamp", camTransform->timestamp);
+    if (camTransform->timestamp != cam->transformTimestamp || cam->isDirty) {
+        cam->isDirty = 1;
+        rs_matrix4x4 camPosMatrix;
+        rsMatrixLoad(&camPosMatrix, &camTransform->globalMat);
+        float4 zero = {0.0f, 0.0f, 0.0f, 1.0f};
+        cam->position = rsMatrixMultiply(&camPosMatrix, zero);
+
+        rsMatrixInverse(&camPosMatrix);
+        rsMatrixLoad(&cam->view, &camPosMatrix);
+
+        rsMatrixLoad(&cam->viewProj, &cam->proj);
+        rsMatrixMultiply(&cam->viewProj, &cam->view);
+
+        rsExtractFrustumPlanes(&cam->viewProj,
+                               &cam->frustumPlanes[0], &cam->frustumPlanes[1],
+                               &cam->frustumPlanes[2], &cam->frustumPlanes[3],
+                               &cam->frustumPlanes[3], &cam->frustumPlanes[4]);
+    }
+
+    if (cam->isDirty) {
+        cam->timestamp ++;
+    }
+
+    cam->isDirty = 0;
+    cam->transformTimestamp = camTransform->timestamp;
+
+#ifdef DEBUG_CAMERA
+    printCameraInfo(cam);
+#endif //DEBUG_CAMERA
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs
new file mode 100644
index 0000000..024e026
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs
@@ -0,0 +1,86 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+static void getTransformedSphere(SgRenderable *obj) {
+    obj->worldBoundingSphere = obj->boundingSphere;
+    obj->worldBoundingSphere.w = 1.0f;
+    const SgTransform *objTransform = (const SgTransform *)rsGetElementAt(obj->transformMatrix, 0);
+    obj->worldBoundingSphere = rsMatrixMultiply(&objTransform->globalMat, obj->worldBoundingSphere);
+
+    const float4 unitVec = {0.57735f, 0.57735f, 0.57735f, 0.0f};
+    float4 scaledVec = rsMatrixMultiply(&objTransform->globalMat, unitVec);
+    scaledVec.w = 0.0f;
+    obj->worldBoundingSphere.w = obj->boundingSphere.w * length(scaledVec);
+}
+
+static bool frustumCulled(SgRenderable *obj, SgCamera *cam) {
+    if (!obj->bVolInitialized) {
+        float minX, minY, minZ, maxX, maxY, maxZ;
+        rsgMeshComputeBoundingBox(obj->mesh,
+                                  &minX, &minY, &minZ,
+                                  &maxX, &maxY, &maxZ);
+        //rsDebug("min", minX, minY, minZ);
+        //rsDebug("max", maxX, maxY, maxZ);
+        float4 sphere;
+        sphere.x = (maxX + minX) * 0.5f;
+        sphere.y = (maxY + minY) * 0.5f;
+        sphere.z = (maxZ + minZ) * 0.5f;
+        float3 radius;
+        radius.x = (maxX - sphere.x);
+        radius.y = (maxY - sphere.y);
+        radius.z = (maxZ - sphere.z);
+
+        sphere.w = length(radius);
+        obj->boundingSphere = sphere;
+        obj->bVolInitialized = 1;
+        //rsDebug("Sphere", sphere);
+    }
+
+    getTransformedSphere(obj);
+
+    return !rsIsSphereInFrustum(&obj->worldBoundingSphere,
+                                &cam->frustumPlanes[0], &cam->frustumPlanes[1],
+                                &cam->frustumPlanes[2], &cam->frustumPlanes[3],
+                                &cam->frustumPlanes[4], &cam->frustumPlanes[5]);
+}
+
+
+void root(rs_allocation *v_out, const void *usrData) {
+
+    SgRenderable *drawable = (SgRenderable *)rsGetElementAt(*v_out, 0);
+    const SgCamera *camera = (const SgCamera*)usrData;
+
+    drawable->isVisible = 0;
+    // Not loaded yet
+    if (!rsIsObject(drawable->mesh) || drawable->cullType == CULL_ALWAYS) {
+        return;
+    }
+
+    // check to see if we are culling this object and if it's
+    // outside the frustum
+    if (drawable->cullType == CULL_FRUSTUM && frustumCulled(drawable, (SgCamera*)camera)) {
+#ifdef DEBUG_RENDERABLES
+        rsDebug("Culled", drawable);
+        printName(drawable->name);
+#endif // DEBUG_RENDERABLES
+        return;
+    }
+    drawable->isVisible = 1;
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs
new file mode 100644
index 0000000..b438a43
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs
@@ -0,0 +1,61 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+// The sole purpose of this script is to have various structs exposed
+// so that java reflected classes are generated
+#include "scenegraph_objects.rsh"
+
+// Export our native constants to java so that we don't have parallel definitions
+const int ShaderParam_FLOAT4_DATA = SHADER_PARAM_FLOAT4_DATA;
+const int ShaderParam_TRANSFORM_DATA = SHADER_PARAM_TRANSFORM_DATA;
+const int ShaderParam_TRANSFORM_MODEL = SHADER_PARAM_TRANSFORM_MODEL;
+
+const int ShaderParam_FLOAT4_CAMERA_POS = SHADER_PARAM_FLOAT4_CAMERA_POS;
+const int ShaderParam_FLOAT4_CAMERA_DIR = SHADER_PARAM_FLOAT4_CAMERA_DIR;
+const int ShaderParam_TRANSFORM_VIEW = SHADER_PARAM_TRANSFORM_VIEW;
+const int ShaderParam_TRANSFORM_PROJ = SHADER_PARAM_TRANSFORM_PROJ;
+const int ShaderParam_TRANSFORM_VIEW_PROJ = SHADER_PARAM_TRANSFORM_VIEW_PROJ;
+const int ShaderParam_TRANSFORM_MODEL_VIEW = SHADER_PARAM_TRANSFORM_MODEL_VIEW;
+const int ShaderParam_TRANSFORM_MODEL_VIEW_PROJ = SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ;
+
+const int ShaderParam_FLOAT4_LIGHT_COLOR = SHADER_PARAM_FLOAT4_LIGHT_COLOR;
+const int ShaderParam_FLOAT4_LIGHT_POS = SHADER_PARAM_FLOAT4_LIGHT_POS;
+const int ShaderParam_FLOAT4_LIGHT_DIR = SHADER_PARAM_FLOAT4_LIGHT_DIR;
+
+const int ShaderParam_TEXTURE = SHADER_PARAM_TEXTURE;
+
+const int Transform_TRANSLATE = TRANSFORM_TRANSLATE;
+const int Transform_ROTATE = TRANSFORM_ROTATE;
+const int Transform_SCALE = TRANSFORM_SCALE;
+
+const int TextureType_TEXTURE_2D = TEXTURE_2D;
+const int TextureType_TEXTURE_CUBE = TEXTURE_CUBE;
+const int TextureType_TEXTURE_RENDER_TARGET = TEXTURE_RENDER_TARGET;
+
+SgTransform *exportPtr;
+SgTransformComponent *componentPtr;
+SgRenderState *sExport;
+SgRenderable *drExport;
+SgRenderPass *pExport;
+SgCamera *exportPtrCam;
+SgLight *exportPtrLight;
+SgShaderParam *spExport;
+SgShaderParamData *spDataExport;
+SgVertexShader *pvExport;
+SgFragmentShader *pfExport;
+SgTexture *texExport;
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs
new file mode 100644
index 0000000..7202285
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs
@@ -0,0 +1,30 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+//#define DEBUG_PARAMS
+
+#include "params.rsh"
+
+void root(rs_allocation *v_out, const void *usrData) {
+    SgFragmentShader *shader = (SgFragmentShader *)rsGetElementAt(*v_out, 0);
+    const SgCamera *camera = (const SgCamera*)usrData;
+    processAllParams(shader->shaderConst, shader->shaderConstParams, camera);
+    processTextureParams(shader);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs
new file mode 100644
index 0000000..e11979f
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs
@@ -0,0 +1,33 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+//#define DEBUG_LIGHT
+#include "scenegraph_objects.rsh"
+
+void root(const rs_allocation *v_in, rs_allocation *v_out) {
+
+    SgLight *light = (SgLight *)rsGetElementAt(*v_in, 0);
+    const SgTransform *lTransform = (const SgTransform *)rsGetElementAt(light->transformMatrix, 0);
+
+    float4 zero = {0.0f, 0.0f, 0.0f, 1.0f};
+    light->position = rsMatrixMultiply(&lTransform->globalMat, zero);
+
+#ifdef DEBUG_LIGHT
+    printLightInfo(light);
+#endif //DEBUG_LIGHT
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs
new file mode 100644
index 0000000..0d524a6
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs
@@ -0,0 +1,36 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+//#define DEBUG_PARAMS
+
+#include "params.rsh"
+
+void root(rs_allocation *v_out, const void *usrData) {
+
+    SgRenderable *drawable = (SgRenderable *)rsGetElementAt(*v_out, 0);
+    // Visibility flag was set earlier in the cull stage
+    if (!drawable->isVisible) {
+        return;
+    }
+
+    const SgCamera *camera = (const SgCamera*)usrData;
+    processAllParams(drawable->pf_const, drawable->pf_constParams, camera);
+    processAllParams(drawable->pv_const, drawable->pv_constParams, camera);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
new file mode 100644
index 0000000..575794b
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
@@ -0,0 +1,193 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+//#define DEBUG_PARAMS
+static void debugParam(SgShaderParam *p, SgShaderParamData *pData) {
+    rsDebug("____________ Param ____________", p);
+    printName(pData->paramName);
+    rsDebug("bufferOffset", p->bufferOffset);
+    rsDebug("type ", pData->type);
+    rsDebug("data timestamp ", pData->timestamp);
+    rsDebug("param timestamp", p->dataTimestamp);
+
+    const SgTransform *pTransform = NULL;
+    if (rsIsObject(pData->transform)) {
+        pTransform = (const SgTransform *)rsGetElementAt(pData->transform, 0);
+
+        rsDebug("transform", pTransform);
+        printName(pTransform->name);
+        rsDebug("timestamp", pTransform->timestamp);
+        rsDebug("param timestamp", p->transformTimestamp);
+    }
+
+    const SgLight *pLight = NULL;
+    if (rsIsObject(pData->light)) {
+        pLight = (const SgLight *)rsGetElementAt(pData->light, 0);
+        printLightInfo(pLight);
+    }
+}
+
+
+static void writeFloatData(float *ptr, const float4 *input, uint32_t vecSize) {
+#ifdef DEBUG_PARAMS
+    rsDebug("Writing value ", *input);
+    rsDebug("Writing vec size ", vecSize);
+#endif // DEBUG_PARAMS
+
+    switch (vecSize) {
+    case 1:
+        *ptr = input->x;
+        break;
+    case 2:
+        *((float2*)ptr) = (*input).xy;
+        break;
+    case 3:
+        *((float3*)ptr) = (*input).xyz;
+        break;
+    case 4:
+        *((float4*)ptr) = *input;
+        break;
+    }
+}
+
+static bool processParam(SgShaderParam *p, SgShaderParamData *pData,
+                         uint8_t *constantBuffer,
+                         const SgCamera *currentCam,
+                         SgFragmentShader *shader) {
+    bool isDataOnly = (pData->type > SHADER_PARAM_DATA_ONLY);
+    const SgTransform *pTransform = NULL;
+    if (rsIsObject(pData->transform)) {
+        pTransform = (const SgTransform *)rsGetElementAt(pData->transform, 0);
+    }
+
+    if (isDataOnly) {
+        // If we are a transform param and our transform is unchanged, nothing to do
+        if (pTransform) {
+            if (p->transformTimestamp == pTransform->timestamp) {
+                return false;
+            }
+            p->transformTimestamp = pTransform->timestamp;
+        } else {
+            if (p->dataTimestamp == pData->timestamp) {
+                return false;
+            }
+            p->dataTimestamp = pData->timestamp;
+        }
+    }
+
+    const SgLight *pLight = NULL;
+    if (rsIsObject(pData->light)) {
+        pLight = (const SgLight *)rsGetElementAt(pData->light, 0);
+    }
+
+    uint8_t *dataPtr = NULL;
+    const SgTexture *tex = NULL;
+    if (pData->type == SHADER_PARAM_TEXTURE) {
+        tex = rsGetElementAt(pData->texture, 0);
+    } else {
+        dataPtr = constantBuffer + p->bufferOffset;
+    }
+
+    switch (pData->type) {
+    case SHADER_PARAM_TEXTURE:
+        rsgBindTexture(shader->program, p->bufferOffset, tex->texture);
+        break;
+    case SHADER_PARAM_FLOAT4_DATA:
+        writeFloatData((float*)dataPtr, &pData->float_value, p->float_vecSize);
+        break;
+    case SHADER_PARAM_FLOAT4_CAMERA_POS:
+        writeFloatData((float*)dataPtr, &currentCam->position, p->float_vecSize);
+        break;
+    case SHADER_PARAM_FLOAT4_CAMERA_DIR: break;
+    case SHADER_PARAM_FLOAT4_LIGHT_COLOR:
+        writeFloatData((float*)dataPtr, &pLight->color, p->float_vecSize);
+        break;
+    case SHADER_PARAM_FLOAT4_LIGHT_POS:
+        writeFloatData((float*)dataPtr, &pLight->position, p->float_vecSize);
+        break;
+    case SHADER_PARAM_FLOAT4_LIGHT_DIR: break;
+
+    case SHADER_PARAM_TRANSFORM_DATA:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &pTransform->globalMat);
+        break;
+    case SHADER_PARAM_TRANSFORM_VIEW:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->view);
+        break;
+    case SHADER_PARAM_TRANSFORM_PROJ:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->proj);
+        break;
+    case SHADER_PARAM_TRANSFORM_VIEW_PROJ:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->viewProj);
+        break;
+    case SHADER_PARAM_TRANSFORM_MODEL:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &pTransform->globalMat);
+        break;
+    case SHADER_PARAM_TRANSFORM_MODEL_VIEW:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->view);
+        rsMatrixLoadMultiply((rs_matrix4x4*)dataPtr,
+                             (rs_matrix4x4*)dataPtr,
+                             &pTransform->globalMat);
+        break;
+    case SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ:
+        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->viewProj);
+        rsMatrixLoadMultiply((rs_matrix4x4*)dataPtr,
+                             (rs_matrix4x4*)dataPtr,
+                             &pTransform->globalMat);
+        break;
+    }
+    return true;
+}
+
+static void processAllParams(rs_allocation shaderConst,
+                             rs_allocation allParams,
+                             const SgCamera *camera) {
+    if (rsIsObject(shaderConst)) {
+        uint8_t *constantBuffer = (uint8_t*)rsGetElementAt(shaderConst, 0);
+
+        int numParams = 0;
+        if (rsIsObject(allParams)) {
+            numParams = rsAllocationGetDimX(allParams);
+        }
+        bool updated = false;
+        for (int i = 0; i < numParams; i ++) {
+            SgShaderParam *current = (SgShaderParam*)rsGetElementAt(allParams, i);
+            SgShaderParamData *currentData = (SgShaderParamData*)rsGetElementAt(current->data, 0);
+#ifdef DEBUG_PARAMS
+            debugParam(current, currentData);
+#endif // DEBUG_PARAMS
+            updated = processParam(current, currentData, constantBuffer, camera, NULL) || updated;
+        }
+    }
+}
+
+static void processTextureParams(SgFragmentShader *shader) {
+    int numParams = 0;
+    if (rsIsObject(shader->shaderTextureParams)) {
+        numParams = rsAllocationGetDimX(shader->shaderTextureParams);
+    }
+    for (int i = 0; i < numParams; i ++) {
+        SgShaderParam *current = (SgShaderParam*)rsGetElementAt(shader->shaderTextureParams, i);
+        SgShaderParamData *currentData = (SgShaderParamData*)rsGetElementAt(current->data, 0);
+#ifdef DEBUG_PARAMS
+        debugParam(current, currentData);
+#endif // DEBUG_PARAMS
+        processParam(current, currentData, NULL, NULL, shader);
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
new file mode 100644
index 0000000..8a73dbd
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
@@ -0,0 +1,244 @@
+// Copyright (C) 2011-2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "rs_graphics.rsh"
+#include "scenegraph_objects.rsh"
+
+rs_script gTransformScript;
+rs_script gCameraScript;
+rs_script gLightScript;
+rs_script gObjectParamsScript;
+rs_script gFragmentParamsScript;
+rs_script gVertexParamsScript;
+rs_script gCullScript;
+
+SgTransform *gRootNode;
+rs_allocation gCameras;
+rs_allocation gLights;
+rs_allocation gFragmentShaders;
+rs_allocation gVertexShaders;
+rs_allocation gRenderableObjects;
+
+rs_allocation gRenderPasses;
+
+// Temporary shaders
+rs_program_store gPFSBackground;
+
+uint32_t *gFrontToBack;
+static uint32_t gFrontToBackCount = 0;
+uint32_t *gBackToFront;
+static uint32_t gBackToFrontCount = 0;
+
+static SgCamera *gActiveCamera = NULL;
+
+static rs_allocation nullAlloc;
+
+// #define DEBUG_RENDERABLES
+static void draw(SgRenderable *obj) {
+#ifdef DEBUG_RENDERABLES
+    const SgTransform *objTransform = (const SgTransform *)rsGetElementAt(obj->transformMatrix, 0);
+    rsDebug("**** Drawing object with transform", obj);
+    printName(objTransform->name);
+    rsDebug("Model matrix: ", &objTransform->globalMat);
+    printName(obj->name);
+#endif //DEBUG_RENDERABLES
+
+    const SgRenderState *renderState = (const SgRenderState *)rsGetElementAt(obj->render_state, 0);
+    const SgVertexShader *pv = (const SgVertexShader *)rsGetElementAt(renderState->pv, 0);
+    const SgFragmentShader *pf = (const SgFragmentShader *)rsGetElementAt(renderState->pf, 0);
+
+    if (pv->objectConstIndex != -1) {
+        rsgBindConstant(pv->program, pv->objectConstIndex, obj->pv_const);
+    }
+    if (pf->objectConstIndex != -1) {
+        rsgBindConstant(pf->program, pf->objectConstIndex, obj->pf_const);
+    }
+
+    if (rsIsObject(renderState->ps)) {
+        rsgBindProgramStore(renderState->ps);
+    } else {
+        rsgBindProgramStore(gPFSBackground);
+    }
+
+    if (rsIsObject(renderState->pr)) {
+        rsgBindProgramRaster(renderState->pr);
+    } else {
+        rs_program_raster pr;
+        rsgBindProgramRaster(pr);
+    }
+
+    rsgBindProgramVertex(pv->program);
+    rsgBindProgramFragment(pf->program);
+
+    for (uint32_t i = 0; i < obj->pf_num_textures; i ++) {
+        const SgTexture *tex = rsGetElementAt(obj->pf_textures[i], 0);
+        rsgBindTexture(pf->program, i, tex->texture);
+    }
+
+    rsgDrawMesh(obj->mesh, obj->meshIndex);
+}
+
+static void sortToBucket(SgRenderable *obj) {
+    const SgRenderState *renderState = (const SgRenderState *)rsGetElementAt(obj->render_state, 0);
+    if (rsIsObject(renderState->ps)) {
+        bool isOpaque = false;
+        if (isOpaque) {
+            gFrontToBack[gFrontToBackCount++] = (uint32_t)obj;
+        } else {
+            gBackToFront[gBackToFrontCount++] = (uint32_t)obj;
+        }
+    } else {
+        gFrontToBack[gFrontToBackCount++] = (uint32_t)obj;
+    }
+}
+
+static void updateActiveCamera(rs_allocation cam) {
+    gActiveCamera = (SgCamera *)rsGetElementAt(cam, 0);
+}
+
+static void prepareCameras() {
+    // now compute all the camera matrices
+    if (rsIsObject(gCameras)) {
+        float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+        rsForEach(gCameraScript, gCameras, nullAlloc, &aspect, sizeof(aspect));
+    }
+}
+
+static void prepareLights() {
+    if (rsIsObject(gLights)) {
+        rsForEach(gLightScript, gLights, nullAlloc);
+    }
+}
+
+static void drawSorted() {
+    for (int i = 0; i < gFrontToBackCount; i ++) {
+        SgRenderable *current = (SgRenderable*)gFrontToBack[i];
+        draw(current);
+    }
+
+    for (int i = 0; i < gBackToFrontCount; i ++) {
+        SgRenderable *current = (SgRenderable*)gBackToFront[i];
+        draw(current);
+    }
+}
+
+static void drawAllObjects(rs_allocation allObj) {
+    if (!rsIsObject(allObj)) {
+        return;
+    }
+
+    if (rsIsObject(gVertexShaders)) {
+        rsForEach(gVertexParamsScript, nullAlloc, gVertexShaders,
+                  gActiveCamera, sizeof(gActiveCamera));
+    }
+    if (rsIsObject(gFragmentShaders)) {
+        rsForEach(gFragmentParamsScript, nullAlloc, gFragmentShaders,
+                  gActiveCamera, sizeof(gActiveCamera));
+    }
+
+    // Run the params and cull script
+    rsForEach(gCullScript, nullAlloc, allObj, gActiveCamera, sizeof(gActiveCamera));
+    rsForEach(gObjectParamsScript, nullAlloc, allObj, gActiveCamera, sizeof(gActiveCamera));
+
+    int numRenderables = rsAllocationGetDimX(allObj);
+    for (int i = 0; i < numRenderables; i ++) {
+        rs_allocation *drawAlloc = (rs_allocation*)rsGetElementAt(allObj, i);
+        SgRenderable *current = (SgRenderable*)rsGetElementAt(*drawAlloc, 0);
+        if (current->isVisible) {
+            sortToBucket(current);
+        }
+    }
+    drawSorted();
+}
+
+int root(void) {
+#ifdef DEBUG_RENDERABLES
+    rsDebug("=============================================================================", 0);
+#endif // DEBUG_RENDERABLES
+
+    // first step is to update the transform hierachy
+    if (gRootNode && rsIsObject(gRootNode->children)) {
+        rsForEach(gTransformScript, gRootNode->children, nullAlloc, 0, 0);
+    }
+
+    prepareCameras();
+    prepareLights();
+
+    if (rsIsObject(gRenderPasses)) {
+        rsgClearDepth(1.0f);
+        int numPasses = rsAllocationGetDimX(gRenderPasses);
+        for (uint i = 0; i < numPasses; i ++) {
+            gFrontToBackCount = 0;
+            gBackToFrontCount = 0;
+            SgRenderPass *pass = (SgRenderPass*)rsGetElementAt(gRenderPasses, i);
+            if (rsIsObject(pass->color_target)) {
+                rsgBindColorTarget(pass->color_target, 0);
+            }
+            if (rsIsObject(pass->depth_target)) {
+                rsgBindDepthTarget(pass->depth_target);
+            }
+            if (!rsIsObject(pass->color_target) &&
+                !rsIsObject(pass->depth_target)) {
+                rsgClearAllRenderTargets();
+            }
+            updateActiveCamera(pass->camera);
+            if (pass->should_clear_color) {
+                rsgClearColor(pass->clear_color.x, pass->clear_color.y,
+                              pass->clear_color.z, pass->clear_color.w);
+            }
+            if (pass->should_clear_depth) {
+                rsgClearDepth(pass->clear_depth);
+            }
+            drawAllObjects(pass->objects);
+        }
+    } else {
+        gFrontToBackCount = 0;
+        gBackToFrontCount = 0;
+        rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+        rsgClearDepth(1.0f);
+
+        if (rsIsObject(gCameras)) {
+            rs_allocation *camAlloc = (rs_allocation*)rsGetElementAt(gCameras, 0);
+            updateActiveCamera(*camAlloc);
+        }
+        drawAllObjects(gRenderableObjects);
+    }
+    return 10;
+}
+
+// Search through sorted and culled objects
+void pick(int screenX, int screenY) {
+    float3 pnt, vec;
+    getCameraRay(gActiveCamera, screenX, screenY, &pnt, &vec);
+
+    for (int i = 0; i < gFrontToBackCount; i ++) {
+        SgRenderable *current = (SgRenderable*)gFrontToBack[i];
+        bool isPicked = intersect(current, pnt, vec);
+        if (isPicked) {
+            current->cullType = CULL_ALWAYS;
+        }
+    }
+
+    for (int i = 0; i < gBackToFrontCount; i ++) {
+        SgRenderable *current = (SgRenderable*)gBackToFront[i];
+        bool isPicked = intersect(current, pnt, vec);
+        if (isPicked) {
+            current->cullType = CULL_ALWAYS;
+        }
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
new file mode 100644
index 0000000..bdca3ab
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
@@ -0,0 +1,323 @@
+// Copyright (C) 2011-2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#ifndef _TRANSFORM_DEF_
+#define _TRANSFORM_DEF_
+
+#include "rs_graphics.rsh"
+
+#define TRANSFORM_NONE      0
+#define TRANSFORM_TRANSLATE 1
+#define TRANSFORM_ROTATE    2
+#define TRANSFORM_SCALE     3
+
+#define CULL_FRUSTUM 0
+#define CULL_ALWAYS  2
+
+#define LIGHT_POINT       0
+#define LIGHT_DIRECTIONAL 1
+
+// Shader params that involve only data
+#define SHADER_PARAM_DATA_ONLY                 10000
+#define SHADER_PARAM_FLOAT4_DATA               10001
+#define SHADER_PARAM_TRANSFORM_DATA            10002
+#define SHADER_PARAM_TRANSFORM_MODEL           10003
+
+// Shader params that involve camera
+#define SHADER_PARAM_CAMERA                    1000
+#define SHADER_PARAM_FLOAT4_CAMERA_POS         1001
+#define SHADER_PARAM_FLOAT4_CAMERA_DIR         1002
+#define SHADER_PARAM_TRANSFORM_VIEW            1003
+#define SHADER_PARAM_TRANSFORM_PROJ            1004
+#define SHADER_PARAM_TRANSFORM_VIEW_PROJ       1005
+#define SHADER_PARAM_TRANSFORM_MODEL_VIEW      1006
+#define SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ 1007
+
+// Shader Params that only involve lights
+#define SHADER_PARAM_LIGHT                     100
+#define SHADER_PARAM_FLOAT4_LIGHT_COLOR        103
+#define SHADER_PARAM_FLOAT4_LIGHT_POS          104
+#define SHADER_PARAM_FLOAT4_LIGHT_DIR          105
+
+#define SHADER_PARAM_TEXTURE                   10
+
+#define TEXTURE_NONE          0
+#define TEXTURE_2D            1
+#define TEXTURE_CUBE          2
+#define TEXTURE_RENDER_TARGET 3
+
+typedef struct TransformComponent_s {
+    float4 value;
+    int type;
+    rs_allocation name;
+} SgTransformComponent;
+
+typedef struct __attribute__((packed, aligned(4))) SgTransform {
+    rs_matrix4x4 globalMat;
+    rs_matrix4x4 localMat;
+
+    rs_allocation components;
+    int isDirty;
+
+    rs_allocation children;
+    rs_allocation name;
+
+    // Used to check whether transform params need to be updated
+    uint32_t timestamp;
+} SgTransform;
+
+typedef struct VertexShader_s {
+    rs_program_vertex program;
+    // Buffer with vertex constant data
+    rs_allocation shaderConst;
+    // ShaderParam's that populate data
+    rs_allocation shaderConstParams;
+    // location of the per object constants on the buffer
+    int objectConstIndex;
+} SgVertexShader;
+
+typedef struct FragmentShader_s {
+    rs_program_fragment program;
+    // Buffer with vertex constant data
+    rs_allocation shaderConst;
+    // ShaderParam's that populate data
+    rs_allocation shaderConstParams;
+    // ShaderParam's that set textures
+    rs_allocation shaderTextureParams;
+    // location of the per object constants on the buffer
+    int objectConstIndex;
+} SgFragmentShader;
+
+typedef struct RenderState_s {
+    rs_allocation pv; // VertexShader struct
+    rs_allocation pf; // FragmentShader struct
+    rs_program_store ps;
+    rs_program_raster pr;
+} SgRenderState;
+
+typedef struct Renderable_s {
+    rs_allocation render_state;
+    // Buffer with vertex constant data
+    rs_allocation pv_const;
+    // ShaderParam's that populate data
+    rs_allocation pv_constParams;
+    // Buffer with fragment constant data
+    rs_allocation pf_const;
+    // ShaderParam's that populate data
+    rs_allocation pf_constParams;
+    rs_allocation pf_textures[8];
+    int pf_num_textures;
+    rs_mesh mesh;
+    int meshIndex;
+    rs_allocation transformMatrix;
+    rs_allocation name;
+    float4 boundingSphere;
+    float4 worldBoundingSphere;
+    int bVolInitialized;
+    int cullType; // specifies whether to frustum cull
+    int isVisible;
+} SgRenderable;
+
+typedef struct RenderPass_s {
+    rs_allocation color_target;
+    rs_allocation depth_target;
+    rs_allocation camera;
+    rs_allocation objects;
+
+    float4 clear_color;
+    float clear_depth;
+    bool should_clear_color;
+    bool should_clear_depth;
+} SgRenderPass;
+
+typedef struct Camera_s {
+    rs_matrix4x4 proj;
+    rs_matrix4x4 view;
+    rs_matrix4x4 viewProj;
+    float4 position;
+    float near;
+    float far;
+    float horizontalFOV;
+    float aspect;
+    rs_allocation name;
+    rs_allocation transformMatrix;
+    float4 frustumPlanes[6];
+
+    int isDirty;
+    // Timestamp of the camera itself to signal params if anything changes
+    uint32_t timestamp;
+    // Timestamp of our transform
+    uint32_t transformTimestamp;
+} SgCamera;
+
+typedef struct Light_s {
+    float4 position;
+    float4 color;
+    float intensity;
+    int type;
+    rs_allocation name;
+    rs_allocation transformMatrix;
+} SgLight;
+
+// This represents the shader parameter data needed to set a float or transform data
+typedef struct ShaderParamData_s {
+    int type;
+    float4 float_value;
+    uint32_t timestamp;
+    rs_allocation paramName;
+    rs_allocation camera;
+    rs_allocation light;
+    rs_allocation transform;
+    rs_allocation texture;
+} SgShaderParamData;
+
+// This represents a shader parameter that knows how to update itself for a given
+// renderable or shader and contains a timestamp for the last time this buffer was updated
+typedef struct ShaderParam_s {
+    // Used to check whether transform params need to be updated
+    uint32_t transformTimestamp;
+    // Used to check whether data params need to be updated
+    // These are used when somebody set the matrix of float value directly in java
+    uint32_t dataTimestamp;
+    // Specifies where in the constant buffer data gets written to
+    int bufferOffset;
+    // An instance of SgShaderParamData that could be shared by multiple objects
+    rs_allocation data;
+    // How many components of the vector we need to write
+    int float_vecSize;
+} SgShaderParam;
+
+// This represents a texture object
+typedef struct Texture_s {
+    uint32_t type;
+    rs_allocation texture;
+} SgTexture;
+
+static void printName(rs_allocation name) {
+    if (!rsIsObject(name)) {
+        rsDebug("no name", 0);
+        return;
+    }
+
+    rsDebug((const char*)rsGetElementAt(name, 0), 0);
+}
+
+static void printCameraInfo(const SgCamera *cam) {
+    rsDebug("***** Camera information. ptr:", cam);
+    printName(cam->name);
+    const SgTransform *camTransform = (const SgTransform *)rsGetElementAt(cam->transformMatrix, 0);
+    rsDebug("Transform name:", camTransform);
+    printName(camTransform->name);
+
+    rsDebug("Aspect: ", cam->aspect);
+    rsDebug("Near: ", cam->near);
+    rsDebug("Far: ", cam->far);
+    rsDebug("Fov: ", cam->horizontalFOV);
+    rsDebug("Position: ", cam->position);
+    rsDebug("Proj: ", &cam->proj);
+    rsDebug("View: ", &cam->view);
+}
+
+static void printLightInfo(const SgLight *light) {
+    rsDebug("***** Light information. ptr:", light);
+    printName(light->name);
+    const SgTransform *lTransform = (const SgTransform *)rsGetElementAt(light->transformMatrix, 0);
+    rsDebug("Transform name:", lTransform);
+    printName(lTransform->name);
+
+    rsDebug("Position: ", light->position);
+    rsDebug("Color : ", light->color);
+    rsDebug("Intensity: ", light->intensity);
+    rsDebug("Type: ", light->type);
+}
+
+static void getCameraRay(const SgCamera *cam, int screenX, int screenY, float3 *pnt, float3 *vec) {
+    rsDebug("=================================", screenX);
+    rsDebug("Point X", screenX);
+    rsDebug("Point Y", screenY);
+
+    rs_matrix4x4 mvpInv;
+    rsMatrixLoad(&mvpInv, &cam->viewProj);
+    rsMatrixInverse(&mvpInv);
+
+    float width = (float)rsgGetWidth();
+    float height = (float)rsgGetHeight();
+
+    float4 pos = {(float)screenX, height - (float)screenY, 0.0f, 1.0f};
+
+    pos.x /= width;
+    pos.y /= height;
+
+    rsDebug("Pre Norm X", pos.x);
+    rsDebug("Pre Norm Y", pos.y);
+
+    pos.xy = pos.xy * 2.0f - 1.0f;
+
+    rsDebug("Norm X", pos.x);
+    rsDebug("Norm Y", pos.y);
+
+    pos = rsMatrixMultiply(&mvpInv, pos);
+    float oneOverW = 1.0f / pos.w;
+    pos.xyz *= oneOverW;
+
+    rsDebug("World X", pos.x);
+    rsDebug("World Y", pos.y);
+    rsDebug("World Z", pos.z);
+
+    rsDebug("Cam X", cam->position.x);
+    rsDebug("Cam Y", cam->position.y);
+    rsDebug("Cam Z", cam->position.z);
+
+    *vec = normalize(pos.xyz - cam->position.xyz);
+    rsDebug("Vec X", vec->x);
+    rsDebug("Vec Y", vec->y);
+    rsDebug("Vec Z", vec->z);
+    *pnt = cam->position.xyz;
+}
+
+static bool intersect(const SgRenderable *obj, float3 pnt, float3 vec) {
+    // Solving for t^2 + Bt + C = 0
+    float3 originMinusCenter = pnt - obj->worldBoundingSphere.xyz;
+    float B = dot(originMinusCenter, vec) * 2.0f;
+    float C = dot(originMinusCenter, originMinusCenter) -
+              obj->worldBoundingSphere.w * obj->worldBoundingSphere.w;
+
+    float discriminant = B * B - 4.0f * C;
+    if (discriminant < 0.0f) {
+        return false;
+    }
+    discriminant = sqrt(discriminant);
+
+    float t0 = (-B - discriminant) * 0.5f;
+    float t1 = (-B + discriminant) * 0.5f;
+
+    if (t0 > t1) {
+        float temp = t0;
+        t0 = t1;
+        t1 = temp;
+    }
+
+    // The sphere is behind us
+    if (t1 < 0.0f) {
+        return false;
+    }
+    return true;
+}
+
+
+#endif // _TRANSFORM_DEF_
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
new file mode 100644
index 0000000..941b5a8
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
@@ -0,0 +1,127 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.modelviewer)
+
+#include "scenegraph_objects.rsh"
+
+rs_script gTransformScript;
+
+typedef struct {
+    int changed;
+    rs_matrix4x4 *mat;
+} ParentData;
+
+//#define DEBUG_TRANSFORMS
+static void debugTransform(SgTransform *data, const ParentData *parent) {
+    rsDebug("****** <Transform> ******", (int)data);
+    printName(data->name);
+    rsDebug("isDirty", data->isDirty);
+    rsDebug("parent", (int)parent);
+    rsDebug("child ", rsIsObject(data->children));
+
+    // Refresh matrices if dirty
+    if (data->isDirty && rsIsObject(data->components)) {
+        uint32_t numComponenets = rsAllocationGetDimX(data->components);
+        for (int i = 0; i < numComponenets; i ++) {
+            const SgTransformComponent *comp = NULL;
+            comp = (const SgTransformComponent *)rsGetElementAt(data->components, i);
+
+            if (rsIsObject(comp->name)) {
+                rsDebug((const char*)rsGetElementAt(comp->name, 0), comp->value);
+                rsDebug("Type", comp->type);
+            } else {
+                rsDebug("no name", comp->value);
+                rsDebug("Type", comp->type);
+            }
+        }
+    }
+
+    rsDebug("timestamp", data->timestamp);
+    rsDebug("****** </Transform> ******", (int)data);
+}
+
+static void appendTransformation(int type, float4 data, rs_matrix4x4 *mat) {
+    rs_matrix4x4 temp;
+
+    switch (type) {
+    case TRANSFORM_TRANSLATE:
+        rsMatrixLoadTranslate(&temp, data.x, data.y, data.z);
+        break;
+    case TRANSFORM_ROTATE:
+        rsMatrixLoadRotate(&temp, data.w, data.x, data.y, data.z);
+        break;
+    case TRANSFORM_SCALE:
+        rsMatrixLoadScale(&temp, data.x, data.y, data.z);
+        break;
+    }
+    rsMatrixMultiply(mat, &temp);
+}
+
+void root(const rs_allocation *v_in, rs_allocation *v_out, const void *usrData) {
+
+    SgTransform *data = (SgTransform *)rsGetElementAt(*v_in, 0);
+    const ParentData *parent = (const ParentData *)usrData;
+
+#ifdef DEBUG_TRANSFORMS
+    debugTransform(data, parent);
+#endif //DEBUG_TRANSFORMS
+
+    rs_matrix4x4 *localMat = &data->localMat;
+    rs_matrix4x4 *globalMat = &data->globalMat;
+
+    // Refresh matrices if dirty
+    if (data->isDirty && rsIsObject(data->components)) {
+        bool resetLocal = false;
+        uint32_t numComponenets = rsAllocationGetDimX(data->components);
+        for (int i = 0; i < numComponenets; i ++) {
+            if (!resetLocal) {
+                // Reset our local matrix only for component transforms
+                rsMatrixLoadIdentity(localMat);
+                resetLocal = true;
+            }
+            const SgTransformComponent *comp = NULL;
+            comp = (const SgTransformComponent *)rsGetElementAt(data->components, i);
+            appendTransformation(comp->type, comp->value, localMat);
+        }
+    }
+
+    if (parent) {
+        data->isDirty = (parent->changed || data->isDirty) ? 1 : 0;
+        if (data->isDirty) {
+            rsMatrixLoad(globalMat, parent->mat);
+            rsMatrixMultiply(globalMat, localMat);
+        }
+    } else if (data->isDirty) {
+        rsMatrixLoad(globalMat, localMat);
+    }
+
+    ParentData toChild;
+    toChild.changed = 0;
+    toChild.mat = globalMat;
+
+    if (data->isDirty) {
+        toChild.changed = 1;
+        data->timestamp ++;
+    }
+
+    if (rsIsObject(data->children)) {
+        rs_allocation nullAlloc;
+        rsForEach(gTransformScript, data->children, nullAlloc, &toChild, sizeof(toChild));
+    }
+
+    data->isDirty = 0;
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs
new file mode 100644
index 0000000..88955a8
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs
@@ -0,0 +1,29 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "scenegraph_objects.rsh"
+
+//#define DEBUG_PARAMS
+
+#include "params.rsh"
+
+void root(rs_allocation *v_out, const void *usrData) {
+    SgVertexShader *shader = (SgVertexShader *)rsGetElementAt(*v_out, 0);
+    const SgCamera *camera = (const SgCamera*)usrData;
+    processAllParams(shader->shaderConst, shader->shaderConstParams, camera);
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java
new file mode 100644
index 0000000..420e133
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java
@@ -0,0 +1,110 @@
+/*
+ * 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.android.testapp;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+ * A list view where the last item the user clicked is placed in
+ * the "activated" state, causing its background to highlight.
+ */
+public class FileSelector extends ListActivity {
+
+    File[] mCurrentSubList;
+    File mCurrentFile;
+
+    class DAEFilter implements FileFilter {
+        public boolean accept(File file) {
+            if (file.isDirectory()) {
+                return true;
+            }
+            return file.getName().endsWith(".dae");
+        }
+    }
+
+    private void populateList(File file) {
+
+        mCurrentFile = file;
+        setTitle(mCurrentFile.getAbsolutePath() + "/*.dae");
+        List<String> names = new ArrayList<String>();
+        names.add("..");
+
+        mCurrentSubList = mCurrentFile.listFiles(new DAEFilter());
+
+        if (mCurrentSubList != null) {
+            for (int i = 0; i < mCurrentSubList.length; i ++) {
+                String fileName = mCurrentSubList[i].getName();
+                if (mCurrentSubList[i].isDirectory()) {
+                    fileName = "/" + fileName;
+                }
+                names.add(fileName);
+            }
+        }
+
+        // Use the built-in layout for showing a list item with a single
+        // line of text whose background is changes when activated.
+        setListAdapter(new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_activated_1, names));
+        getListView().setTextFilterEnabled(true);
+
+        // Tell the list view to show one checked/activated item at a time.
+        getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        populateList(new File("/sdcard/"));
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        if (position == 0) {
+            File parent = mCurrentFile.getParentFile();
+            if (parent == null) {
+                return;
+            }
+            populateList(parent);
+            return;
+        }
+
+        // the first thing in list is parent directory
+        File selectedFile = mCurrentSubList[position - 1];
+        if (selectedFile.isDirectory()) {
+            populateList(selectedFile);
+            return;
+        }
+
+        Intent resultIntent = new Intent();
+        resultIntent.setData(Uri.fromFile(selectedFile));
+        setResult(RESULT_OK, resultIntent);
+        finish();
+    }
+
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java
new file mode 100644
index 0000000..28f916c
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java
@@ -0,0 +1,192 @@
+/*

+ * 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.android.testapp;

+

+import java.util.ArrayList;

+

+import com.android.scenegraph.*;

+

+import android.content.res.Resources;

+import android.graphics.Bitmap;

+import android.graphics.BitmapFactory;

+import android.os.AsyncTask;

+import android.renderscript.*;

+import android.renderscript.Allocation.MipmapControl;

+import android.renderscript.Element.Builder;

+import android.renderscript.Font.Style;

+import android.renderscript.Program.TextureType;

+import android.renderscript.ProgramStore.DepthFunc;

+import android.util.Log;

+

+class FullscreenBlur {

+

+    static TextureRenderTarget sRenderTargetBlur0Color;

+    static TextureRenderTarget sRenderTargetBlur0Depth;

+    static TextureRenderTarget sRenderTargetBlur1Color;

+    static TextureRenderTarget sRenderTargetBlur1Depth;

+    static TextureRenderTarget sRenderTargetBlur2Color;

+    static TextureRenderTarget sRenderTargetBlur2Depth;

+

+    static FragmentShader mPF_BlurH;

+    static FragmentShader mPF_BlurV;

+    static FragmentShader mPF_SelectColor;

+    static FragmentShader mPF_Texture;

+    static VertexShader mPV_Paint;

+    static VertexShader mPV_Blur;

+

+    static int targetWidth;

+    static int targetHeight;

+

+    // This is only used when full screen blur is enabled

+    // Basically, it's the offscreen render targets

+    static void createRenderTargets(RenderScriptGL rs, int w, int h) {

+        targetWidth = w/8;

+        targetHeight = h/8;

+        Type.Builder b = new Type.Builder(rs, Element.RGBA_8888(rs));

+        Type renderType = b.setX(targetWidth).setY(targetHeight).create();

+        int usage = Allocation.USAGE_GRAPHICS_TEXTURE | Allocation.USAGE_GRAPHICS_RENDER_TARGET;

+        sRenderTargetBlur0Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+        sRenderTargetBlur1Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+        sRenderTargetBlur2Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+

+        b = new Type.Builder(rs, Element.createPixel(rs, Element.DataType.UNSIGNED_16,

+                                                     Element.DataKind.PIXEL_DEPTH));

+        renderType = b.setX(targetWidth).setY(targetHeight).create();

+        usage = Allocation.USAGE_GRAPHICS_RENDER_TARGET;

+        sRenderTargetBlur0Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+        sRenderTargetBlur1Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+        sRenderTargetBlur2Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

+    }

+

+    static void addOffsets(Renderable quad, float advance) {

+        quad.appendSourceParams(new Float4Param("blurOffset0", - advance * 2.5f));

+        quad.appendSourceParams(new Float4Param("blurOffset1", - advance * 0.5f));

+        quad.appendSourceParams(new Float4Param("blurOffset2", advance * 1.5f));

+        quad.appendSourceParams(new Float4Param("blurOffset3", advance * 3.5f));

+    }

+

+    static RenderPass addPass(Scene scene, Camera cam, TextureRenderTarget color, TextureRenderTarget depth) {

+        RenderPass pass = new RenderPass();

+        pass.setColorTarget(color);

+        pass.setDepthTarget(depth);

+        pass.setShouldClearColor(false);

+        pass.setShouldClearDepth(false);

+        pass.setCamera(cam);

+        scene.appendRenderPass(pass);

+        return pass;

+    }

+

+    static void addBlurPasses(Scene scene, RenderScriptGL rs, Camera cam) {

+        SceneManager sceneManager = SceneManager.getInstance();

+        ArrayList<RenderableBase> allDraw = scene.getRenderables();

+        int numDraw = allDraw.size();

+

+        ProgramRaster cullNone = ProgramRaster.CULL_NONE(rs);

+        ProgramStore blendAdd = SceneManager.BLEND_ADD_DEPTH_NONE(rs);

+        ProgramStore blendNone = ProgramStore.BLEND_NONE_DEPTH_NONE(rs);

+

+        RenderState drawTex = new RenderState(mPV_Blur, mPF_Texture, blendAdd, cullNone);

+        RenderState selectCol = new RenderState(mPV_Blur, mPF_SelectColor, blendNone, cullNone);

+        RenderState hBlur = new RenderState(mPV_Blur, mPF_BlurH, blendNone, cullNone);

+        RenderState vBlur = new RenderState(mPV_Blur, mPF_BlurV, blendNone, cullNone);

+

+        // Renders the scene off screen

+        RenderPass blurSourcePass = addPass(scene, cam,

+                                            sRenderTargetBlur0Color,

+                                            sRenderTargetBlur0Depth);

+        blurSourcePass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));

+        blurSourcePass.setShouldClearColor(true);

+        blurSourcePass.setClearDepth(1.0f);

+        blurSourcePass.setShouldClearDepth(true);

+        for (int i = 0; i < numDraw; i ++) {

+            blurSourcePass.appendRenderable((Renderable)allDraw.get(i));

+        }

+

+        // Pass for selecting bright colors

+        RenderPass selectColorPass = addPass(scene, cam,

+                                             sRenderTargetBlur2Color,

+                                             sRenderTargetBlur2Depth);

+        Renderable quad = sceneManager.getRenderableQuad("ScreenAlignedQuadS", selectCol);

+        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur0Color));

+        selectColorPass.appendRenderable(quad);

+

+        // Horizontal blur

+        RenderPass horizontalBlurPass = addPass(scene, cam,

+                                                sRenderTargetBlur1Color,

+                                                sRenderTargetBlur1Depth);

+        quad = sceneManager.getRenderableQuad("ScreenAlignedQuadH", hBlur);

+        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur2Color));

+        addOffsets(quad, 1.0f / (float)targetWidth);

+        horizontalBlurPass.appendRenderable(quad);

+

+        // Vertical Blur

+        RenderPass verticalBlurPass = addPass(scene, cam,

+                                              sRenderTargetBlur2Color,

+                                              sRenderTargetBlur2Depth);

+        quad = sceneManager.getRenderableQuad("ScreenAlignedQuadV", vBlur);

+        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur1Color));

+        addOffsets(quad, 1.0f / (float)targetHeight);

+        verticalBlurPass.appendRenderable(quad);

+    }

+

+    // Additively renders the blurred colors on top of the scene

+    static void addCompositePass(Scene scene, RenderScriptGL rs, Camera cam) {

+        SceneManager sceneManager = SceneManager.getInstance();

+        RenderState drawTex = new RenderState(mPV_Blur, mPF_Texture,

+                                              SceneManager.BLEND_ADD_DEPTH_NONE(rs),

+                                              ProgramRaster.CULL_NONE(rs));

+

+        RenderPass compositePass = addPass(scene, cam, null, null);

+        Renderable quad = sceneManager.getRenderableQuad("ScreenAlignedQuadComposite", drawTex);

+        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur2Color));

+        compositePass.appendRenderable(quad);

+    }

+

+    static private FragmentShader getShader(Resources res, RenderScriptGL rs,

+                                            int resID, Type constants) {

+        FragmentShader.Builder fb = new FragmentShader.Builder(rs);

+        fb.setShader(res, resID);

+        fb.addTexture(TextureType.TEXTURE_2D, "color");

+        if (constants != null) {

+            fb.setObjectConst(constants);

+        }

+        FragmentShader prog = fb.create();

+        prog.getProgram().bindSampler(Sampler.CLAMP_LINEAR(rs), 0);

+        return prog;

+    }

+

+    static void initShaders(Resources res, RenderScriptGL rs) {

+        ScriptField_BlurOffsets blurConst = new ScriptField_BlurOffsets(rs, 1);

+        VertexShader.Builder vb = new VertexShader.Builder(rs);

+        vb.addInput(ScriptField_VertexShaderInputs.createElement(rs));

+        vb.setShader(res, R.raw.blur_vertex);

+        mPV_Blur = vb.create();

+

+        mPF_Texture = getShader(res, rs, R.raw.texture, null);

+        mPF_Texture.getProgram().bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(rs), 0);

+        mPF_BlurH = getShader(res, rs, R.raw.blur_h, blurConst.getAllocation().getType());

+        mPF_BlurV = getShader(res, rs, R.raw.blur_v, blurConst.getAllocation().getType());

+        mPF_SelectColor = getShader(res, rs, R.raw.select_color, null);

+    }

+

+}

+

+

+

+

+

diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java
new file mode 100644
index 0000000..314db80
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 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.testapp;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.view.Window;
+import android.net.Uri;
+
+import java.lang.Runtime;
+
+public class SimpleApp extends Activity {
+
+    private SimpleAppView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new SimpleAppView(this);
+        setContentView(mView);
+    }
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java
new file mode 100644
index 0000000..fff6f34
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2012 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.testapp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import com.android.scenegraph.*;
+import com.android.scenegraph.SceneManager.SceneLoadedCallback;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Program.TextureType;
+import android.util.Log;
+
+// This is where the scenegraph and the rendered objects are initialized and used
+public class SimpleAppRS {
+    SceneManager mSceneManager;
+
+    RenderScriptGL mRS;
+    Resources mRes;
+
+    Scene mScene;
+    Mesh mSimpleMesh;
+    Mesh mSphereMesh;
+    Mesh mCubeMesh;
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        mSceneManager = SceneManager.getInstance();
+        mSceneManager.initRS(mRS, mRes, width, height);
+
+        mScene = new Scene();
+
+        setupGeometry();
+        setupColoredQuad();
+        setupTexturedQuad();
+        setupShadedGeometry();
+        setupCamera();
+        setupRenderPass();
+
+        mSceneManager.setActiveScene(mScene);
+
+        mScene.initRS();
+        mRS.bindRootScript(mSceneManager.getRenderLoop());
+    }
+
+    private void setupGeometry() {
+        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, 3,
+                                                         Mesh.TriangleMeshBuilder.TEXTURE_0);
+
+        // Create four vertices with texture coordinates
+        tmb.setTexture(0.0f, 1.0f).addVertex(-1.0f, 1.0f, 0.0f);
+        tmb.setTexture(0.0f, 0.0f).addVertex(-1.0f, -1.0f, 0.0f);
+        tmb.setTexture(1.0f, 0.0f).addVertex(1.0f, -1.0f, 0.0f);
+        tmb.setTexture(1.0f, 1.0f).addVertex(1.0f, 1.0f, 0.0f);
+
+        tmb.addTriangle(0, 1, 2);
+        tmb.addTriangle(2, 3, 0);
+        mSimpleMesh = tmb.create(true);
+
+        // Load a file that constains two pieces of geometry, a sphere and a cube
+        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.unit_obj);
+        for (int i = 0; i < model.getIndexEntryCount(); i ++) {
+            FileA3D.IndexEntry entry = model.getIndexEntry(i);
+            if (entry != null && entry.getName().equals("CubeMesh")) {
+                mCubeMesh = entry.getMesh();
+            } else if (entry != null && entry.getName().equals("SphereMesh")) {
+                mSphereMesh = entry.getMesh();
+            }
+        }
+    }
+
+    private void setupColoredQuad() {
+        // Built-in shader that provides position, texcoord and normal
+        VertexShader genericV = SceneManager.getDefaultVS();
+        // Built-in shader that displays a color
+        FragmentShader colorF = SceneManager.getColorFS();
+        RenderState colorRS = new RenderState(genericV, colorF, null, null);
+
+        // Draw a simple colored quad
+        Renderable quad = mScene.appendNewRenderable();
+        quad.setMesh(mSimpleMesh);
+        // Our shader has a constant input called "color"
+        // This tells the scenegraph to assign the following float3 to that input
+        quad.appendSourceParams(new Float4Param("color", 0.2f, 0.3f, 0.4f));
+        quad.setRenderState(colorRS);
+    }
+
+    private void setupTexturedQuad() {
+        // Built-in shader that provides position, texcoord and normal
+        VertexShader genericV = SceneManager.getDefaultVS();
+        // Built-in shader that displays a texture
+        FragmentShader textureF = SceneManager.getTextureFS();
+        // We want to use transparency based on the alpha channel of the texture
+        ProgramStore alphaBlend = ProgramStore.BLEND_ALPHA_DEPTH_TEST(mRS);
+        RenderState texRS = new RenderState(genericV, textureF, alphaBlend, null);
+
+        // Draw a textured quad
+        Renderable quad = mScene.appendNewRenderable();
+        quad.setMesh(mSimpleMesh);
+        // Make a transform to position the quad
+        CompoundTransform t = mScene.appendNewCompoundTransform();
+        t.addTranslate("position", new Float3(2, 2, 0));
+        quad.setTransform(t);
+        // Our fragment shader has a constant texture input called "color"
+        // This will assign an icon from drawables to that input
+        quad.appendSourceParams(new TextureParam("color", new Texture2D(R.drawable.icon)));
+        quad.setRenderState(texRS);
+    }
+
+    private FragmentShader createLambertShader() {
+        // Describe what constant inputs our shader wants
+        Element.Builder b = new Element.Builder(mRS);
+        b.add(Element.F32_4(mRS), "cameraPos");
+
+        // Create a shader from a text file in resources
+        FragmentShader.Builder fb = new FragmentShader.Builder(mRS);
+        // Tell the shader what constants we want
+        fb.setShaderConst(new Type.Builder(mRS, b.create()).setX(1).create());
+        // Shader code location
+        fb.setShader(mRes, R.raw.diffuse);
+        // We want a texture called diffuse on our shader
+        fb.addTexture(TextureType.TEXTURE_2D, "diffuse");
+        FragmentShader shader = fb.create();
+        mScene.appendShader(shader);
+        return shader;
+    }
+
+    private void setupShadedGeometry() {
+        // Built-in shader that provides position, texcoord and normal
+        VertexShader genericV = SceneManager.getDefaultVS();
+        // Custom shader
+        FragmentShader diffuseF = createLambertShader();
+        RenderState diffuseRS = new RenderState(genericV, diffuseF, null, null);
+
+        // Draw a sphere
+        Renderable sphere = mScene.appendNewRenderable();
+        // Use the sphere geometry loaded earlier
+        sphere.setMesh(mSphereMesh);
+        // Make a transform to position the sphere
+        CompoundTransform t = mScene.appendNewCompoundTransform();
+        t.addTranslate("position", new Float3(-1, 2, 3));
+        t.addScale("scale", new Float3(1.4f, 1.4f, 1.4f));
+        sphere.setTransform(t);
+        // Tell the renderable which texture to use when we draw
+        // This will mean a texture param in the shader called "diffuse"
+        // will be assigned a texture called red.jpg
+        sphere.appendSourceParams(new TextureParam("diffuse", new Texture2D("", "red.jpg")));
+        sphere.setRenderState(diffuseRS);
+
+        // Draw a cube
+        Renderable cube = mScene.appendNewRenderable();
+        cube.setMesh(mCubeMesh);
+        t = mScene.appendNewCompoundTransform();
+        t.addTranslate("position", new Float3(-2, -2.1f, 0));
+        t.addRotate("rotateX", new Float3(1, 0, 0), 30);
+        t.addRotate("rotateY", new Float3(0, 1, 0), 30);
+        t.addScale("scale", new Float3(2, 2, 2));
+        cube.setTransform(t);
+        cube.appendSourceParams(new TextureParam("diffuse", new Texture2D("", "orange.jpg")));
+        cube.setRenderState(diffuseRS);
+    }
+
+    private void setupCamera() {
+        Camera camera = mScene.appendNewCamera();
+        camera.setFar(200);
+        camera.setNear(0.1f);
+        camera.setFOV(60);
+        CompoundTransform cameraTransform = mScene.appendNewCompoundTransform();
+        cameraTransform.addTranslate("camera", new Float3(0, 0, 10));
+        camera.setTransform(cameraTransform);
+    }
+
+    private void setupRenderPass() {
+        RenderPass mainPass = mScene.appendNewRenderPass();
+        mainPass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));
+        mainPass.setShouldClearColor(true);
+        mainPass.setClearDepth(1.0f);
+        mainPass.setShouldClearDepth(true);
+        mainPass.setCamera(mScene.getCameras().get(0));
+        ArrayList<RenderableBase> allRender = mScene.getRenderables();
+        for (RenderableBase renderable : allRender) {
+            mainPass.appendRenderable((Renderable)renderable);
+        }
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java
new file mode 100644
index 0000000..053e545
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 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.testapp;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+public class SimpleAppView extends RSSurfaceView {
+
+    public SimpleAppView(Context context) {
+        super(context);
+    }
+
+    private RenderScriptGL mRS;
+    SimpleAppRS mRender;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            sc.setDepth(16, 24);
+            mRS = createRenderScriptGL(sc);
+            mRS.setSurface(holder, w, h);
+            mRender = new SimpleAppRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if (mRS != null) {
+            mRender = null;
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java
new file mode 100644
index 0000000..385a7ab
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java
@@ -0,0 +1,115 @@
+/*
+ * 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.android.testapp;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+import android.view.MenuInflater;
+import android.view.Window;
+import android.net.Uri;
+
+import java.lang.Runtime;
+
+public class TestApp extends Activity {
+
+    private TestAppView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new TestAppView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.resume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.pause();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.loader_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle item selection
+        switch (item.getItemId()) {
+        case R.id.load_model:
+            loadModel();
+            return true;
+        case R.id.use_blur:
+            mView.mRender.toggleBlur();
+            return true;
+        default:
+            return super.onOptionsItemSelected(item);
+        }
+    }
+
+    private static final int FIND_DAE_MODEL = 10;
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode == RESULT_OK) {
+            if (requestCode == FIND_DAE_MODEL) {
+                Uri selectedImageUri = data.getData();
+                Log.e("Selected Path: ", selectedImageUri.getPath());
+                mView.mRender.loadModel(selectedImageUri.getPath());
+            }
+        }
+    }
+
+    public void loadModel() {
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_PICK);
+        intent.setClassName("com.android.testapp",
+                            "com.android.testapp.FileSelector");
+        startActivityForResult(intent, FIND_DAE_MODEL);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java
new file mode 100644
index 0000000..5bd8f0b
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 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.testapp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import com.android.scenegraph.SceneManager;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Allocation.MipmapControl;
+import android.renderscript.Element.Builder;
+import android.renderscript.Font.Style;
+import android.renderscript.Program.TextureType;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+// This is where the scenegraph and the rendered objects are initialized and used
+public class TestAppLoadingScreen {
+
+    private static String TAG = "TestAppLoadingScreen";
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private ScriptC_test_app mScript;
+
+    public TestAppLoadingScreen(RenderScriptGL rs, Resources res) {
+        mRS = rs;
+        mRes = res;
+        // Shows the loading screen with some text
+        renderLoading();
+        // Adds a little 3D bugdroid model to the laoding screen asynchronously.
+        new LoadingScreenLoaderTask().execute();
+    }
+
+    public void showLoadingScreen(boolean show) {
+        if (show) {
+            mRS.bindRootScript(mScript);
+        } else {
+            mRS.bindRootScript(SceneManager.getInstance().getRenderLoop());
+        }
+    }
+
+    // The loading screen has some elements that shouldn't be loaded on the UI thread
+    private class LoadingScreenLoaderTask extends AsyncTask<String, Void, Boolean> {
+        Allocation robotTex;
+        Mesh robotMesh;
+        protected Boolean doInBackground(String... names) {
+            long start = System.currentTimeMillis();
+            robotTex = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
+                                                           MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
+                                                           Allocation.USAGE_GRAPHICS_TEXTURE);
+
+            FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
+            FileA3D.IndexEntry entry = model.getIndexEntry(0);
+            if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
+                robotMesh = entry.getMesh();
+            }
+
+            mScript.set_gPFSBackground(ProgramStore.BLEND_NONE_DEPTH_TEST(mRS));
+
+            ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
+            b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
+                         ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
+            ProgramFragment pfDefault = b.create();
+            pfDefault.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
+            mScript.set_gPFBackground(pfDefault);
+
+            ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
+            ProgramVertexFixedFunction pvDefault = pvb.create();
+            ProgramVertexFixedFunction.Constants va = new ProgramVertexFixedFunction.Constants(mRS);
+            ((ProgramVertexFixedFunction)pvDefault).bindConstants(va);
+            mScript.set_gPVBackground(pvDefault);
+
+            long end = System.currentTimeMillis();
+            Log.v("TIMER", "Loading load time: " + (end - start));
+            return new Boolean(true);
+        }
+
+        protected void onPostExecute(Boolean result) {
+            mScript.set_gRobotTex(robotTex);
+            mScript.set_gRobotMesh(robotMesh);
+        }
+    }
+
+    // Creates a simple script to show a loding screen until everything is initialized
+    // Could also be used to do some custom renderscript work before handing things over
+    // to the scenegraph
+    void renderLoading() {
+        mScript = new ScriptC_test_app(mRS, mRes, R.raw.test_app);
+        mRS.bindRootScript(mScript);
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
new file mode 100644
index 0000000..f159e85
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011-2012 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.testapp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import com.android.scenegraph.*;
+import com.android.scenegraph.SceneManager.SceneLoadedCallback;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.renderscript.*;
+import android.renderscript.Program.TextureType;
+import android.util.Log;
+
+// This is where the scenegraph and the rendered objects are initialized and used
+public class TestAppRS {
+
+    private static String modelName = "orientation_test.dae";
+    private static String TAG = "TestAppRS";
+    private static String mFilePath = "";
+
+    int mWidth;
+    int mHeight;
+
+    boolean mUseBlur;
+
+    TestAppLoadingScreen mLoadingScreen;
+
+    // Used to asynchronously load scene elements like meshes and transform hierarchies
+    SceneLoadedCallback mLoadedCallback = new SceneLoadedCallback() {
+        public void run() {
+            prepareToRender(mLoadedScene);
+        }
+    };
+
+    // Top level class that initializes all the elements needed to use the scene graph
+    SceneManager mSceneManager;
+
+    // Used to move the camera around in the 3D world
+    TouchHandler mTouchHandler;
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+
+    // Shaders
+    private FragmentShader mPaintF;
+    private FragmentShader mLightsF;
+    private FragmentShader mAluminumF;
+    private FragmentShader mPlasticF;
+    private FragmentShader mDiffuseF;
+    private FragmentShader mTextureF;
+    private VertexShader mGenericV;
+
+    Scene mActiveScene;
+
+    // This is a part of the test app, it's used to tests multiple render passes and is toggled
+    // on and off in the menu, off by default
+    void toggleBlur() {
+        mUseBlur = !mUseBlur;
+
+        mActiveScene.clearRenderPasses();
+        initRenderPasses();
+        mActiveScene.initRenderPassRS(mRS, mSceneManager);
+
+        // This is just a hardcoded object in the scene that gets turned on and off for the demo
+        // to make things look a bit better. This could be deleted in the cleanup
+        Renderable plane = (Renderable)mActiveScene.getRenderableByName("pPlaneShape1");
+        if (plane != null) {
+            plane.setVisible(!mUseBlur);
+        }
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mUseBlur = false;
+        mRS = rs;
+        mRes = res;
+        mWidth = width;
+        mHeight = height;
+
+        mTouchHandler = new TouchHandler();
+
+        mSceneManager = SceneManager.getInstance();
+        // Initializes all the RS specific scenegraph elements
+        mSceneManager.initRS(mRS, mRes, mWidth, mHeight);
+
+        mLoadingScreen = new TestAppLoadingScreen(mRS, mRes);
+
+        // Initi renderscript stuff specific to the app. This will need to be abstracted out later.
+        FullscreenBlur.createRenderTargets(mRS, mWidth, mHeight);
+        initPaintShaders();
+
+        // Load a scene to render
+        mSceneManager.loadModel(mFilePath + modelName, mLoadedCallback);
+    }
+
+    // When a new model file is selected from the UI, this function gets called to init everything
+    void loadModel(String path) {
+        mLoadingScreen.showLoadingScreen(true);
+        mActiveScene.destroyRS();
+        mSceneManager.loadModel(path, mLoadedCallback);
+    }
+
+    public void onActionDown(float x, float y) {
+        mTouchHandler.onActionDown(x, y);
+    }
+
+    public void onActionScale(float scale) {
+        mTouchHandler.onActionScale(scale);
+    }
+
+    public void onActionMove(float x, float y) {
+        mTouchHandler.onActionMove(x, y);
+    }
+
+    FragmentShader createFromResource(int id, boolean addCubemap, Type constType) {
+        FragmentShader.Builder fb = new FragmentShader.Builder(mRS);
+        fb.setShaderConst(constType);
+        fb.setShader(mRes, id);
+        fb.addTexture(TextureType.TEXTURE_2D, "diffuse");
+        if (addCubemap) {
+            fb.addShaderTexture(TextureType.TEXTURE_CUBE, "reflection");
+        }
+        FragmentShader pf = fb.create();
+        pf.getProgram().bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS), 0);
+        if (addCubemap) {
+            pf.getProgram().bindSampler(Sampler.CLAMP_LINEAR_MIP_LINEAR(mRS), 1);
+        }
+        return pf;
+    }
+
+    private void initPaintShaders() {
+        mGenericV = SceneManager.getDefaultVS();
+
+        ScriptField_CameraParams camParams = new ScriptField_CameraParams(mRS, 1);
+        Type camParamType = camParams.getAllocation().getType();
+        ScriptField_LightParams lightParams = new ScriptField_LightParams(mRS, 1);
+
+        mPaintF = createFromResource(R.raw.paintf, true, camParamType);
+        // Assign a reflection map
+        TextureCube envCube = new TextureCube("sdcard/scenegraph/", "cube_env.png");
+        mPaintF.appendSourceParams(new TextureParam("reflection", envCube));
+
+        mAluminumF = createFromResource(R.raw.metal, true, camParamType);
+        TextureCube diffCube = new TextureCube("sdcard/scenegraph/", "cube_spec.png");
+        mAluminumF.appendSourceParams(new TextureParam("reflection", diffCube));
+
+        mPlasticF = createFromResource(R.raw.plastic, false, camParamType);
+        mDiffuseF = createFromResource(R.raw.diffuse, false, camParamType);
+        mTextureF = SceneManager.getTextureFS();
+
+        FragmentShader.Builder fb = new FragmentShader.Builder(mRS);
+        fb.setObjectConst(lightParams.getAllocation().getType());
+        fb.setShader(mRes, R.raw.plastic_lights);
+        mLightsF = fb.create();
+
+        FullscreenBlur.initShaders(mRes, mRS);
+    }
+
+    void initRenderPasses() {
+        ArrayList<RenderableBase> allDraw = mActiveScene.getRenderables();
+        int numDraw = allDraw.size();
+
+        if (mUseBlur) {
+            FullscreenBlur.addBlurPasses(mActiveScene, mRS, mTouchHandler.getCamera());
+        }
+
+        RenderPass mainPass = new RenderPass();
+        mainPass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));
+        mainPass.setShouldClearColor(true);
+        mainPass.setClearDepth(1.0f);
+        mainPass.setShouldClearDepth(true);
+        mainPass.setCamera(mTouchHandler.getCamera());
+        for (int i = 0; i < numDraw; i ++) {
+            mainPass.appendRenderable((Renderable)allDraw.get(i));
+        }
+        mActiveScene.appendRenderPass(mainPass);
+
+        if (mUseBlur) {
+            FullscreenBlur.addCompositePass(mActiveScene, mRS, mTouchHandler.getCamera());
+        }
+    }
+
+    private void addShadersToScene() {
+        mActiveScene.appendShader(mPaintF);
+        mActiveScene.appendShader(mLightsF);
+        mActiveScene.appendShader(mAluminumF);
+        mActiveScene.appendShader(mPlasticF);
+        mActiveScene.appendShader(mDiffuseF);
+        mActiveScene.appendShader(mTextureF);
+    }
+
+    public void prepareToRender(Scene s) {
+        mSceneManager.setActiveScene(s);
+        mActiveScene = s;
+        mTouchHandler.init(mActiveScene);
+        addShadersToScene();
+        RenderState plastic = new RenderState(mGenericV, mPlasticF, null, null);
+        RenderState diffuse = new RenderState(mGenericV, mDiffuseF, null, null);
+        RenderState paint = new RenderState(mGenericV, mPaintF, null, null);
+        RenderState aluminum = new RenderState(mGenericV, mAluminumF, null, null);
+        RenderState lights = new RenderState(mGenericV, mLightsF, null, null);
+        RenderState glassTransp = new RenderState(mGenericV, mPaintF,
+                                                  ProgramStore.BLEND_ALPHA_DEPTH_TEST(mRS), null);
+
+        initRenderPasses();
+
+        mActiveScene.assignRenderState(plastic);
+
+        mActiveScene.assignRenderStateToMaterial(diffuse, "lambert2$");
+
+        mActiveScene.assignRenderStateToMaterial(paint, "^Paint");
+        mActiveScene.assignRenderStateToMaterial(paint, "^Carbon");
+        mActiveScene.assignRenderStateToMaterial(paint, "^Glass");
+        mActiveScene.assignRenderStateToMaterial(paint, "^MainGlass");
+
+        mActiveScene.assignRenderStateToMaterial(aluminum, "^Metal");
+        mActiveScene.assignRenderStateToMaterial(aluminum, "^Brake");
+
+        mActiveScene.assignRenderStateToMaterial(glassTransp, "^GlassLight");
+
+        mActiveScene.assignRenderStateToMaterial(lights, "^LightBlinn");
+
+        Renderable plane = (Renderable)mActiveScene.getRenderableByName("pPlaneShape1");
+        if (plane != null) {
+            RenderState texState = new RenderState(mGenericV, mTextureF, null, null);
+            plane.setRenderState(texState);
+            plane.setVisible(!mUseBlur);
+        }
+
+        long start = System.currentTimeMillis();
+        mActiveScene.initRS();
+        long end = System.currentTimeMillis();
+        Log.v("TIMER", "Scene init time: " + (end - start));
+
+        mLoadingScreen.showLoadingScreen(false);
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java
new file mode 100644
index 0000000..687f35b
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java
@@ -0,0 +1,152 @@
+/*
+ * 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.android.testapp;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+public class TestAppView extends RSSurfaceView {
+
+    public TestAppView(Context context) {
+        super(context);
+        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
+    }
+
+    private RenderScriptGL mRS;
+    TestAppRS mRender;
+
+    private ScaleGestureDetector mScaleDetector;
+    private static final int INVALID_POINTER_ID = -1;
+    private int mActivePointerId = INVALID_POINTER_ID;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            sc.setDepth(16, 24);
+            mRS = createRenderScriptGL(sc);
+            mRS.setSurface(holder, w, h);
+            mRender = new TestAppRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if (mRS != null) {
+            mRender = null;
+            mRS = null;
+            destroyRenderScriptGL();
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        // break point at here
+        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        mScaleDetector.onTouchEvent(ev);
+
+        boolean ret = false;
+        float x = ev.getX();
+        float y = ev.getY();
+
+        final int action = ev.getAction();
+
+        switch (action & MotionEvent.ACTION_MASK) {
+        case MotionEvent.ACTION_DOWN: {
+            mRender.onActionDown(x, y);
+            mActivePointerId = ev.getPointerId(0);
+            ret = true;
+            break;
+        }
+        case MotionEvent.ACTION_MOVE: {
+            if (!mScaleDetector.isInProgress()) {
+                mRender.onActionMove(x, y);
+            }
+            mRender.onActionDown(x, y);
+            ret = true;
+            break;
+        }
+
+        case MotionEvent.ACTION_UP: {
+            mActivePointerId = INVALID_POINTER_ID;
+            break;
+        }
+
+        case MotionEvent.ACTION_CANCEL: {
+            mActivePointerId = INVALID_POINTER_ID;
+            break;
+        }
+
+        case MotionEvent.ACTION_POINTER_UP: {
+            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
+                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+            final int pointerId = ev.getPointerId(pointerIndex);
+            if (pointerId == mActivePointerId) {
+                // This was our active pointer going up. Choose a new
+                // active pointer and adjust accordingly.
+                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+                x = ev.getX(newPointerIndex);
+                y = ev.getY(newPointerIndex);
+                mRender.onActionDown(x, y);
+                mActivePointerId = ev.getPointerId(newPointerIndex);
+            }
+            break;
+        }
+        }
+
+        return ret;
+    }
+
+    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+        @Override
+        public boolean onScale(ScaleGestureDetector detector) {
+            mRender.onActionScale(detector.getScaleFactor());
+            return true;
+        }
+    }
+}
+
+
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
new file mode 100644
index 0000000..e272cc5
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
@@ -0,0 +1,109 @@
+/*
+ * 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.android.testapp;
+
+import android.util.Log;
+import android.renderscript.Float3;
+import com.android.scenegraph.*;
+import com.android.scenegraph.CompoundTransform.RotateComponent;
+import com.android.scenegraph.CompoundTransform.TranslateComponent;
+
+public class TouchHandler {
+    private static String TAG = "TouchHandler";
+
+    float mLastX;
+    float mLastY;
+
+    float mRotateXValue;
+    float mRotateYValue;
+    Float3 mDistValue;
+    Float3 mPosValue;
+
+    CompoundTransform mCameraRig;
+    RotateComponent mRotateX;
+    RotateComponent mRotateY;
+    TranslateComponent mDist;
+    TranslateComponent mPosition;
+    Camera mCamera;
+
+    public void init(Scene scene) {
+        // Some initial values for camera position
+        mRotateXValue = -20;
+        mRotateYValue = 45;
+        mDistValue = new Float3(0, 0, 45);
+        mPosValue = new Float3(0, 4, 0);
+
+        // Make a camera transform we can manipulate
+        mCameraRig = scene.appendNewCompoundTransform();
+        mCameraRig.setName("CameraRig");
+
+        mPosition = mCameraRig.addTranslate("Position", mPosValue);
+        mRotateY  = mCameraRig.addRotate("RotateY", new Float3(0, 1, 0), mRotateYValue);
+        mRotateX  = mCameraRig.addRotate("RotateX", new Float3(1, 0, 0), mRotateXValue);
+        mDist     = mCameraRig.addTranslate("Distance", mDistValue);
+
+        mCamera = scene.appendNewCamera();
+        mCamera.setTransform(mCameraRig);
+    }
+
+    public Camera getCamera() {
+        return mCamera;
+    }
+
+    public void onActionDown(float x, float y) {
+        mLastX = x;
+        mLastY = y;
+    }
+
+    public void onActionScale(float scale) {
+        if (mDist == null) {
+            return;
+        }
+        mDistValue.z *= 1.0f / scale;
+        mDistValue.z = Math.max(10.0f, Math.min(mDistValue.z, 150.0f));
+        mDist.setValue(mDistValue);
+    }
+
+    public void onActionMove(float x, float y) {
+        if (mRotateX == null) {
+            return;
+        }
+
+        float dx = mLastX - x;
+        float dy = mLastY - y;
+
+        if (Math.abs(dy) <= 2.0f) {
+            dy = 0.0f;
+        }
+        if (Math.abs(dx) <= 2.0f) {
+            dx = 0.0f;
+        }
+
+        mRotateYValue += dx * 0.25f;
+        mRotateYValue %= 360.0f;
+
+        mRotateXValue  += dy * 0.25f;
+        mRotateXValue  = Math.max(mRotateXValue , -80.0f);
+        mRotateXValue  = Math.min(mRotateXValue , 0.0f);
+
+        mRotateX.setAngle(mRotateXValue);
+        mRotateY.setAngle(mRotateYValue);
+
+        mLastX = x;
+        mLastY = y;
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
new file mode 100644
index 0000000..997a1a7
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
@@ -0,0 +1,86 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.testapp)
+
+#include "rs_graphics.rsh"
+#include "test_app.rsh"
+
+// Making sure these get reflected
+FBlurOffsets *blurExport;
+VShaderInputs *iExport;
+FShaderParams *fConst;
+FShaderLightParams *fConts2;
+VSParams *vConst2;
+VObjectParams *vConst3;
+
+rs_program_vertex gPVBackground;
+rs_program_fragment gPFBackground;
+
+rs_allocation gRobotTex;
+rs_mesh gRobotMesh;
+
+rs_program_store gPFSBackground;
+
+float gRotate;
+
+void init() {
+    gRotate = 0.0f;
+}
+
+static int pos = 50;
+static float gRotateY = 120.0f;
+static float3 gLookAt = 0;
+static float gZoom = 50.0f;
+static void displayLoading() {
+    if (rsIsObject(gRobotTex) && rsIsObject(gRobotMesh)) {
+        rsgBindProgramVertex(gPVBackground);
+        rs_matrix4x4 proj;
+        float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+        rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
+        rsgProgramVertexLoadProjectionMatrix(&proj);
+
+        rsgBindProgramFragment(gPFBackground);
+        rsgBindProgramStore(gPFSBackground);
+        rsgBindTexture(gPFBackground, 0, gRobotTex);
+
+        rs_matrix4x4 matrix;
+        rsMatrixLoadIdentity(&matrix);
+        // Position our models on the screen
+        gRotateY += rsGetDt()*100;
+        rsMatrixTranslate(&matrix, 0, 0, -gZoom);
+        rsMatrixRotate(&matrix, 20.0f, 1.0f, 0.0f, 0.0f);
+        rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
+        rsMatrixScale(&matrix, 0.2f, 0.2f, 0.2f);
+        rsgProgramVertexLoadModelMatrix(&matrix);
+        rsgDrawMesh(gRobotMesh);
+    }
+
+    uint width = rsgGetWidth();
+    uint height = rsgGetHeight();
+    int left = 0, right = 0, top = 0, bottom = 0;
+    const char* text = "Initializing...";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    int centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(text, centeredPos, height / 2 + height / 10);
+}
+
+int root(void) {
+    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgClearDepth(1.0f);
+    displayLoading();
+    return 30;
+}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh
new file mode 100644
index 0000000..5fbcbb2
--- /dev/null
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh
@@ -0,0 +1,52 @@
+// Copyright (C) 2012 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.testapp)
+
+// Helpers
+typedef struct ViewProjParams {
+    rs_matrix4x4 viewProj;
+} VSParams;
+
+typedef struct ModelParams {
+    rs_matrix4x4 model;
+} VObjectParams;
+
+typedef struct CameraParams {
+    float4 cameraPos;
+} FShaderParams;
+
+typedef struct LightParams {
+    float4 lightPos_0;
+    float4 lightColor_0;
+    float4 lightPos_1;
+    float4 lightColor_1;
+    float4 cameraPos;
+    float4 diffuse;
+} FShaderLightParams;
+
+typedef struct BlurOffsets {
+    float blurOffset0;
+    float blurOffset1;
+    float blurOffset2;
+    float blurOffset3;
+} FBlurOffsets;
+
+typedef struct VertexShaderInputs {
+    float4 position;
+    float3 normal;
+    float2 texture0;
+} VShaderInputs;
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
index 2293678..6f56223 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
@@ -67,16 +67,24 @@
         unitTests.add(new UT_primitives(this, mRes, mCtx));
         unitTests.add(new UT_constant(this, mRes, mCtx));
         unitTests.add(new UT_vector(this, mRes, mCtx));
+        unitTests.add(new UT_array_init(this, mRes, mCtx));
         unitTests.add(new UT_rsdebug(this, mRes, mCtx));
         unitTests.add(new UT_rstime(this, mRes, mCtx));
         unitTests.add(new UT_rstypes(this, mRes, mCtx));
         unitTests.add(new UT_alloc(this, mRes, mCtx));
         unitTests.add(new UT_refcount(this, mRes, mCtx));
         unitTests.add(new UT_foreach(this, mRes, mCtx));
+        unitTests.add(new UT_noroot(this, mRes, mCtx));
         unitTests.add(new UT_atomic(this, mRes, mCtx));
         unitTests.add(new UT_struct(this, mRes, mCtx));
         unitTests.add(new UT_math(this, mRes, mCtx));
+        unitTests.add(new UT_mesh(this, mRes, mCtx));
+        unitTests.add(new UT_element(this, mRes, mCtx));
+        unitTests.add(new UT_sampler(this, mRes, mCtx));
+        unitTests.add(new UT_program_store(this, mRes, mCtx));
+        unitTests.add(new UT_program_raster(this, mRes, mCtx));
         unitTests.add(new UT_fp_mad(this, mRes, mCtx));
+
         /*
         unitTests.add(new UnitTest(null, "<Pass>", 1));
         unitTests.add(new UnitTest());
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_array_init.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_array_init.java
new file mode 100644
index 0000000..b98b753
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_array_init.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 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.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_array_init extends UnitTest {
+    private Resources mRes;
+
+    protected UT_array_init(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Array Init", ctx);
+        mRes = res;
+    }
+
+    private void checkInit(ScriptC_array_init s) {
+        float[] fa = s.get_fa();
+        _RS_ASSERT("fa[0] == 1.0", fa[0] == 1.0);
+        _RS_ASSERT("fa[1] == 9.9999f", fa[1] == 9.9999f);
+        _RS_ASSERT("fa[2] == 0", fa[2] == 0);
+        _RS_ASSERT("fa[3] == 0", fa[3] == 0);
+        _RS_ASSERT("fa.length == 4", fa.length == 4);
+
+        double[] da = s.get_da();
+        _RS_ASSERT("da[0] == 7.0", da[0] == 7.0);
+        _RS_ASSERT("da[1] == 8.88888", da[1] == 8.88888);
+        _RS_ASSERT("da.length == 2", da.length == 2);
+
+        byte[] ca = s.get_ca();
+        _RS_ASSERT("ca[0] == 'a'", ca[0] == 'a');
+        _RS_ASSERT("ca[1] == 7", ca[1] == 7);
+        _RS_ASSERT("ca[2] == 'b'", ca[2] == 'b');
+        _RS_ASSERT("ca[3] == 'c'", ca[3] == 'c');
+        _RS_ASSERT("ca.length == 4", ca.length == 4);
+
+        short[] sa = s.get_sa();
+        _RS_ASSERT("sa[0] == 1", sa[0] == 1);
+        _RS_ASSERT("sa[1] == 1", sa[1] == 1);
+        _RS_ASSERT("sa[2] == 2", sa[2] == 2);
+        _RS_ASSERT("sa[3] == 3", sa[3] == 3);
+        _RS_ASSERT("sa.length == 4", sa.length == 4);
+
+        int[] ia = s.get_ia();
+        _RS_ASSERT("ia[0] == 5", ia[0] == 5);
+        _RS_ASSERT("ia[1] == 8", ia[1] == 8);
+        _RS_ASSERT("ia[2] == 0", ia[2] == 0);
+        _RS_ASSERT("ia[3] == 0", ia[3] == 0);
+        _RS_ASSERT("ia.length == 4", ia.length == 4);
+
+        long[] la = s.get_la();
+        _RS_ASSERT("la[0] == 13", la[0] == 13);
+        _RS_ASSERT("la[1] == 21", la[1] == 21);
+        _RS_ASSERT("la.length == 4", la.length == 2);
+
+        long[] lla = s.get_lla();
+        _RS_ASSERT("lla[0] == 34", lla[0] == 34);
+        _RS_ASSERT("lla[1] == 0", lla[1] == 0);
+        _RS_ASSERT("lla[2] == 0", lla[2] == 0);
+        _RS_ASSERT("lla[3] == 0", lla[3] == 0);
+        _RS_ASSERT("lla.length == 4", lla.length == 4);
+
+        boolean[] ba = s.get_ba();
+        _RS_ASSERT("ba[0] == true", ba[0] == true);
+        _RS_ASSERT("ba[1] == false", ba[1] == false);
+        _RS_ASSERT("ba[2] == false", ba[2] == false);
+        _RS_ASSERT("ba.length == 3", ba.length == 3);
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        ScriptC_array_init s = new ScriptC_array_init(pRS, mRes, R.raw.array_init);
+        pRS.setMessageHandler(mRsMessage);
+        checkInit(s);
+        s.invoke_array_init_test();
+        pRS.finish();
+        waitForMessage();
+        pRS.destroy();
+        passTest();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_element.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_element.java
new file mode 100644
index 0000000..3e2a2ca
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_element.java
@@ -0,0 +1,132 @@
+/*
+ * 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.android.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Element.*;
+import android.renderscript.Element.DataKind.*;
+import android.renderscript.Element.DataType.*;
+
+public class UT_element extends UnitTest {
+    private Resources mRes;
+
+    Element simpleElem;
+    Element complexElem;
+
+    final String subElemNames[] = {
+        "subElem0",
+        "subElem1",
+        "subElem2",
+        "arrayElem0",
+        "arrayElem1",
+        "subElem3",
+        "subElem4",
+        "subElem5",
+        "subElem6",
+        "subElem_7",
+    };
+
+    final int subElemArraySizes[] = {
+        1,
+        1,
+        1,
+        2,
+        5,
+        1,
+        1,
+        1,
+        1,
+        1,
+    };
+
+    final int subElemOffsets[] = {
+        0,
+        4,
+        8,
+        12,
+        20,
+        40,
+        44,
+        48,
+        64,
+        80,
+    };
+
+    protected UT_element(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Element", ctx);
+        mRes = res;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_element s) {
+        simpleElem = Element.F32_3(RS);
+        complexElem = ScriptField_ComplexStruct.createElement(RS);
+        s.set_simpleElem(simpleElem);
+        s.set_complexElem(complexElem);
+
+        ScriptField_ComplexStruct data = new ScriptField_ComplexStruct(RS, 1);
+        s.bind_complexStruct(data);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_element s = new ScriptC_element(pRS, mRes, R.raw.element);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_element_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    private void testJavaSide(RenderScript RS) {
+
+        int subElemCount = simpleElem.getSubElementCount();
+        _RS_ASSERT("subElemCount == 0", subElemCount == 0);
+        _RS_ASSERT("simpleElem.getDataKind() == USER",
+                   simpleElem.getDataKind() == DataKind.USER);
+        _RS_ASSERT("simpleElem.getDataType() == FLOAT_32",
+                   simpleElem.getDataType() == DataType.FLOAT_32);
+
+        subElemCount = complexElem.getSubElementCount();
+        _RS_ASSERT("subElemCount == 10", subElemCount == 10);
+        _RS_ASSERT("complexElem.getDataKind() == USER",
+                   complexElem.getDataKind() == DataKind.USER);
+        _RS_ASSERT("complexElemsimpleElem.getDataType() == NONE",
+                   complexElem.getDataType() == DataType.NONE);
+        _RS_ASSERT("complexElem.getSizeBytes() == ScriptField_ComplexStruct.Item.sizeof",
+                   complexElem.getSizeBytes() == ScriptField_ComplexStruct.Item.sizeof);
+
+        for (int i = 0; i < subElemCount; i ++) {
+            _RS_ASSERT("complexElem.getSubElement(i) != null",
+                       complexElem.getSubElement(i) != null);
+            _RS_ASSERT("complexElem.getSubElementName(i).equals(subElemNames[i])",
+                       complexElem.getSubElementName(i).equals(subElemNames[i]));
+            _RS_ASSERT("complexElem.getSubElementArraySize(i) == subElemArraySizes[i]",
+                       complexElem.getSubElementArraySize(i) == subElemArraySizes[i]);
+            _RS_ASSERT("complexElem.getSubElementOffsetBytes(i) == subElemOffsets[i]",
+                       complexElem.getSubElementOffsetBytes(i) == subElemOffsets[i]);
+        }
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testScriptSide(pRS);
+        testJavaSide(pRS);
+        passTest();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach.java
index 1d2555e..04e9270 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_foreach.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-2012 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.
@@ -48,6 +48,9 @@
         pRS.setMessageHandler(mRsMessage);
         initializeGlobals(pRS, s);
         s.forEach_root(A);
+        s.invoke_verify_root();
+        s.forEach_foo(A, A);
+        s.invoke_verify_foo();
         s.invoke_foreach_test();
         pRS.finish();
         waitForMessage();
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_mesh.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_mesh.java
new file mode 100644
index 0000000..0c93702
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_mesh.java
@@ -0,0 +1,75 @@
+/*
+ * 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.android.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Mesh.*;
+
+public class UT_mesh extends UnitTest {
+    private Resources mRes;
+
+    Mesh mesh;
+
+    protected UT_mesh(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Mesh", ctx);
+        mRes = res;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_mesh s) {
+        Allocation vAlloc0 = Allocation.createSized(RS, Element.F32(RS), 10);
+        Allocation vAlloc1 = Allocation.createSized(RS, Element.F32_2(RS), 10);
+
+        Allocation iAlloc0 = Allocation.createSized(RS, Element.I16(RS), 10);
+        Allocation iAlloc2 = Allocation.createSized(RS, Element.I16(RS), 10);
+
+        Mesh.AllocationBuilder mBuilder = new Mesh.AllocationBuilder(RS);
+        mBuilder.addVertexAllocation(vAlloc0);
+        mBuilder.addVertexAllocation(vAlloc1);
+
+        mBuilder.addIndexSetAllocation(iAlloc0, Primitive.POINT);
+        mBuilder.addIndexSetType(Primitive.LINE);
+        mBuilder.addIndexSetAllocation(iAlloc2, Primitive.TRIANGLE);
+
+        s.set_mesh(mBuilder.create());
+        s.set_vertexAlloc0(vAlloc0);
+        s.set_vertexAlloc1(vAlloc1);
+        s.set_indexAlloc0(iAlloc0);
+        s.set_indexAlloc2(iAlloc2);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_mesh s = new ScriptC_mesh(pRS, mRes, R.raw.mesh);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_mesh_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    private void testJavaSide(RenderScript RS) {
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testScriptSide(pRS);
+        testJavaSide(pRS);
+        passTest();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_noroot.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_noroot.java
new file mode 100644
index 0000000..c660fc5
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_noroot.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011-2012 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.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_noroot extends UnitTest {
+    private Resources mRes;
+    private Allocation A;
+
+    protected UT_noroot(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "ForEach (no root)", ctx);
+        mRes = res;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_noroot s) {
+        Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
+        int X = 5;
+        int Y = 7;
+        s.set_dimX(X);
+        s.set_dimY(Y);
+        typeBuilder.setX(X).setY(Y);
+        A = Allocation.createTyped(RS, typeBuilder.create());
+        s.bind_a(A);
+
+        return;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        ScriptC_noroot s = new ScriptC_noroot(pRS, mRes, R.raw.noroot);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.forEach_foo(A, A);
+        s.invoke_verify_foo();
+        s.invoke_noroot_test();
+        pRS.finish();
+        waitForMessage();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_raster.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_raster.java
new file mode 100644
index 0000000..1de4d71
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_raster.java
@@ -0,0 +1,81 @@
+/*
+ * 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.android.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramRaster;
+import android.renderscript.ProgramRaster.CullMode;
+
+public class UT_program_raster extends UnitTest {
+    private Resources mRes;
+
+    ProgramRaster pointSpriteEnabled;
+    ProgramRaster cullMode;
+
+    protected UT_program_raster(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "ProgramRaster", ctx);
+        mRes = res;
+    }
+
+    private ProgramRaster.Builder getDefaultBuilder(RenderScript RS) {
+        ProgramRaster.Builder b = new ProgramRaster.Builder(RS);
+        b.setCullMode(CullMode.BACK);
+        b.setPointSpriteEnabled(false);
+        return b;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_program_raster s) {
+        ProgramRaster.Builder b = getDefaultBuilder(RS);
+        pointSpriteEnabled = b.setPointSpriteEnabled(true).create();
+        b = getDefaultBuilder(RS);
+        cullMode = b.setCullMode(CullMode.FRONT).create();
+
+        s.set_pointSpriteEnabled(pointSpriteEnabled);
+        s.set_cullMode(cullMode);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_program_raster s = new ScriptC_program_raster(pRS, mRes, R.raw.program_raster);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_program_raster_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    private void testJavaSide(RenderScript RS) {
+        _RS_ASSERT("pointSpriteEnabled.getPointSpriteEnabled() == true",
+                    pointSpriteEnabled.getPointSpriteEnabled() == true);
+        _RS_ASSERT("pointSpriteEnabled.getCullMode() == ProgramRaster.CullMode.BACK",
+                    pointSpriteEnabled.getCullMode() == ProgramRaster.CullMode.BACK);
+
+        _RS_ASSERT("cullMode.getPointSpriteEnabled() == false",
+                    cullMode.getPointSpriteEnabled() == false);
+        _RS_ASSERT("cullMode.getCullMode() == ProgramRaster.CullMode.FRONT",
+                    cullMode.getCullMode() == ProgramRaster.CullMode.FRONT);
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testScriptSide(pRS);
+        testJavaSide(pRS);
+        passTest();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_store.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_store.java
new file mode 100644
index 0000000..72a401d
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_program_store.java
@@ -0,0 +1,175 @@
+/*
+ * 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.android.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramStore.BlendDstFunc;
+import android.renderscript.ProgramStore.BlendSrcFunc;
+import android.renderscript.ProgramStore.Builder;
+import android.renderscript.ProgramStore.DepthFunc;
+
+public class UT_program_store extends UnitTest {
+    private Resources mRes;
+
+    ProgramStore ditherEnable;
+    ProgramStore colorRWriteEnable;
+    ProgramStore colorGWriteEnable;
+    ProgramStore colorBWriteEnable;
+    ProgramStore colorAWriteEnable;
+    ProgramStore blendSrc;
+    ProgramStore blendDst;
+    ProgramStore depthWriteEnable;
+    ProgramStore depthFunc;
+
+    protected UT_program_store(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "ProgramStore", ctx);
+        mRes = res;
+    }
+
+    private ProgramStore.Builder getDefaultBuilder(RenderScript RS) {
+        ProgramStore.Builder b = new ProgramStore.Builder(RS);
+        b.setBlendFunc(ProgramStore.BlendSrcFunc.ZERO, ProgramStore.BlendDstFunc.ZERO);
+        b.setColorMaskEnabled(false, false, false, false);
+        b.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        b.setDepthMaskEnabled(false);
+        b.setDitherEnabled(false);
+        return b;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_program_store s) {
+        ProgramStore.Builder b = getDefaultBuilder(RS);
+        ditherEnable = b.setDitherEnabled(true).create();
+
+        b = getDefaultBuilder(RS);
+        colorRWriteEnable = b.setColorMaskEnabled(true,  false, false, false).create();
+
+        b = getDefaultBuilder(RS);
+        colorGWriteEnable = b.setColorMaskEnabled(false, true,  false, false).create();
+
+        b = getDefaultBuilder(RS);
+        colorBWriteEnable = b.setColorMaskEnabled(false, false, true,  false).create();
+
+        b = getDefaultBuilder(RS);
+        colorAWriteEnable = b.setColorMaskEnabled(false, false, false, true).create();
+
+        b = getDefaultBuilder(RS);
+        blendSrc = b.setBlendFunc(ProgramStore.BlendSrcFunc.DST_COLOR,
+                                  ProgramStore.BlendDstFunc.ZERO).create();
+
+        b = getDefaultBuilder(RS);
+        blendDst = b.setBlendFunc(ProgramStore.BlendSrcFunc.ZERO,
+                                  ProgramStore.BlendDstFunc.DST_ALPHA).create();
+
+        b = getDefaultBuilder(RS);
+        depthWriteEnable = b.setDepthMaskEnabled(true).create();
+
+        b = getDefaultBuilder(RS);
+        depthFunc = b.setDepthFunc(ProgramStore.DepthFunc.GREATER).create();
+
+        s.set_ditherEnable(ditherEnable);
+        s.set_colorRWriteEnable(colorRWriteEnable);
+        s.set_colorGWriteEnable(colorGWriteEnable);
+        s.set_colorBWriteEnable(colorBWriteEnable);
+        s.set_colorAWriteEnable(colorAWriteEnable);
+        s.set_blendSrc(blendSrc);
+        s.set_blendDst(blendDst);
+        s.set_depthWriteEnable(depthWriteEnable);
+        s.set_depthFunc(depthFunc);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_program_store s = new ScriptC_program_store(pRS, mRes, R.raw.program_store);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_program_store_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    void checkObject(ProgramStore ps,
+                     boolean depthMask,
+                     DepthFunc df,
+                     BlendSrcFunc bsf,
+                     BlendDstFunc bdf,
+                     boolean R,
+                     boolean G,
+                     boolean B,
+                     boolean A,
+                     boolean dither) {
+        _RS_ASSERT("ps.getDepthMaskEnabled() == depthMask", ps.getDepthMaskEnabled() == depthMask);
+        _RS_ASSERT("ps.getDepthFunc() == df", ps.getDepthFunc() == df);
+        _RS_ASSERT("ps.getBlendSrcFunc() == bsf", ps.getBlendSrcFunc() == bsf);
+        _RS_ASSERT("ps.getBlendDstFunc() == bdf", ps.getBlendDstFunc() == bdf);
+        _RS_ASSERT("ps.getColorMaskREnabled() == R", ps.getColorMaskREnabled() == R);
+        _RS_ASSERT("ps.getColorMaskGEnabled() == G", ps.getColorMaskGEnabled() == G);
+        _RS_ASSERT("ps.getColorMaskBEnabled() == B", ps.getColorMaskBEnabled() == B);
+        _RS_ASSERT("ps.getColorMaskAEnabled() == A", ps.getColorMaskAEnabled() == A);
+        _RS_ASSERT("ps.getDitherEnabled() == dither", ps.getDitherEnabled() == dither);
+    }
+
+    void varyBuilderColorAndDither(ProgramStore.Builder pb,
+                                   boolean depthMask,
+                                   DepthFunc df,
+                                   BlendSrcFunc bsf,
+                                   BlendDstFunc bdf) {
+        for (int r = 0; r <= 1; r++) {
+            boolean isR = (r == 1);
+            for (int g = 0; g <= 1; g++) {
+                boolean isG = (g == 1);
+                for (int b = 0; b <= 1; b++) {
+                    boolean isB = (b == 1);
+                    for (int a = 0; a <= 1; a++) {
+                        boolean isA = (a == 1);
+                        for (int dither = 0; dither <= 1; dither++) {
+                            boolean isDither = (dither == 1);
+                            pb.setDitherEnabled(isDither);
+                            pb.setColorMaskEnabled(isR, isG, isB, isA);
+                            ProgramStore ps = pb.create();
+                            checkObject(ps, depthMask, df, bsf, bdf, isR, isG, isB, isA, isDither);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void testJavaSide(RenderScript RS) {
+        for (int depth = 0; depth <= 1; depth++) {
+            boolean depthMask = (depth == 1);
+            for (DepthFunc df : DepthFunc.values()) {
+                for (BlendSrcFunc bsf : BlendSrcFunc.values()) {
+                    for (BlendDstFunc bdf : BlendDstFunc.values()) {
+                        ProgramStore.Builder b = new ProgramStore.Builder(RS);
+                        b.setDepthFunc(df);
+                        b.setDepthMaskEnabled(depthMask);
+                        b.setBlendFunc(bsf, bdf);
+                        varyBuilderColorAndDither(b, depthMask, df, bsf, bdf);
+                    }
+                }
+            }
+        }
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testJavaSide(pRS);
+        testScriptSide(pRS);
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_sampler.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_sampler.java
new file mode 100644
index 0000000..c328cf6
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_sampler.java
@@ -0,0 +1,150 @@
+/*
+ * 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.android.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Sampler;
+import android.renderscript.Sampler.Value;
+
+public class UT_sampler extends UnitTest {
+    private Resources mRes;
+
+    Sampler minification;
+    Sampler magnification;
+    Sampler wrapS;
+    Sampler wrapT;
+    Sampler anisotropy;
+
+    protected UT_sampler(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Sampler", ctx);
+        mRes = res;
+    }
+
+    private Sampler.Builder getDefaultBuilder(RenderScript RS) {
+        Sampler.Builder b = new Sampler.Builder(RS);
+        b.setMinification(Value.NEAREST);
+        b.setMagnification(Value.NEAREST);
+        b.setWrapS(Value.CLAMP);
+        b.setWrapT(Value.CLAMP);
+        b.setAnisotropy(1.0f);
+        return b;
+    }
+
+    private void initializeGlobals(RenderScript RS, ScriptC_sampler s) {
+        Sampler.Builder b = getDefaultBuilder(RS);
+        b.setMinification(Value.LINEAR_MIP_LINEAR);
+        minification = b.create();
+
+        b = getDefaultBuilder(RS);
+        b.setMagnification(Value.LINEAR);
+        magnification = b.create();
+
+        b = getDefaultBuilder(RS);
+        b.setWrapS(Value.WRAP);
+        wrapS = b.create();
+
+        b = getDefaultBuilder(RS);
+        b.setWrapT(Value.WRAP);
+        wrapT = b.create();
+
+        b = getDefaultBuilder(RS);
+        b.setAnisotropy(8.0f);
+        anisotropy = b.create();
+
+        s.set_minification(minification);
+        s.set_magnification(magnification);
+        s.set_wrapS(wrapS);
+        s.set_wrapT(wrapT);
+        s.set_anisotropy(anisotropy);
+    }
+
+    private void testScriptSide(RenderScript pRS) {
+        ScriptC_sampler s = new ScriptC_sampler(pRS, mRes, R.raw.sampler);
+        pRS.setMessageHandler(mRsMessage);
+        initializeGlobals(pRS, s);
+        s.invoke_sampler_test();
+        pRS.finish();
+        waitForMessage();
+    }
+
+    private void testJavaSide(RenderScript RS) {
+        _RS_ASSERT("minification.getMagnification() == Sampler.Value.NEAREST",
+                    minification.getMagnification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("minification.getMinification() == Sampler.Value.LINEAR_MIP_LINEAR",
+                    minification.getMinification() == Sampler.Value.LINEAR_MIP_LINEAR);
+        _RS_ASSERT("minification.getWrapS() == Sampler.Value.CLAMP",
+                    minification.getWrapS() == Sampler.Value.CLAMP);
+        _RS_ASSERT("minification.getWrapT() == Sampler.Value.CLAMP",
+                    minification.getWrapT() == Sampler.Value.CLAMP);
+        _RS_ASSERT("minification.getAnisotropy() == 1.0f",
+                    minification.getAnisotropy() == 1.0f);
+
+        _RS_ASSERT("magnification.getMagnification() == Sampler.Value.LINEAR",
+                    magnification.getMagnification() == Sampler.Value.LINEAR);
+        _RS_ASSERT("magnification.getMinification() == Sampler.Value.NEAREST",
+                    magnification.getMinification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("magnification.getWrapS() == Sampler.Value.CLAMP",
+                    magnification.getWrapS() == Sampler.Value.CLAMP);
+        _RS_ASSERT("magnification.getWrapT() == Sampler.Value.CLAMP",
+                    magnification.getWrapT() == Sampler.Value.CLAMP);
+        _RS_ASSERT("magnification.getAnisotropy() == 1.0f",
+                    magnification.getAnisotropy() == 1.0f);
+
+        _RS_ASSERT("wrapS.getMagnification() == Sampler.Value.NEAREST",
+                    wrapS.getMagnification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("wrapS.getMinification() == Sampler.Value.NEAREST",
+                    wrapS.getMinification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("wrapS.getWrapS() == Sampler.Value.WRAP",
+                    wrapS.getWrapS() == Sampler.Value.WRAP);
+        _RS_ASSERT("wrapS.getWrapT() == Sampler.Value.CLAMP",
+                    wrapS.getWrapT() == Sampler.Value.CLAMP);
+        _RS_ASSERT("wrapS.getAnisotropy() == 1.0f",
+                    wrapS.getAnisotropy() == 1.0f);
+
+        _RS_ASSERT("wrapT.getMagnification() == Sampler.Value.NEAREST",
+                    wrapT.getMagnification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("wrapT.getMinification() == Sampler.Value.NEAREST",
+                    wrapT.getMinification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("wrapT.getWrapS() == Sampler.Value.CLAMP",
+                    wrapT.getWrapS() == Sampler.Value.CLAMP);
+        _RS_ASSERT("wrapT.getWrapT() == Sampler.Value.WRAP",
+                    wrapT.getWrapT() == Sampler.Value.WRAP);
+        _RS_ASSERT("wrapT.getAnisotropy() == 1.0f",
+                    wrapT.getAnisotropy() == 1.0f);
+
+        _RS_ASSERT("anisotropy.getMagnification() == Sampler.Value.NEAREST",
+                    anisotropy.getMagnification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("anisotropy.getMinification() == Sampler.Value.NEAREST",
+                    anisotropy.getMinification() == Sampler.Value.NEAREST);
+        _RS_ASSERT("anisotropy.getWrapS() == Sampler.Value.CLAMP",
+                    anisotropy.getWrapS() == Sampler.Value.CLAMP);
+        _RS_ASSERT("anisotropy.getWrapT() == Sampler.Value.CLAMP",
+                    anisotropy.getWrapT() == Sampler.Value.CLAMP);
+        _RS_ASSERT("anisotropy.getAnisotropy() == 1.0f",
+                    anisotropy.getAnisotropy() == 8.0f);
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        testScriptSide(pRS);
+        testJavaSide(pRS);
+        passTest();
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UnitTest.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UnitTest.java
index edff83f..fbac124 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/UnitTest.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UnitTest.java
@@ -58,8 +58,8 @@
 
     protected void _RS_ASSERT(String message, boolean b) {
         if(b == false) {
-            result = -1;
             Log.e(name, message + " FAILED");
+            failTest();
         }
     }
 
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/array_init.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/array_init.rs
new file mode 100644
index 0000000..842249a
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/array_init.rs
@@ -0,0 +1,58 @@
+#include "shared.rsh"
+
+// Testing constant array initialization
+float fa[4] = {1.0, 9.9999f};
+double da[2] = {7.0, 8.88888};
+char ca[4] = {'a', 7, 'b', 'c'};
+short sa[4] = {1, 1, 2, 3};
+int ia[4] = {5, 8};
+long la[2] = {13, 21};
+long long lla[4] = {34};
+bool ba[3] = {true, false};
+
+void array_init_test() {
+    bool failed = false;
+
+    _RS_ASSERT(fa[0] == 1.0);
+    _RS_ASSERT(fa[1] == 9.9999f);
+    _RS_ASSERT(fa[2] == 0);
+    _RS_ASSERT(fa[3] == 0);
+
+    _RS_ASSERT(da[0] == 7.0);
+    _RS_ASSERT(da[1] == 8.88888);
+
+    _RS_ASSERT(ca[0] == 'a');
+    _RS_ASSERT(ca[1] == 7);
+    _RS_ASSERT(ca[2] == 'b');
+    _RS_ASSERT(ca[3] == 'c');
+
+    _RS_ASSERT(sa[0] == 1);
+    _RS_ASSERT(sa[1] == 1);
+    _RS_ASSERT(sa[2] == 2);
+    _RS_ASSERT(sa[3] == 3);
+
+    _RS_ASSERT(ia[0] == 5);
+    _RS_ASSERT(ia[1] == 8);
+    _RS_ASSERT(ia[2] == 0);
+    _RS_ASSERT(ia[3] == 0);
+
+    _RS_ASSERT(la[0] == 13);
+    _RS_ASSERT(la[1] == 21);
+
+    _RS_ASSERT(lla[0] == 34);
+    _RS_ASSERT(lla[1] == 0);
+    _RS_ASSERT(lla[2] == 0);
+    _RS_ASSERT(lla[3] == 0);
+
+    _RS_ASSERT(ba[0] == true);
+    _RS_ASSERT(ba[1] == false);
+    _RS_ASSERT(ba[2] == false);
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/element.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/element.rs
new file mode 100644
index 0000000..0c42d84
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/element.rs
@@ -0,0 +1,158 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+rs_element simpleElem;
+rs_element complexElem;
+typedef struct ComplexStruct {
+    float subElem0;
+    float subElem1;
+    int subElem2;
+    float arrayElem0[2];
+    int arrayElem1[5];
+    char subElem3;
+    float subElem4;
+    float2 subElem5;
+    float3 subElem6;
+    float4 subElem_7;
+} ComplexStruct_t;
+
+ComplexStruct_t *complexStruct;
+
+static const char *subElemNames[] = {
+    "subElem0",
+    "subElem1",
+    "subElem2",
+    "arrayElem0",
+    "arrayElem1",
+    "subElem3",
+    "subElem4",
+    "subElem5",
+    "subElem6",
+    "subElem_7",
+};
+
+static uint32_t subElemNamesSizes[] = {
+    8,
+    8,
+    8,
+    10,
+    10,
+    8,
+    8,
+    8,
+    8,
+    9,
+};
+
+static uint32_t subElemArraySizes[] = {
+    1,
+    1,
+    1,
+    2,
+    5,
+    1,
+    1,
+    1,
+    1,
+    1,
+};
+
+static void resetStruct() {
+    uint8_t *bytePtr = (uint8_t*)complexStruct;
+    uint32_t sizeOfStruct = sizeof(*complexStruct);
+    for(uint32_t i = 0; i < sizeOfStruct; i ++) {
+        bytePtr[i] = 0;
+    }
+}
+
+static bool equals(const char *name0, const char * name1, uint32_t len) {
+    for (uint32_t i = 0; i < len; i ++) {
+        if (name0[i] != name1[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool test_element_getters() {
+    bool failed = false;
+
+    uint32_t subElemOffsets[10];
+    uint32_t index = 0;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem0   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem1   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem2   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->arrayElem0 - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->arrayElem1 - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem3   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem4   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem5   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem6   - (uint32_t)complexStruct;
+    subElemOffsets[index++] = (uint32_t)&complexStruct->subElem_7  - (uint32_t)complexStruct;
+
+    uint32_t subElemCount = rsElementGetSubElementCount(simpleElem);
+    _RS_ASSERT(subElemCount == 0);
+    _RS_ASSERT(rsElementGetDataKind(simpleElem) == RS_KIND_USER);
+    _RS_ASSERT(rsElementGetDataType(simpleElem) == RS_TYPE_FLOAT_32);
+    _RS_ASSERT(rsElementGetVectorSize(simpleElem) == 3);
+
+    subElemCount = rsElementGetSubElementCount(complexElem);
+    _RS_ASSERT(subElemCount == 10);
+    _RS_ASSERT(rsElementGetDataKind(complexElem) == RS_KIND_USER);
+    _RS_ASSERT(rsElementGetDataType(complexElem) == RS_TYPE_NONE);
+    _RS_ASSERT(rsElementGetVectorSize(complexElem) == 1);
+    _RS_ASSERT(rsElementGetSizeBytes(complexElem) == sizeof(*complexStruct));
+
+    char buffer[64];
+    for (uint32_t i = 0; i < subElemCount; i ++) {
+        rs_element subElem = rsElementGetSubElement(complexElem, i);
+        _RS_ASSERT(rsIsObject(subElem));
+
+        _RS_ASSERT(rsElementGetSubElementNameLength(complexElem, i) == subElemNamesSizes[i] + 1);
+
+        uint32_t written = rsElementGetSubElementName(complexElem, i, buffer, 64);
+        _RS_ASSERT(written == subElemNamesSizes[i]);
+        _RS_ASSERT(equals(buffer, subElemNames[i], written));
+
+        _RS_ASSERT(rsElementGetSubElementArraySize(complexElem, i) == subElemArraySizes[i]);
+        _RS_ASSERT(rsElementGetSubElementOffsetBytes(complexElem, i) == subElemOffsets[i]);
+    }
+
+    // Tests error checking
+    rs_element subElem = rsElementGetSubElement(complexElem, subElemCount);
+    _RS_ASSERT(!rsIsObject(subElem));
+
+    _RS_ASSERT(rsElementGetSubElementNameLength(complexElem, subElemCount) == 0);
+
+    _RS_ASSERT(rsElementGetSubElementName(complexElem, subElemCount, buffer, 64) == 0);
+    _RS_ASSERT(rsElementGetSubElementName(complexElem, 0, NULL, 64) == 0);
+    _RS_ASSERT(rsElementGetSubElementName(complexElem, 0, buffer, 0) == 0);
+    uint32_t written = rsElementGetSubElementName(complexElem, 0, buffer, 5);
+    _RS_ASSERT(written == 4);
+    _RS_ASSERT(buffer[4] == '\0');
+
+    _RS_ASSERT(rsElementGetSubElementArraySize(complexElem, subElemCount) == 0);
+    _RS_ASSERT(rsElementGetSubElementOffsetBytes(complexElem, subElemCount) == 0);
+
+    if (failed) {
+        rsDebug("test_element_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_element_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void element_test() {
+    bool failed = false;
+    failed |= test_element_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/foreach.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/foreach.rs
index 3ba3eef..ac527b5 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/foreach.rs
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/foreach.rs
@@ -3,12 +3,19 @@
 int *a;
 int dimX;
 int dimY;
+static bool failed = false;
 
 void root(int *out, uint32_t x, uint32_t y) {
     *out = x + y * dimX;
 }
 
-static bool test_foreach_output() {
+void foo(const int *in, int *out, uint32_t x, uint32_t y) {
+    _RS_ASSERT(*in == (x + y * dimX));
+    *out = 99 + x + y * dimX;
+    _RS_ASSERT(*out == (99 + x + y * dimX));
+}
+
+static bool test_root_output() {
     bool failed = false;
     int i, j;
 
@@ -19,19 +26,44 @@
     }
 
     if (failed) {
-        rsDebug("test_foreach_output FAILED", 0);
+        rsDebug("test_root_output FAILED", 0);
     }
     else {
-        rsDebug("test_foreach_output PASSED", 0);
+        rsDebug("test_root_output PASSED", 0);
     }
 
     return failed;
 }
 
-void foreach_test() {
+static bool test_foo_output() {
     bool failed = false;
-    failed |= test_foreach_output();
+    int i, j;
 
+    for (j = 0; j < dimY; j++) {
+        for (i = 0; i < dimX; i++) {
+            _RS_ASSERT(a[i + j * dimX] == (99 + i + j * dimX));
+        }
+    }
+
+    if (failed) {
+        rsDebug("test_foo_output FAILED", 0);
+    }
+    else {
+        rsDebug("test_foo_output PASSED", 0);
+    }
+
+    return failed;
+}
+
+void verify_root() {
+    failed |= test_root_output();
+}
+
+void verify_foo() {
+    failed |= test_foo_output();
+}
+
+void foreach_test() {
     if (failed) {
         rsSendToClientBlocking(RS_MSG_TEST_FAILED);
     }
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/mesh.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/mesh.rs
new file mode 100644
index 0000000..627ab99
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/mesh.rs
@@ -0,0 +1,64 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+rs_mesh mesh;
+rs_allocation vertexAlloc0;
+rs_allocation vertexAlloc1;
+
+rs_allocation indexAlloc0;
+rs_allocation indexAlloc2;
+
+static bool test_mesh_getters() {
+    bool failed = false;
+
+    _RS_ASSERT(rsMeshGetVertexAllocationCount(mesh) == 2);
+    _RS_ASSERT(rsMeshGetPrimitiveCount(mesh) == 3);
+
+    rs_allocation meshV0 = rsMeshGetVertexAllocation(mesh, 0);
+    rs_allocation meshV1 = rsMeshGetVertexAllocation(mesh, 1);
+    rs_allocation meshV2 = rsMeshGetVertexAllocation(mesh, 2);
+    _RS_ASSERT(meshV0.p == vertexAlloc0.p);
+    _RS_ASSERT(meshV1.p == vertexAlloc1.p);
+    _RS_ASSERT(!rsIsObject(meshV2));
+
+    rs_allocation meshI0 = rsMeshGetIndexAllocation(mesh, 0);
+    rs_allocation meshI1 = rsMeshGetIndexAllocation(mesh, 1);
+    rs_allocation meshI2 = rsMeshGetIndexAllocation(mesh, 2);
+    rs_allocation meshI3 = rsMeshGetIndexAllocation(mesh, 3);
+    _RS_ASSERT(meshI0.p == indexAlloc0.p);
+    _RS_ASSERT(!rsIsObject(meshI1));
+    _RS_ASSERT(meshI2.p == indexAlloc2.p);
+    _RS_ASSERT(!rsIsObject(meshI3));
+
+    rs_primitive p0 = rsMeshGetPrimitive(mesh, 0);
+    rs_primitive p1 = rsMeshGetPrimitive(mesh, 1);
+    rs_primitive p2 = rsMeshGetPrimitive(mesh, 2);
+    rs_primitive p3 = rsMeshGetPrimitive(mesh, 3);
+
+    _RS_ASSERT(p0 == RS_PRIMITIVE_POINT);
+    _RS_ASSERT(p1 == RS_PRIMITIVE_LINE);
+    _RS_ASSERT(p2 == RS_PRIMITIVE_TRIANGLE);
+    _RS_ASSERT(p3 == RS_PRIMITIVE_INVALID);
+
+    if (failed) {
+        rsDebug("test_mesh_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_mesh_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void mesh_test() {
+    bool failed = false;
+    failed |= test_mesh_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/noroot.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/noroot.rs
new file mode 100644
index 0000000..33944aa
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/noroot.rs
@@ -0,0 +1,44 @@
+#include "shared.rsh"
+
+int *a;
+int dimX;
+int dimY;
+static bool failed = false;
+
+void foo(const int *in, int *out, uint32_t x, uint32_t y) {
+    *out = 99 + x + y * dimX;
+}
+
+static bool test_foo_output() {
+    bool failed = false;
+    int i, j;
+
+    for (j = 0; j < dimY; j++) {
+        for (i = 0; i < dimX; i++) {
+            _RS_ASSERT(a[i + j * dimX] == (99 + i + j * dimX));
+        }
+    }
+
+    if (failed) {
+        rsDebug("test_foo_output FAILED", 0);
+    }
+    else {
+        rsDebug("test_foo_output PASSED", 0);
+    }
+
+    return failed;
+}
+
+void verify_foo() {
+    failed |= test_foo_output();
+}
+
+void noroot_test() {
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/program_raster.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/program_raster.rs
new file mode 100644
index 0000000..11b8c30
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/program_raster.rs
@@ -0,0 +1,37 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+rs_program_raster pointSpriteEnabled;
+rs_program_raster cullMode;
+
+static bool test_program_raster_getters() {
+    bool failed = false;
+
+    _RS_ASSERT(rsgProgramRasterGetPointSpriteEnabled(pointSpriteEnabled) == true);
+    _RS_ASSERT(rsgProgramRasterGetCullMode(pointSpriteEnabled) == RS_CULL_BACK);
+
+    _RS_ASSERT(rsgProgramRasterGetPointSpriteEnabled(cullMode) == false);
+    _RS_ASSERT(rsgProgramRasterGetCullMode(cullMode) == RS_CULL_FRONT);
+
+    if (failed) {
+        rsDebug("test_program_raster_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_program_raster_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void program_raster_test() {
+    bool failed = false;
+    failed |= test_program_raster_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/program_store.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/program_store.rs
new file mode 100644
index 0000000..3cd8a20
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/program_store.rs
@@ -0,0 +1,128 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+rs_program_store ditherEnable;
+rs_program_store colorRWriteEnable;
+rs_program_store colorGWriteEnable;
+rs_program_store colorBWriteEnable;
+rs_program_store colorAWriteEnable;
+rs_program_store blendSrc;
+rs_program_store blendDst;
+rs_program_store depthWriteEnable;
+rs_program_store depthFunc;
+
+static bool test_program_store_getters() {
+    bool failed = false;
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(depthFunc) == RS_DEPTH_FUNC_GREATER);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(depthFunc) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(depthFunc) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(depthFunc) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(depthWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(depthWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(depthWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(depthWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(depthWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(colorRWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(colorRWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(colorRWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(colorRWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(colorRWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(colorGWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(colorGWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(colorGWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(colorGWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(colorGWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(colorBWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(colorBWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(colorBWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(colorBWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(colorBWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(colorAWriteEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(colorAWriteEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(colorAWriteEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(colorAWriteEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(colorAWriteEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(ditherEnable) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(ditherEnable) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(ditherEnable) == true);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(ditherEnable) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(ditherEnable) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(blendSrc) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(blendSrc) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(blendSrc) == RS_BLEND_SRC_DST_COLOR);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(blendSrc) == RS_BLEND_DST_ZERO);
+
+    _RS_ASSERT(rsgProgramStoreGetDepthFunc(blendDst) == RS_DEPTH_FUNC_ALWAYS);
+    _RS_ASSERT(rsgProgramStoreGetDepthMask(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskR(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskG(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskB(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetColorMaskA(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetDitherEnabled(blendDst) == false);
+    _RS_ASSERT(rsgProgramStoreGetBlendSrcFunc(blendDst) == RS_BLEND_SRC_ZERO);
+    _RS_ASSERT(rsgProgramStoreGetBlendDstFunc(blendDst) == RS_BLEND_DST_DST_ALPHA);
+
+    if (failed) {
+        rsDebug("test_program_store_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_program_store_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void program_store_test() {
+    bool failed = false;
+    failed |= test_program_store_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/sampler.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/sampler.rs
new file mode 100644
index 0000000..ac9a549
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/sampler.rs
@@ -0,0 +1,63 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+rs_sampler minification;
+rs_sampler magnification;
+rs_sampler wrapS;
+rs_sampler wrapT;
+rs_sampler anisotropy;
+
+static bool test_sampler_getters() {
+    bool failed = false;
+
+    _RS_ASSERT(rsgSamplerGetMagnification(minification) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetMinification(minification) == RS_SAMPLER_LINEAR_MIP_LINEAR);
+    _RS_ASSERT(rsgSamplerGetWrapS(minification) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetWrapT(minification) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(minification) == 1.0f);
+
+    _RS_ASSERT(rsgSamplerGetMagnification(magnification) == RS_SAMPLER_LINEAR);
+    _RS_ASSERT(rsgSamplerGetMinification(magnification) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetWrapS(magnification) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetWrapT(magnification) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(magnification) == 1.0f);
+
+    _RS_ASSERT(rsgSamplerGetMagnification(wrapS) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetMinification(wrapS) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetWrapS(wrapS) == RS_SAMPLER_WRAP);
+    _RS_ASSERT(rsgSamplerGetWrapT(wrapS) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(wrapS) == 1.0f);
+
+    _RS_ASSERT(rsgSamplerGetMagnification(wrapT) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetMinification(wrapT) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetWrapS(wrapT) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetWrapT(wrapT) == RS_SAMPLER_WRAP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(wrapT) == 1.0f);
+
+    _RS_ASSERT(rsgSamplerGetMagnification(anisotropy) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetMinification(anisotropy) == RS_SAMPLER_NEAREST);
+    _RS_ASSERT(rsgSamplerGetWrapS(anisotropy) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetWrapT(anisotropy) == RS_SAMPLER_CLAMP);
+    _RS_ASSERT(rsgSamplerGetAnisotropy(anisotropy) == 8.0f);
+
+    if (failed) {
+        rsDebug("test_sampler_getters FAILED", 0);
+    }
+    else {
+        rsDebug("test_sampler_getters PASSED", 0);
+    }
+
+    return failed;
+}
+
+void sampler_test() {
+    bool failed = false;
+    failed |= test_sampler_getters();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tests/SerialChat/Android.mk b/tests/SerialChat/Android.mk
new file mode 100644
index 0000000..a534e1a
--- /dev/null
+++ b/tests/SerialChat/Android.mk
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := SerialChat
+
+include $(BUILD_PACKAGE)
diff --git a/tests/SerialChat/AndroidManifest.xml b/tests/SerialChat/AndroidManifest.xml
new file mode 100644
index 0000000..0efdb58
--- /dev/null
+++ b/tests/SerialChat/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.serialchat">
+
+    <uses-permission android:name="android.permission.SERIAL_PORT"/>
+
+    <application android:label="Serial Chat">
+        <activity android:name="SerialChat" android:label="Serial Chat">
+            <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/tests/SerialChat/res/layout/serial_chat.xml b/tests/SerialChat/res/layout/serial_chat.xml
new file mode 100644
index 0000000..596ecbf
--- /dev/null
+++ b/tests/SerialChat/res/layout/serial_chat.xml
@@ -0,0 +1,47 @@
+<?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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    >
+
+    <ScrollView android:id="@+id/scroll"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        >
+        <TextView android:id="@+id/log"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="25dp"
+            android:textSize="12sp"
+            android:textColor="#ffffffff"
+            />
+    </ScrollView>
+
+    <EditText android:id="@+id/message"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:capitalize="sentences"
+        android:autoText="true"
+        android:singleLine="true"
+        />
+
+</LinearLayout>
+
+
diff --git a/tests/SerialChat/src/com/android/serialchat/SerialChat.java b/tests/SerialChat/src/com/android/serialchat/SerialChat.java
new file mode 100644
index 0000000..faec312
--- /dev/null
+++ b/tests/SerialChat/src/com/android/serialchat/SerialChat.java
@@ -0,0 +1,163 @@
+/*
+ * 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.android.serialchat;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.SerialManager;
+import android.hardware.SerialPort;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.util.Log;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.nio.ByteBuffer;
+import java.io.IOException;
+
+public class SerialChat extends Activity implements Runnable, TextView.OnEditorActionListener {
+
+    private static final String TAG = "SerialChat";
+
+    private TextView mLog;
+    private EditText mEditText;
+    private ByteBuffer mInputBuffer;
+    private ByteBuffer mOutputBuffer;
+    private SerialManager mSerialManager;
+    private SerialPort mSerialPort;
+    private boolean mPermissionRequestPending;
+
+    private static final int MESSAGE_LOG = 1;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mSerialManager = (SerialManager)getSystemService(Context.SERIAL_SERVICE);
+        setContentView(R.layout.serial_chat);
+        mLog = (TextView)findViewById(R.id.log);
+        mEditText = (EditText)findViewById(R.id.message);
+        mEditText.setOnEditorActionListener(this);
+
+        if (false) {
+            mInputBuffer = ByteBuffer.allocateDirect(1024);
+            mOutputBuffer = ByteBuffer.allocateDirect(1024);
+        } else {
+            mInputBuffer = ByteBuffer.allocate(1024);
+            mOutputBuffer = ByteBuffer.allocate(1024);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        String[] ports = mSerialManager.getSerialPorts();
+        if (ports != null && ports.length > 0) {
+            try {
+                mSerialPort = mSerialManager.openSerialPort(ports[0], 115200);
+                if (mSerialPort != null) {
+                    new Thread(this).start();
+                }
+            } catch (IOException e) {
+            }
+        }
+
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+    
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mSerialPort != null) {
+            try {
+                mSerialPort.close();
+            } catch (IOException e) {
+            }
+            mSerialPort = null;
+        }
+        super.onDestroy();
+    }
+
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        if (/* actionId == EditorInfo.IME_ACTION_DONE && */ mSerialPort != null) {
+            try {
+                String text = v.getText().toString();
+                Log.d(TAG, "write: " + text);
+                byte[] bytes = text.getBytes();
+                mOutputBuffer.clear();
+                mOutputBuffer.put(bytes);
+                mSerialPort.write(mOutputBuffer, bytes.length);
+            } catch (IOException e) {
+                Log.e(TAG, "write failed", e);
+            }
+            v.setText("");
+            return true;
+        }
+        Log.d(TAG, "onEditorAction " + actionId + " event: " + event);
+        return false;
+    }
+
+    public void run() {
+        Log.d(TAG, "run");
+        int ret = 0;
+        byte[] buffer = new byte[1024];
+        while (ret >= 0) {
+            try {
+                Log.d(TAG, "calling read");
+                mInputBuffer.clear();
+                ret = mSerialPort.read(mInputBuffer);
+                Log.d(TAG, "read returned " + ret);
+                mInputBuffer.get(buffer, 0, ret);
+            } catch (IOException e) {
+                Log.e(TAG, "read failed", e);
+                break;
+            }
+
+            if (ret > 0) {
+                Message m = Message.obtain(mHandler, MESSAGE_LOG);
+                String text = new String(buffer, 0, ret);
+                Log.d(TAG, "chat: " + text);
+                m.obj = text;
+                mHandler.sendMessage(m);
+            }
+        }
+        Log.d(TAG, "thread out");
+    }
+
+   Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_LOG:
+                    mLog.setText(mLog.getText() + (String)msg.obj);
+                    break;
+             }
+        }
+    };
+}
+
+
diff --git a/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java b/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
index 5f53a9b..1a2dcb9 100644
--- a/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
+++ b/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
@@ -19,12 +19,21 @@
 import com.android.internal.os.RuntimeInit;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessErrorStateInfo;
 import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This smoke test is designed to quickly sniff for any error conditions
@@ -32,53 +41,125 @@
  */
 public class ProcessErrorsTest extends AndroidTestCase {
     
-    private final String TAG = "ProcessErrorsTest";
+    private static final String TAG = "ProcessErrorsTest";
     
     protected ActivityManager mActivityManager;
+    protected PackageManager mPackageManager;
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mActivityManager = (ActivityManager) 
+        mActivityManager = (ActivityManager)
                 getContext().getSystemService(Context.ACTIVITY_SERVICE);
+        mPackageManager = getContext().getPackageManager();
     }
 
     public void testSetUpConditions() throws Exception {
         assertNotNull(mActivityManager);
+        assertNotNull(mPackageManager);
     }
 
     public void testNoProcessErrors() throws Exception {
-        List<ActivityManager.ProcessErrorStateInfo> errList;        
+        final String reportMsg = checkForProcessErrors();
+        if (reportMsg != null) {
+            Log.w(TAG, reportMsg);
+        }
+
+        // report a non-empty list back to the test framework
+        assertNull(reportMsg, reportMsg);
+    }
+
+    private String checkForProcessErrors() throws Exception {
+        List<ProcessErrorStateInfo> errList;
         errList = mActivityManager.getProcessesInErrorState();
         
         // note: this contains information about each process that is currently in an error
         // condition.  if the list is empty (null) then "we're good".  
         
         // if the list is non-empty, then it's useful to report the contents of the list
-        // we'll put a copy in the log, and we'll report it back to the framework via the assert.
         final String reportMsg = reportListContents(errList);
-        if (reportMsg != null) {
-            Log.w(TAG, reportMsg);
-        }
-        
-        // report a non-empty list back to the test framework
-        assertNull(reportMsg, errList);
+        return reportMsg;
     }
-    
+
+    /**
+     * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes
+     * happened while doing so.
+     * <p />
+     * FIXME: Doesn't detect multiple crashing apps properly, since the crash dialog for the
+     * FIXME: first app doesn't go away.
+     */
+    public void testRunAllActivities() throws Exception {
+        final Intent home = new Intent(Intent.ACTION_MAIN);
+        home.addCategory(Intent.CATEGORY_HOME);
+        home.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final Intent launchable = new Intent(Intent.ACTION_MAIN);
+        launchable.addCategory(Intent.CATEGORY_LAUNCHER);
+        final List<ResolveInfo> activities = mPackageManager.queryIntentActivities(launchable, 0);
+        final Set<ProcessError> errSet = new HashSet<ProcessError>();
+
+        for (ResolveInfo info : activities) {
+            Log.i(TAG, String.format("Got %s/%s", info.activityInfo.packageName,
+                    info.activityInfo.name));
+
+            // build an Intent to launch the app
+            final ComponentName component = new ComponentName(info.activityInfo.packageName,
+                    info.activityInfo.name);
+            final Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setComponent(component);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            // launch app, and wait 7 seconds for it to start/settle
+            getContext().startActivity(intent);
+            try {
+                Thread.sleep(7000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+
+            // See if there are any errors
+            Collection<ProcessErrorStateInfo> procs = mActivityManager.getProcessesInErrorState();
+            if (procs != null) {
+                errSet.addAll(ProcessError.fromCollection(procs));
+            }
+
+            // Send the "home" intent and wait 2 seconds for us to get there
+            getContext().startActivity(home);
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+
+        if (!errSet.isEmpty()) {
+            fail(String.format("Got %d errors: %s", errSet.size(),
+                    reportWrappedListContents(errSet)));
+        }
+    }
+
+    private String reportWrappedListContents(Collection<ProcessError> errList) {
+        List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size());
+        for (ProcessError err : errList) {
+            newList.add(err.info);
+        }
+        return reportListContents(newList);
+    }
+
     /**
      * This helper function will dump the actual error reports.
      * 
      * @param errList The error report containing one or more error records.
      * @return Returns a string containing all of the errors.
      */
-    private String reportListContents(List<ActivityManager.ProcessErrorStateInfo> errList) {
+    private String reportListContents(Collection<ProcessErrorStateInfo> errList) {
         if (errList == null) return null;
 
         StringBuilder builder = new StringBuilder();
 
-        Iterator<ActivityManager.ProcessErrorStateInfo> iter = errList.iterator();
+        Iterator<ProcessErrorStateInfo> iter = errList.iterator();
         while (iter.hasNext()) {
-            ActivityManager.ProcessErrorStateInfo entry = iter.next();
+            ProcessErrorStateInfo entry = iter.next();
 
             String condition;
             switch (entry.condition) {
@@ -96,8 +177,77 @@
             builder.append("Process error ").append(condition).append(" ");
             builder.append(" ").append(entry.shortMsg);
             builder.append(" detected in ").append(entry.processName).append(" ").append(entry.tag);
+            builder.append("\n");
         }
         return builder.toString();
     }
-    
+
+    /**
+     * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent
+     * crashes are considered equal).
+     */
+    private static class ProcessError {
+        public final ProcessErrorStateInfo info;
+
+        public ProcessError(ProcessErrorStateInfo newInfo) {
+            info = newInfo;
+        }
+
+        public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in)
+                {
+            List<ProcessError> out = new ArrayList<ProcessError>(in.size());
+            for (ProcessErrorStateInfo info : in) {
+                out.add(new ProcessError(info));
+            }
+            return out;
+        }
+
+        private boolean strEquals(String a, String b) {
+            if ((a == null) && (b == null)) {
+                return true;
+            } else if ((a == null) || (b == null)) {
+                return false;
+            } else {
+                return a.equals(b);
+            }
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other == null) return false;
+            if (!(other instanceof ProcessError)) return false;
+            ProcessError peOther = (ProcessError) other;
+
+            return (info.condition == peOther.info.condition)
+                    && strEquals(info.longMsg, peOther.info.longMsg)
+                    && (info.pid == peOther.info.pid)
+                    && strEquals(info.processName, peOther.info.processName)
+                    && strEquals(info.shortMsg, peOther.info.shortMsg)
+                    && strEquals(info.stackTrace, peOther.info.stackTrace)
+                    && strEquals(info.tag, peOther.info.tag)
+                    && (info.uid == peOther.info.uid);
+        }
+
+        private int hash(Object obj) {
+            if (obj == null) {
+                return 13;
+            } else {
+                return obj.hashCode();
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            int code = 17;
+            code += info.condition;
+            code *= hash(info.longMsg);
+            code += info.pid;
+            code *= hash(info.processName);
+            code *= hash(info.shortMsg);
+            code *= hash(info.stackTrace);
+            code *= hash(info.tag);
+            code += info.uid;
+            return code;
+        }
+    }
 }
diff --git a/tests/SmokeTestApps/Android.mk b/tests/SmokeTestApps/Android.mk
new file mode 100644
index 0000000..3f5f011
--- /dev/null
+++ b/tests/SmokeTestApps/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SmokeTestTriggerApps
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/SmokeTestApps/AndroidManifest.xml b/tests/SmokeTestApps/AndroidManifest.xml
new file mode 100644
index 0000000..0f20107
--- /dev/null
+++ b/tests/SmokeTestApps/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.smoketest.triggers">
+
+    <application android:label="something">
+        <activity android:name=".CrashyApp"
+                  android:label="Test Crashy App">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".CrashyApp2"
+                  android:label="Test Crashy App2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".UnresponsiveApp"
+                  android:label="Test Unresponsive App">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/SmokeTestApps/README b/tests/SmokeTestApps/README
new file mode 100644
index 0000000..04aa366
--- /dev/null
+++ b/tests/SmokeTestApps/README
@@ -0,0 +1,3 @@
+The apps in this folder are intentionally bad-behaving apps that are intended
+to trigger the smoke tests to fail.  They are otherwise not useful.
+
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
new file mode 100644
index 0000000..c11b0f3
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 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.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class CrashyApp extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Crashy Android");
+        setContentView(tv);
+    }
+
+    @Override
+    public void onResume() {
+        ((String) null).length();
+    }
+}
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java
new file mode 100644
index 0000000..3ef5b2b
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class CrashyApp2 extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Other Crashy Android");
+        setContentView(tv);
+    }
+
+
+    @Override
+    public void onResume() {
+        throw new RuntimeException("Two drums and a cymbal fall off a cliff...");
+    }
+}
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java
new file mode 100644
index 0000000..1291897
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 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.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class UnresponsiveApp extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Unresponsive Android");
+        setContentView(tv);
+    }
+
+    @Override
+    public void onResume() {
+        // Attempt to provoke the ire of the ActivityManager
+        while (true) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+    }
+}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index f463a19..ae01c75 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -765,22 +765,70 @@
             }
         },
 
-        new Test("System priority notification") {
+        new Test("PRIORITY_HIGH") {
             public void run() {
                 Notification n = new Notification.Builder(NotificationTestList.this)
-                    .setSmallIcon(R.drawable.notification1)
-                    .setContentTitle("System priority")
+                    .setSmallIcon(R.drawable.notification5)
+                    .setContentTitle("High priority")
                     .setContentText("This should appear before all others")
+                    .setPriority(Notification.PRIORITY_HIGH)
                     .getNotification();
 
                 int[] idOut = new int[1];
                 try {
                     INotificationManager directLine = mNM.getService();
-                    directLine.enqueueNotificationWithTagPriority(
+                    directLine.enqueueNotificationWithTag(
+                            getPackageName(),
+                            null, 
+                            100, 
+                            n,
+                            idOut);
+                } catch (android.os.RemoteException ex) {
+                    // oh well
+                }
+            }
+        },
+
+        new Test("PRIORITY_MAX") {
+            public void run() {
+                Notification n = new Notification.Builder(NotificationTestList.this)
+                    .setSmallIcon(R.drawable.notification9)
+                    .setContentTitle("MAX priority")
+                    .setContentText("This might appear as an intruder alert")
+                    .setPriority(Notification.PRIORITY_MAX)
+                    .getNotification();
+
+                int[] idOut = new int[1];
+                try {
+                    INotificationManager directLine = mNM.getService();
+                    directLine.enqueueNotificationWithTag(
+                            getPackageName(),
+                            null, 
+                            200, 
+                            n,
+                            idOut);
+                } catch (android.os.RemoteException ex) {
+                    // oh well
+                }
+            }
+        },
+
+        new Test("PRIORITY_MIN") {
+            public void run() {
+                Notification n = new Notification.Builder(NotificationTestList.this)
+                    .setSmallIcon(R.drawable.notification0)
+                    .setContentTitle("MIN priority")
+                    .setContentText("You should not see this")
+                    .setPriority(Notification.PRIORITY_MIN)
+                    .getNotification();
+
+                int[] idOut = new int[1];
+                try {
+                    INotificationManager directLine = mNM.getService();
+                    directLine.enqueueNotificationWithTag(
                             getPackageName(),
                             null, 
                             1, 
-                            StatusBarNotification.PRIORITY_SYSTEM,
                             n,
                             idOut);
                 } catch (android.os.RemoteException ex) {
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index 31e2ec5..88794c2 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := backup_helper_test
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SHARED_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES := libandroidfw libutils
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/tests/backup/backup_helper_test.cpp b/tests/backup/backup_helper_test.cpp
index 04358ad..b5f6ff5 100644
--- a/tests/backup/backup_helper_test.cpp
+++ b/tests/backup/backup_helper_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <utils/BackupHelpers.h>
+#include <androidfw/BackupHelpers.h>
 
 #include <stdio.h>
 #include <string.h>
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 1c653e1..5924952 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -7,14 +7,14 @@
 #define __AAPT_ASSETS_H
 
 #include <stdlib.h>
-#include <utils/AssetManager.h>
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
 #include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/ResourceTypes.h>
+#include <utils/RefBase.h>
 #include <utils/SortedVector.h>
 #include <utils/String8.h>
+#include <utils/String8.h>
 #include <utils/Vector.h>
-#include <utils/RefBase.h>
 #include "ZipFile.h"
 
 #include "Bundle.h"
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index cb55a9c..d0a81dc 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -38,6 +38,7 @@
 #LOCAL_WHOLE_STATIC_LIBRARIES := 
 LOCAL_STATIC_LIBRARIES := \
 	libhost \
+	libandroidfw \
 	libutils \
 	libcutils \
 	libexpat \
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index ffbe875..6402e3c 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -8,7 +8,7 @@
 
 #include "Images.h"
 
-#include <utils/ResourceTypes.h>
+#include <androidfw/ResourceTypes.h>
 #include <utils/ByteOrder.h>
 
 #include <png.h>
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 7a0499c..0c0b2ea 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -9,8 +9,8 @@
 #include "XMLNode.h"
 #include "ResourceFilter.h"
 
+#include <androidfw/ResourceTypes.h>
 #include <utils/ByteOrder.h>
-#include <utils/ResourceTypes.h>
 #include <stdarg.h>
 
 #define NOISY(x) //x
diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h
index 255bdbf..86044ed 100644
--- a/tools/aapt/StringPool.h
+++ b/tools/aapt/StringPool.h
@@ -10,7 +10,7 @@
 #include "Main.h"
 #include "AaptAssets.h"
 
-#include <utils/ResourceTypes.h>
+#include <androidfw/ResourceTypes.h>
 #include <utils/String16.h>
 #include <utils/TextOutput.h>
 
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 95a68d1..8d7acee 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -21,6 +21,7 @@
 
 const char* const RESOURCES_ROOT_NAMESPACE = "http://schemas.android.com/apk/res/";
 const char* const RESOURCES_ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android";
+const char* const RESOURCES_AUTO_PACKAGE_NAMESPACE = "http://schemas.android.com/apk/res-auto";
 const char* const RESOURCES_ROOT_PRV_NAMESPACE = "http://schemas.android.com/apk/prv/res/";
 
 const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
@@ -44,16 +45,21 @@
 }
 
 static const String16 RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE);
+static const String16 RESOURCES_PREFIX_AUTO_PACKAGE(RESOURCES_AUTO_PACKAGE_NAMESPACE);
 static const String16 RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE);
 static const String16 RESOURCES_TOOLS_NAMESPACE("http://schemas.android.com/tools");
 
-String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic)
+String16 getNamespaceResourcePackage(String16 appPackage, String16 namespaceUri, bool* outIsPublic)
 {
     //printf("%s starts with %s?\n", String8(namespaceUri).string(),
     //       String8(RESOURCES_PREFIX).string());
     size_t prefixSize;
     bool isPublic = true;
-    if (namespaceUri.startsWith(RESOURCES_PREFIX)) {
+    if(namespaceUri.startsWith(RESOURCES_PREFIX_AUTO_PACKAGE)) {
+        NOISY(printf("Using default application package: %s -> %s\n", String8(namespaceUri).string(), String8(appPackage).string()));
+        isPublic = true;
+        return appPackage;
+    } else if (namespaceUri.startsWith(RESOURCES_PREFIX)) {
         prefixSize = RESOURCES_PREFIX.size();
     } else if (namespaceUri.startsWith(RESOURCES_PRV_PREFIX)) {
         isPublic = false;
@@ -926,7 +932,7 @@
             const attribute_entry& e = mAttributes.itemAt(i);
             if (e.ns.size() <= 0) continue;
             bool nsIsPublic;
-            String16 pkg(getNamespaceResourcePackage(e.ns, &nsIsPublic));
+            String16 pkg(getNamespaceResourcePackage(String16(assets->getPackage()), e.ns, &nsIsPublic));
             NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n",
                     String8(getElementName()).string(),
                     String8(e.name).string(),
diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp
index 0705be3..8057068 100644
--- a/tools/aapt/ZipFile.cpp
+++ b/tools/aapt/ZipFile.cpp
@@ -20,7 +20,7 @@
 
 #define LOG_TAG "zip"
 
-#include <utils/ZipUtils.h>
+#include <androidfw/ZipUtils.h>
 #include <utils/Log.h>
 
 #include "ZipFile.h"
diff --git a/tools/aidl/AST.cpp b/tools/aidl/AST.cpp
index 752ef7c..bfa6765 100755
--- a/tools/aidl/AST.cpp
+++ b/tools/aidl/AST.cpp
@@ -111,6 +111,21 @@
     fprintf(to, "%s", this->value.c_str());
 }
 
+StringLiteralExpression::StringLiteralExpression(const string& v)
+    :value(v)
+{
+}
+
+StringLiteralExpression::~StringLiteralExpression()
+{
+}
+
+void
+StringLiteralExpression::Write(FILE* to)
+{
+    fprintf(to, "\"%s\"", this->value.c_str());
+}
+
 Variable::Variable()
     :type(NULL),
      name(),
@@ -277,6 +292,17 @@
 {
 }
 
+MethodCall::MethodCall(const string& n, int argc = 0, ...)
+    :obj(NULL),
+     clazz(NULL),
+     name(n)
+{
+  va_list args;
+  va_start(args, argc);
+  init(argc, args);
+  va_end(args);
+}
+
 MethodCall::MethodCall(Expression* o, const string& n)
     :obj(o),
      clazz(NULL),
@@ -367,11 +393,29 @@
 {
 }
 
+NewExpression::NewExpression(Type* t, int argc = 0, ...)
+    :type(t)
+{
+  va_list args;
+  va_start(args, argc);
+  init(argc, args);
+  va_end(args);
+}
+
 NewExpression::~NewExpression()
 {
 }
 
 void
+NewExpression::init(int n, va_list args)
+{
+    for (int i=0; i<n; i++) {
+        Expression* expression = (Expression*)va_arg(args, void*);
+        this->arguments.push_back(expression);
+    }
+}
+
+void
 NewExpression::Write(FILE* to)
 {
     fprintf(to, "new %s(", this->type->InstantiableName().c_str());
@@ -636,6 +680,20 @@
     fprintf(to, "}\n");
 }
 
+Break::Break()
+{
+}
+
+Break::~Break()
+{
+}
+
+void
+Break::Write(FILE* to)
+{
+    fprintf(to, "break;\n");
+}
+
 Method::Method()
     :ClassElement(),
      modifiers(0),
@@ -678,7 +736,7 @@
         fprintf(to, "%s\n", this->comment.c_str());
     }
 
-    WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL | OVERRIDE);
+    WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | ABSTRACT | FINAL | OVERRIDE);
 
     if (this->returnType != NULL) {
         string dim;
diff --git a/tools/aidl/AST.h b/tools/aidl/AST.h
index 3156356..ead5e7a 100755
--- a/tools/aidl/AST.h
+++ b/tools/aidl/AST.h
@@ -54,6 +54,16 @@
     virtual void Write(FILE* to);
 };
 
+// TODO: also escape the contents.  not needed for now
+struct StringLiteralExpression : public Expression
+{
+    string value;
+
+    StringLiteralExpression(const string& value);
+    virtual ~StringLiteralExpression();
+    virtual void Write(FILE* to);
+};
+
 struct Variable : public Expression
 {
     Type* type;
@@ -104,7 +114,7 @@
     virtual void Write(FILE* to) = 0;
 };
 
-struct StatementBlock
+struct StatementBlock : public Statement
 {
     vector<Statement*> statements;
 
@@ -146,6 +156,7 @@
     vector<string> exceptions;
 
     MethodCall(const string& name);
+    MethodCall(const string& name, int argc, ...);
     MethodCall(Expression* obj, const string& name);
     MethodCall(Type* clazz, const string& name);
     MethodCall(Expression* obj, const string& name, int argc, ...);
@@ -174,8 +185,12 @@
     vector<Expression*> arguments;
 
     NewExpression(Type* type);
+    NewExpression(Type* type, int argc, ...);
     virtual ~NewExpression();
     virtual void Write(FILE* to);
+
+private:
+    void init(int n, va_list args);
 };
 
 struct NewArrayExpression : public Expression
@@ -292,6 +307,13 @@
     virtual void Write(FILE* to);
 };
 
+struct Break : public Statement
+{
+    Break();
+    virtual ~Break();
+    virtual void Write(FILE* to);
+};
+
 struct Method : public ClassElement
 {
     string comment;
diff --git a/tools/aidl/Android.mk b/tools/aidl/Android.mk
index 2ad0728..77d46ab 100644
--- a/tools/aidl/Android.mk
+++ b/tools/aidl/Android.mk
@@ -17,7 +17,9 @@
 	search_path.cpp \
 	AST.cpp \
 	Type.cpp \
-	generate_java.cpp
+	generate_java.cpp \
+	generate_java_binder.cpp \
+	generate_java_rpc.cpp
 
 LOCAL_CFLAGS := -g
 LOCAL_MODULE := aidl
diff --git a/tools/aidl/Type.cpp b/tools/aidl/Type.cpp
index 6b69864..d572af6 100755
--- a/tools/aidl/Type.cpp
+++ b/tools/aidl/Type.cpp
@@ -11,6 +11,7 @@
 Type* FLOAT_TYPE;
 Type* DOUBLE_TYPE;
 Type* STRING_TYPE;
+Type* OBJECT_TYPE;
 Type* CHAR_SEQUENCE_TYPE;
 Type* TEXT_UTILS_TYPE;
 Type* REMOTE_EXCEPTION_TYPE;
@@ -21,9 +22,13 @@
 Type* BINDER_PROXY_TYPE;
 Type* PARCEL_TYPE;
 Type* PARCELABLE_INTERFACE_TYPE;
+Type* CONTEXT_TYPE;
 Type* MAP_TYPE;
 Type* LIST_TYPE;
 Type* CLASSLOADER_TYPE;
+Type* RPC_DATA_TYPE;
+Type* RPC_ERROR_TYPE;
+Type* EVENT_FAKE_TYPE;
 
 Expression* NULL_VALUE;
 Expression* THIS_VALUE;
@@ -34,38 +39,48 @@
 void
 register_base_types()
 {
-    VOID_TYPE = new BasicType("void", "XXX", "XXX", "XXX", "XXX", "XXX");
+    VOID_TYPE = new BasicType("void",
+            "XXX", "XXX", "XXX", "XXX", "XXX",
+            "XXX", "XXX", "XXX", "XXX", "XXX");
     NAMES.Add(VOID_TYPE);
 
     BOOLEAN_TYPE = new BooleanType();
     NAMES.Add(BOOLEAN_TYPE);
 
-    BYTE_TYPE = new BasicType("byte", "writeByte", "readByte",
-                "writeByteArray", "createByteArray", "readByteArray");
+    BYTE_TYPE = new BasicType("byte",
+            "writeByte", "readByte", "writeByteArray", "createByteArray", "readByteArray",
+            "putByte", "getByte", "putByteArray", "createByteArray", "getByteArray");
     NAMES.Add(BYTE_TYPE);
 
     CHAR_TYPE = new CharType();
     NAMES.Add(CHAR_TYPE);
 
-    INT_TYPE = new BasicType("int", "writeInt", "readInt",
-                "writeIntArray", "createIntArray", "readIntArray");
+    INT_TYPE = new BasicType("int",
+            "writeInt", "readInt", "writeIntArray", "createIntArray", "readIntArray",
+            "putInteger", "getInteger", "putIntegerArray", "createIntegerArray", "getIntegerArray");
     NAMES.Add(INT_TYPE);
 
-    LONG_TYPE = new BasicType("long", "writeLong", "readLong",
-                "writeLongArray", "createLongArray", "readLongArray");
+    LONG_TYPE = new BasicType("long",
+            "writeLong", "readLong", "writeLongArray", "createLongArray", "readLongArray",
+            "putLong", "getLong", "putLongArray", "createLongArray", "getLongArray");
     NAMES.Add(LONG_TYPE);
 
-    FLOAT_TYPE = new BasicType("float", "writeFloat", "readFloat",
-                "writeFloatArray", "createFloatArray", "readFloatArray");
+    FLOAT_TYPE = new BasicType("float",
+            "writeFloat", "readFloat", "writeFloatArray", "createFloatArray", "readFloatArray",
+            "putFloat", "getFloat", "putFloatArray", "createFloatArray", "getFloatArray");
     NAMES.Add(FLOAT_TYPE);
 
-    DOUBLE_TYPE = new BasicType("double", "writeDouble", "readDouble",
-                "writeDoubleArray", "createDoubleArray", "readDoubleArray");
+    DOUBLE_TYPE = new BasicType("double",
+            "writeDouble", "readDouble", "writeDoubleArray", "createDoubleArray", "readDoubleArray",
+            "putDouble", "getDouble", "putDoubleArray", "createDoubleArray", "getDoubleArray");
     NAMES.Add(DOUBLE_TYPE);
 
     STRING_TYPE = new StringType();
     NAMES.Add(STRING_TYPE);
 
+    OBJECT_TYPE = new Type("java.lang", "Object", Type::BUILT_IN, false, false, false);
+    NAMES.Add(OBJECT_TYPE);
+
     CHAR_SEQUENCE_TYPE = new CharSequenceType();
     NAMES.Add(CHAR_SEQUENCE_TYPE);
 
@@ -75,8 +90,7 @@
     LIST_TYPE = new ListType();
     NAMES.Add(LIST_TYPE);
 
-    TEXT_UTILS_TYPE = new Type("android.text", "TextUtils",
-                                    Type::BUILT_IN, false, false);
+    TEXT_UTILS_TYPE = new Type("android.text", "TextUtils", Type::BUILT_IN, false, false, false);
     NAMES.Add(TEXT_UTILS_TYPE);
 
     REMOTE_EXCEPTION_TYPE = new RemoteExceptionType();
@@ -103,6 +117,19 @@
     PARCELABLE_INTERFACE_TYPE = new ParcelableInterfaceType();
     NAMES.Add(PARCELABLE_INTERFACE_TYPE);
 
+    CONTEXT_TYPE = new Type("android.content", "Context", Type::BUILT_IN, false, false, false);
+    NAMES.Add(CONTEXT_TYPE);
+
+    RPC_DATA_TYPE = new RpcDataType();
+    NAMES.Add(RPC_DATA_TYPE);
+
+    RPC_ERROR_TYPE = new UserDataType("android.support.place.rpc", "RpcError",
+                                    true, __FILE__, __LINE__);
+    NAMES.Add(RPC_ERROR_TYPE);
+
+    EVENT_FAKE_TYPE = new Type("event", Type::BUILT_IN, false, false, false);
+    NAMES.Add(EVENT_FAKE_TYPE);
+
     CLASSLOADER_TYPE = new ClassLoaderType();
     NAMES.Add(CLASSLOADER_TYPE);
 
@@ -129,27 +156,30 @@
 
 // ================================================================
 
-Type::Type(const string& name, int kind, bool canWriteToParcel, bool canBeOut)
+Type::Type(const string& name, int kind, bool canWriteToParcel, bool canWriteToRpcData,
+        bool canBeOut)
     :m_package(),
      m_name(name),
      m_declFile(""),
      m_declLine(-1),
      m_kind(kind),
      m_canWriteToParcel(canWriteToParcel),
+     m_canWriteToRpcData(canWriteToRpcData),
      m_canBeOut(canBeOut)
 {
     m_qualifiedName = name;
 }
 
 Type::Type(const string& package, const string& name,
-            int kind, bool canWriteToParcel, bool canBeOut,
-            const string& declFile, int declLine)
+            int kind, bool canWriteToParcel, bool canWriteToRpcData,
+            bool canBeOut, const string& declFile, int declLine)
     :m_package(package),
      m_name(name),
      m_declFile(declFile),
      m_declLine(declLine),
      m_kind(kind),
      m_canWriteToParcel(canWriteToParcel),
+     m_canWriteToRpcData(canWriteToRpcData),
      m_canBeOut(canBeOut)
 {
     if (package.length() > 0) {
@@ -182,6 +212,12 @@
 }
 
 string
+Type::RpcCreatorName() const
+{
+    return "";
+}
+
+string
 Type::InstantiableName() const
 {
     return QualifiedName();
@@ -244,6 +280,26 @@
 }
 
 void
+Type::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, int flags)
+{
+    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
+            __FILE__, __LINE__, m_qualifiedName.c_str());
+    addTo->Add(new LiteralExpression("/* WriteToRpcData error "
+                + m_qualifiedName + " */"));
+}
+
+void
+Type::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
+        Variable** cl)
+{
+    fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
+            __FILE__, __LINE__, m_qualifiedName.c_str());
+    addTo->Add(new LiteralExpression("/* ReadFromRpcData error "
+                + m_qualifiedName + " */"));
+}
+
+void
 Type::SetQualifiedName(const string& qualified)
 {
     m_qualifiedName = qualified;
@@ -264,29 +320,35 @@
 
 // ================================================================
 
-BasicType::BasicType(const string& name, const string& marshallMethod,
-                     const string& unmarshallMethod,
-                     const string& writeArray, const string& createArray,
-                     const string& readArray)
-    :Type(name, BUILT_IN, true, false),
-     m_marshallMethod(marshallMethod),
-     m_unmarshallMethod(unmarshallMethod),
-     m_writeArrayMethod(writeArray),
-     m_createArrayMethod(createArray),
-     m_readArrayMethod(readArray)
+BasicType::BasicType(const string& name, const string& marshallParcel,
+          const string& unmarshallParcel, const string& writeArrayParcel,
+          const string& createArrayParcel, const string& readArrayParcel,
+          const string& marshallRpc, const string& unmarshallRpc,
+          const string& writeArrayRpc, const string& createArrayRpc, const string& readArrayRpc)
+    :Type(name, BUILT_IN, true, true, false),
+     m_marshallParcel(marshallParcel),
+     m_unmarshallParcel(unmarshallParcel),
+     m_writeArrayParcel(writeArrayParcel),
+     m_createArrayParcel(createArrayParcel),
+     m_readArrayParcel(readArrayParcel),
+     m_marshallRpc(marshallRpc),
+     m_unmarshallRpc(unmarshallRpc),
+     m_writeArrayRpc(writeArrayRpc),
+     m_createArrayRpc(createArrayRpc),
+     m_readArrayRpc(readArrayRpc)
 {
 }
 
 void
 BasicType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
 {
-    addTo->Add(new MethodCall(parcel, m_marshallMethod, 1, v));
+    addTo->Add(new MethodCall(parcel, m_marshallParcel, 1, v));
 }
 
 void
 BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
-    addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallMethod)));
+    addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallParcel)));
 }
 
 bool
@@ -298,27 +360,40 @@
 void
 BasicType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
 {
-    addTo->Add(new MethodCall(parcel, m_writeArrayMethod, 1, v));
+    addTo->Add(new MethodCall(parcel, m_writeArrayParcel, 1, v));
 }
 
 void
 BasicType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                             Variable* parcel, Variable**)
 {
-    addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayMethod)));
+    addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayParcel)));
 }
 
 void
 BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
-    addTo->Add(new MethodCall(parcel, m_readArrayMethod, 1, v));
+    addTo->Add(new MethodCall(parcel, m_readArrayParcel, 1, v));
 }
 
+void
+BasicType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, int flags)
+{
+    addTo->Add(new MethodCall(data, m_marshallRpc, 2, k, v));
+}
+
+void
+BasicType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
+        Variable** cl)
+{
+    addTo->Add(new Assignment(v, new MethodCall(data, m_unmarshallRpc, 1, k)));
+}
 
 // ================================================================
 
 BooleanType::BooleanType()
-    :Type("boolean", BUILT_IN, true, false)
+    :Type("boolean", BUILT_IN, true, true, false)
 {
 }
 
@@ -362,11 +437,24 @@
     addTo->Add(new MethodCall(parcel, "readBooleanArray", 1, v));
 }
 
+void
+BooleanType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, int flags)
+{
+    addTo->Add(new MethodCall(data, "putBoolean", 2, k, v));
+}
+
+void
+BooleanType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
+        Variable** cl)
+{
+    addTo->Add(new Assignment(v, new MethodCall(data, "getBoolean", 1, k)));
+}
 
 // ================================================================
 
 CharType::CharType()
-    :Type("char", BUILT_IN, true, false)
+    :Type("char", BUILT_IN, true, true, false)
 {
 }
 
@@ -408,10 +496,24 @@
     addTo->Add(new MethodCall(parcel, "readCharArray", 1, v));
 }
 
+void
+CharType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, int flags)
+{
+    addTo->Add(new MethodCall(data, "putChar", 2, k, v));
+}
+
+void
+CharType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
+        Variable** cl)
+{
+    addTo->Add(new Assignment(v, new MethodCall(data, "getChar", 1, k)));
+}
+
 // ================================================================
 
 StringType::StringType()
-    :Type("java.lang", "String", BUILT_IN, true, false)
+    :Type("java.lang", "String", BUILT_IN, true, true, false)
 {
 }
 
@@ -458,10 +560,24 @@
     addTo->Add(new MethodCall(parcel, "readStringArray", 1, v));
 }
 
+void
+StringType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, int flags)
+{
+    addTo->Add(new MethodCall(data, "putString", 2, k, v));
+}
+
+void
+StringType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, Variable**)
+{
+    addTo->Add(new Assignment(v, new MethodCall(data, "getString", 1, k)));
+}
+
 // ================================================================
 
 CharSequenceType::CharSequenceType()
-    :Type("java.lang", "CharSequence", BUILT_IN, true, false)
+    :Type("java.lang", "CharSequence", BUILT_IN, true, true, false)
 {
 }
 
@@ -521,7 +637,7 @@
 // ================================================================
 
 RemoteExceptionType::RemoteExceptionType()
-    :Type("android.os", "RemoteException", BUILT_IN, false, false)
+    :Type("android.os", "RemoteException", BUILT_IN, false, false, false)
 {
 }
 
@@ -540,7 +656,7 @@
 // ================================================================
 
 RuntimeExceptionType::RuntimeExceptionType()
-    :Type("java.lang", "RuntimeException", BUILT_IN, false, false)
+    :Type("java.lang", "RuntimeException", BUILT_IN, false, false, false)
 {
 }
 
@@ -560,7 +676,7 @@
 // ================================================================
 
 IBinderType::IBinderType()
-    :Type("android.os", "IBinder", BUILT_IN, true, false)
+    :Type("android.os", "IBinder", BUILT_IN, true, false, false)
 {
 }
 
@@ -599,7 +715,7 @@
 // ================================================================
 
 IInterfaceType::IInterfaceType()
-    :Type("android.os", "IInterface", BUILT_IN, false, false)
+    :Type("android.os", "IInterface", BUILT_IN, false, false, false)
 {
 }
 
@@ -619,7 +735,7 @@
 // ================================================================
 
 BinderType::BinderType()
-    :Type("android.os", "Binder", BUILT_IN, false, false)
+    :Type("android.os", "Binder", BUILT_IN, false, false, false)
 {
 }
 
@@ -640,7 +756,7 @@
 // ================================================================
 
 BinderProxyType::BinderProxyType()
-    :Type("android.os", "BinderProxy", BUILT_IN, false, false)
+    :Type("android.os", "BinderProxy", BUILT_IN, false, false, false)
 {
 }
 
@@ -661,7 +777,7 @@
 // ================================================================
 
 ParcelType::ParcelType()
-    :Type("android.os", "Parcel", BUILT_IN, false, false)
+    :Type("android.os", "Parcel", BUILT_IN, false, false, false)
 {
 }
 
@@ -680,7 +796,7 @@
 // ================================================================
 
 ParcelableInterfaceType::ParcelableInterfaceType()
-    :Type("android.os", "Parcelable", BUILT_IN, false, false)
+    :Type("android.os", "Parcelable", BUILT_IN, false, false, false)
 {
 }
 
@@ -699,7 +815,7 @@
 // ================================================================
 
 MapType::MapType()
-    :Type("java.util", "Map", BUILT_IN, true, true)
+    :Type("java.util", "Map", BUILT_IN, true, false, true)
 {
 }
 
@@ -729,8 +845,7 @@
 }
 
 void
-MapType::ReadFromParcel(StatementBlock* addTo, Variable* v,
-                    Variable* parcel, Variable** cl)
+MapType::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl)
 {
     EnsureClassLoader(addTo, cl);
     addTo->Add(new MethodCall(parcel, "readMap", 2, v, *cl));
@@ -740,7 +855,7 @@
 // ================================================================
 
 ListType::ListType()
-    :Type("java.util", "List", BUILT_IN, true, true)
+    :Type("java.util", "List", BUILT_IN, true, true, true)
 {
 }
 
@@ -771,24 +886,44 @@
     addTo->Add(new MethodCall(parcel, "readList", 2, v, *cl));
 }
 
+void
+ListType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, int flags)
+{
+    addTo->Add(new MethodCall(data, "putList", 2, k, v));
+}
+
+void
+ListType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
+        Variable** cl)
+{
+    addTo->Add(new Assignment(v, new MethodCall(data, "getList", 1, k)));
+}
 
 // ================================================================
 
-ParcelableType::ParcelableType(const string& package, const string& name,
-                        bool builtIn, const string& declFile, int declLine)
-    :Type(package, name, builtIn ? BUILT_IN : PARCELABLE, true, true,
-            declFile, declLine)
+UserDataType::UserDataType(const string& package, const string& name,
+                        bool builtIn, bool canWriteToParcel, bool canWriteToRpcData,
+                        const string& declFile, int declLine)
+    :Type(package, name, builtIn ? BUILT_IN : USERDATA, canWriteToParcel, canWriteToRpcData,
+            true, declFile, declLine)
 {
 }
 
 string
-ParcelableType::CreatorName() const
+UserDataType::CreatorName() const
 {
     return QualifiedName() + ".CREATOR";
 }
 
+string
+UserDataType::RpcCreatorName() const
+{
+    return QualifiedName() + ".RPC_CREATOR";
+}
+
 void
-ParcelableType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+UserDataType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
 {
     // if (v != null) {
     //     parcel.writeInt(1);
@@ -811,7 +946,7 @@
 }
 
 void
-ParcelableType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
+UserDataType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     // if (0 != parcel.readInt()) {
     //     v = CLASS.CREATOR.createFromParcel(parcel)
@@ -832,7 +967,7 @@
 }
 
 void
-ParcelableType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+UserDataType::ReadFromParcel(StatementBlock* addTo, Variable* v,
                     Variable* parcel, Variable**)
 {
     // TODO: really, we don't need to have this extra check, but we
@@ -848,20 +983,20 @@
 }
 
 bool
-ParcelableType::CanBeArray() const
+UserDataType::CanBeArray() const
 {
     return true;
 }
 
 void
-ParcelableType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
+UserDataType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags)
 {
     addTo->Add(new MethodCall(parcel, "writeTypedArray", 2, v,
                 BuildWriteToParcelFlags(flags)));
 }
 
 void
-ParcelableType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
+UserDataType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
                             Variable* parcel, Variable**)
 {
     string creator = v->type->QualifiedName() + ".CREATOR";
@@ -870,20 +1005,36 @@
 }
 
 void
-ParcelableType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
+UserDataType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     string creator = v->type->QualifiedName() + ".CREATOR";
     addTo->Add(new MethodCall(parcel, "readTypedArray", 2,
                     v, new LiteralExpression(creator)));
 }
 
+void
+UserDataType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags)
+{
+    // data.putFlattenable(k, v);
+    addTo->Add(new MethodCall(data, "putFlattenable", 2, k, v));
+}
+
+void
+UserDataType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl)
+{
+    // data.getFlattenable(k, CLASS.RPC_CREATOR);
+    addTo->Add(new Assignment(v, new MethodCall(data, "getFlattenable", 2, k,
+                new FieldVariable(v->type, "RPC_CREATOR"))));
+}
 
 // ================================================================
 
 InterfaceType::InterfaceType(const string& package, const string& name,
                         bool builtIn, bool oneway,
                         const string& declFile, int declLine)
-    :Type(package, name, builtIn ? BUILT_IN : INTERFACE, true, false,
+    :Type(package, name, builtIn ? BUILT_IN : INTERFACE, true, false, false,
                         declFile, declLine)
     ,m_oneway(oneway)
 {
@@ -922,7 +1073,7 @@
 
 GenericType::GenericType(const string& package, const string& name,
                          const vector<Type*>& args)
-    :Type(package, name, BUILT_IN, true, true)
+    :Type(package, name, BUILT_IN, true, true, true)
 {
     m_args = args;
 
@@ -942,6 +1093,12 @@
     SetQualifiedName(m_importName + gen);
 }
 
+const vector<Type*>&
+GenericType::GenericArgumentTypes() const
+{
+    return m_args;
+}
+
 string
 GenericType::GenericArguments() const
 {
@@ -1041,10 +1198,65 @@
     }
 }
 
+void
+GenericListType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, int flags)
+{
+    Type* generic = GenericArgumentTypes()[0];
+    if (generic == RPC_DATA_TYPE) {
+        addTo->Add(new MethodCall(data, "putRpcDataList", 2, k, v));
+    } else if (generic->RpcCreatorName() != "") {
+        addTo->Add(new MethodCall(data, "putFlattenableList", 2, k, v));
+    } else {
+        addTo->Add(new MethodCall(data, "putList", 2, k, v));
+    }
+}
+
+void
+GenericListType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, Variable** cl)
+{
+    Type* generic = GenericArgumentTypes()[0];
+    if (generic == RPC_DATA_TYPE) {
+        addTo->Add(new Assignment(v, new MethodCall(data, "getRpcDataList", 2, k)));
+    } else if (generic->RpcCreatorName() != "") {
+        addTo->Add(new Assignment(v, new MethodCall(data, "getFlattenableList", 2, k, 
+                        new LiteralExpression(generic->RpcCreatorName()))));
+    } else {
+        string classArg = GenericArgumentTypes()[0]->QualifiedName();
+        classArg += ".class";
+        addTo->Add(new Assignment(v, new MethodCall(data, "getList", 2, k,
+                        new LiteralExpression(classArg))));
+    }
+}
+
+
+// ================================================================
+
+RpcDataType::RpcDataType()
+    :UserDataType("android.support.place.rpc", "RpcData", true, true, true)
+{
+}
+
+void
+RpcDataType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data, int flags)
+{
+    addTo->Add(new MethodCall(data, "putRpcData", 2, k, v));
+}
+
+void
+RpcDataType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data,
+        Variable** cl)
+{
+    addTo->Add(new Assignment(v, new MethodCall(data, "getRpcData", 1, k)));
+}
+
+
 // ================================================================
 
 ClassLoaderType::ClassLoaderType()
-    :Type("java.lang", "ClassLoader", BUILT_IN, false, false)
+    :Type("java.lang", "ClassLoader", BUILT_IN, false, false, false)
 {
 }
 
diff --git a/tools/aidl/Type.h b/tools/aidl/Type.h
index 662e3a2..ae12720 100755
--- a/tools/aidl/Type.h
+++ b/tools/aidl/Type.h
@@ -13,7 +13,7 @@
     // kinds
     enum {
         BUILT_IN,
-        PARCELABLE,
+        USERDATA,
         INTERFACE,
         GENERATED
     };
@@ -24,9 +24,9 @@
     };
 
                     Type(const string& name, int kind, bool canWriteToParcel,
-                            bool canBeOut);
+                            bool canWriteToRpcData, bool canBeOut);
                     Type(const string& package, const string& name,
-                            int kind, bool canWriteToParcel, bool canBeOut,
+                            int kind, bool canWriteToParcel, bool canWriteToRpcData, bool canBeOut,
                             const string& declFile = "", int declLine = -1);
     virtual         ~Type();
 
@@ -36,11 +36,13 @@
     inline int      Kind() const                { return m_kind; }
     inline string   DeclFile() const            { return m_declFile; }
     inline int      DeclLine() const            { return m_declLine; }
-    inline bool     CanBeMarshalled() const     { return m_canWriteToParcel; }
+    inline bool     CanWriteToParcel() const    { return m_canWriteToParcel; }
+    inline bool     CanWriteToRpcData() const   { return m_canWriteToRpcData; }
     inline bool     CanBeOutParameter() const   { return m_canBeOut; }
     
     virtual string  ImportType() const;
     virtual string  CreatorName() const;
+    virtual string  RpcCreatorName() const;
     virtual string  InstantiableName() const;
 
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
@@ -59,6 +61,11 @@
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, Variable** cl);
 
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
+
 protected:
     void SetQualifiedName(const string& qualified);
     Expression* BuildWriteToParcelFlags(int flags);
@@ -74,17 +81,24 @@
     int m_declLine;
     int m_kind;
     bool m_canWriteToParcel;
+    bool m_canWriteToRpcData;
     bool m_canBeOut;
 };
 
 class BasicType : public Type
 {
 public:
-                    BasicType(const string& name, const string& marshallMethod,
-                              const string& unmarshallMethod,
-                              const string& writeArray,
-                              const string& createArray,
-                              const string& readArray);
+                    BasicType(const string& name,
+                              const string& marshallParcel,
+                              const string& unmarshallParcel,
+                              const string& writeArrayParcel,
+                              const string& createArrayParcel,
+                              const string& readArrayParcel,
+                              const string& marshallRpc,
+                              const string& unmarshallRpc,
+                              const string& writeArrayRpc,
+                              const string& createArrayRpc,
+                              const string& readArrayRpc);
 
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
@@ -100,12 +114,22 @@
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, Variable** cl);
 
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
+
 private:
-    string m_marshallMethod;
-    string m_unmarshallMethod;
-    string m_writeArrayMethod;
-    string m_createArrayMethod;
-    string m_readArrayMethod;
+    string m_marshallParcel;
+    string m_unmarshallParcel;
+    string m_writeArrayParcel;
+    string m_createArrayParcel;
+    string m_readArrayParcel;
+    string m_marshallRpc;
+    string m_unmarshallRpc;
+    string m_writeArrayRpc;
+    string m_createArrayRpc;
+    string m_readArrayRpc;
 };
 
 class BooleanType : public Type
@@ -126,6 +150,11 @@
                                     Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, Variable** cl);
+
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
 };
 
 class CharType : public Type
@@ -146,6 +175,11 @@
                                     Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, Variable** cl);
+
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
 };
 
 
@@ -169,6 +203,11 @@
                                     Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, Variable** cl);
+
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
 };
 
 class CharSequenceType : public Type
@@ -305,15 +344,22 @@
                                     Variable* parcel, Variable** cl);
     virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, Variable** cl);
+
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
 };
 
-class ParcelableType : public Type
+class UserDataType : public Type
 {
 public:
-                    ParcelableType(const string& package, const string& name,
-                            bool builtIn, const string& declFile, int declLine);
+                    UserDataType(const string& package, const string& name,
+                            bool builtIn, bool canWriteToParcel, bool canWriteToRpcData,
+                            const string& declFile = "", int declLine = -1);
 
     virtual string  CreatorName() const;
+    virtual string  RpcCreatorName() const;
 
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
@@ -330,6 +376,11 @@
                                     Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, Variable** cl);
+
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
 };
 
 class InterfaceType : public Type
@@ -357,6 +408,7 @@
                     GenericType(const string& package, const string& name,
                                  const vector<Type*>& args);
 
+    const vector<Type*>& GenericArgumentTypes() const;
     string          GenericArguments() const;
 
     virtual string  ImportType() const;
@@ -374,6 +426,22 @@
     vector<Type*> m_args;
 };
 
+class RpcDataType : public UserDataType
+{
+public:
+                    RpcDataType();
+
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
+};
+
+class ClassLoaderType : public Type
+{
+public:
+                    ClassLoaderType();
+};
 
 class GenericListType : public GenericType
 {
@@ -391,16 +459,15 @@
     virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, Variable** cl);
 
+    virtual void    WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, int flags);
+    virtual void    CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v,
+                                    Variable* data, Variable** cl);
+    
 private:
     string m_creator;
 };
 
-class ClassLoaderType : public Type
-{
-public:
-                    ClassLoaderType();
-};
-
 class Namespace
 {
 public:
@@ -438,11 +505,13 @@
 
 extern Type* VOID_TYPE;
 extern Type* BOOLEAN_TYPE;
+extern Type* BYTE_TYPE;
 extern Type* CHAR_TYPE;
 extern Type* INT_TYPE;
 extern Type* LONG_TYPE;
 extern Type* FLOAT_TYPE;
 extern Type* DOUBLE_TYPE;
+extern Type* OBJECT_TYPE;
 extern Type* STRING_TYPE;
 extern Type* CHAR_SEQUENCE_TYPE;
 extern Type* TEXT_UTILS_TYPE;
@@ -455,6 +524,13 @@
 extern Type* PARCEL_TYPE;
 extern Type* PARCELABLE_INTERFACE_TYPE;
 
+extern Type* CONTEXT_TYPE;
+
+extern Type* RPC_DATA_TYPE;
+extern Type* RPC_ERROR_TYPE;
+extern Type* RPC_CONTEXT_TYPE;
+extern Type* EVENT_FAKE_TYPE;
+
 extern Expression* NULL_VALUE;
 extern Expression* THIS_VALUE;
 extern Expression* SUPER_VALUE;
diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp
index fb4067a..8dbbf50 100644
--- a/tools/aidl/aidl.cpp
+++ b/tools/aidl/aidl.cpp
@@ -29,7 +29,7 @@
 test_document(document_item_type* d)
 {
     while (d) {
-        if (d->item_type == INTERFACE_TYPE) {
+        if (d->item_type == INTERFACE_TYPE_BINDER) {
             interface_type* c = (interface_type*)d;
             printf("interface %s %s {\n", c->package, c->name.data);
             interface_item_type *q = (interface_item_type*)c->interface_items;
@@ -50,9 +50,14 @@
             }
             printf("}\n");
         }
-        else if (d->item_type == PARCELABLE_TYPE) {
-            parcelable_type* b = (parcelable_type*)d;
-            printf("parcelable %s %s;\n", b->package, b->name.data);
+        else if (d->item_type == USER_DATA_TYPE) {
+            user_data_type* b = (user_data_type*)d;
+            if ((b->flattening_methods & PARCELABLE_DATA) != 0) {
+                printf("parcelable %s %s;\n", b->package, b->name.data);
+            }
+            if ((b->flattening_methods & RPC_DATA) != 0) {
+                printf("flattenable %s %s;\n", b->package, b->name.data);
+            }
         }
         else {
             printf("UNKNOWN d=0x%08lx d->item_type=%d\n", (long)d, d->item_type);
@@ -238,11 +243,12 @@
 {
     int err = 0;
     while (items) {
-        if (items->item_type == PARCELABLE_TYPE) {
-            parcelable_type* p = (parcelable_type*)items;
+        if (items->item_type == USER_DATA_TYPE) {
+            user_data_type* p = (user_data_type*)items;
             err |= check_filename(filename, p->package, &p->name);
         }
-        else if (items->item_type == INTERFACE_TYPE) {
+        else if (items->item_type == INTERFACE_TYPE_BINDER
+                || items->item_type == INTERFACE_TYPE_RPC) {
             interface_type* c = (interface_type*)items;
             err |= check_filename(filename, c->package, &c->name);
         }
@@ -264,8 +270,8 @@
     {
         case Type::INTERFACE:
             return "an interface";
-        case Type::PARCELABLE:
-            return "a parcelable";
+        case Type::USERDATA:
+            return "a user data";
         default:
             return "ERROR";
     }
@@ -290,12 +296,14 @@
     int err = 0;
     while (items) {
         Type* type;
-        if (items->item_type == PARCELABLE_TYPE) {
-            parcelable_type* p = (parcelable_type*)items;
-            type = new ParcelableType(p->package ? p->package : "",
-                            p->name.data, false, filename, p->name.lineno);
+        if (items->item_type == USER_DATA_TYPE) {
+            user_data_type* p = (user_data_type*)items;
+            type = new UserDataType(p->package ? p->package : "", p->name.data,
+                    false, ((p->flattening_methods & PARCELABLE_DATA) != 0),
+                    ((p->flattening_methods & RPC_DATA) != 0), filename, p->name.lineno);
         }
-        else if (items->item_type == INTERFACE_TYPE) {
+        else if (items->item_type == INTERFACE_TYPE_BINDER
+                || items->item_type == INTERFACE_TYPE_RPC) {
             interface_type* c = (interface_type*)items;
             type = new InterfaceType(c->package ? c->package : "",
                             c->name.data, false, c->oneway,
@@ -310,7 +318,7 @@
         if (old == NULL) {
             NAMES.Add(type);
 
-            if (items->item_type == INTERFACE_TYPE) {
+            if (items->item_type == INTERFACE_TYPE_BINDER) {
                 // for interfaces, also add the stub and proxy types, we don't
                 // bother checking these for duplicates, because the parser
                 // won't let us do it.
@@ -319,17 +327,30 @@
                 string name = c->name.data;
                 name += ".Stub";
                 Type* stub = new Type(c->package ? c->package : "",
-                                        name, Type::GENERATED, false, false,
+                                        name, Type::GENERATED, false, false, false,
                                         filename, c->name.lineno);
                 NAMES.Add(stub);
 
                 name = c->name.data;
                 name += ".Stub.Proxy";
                 Type* proxy = new Type(c->package ? c->package : "",
-                                        name, Type::GENERATED, false, false,
+                                        name, Type::GENERATED, false, false, false,
                                         filename, c->name.lineno);
                 NAMES.Add(proxy);
             }
+            else if (items->item_type == INTERFACE_TYPE_RPC) {
+                // for interfaces, also add the service base type, we don't
+                // bother checking these for duplicates, because the parser
+                // won't let us do it.
+                interface_type* c = (interface_type*)items;
+
+                string name = c->name.data;
+                name += ".ServiceBase";
+                Type* base = new Type(c->package ? c->package : "",
+                                        name, Type::GENERATED, false, false, false,
+                                        filename, c->name.lineno);
+                NAMES.Add(base);
+            }
         } else {
             if (old->Kind() == Type::BUILT_IN) {
                 fprintf(stderr, "%s:%d attempt to redefine built in class %s\n",
@@ -381,7 +402,7 @@
 }
 
 static int
-check_method(const char* filename, method_type* m)
+check_method(const char* filename, int kind, method_type* m)
 {
     int err = 0;
 
@@ -394,10 +415,19 @@
         return err;
     }
 
-    if (!returnType->CanBeMarshalled()) {
-        fprintf(stderr, "%s:%d return type %s can't be marshalled.\n", filename,
-                    m->type.type.lineno, m->type.type.data);
-        err = 1;
+    if (returnType == EVENT_FAKE_TYPE) {
+        if (kind != INTERFACE_TYPE_RPC) {
+            fprintf(stderr, "%s:%d event methods only supported for rpc interfaces\n",
+                    filename, m->type.type.lineno);
+            err = 1;
+        }
+    } else {
+        if (!(kind == INTERFACE_TYPE_BINDER ? returnType->CanWriteToParcel()
+                    : returnType->CanWriteToRpcData())) {
+            fprintf(stderr, "%s:%d return type %s can't be marshalled.\n", filename,
+                        m->type.type.lineno, m->type.type.data);
+            err = 1;
+        }
     }
 
     if (m->type.dimension > 0 && !returnType->CanBeArray()) {
@@ -429,14 +459,31 @@
             err = 1;
             goto next;
         }
+
+        if (t == EVENT_FAKE_TYPE) {
+            fprintf(stderr, "%s:%d parameter %s (%d) event can not be used as a parameter %s\n",
+                    filename, m->type.type.lineno, arg->name.data, index,
+                    arg->type.type.data);
+            err = 1;
+            goto next;
+        }
         
-        if (!t->CanBeMarshalled()) {
+        if (!(kind == INTERFACE_TYPE_BINDER ? t->CanWriteToParcel() : t->CanWriteToRpcData())) {
             fprintf(stderr, "%s:%d parameter %d: '%s %s' can't be marshalled.\n",
                         filename, m->type.type.lineno, index,
                         arg->type.type.data, arg->name.data);
             err = 1;
         }
 
+        if (returnType == EVENT_FAKE_TYPE
+                && convert_direction(arg->direction.data) != IN_PARAMETER) {
+            fprintf(stderr, "%s:%d parameter %d: '%s %s' All paremeters on events must be 'in'.\n",
+                    filename, m->type.type.lineno, index,
+                    arg->type.type.data, arg->name.data);
+            err = 1;
+            goto next;
+        }
+
         if (arg->direction.data == NULL
                 && (arg->type.dimension != 0 || t->CanBeOutParameter())) {
             fprintf(stderr, "%s:%d parameter %d: '%s %s' can be an out"
@@ -479,7 +526,7 @@
         // check that the name doesn't match a keyword
         if (matches_keyword(arg->name.data)) {
             fprintf(stderr, "%s:%d parameter %d %s is named the same as a"
-                    " Java keyword\n",
+                    " Java or aidl keyword\n",
                     filename, m->name.lineno, index, arg->name.data);
             err = 1;
         }
@@ -497,8 +544,9 @@
 {
     int err = 0;
     while (items) {
-        // (nothing to check for PARCELABLE_TYPE)
-        if (items->item_type == INTERFACE_TYPE) {
+        // (nothing to check for USER_DATA_TYPE)
+        if (items->item_type == INTERFACE_TYPE_BINDER
+                || items->item_type == INTERFACE_TYPE_RPC) {
             map<string,method_type*> methodNames;
             interface_type* c = (interface_type*)items;
 
@@ -507,7 +555,7 @@
                 if (member->item_type == METHOD_TYPE) {
                     method_type* m = (method_type*)member;
 
-                    err |= check_method(filename, m);
+                    err |= check_method(filename, items->item_type, m);
 
                     // prevent duplicate methods
                     if (methodNames.find(m->name.data) == methodNames.end()) {
@@ -544,26 +592,29 @@
     const document_item_type* next = items->next;
     if (items->next != NULL) {
         int lineno = -1;
-        if (next->item_type == INTERFACE_TYPE) {
+        if (next->item_type == INTERFACE_TYPE_BINDER) {
             lineno = ((interface_type*)next)->interface_token.lineno;
         }
-        else if (next->item_type == PARCELABLE_TYPE) {
-            lineno = ((parcelable_type*)next)->parcelable_token.lineno;
+        else if (next->item_type == INTERFACE_TYPE_RPC) {
+            lineno = ((interface_type*)next)->interface_token.lineno;
+        }
+        else if (next->item_type == USER_DATA_TYPE) {
+            lineno = ((user_data_type*)next)->keyword_token.lineno;
         }
         fprintf(stderr, "%s:%d aidl can only handle one interface per file\n",
                             filename, lineno);
         return 1;
     }
 
-    if (items->item_type == PARCELABLE_TYPE) {
+    if (items->item_type == USER_DATA_TYPE) {
         *onlyParcelable = true;
         if (options.failOnParcelable) {
             fprintf(stderr, "%s:%d aidl can only generate code for interfaces, not"
-                            " parcelables,\n", filename,
-                            ((parcelable_type*)items)->parcelable_token.lineno);
-            fprintf(stderr, "%s:%d .aidl files that only declare parcelables "
-                            "don't need to go in the Makefile.\n", filename,
-                            ((parcelable_type*)items)->parcelable_token.lineno);
+                            " parcelables or flattenables,\n", filename,
+                            ((user_data_type*)items)->keyword_token.lineno);
+            fprintf(stderr, "%s:%d .aidl files that only declare parcelables or flattenables"
+                            "may not go in the Makefile.\n", filename,
+                            ((user_data_type*)items)->keyword_token.lineno);
             return 1;
         }
     } else {
@@ -598,7 +649,7 @@
         slash = "";
     }
 
-    if (items->item_type == INTERFACE_TYPE) {
+    if (items->item_type == INTERFACE_TYPE_BINDER || items->item_type == INTERFACE_TYPE_RPC) {
         fprintf(to, "%s: \\\n", options.outputFileName.c_str());
     } else {
         // parcelable: there's no output file.
@@ -658,12 +709,12 @@
 generate_outputFileName(const Options& options, const document_item_type* items)
 {
     // items has already been checked to have only one interface.
-    if (items->item_type == INTERFACE_TYPE) {
+    if (items->item_type == INTERFACE_TYPE_BINDER || items->item_type == INTERFACE_TYPE_RPC) {
         interface_type* type = (interface_type*)items;
 
         return generate_outputFileName2(options, type->name, type->package);
-    } else if (items->item_type == PARCELABLE_TYPE) {
-        parcelable_type* type = (parcelable_type*)items;
+    } else if (items->item_type == USER_DATA_TYPE) {
+        user_data_type* type = (user_data_type*)items;
         return generate_outputFileName2(options, type->name, type->package);
     }
 
@@ -734,24 +785,40 @@
         document_item_type* doc;
         
         if (0 == strcmp("parcelable", type)) {
-            parcelable_type* parcl = (parcelable_type*)malloc(
-                    sizeof(parcelable_type));
-            memset(parcl, 0, sizeof(parcelable_type));
-            parcl->document_item.item_type = PARCELABLE_TYPE;
-            parcl->parcelable_token.lineno = lineno;
-            parcl->parcelable_token.data = strdup(type);
+            user_data_type* parcl = (user_data_type*)malloc(
+                    sizeof(user_data_type));
+            memset(parcl, 0, sizeof(user_data_type));
+            parcl->document_item.item_type = USER_DATA_TYPE;
+            parcl->keyword_token.lineno = lineno;
+            parcl->keyword_token.data = strdup(type);
             parcl->package = packagename ? strdup(packagename) : NULL;
             parcl->name.lineno = lineno;
             parcl->name.data = strdup(classname);
             parcl->semicolon_token.lineno = lineno;
             parcl->semicolon_token.data = strdup(";");
+            parcl->flattening_methods = PARCELABLE_DATA;
+            doc = (document_item_type*)parcl;
+        }
+        else if (0 == strcmp("flattenable", type)) {
+            user_data_type* parcl = (user_data_type*)malloc(
+                    sizeof(user_data_type));
+            memset(parcl, 0, sizeof(user_data_type));
+            parcl->document_item.item_type = USER_DATA_TYPE;
+            parcl->keyword_token.lineno = lineno;
+            parcl->keyword_token.data = strdup(type);
+            parcl->package = packagename ? strdup(packagename) : NULL;
+            parcl->name.lineno = lineno;
+            parcl->name.data = strdup(classname);
+            parcl->semicolon_token.lineno = lineno;
+            parcl->semicolon_token.data = strdup(";");
+            parcl->flattening_methods = RPC_DATA;
             doc = (document_item_type*)parcl;
         }
         else if (0 == strcmp("interface", type)) {
             interface_type* iface = (interface_type*)malloc(
                     sizeof(interface_type));
             memset(iface, 0, sizeof(interface_type));
-            iface->document_item.item_type = INTERFACE_TYPE;
+            iface->document_item.item_type = INTERFACE_TYPE_BINDER;
             iface->interface_token.lineno = lineno;
             iface->interface_token.data = strdup(type);
             iface->package = packagename ? strdup(packagename) : NULL;
@@ -923,9 +990,14 @@
         }
         document_item_type* doc = g_document;
         string line;
-        if (doc->item_type == PARCELABLE_TYPE) {
-            line = "parcelable ";
-            parcelable_type* parcelable = (parcelable_type*)doc;
+        if (doc->item_type == USER_DATA_TYPE) {
+            user_data_type* parcelable = (user_data_type*)doc;
+            if ((parcelable->flattening_methods & PARCELABLE_DATA) != 0) {
+                line = "parcelable ";
+            }
+            if ((parcelable->flattening_methods & RPC_DATA) != 0) {
+                line = "flattenable ";
+            }
             if (parcelable->package) {
                 line += parcelable->package;
                 line += '.';
@@ -995,5 +1067,3 @@
     fprintf(stderr, "aidl: internal error\n");
     return 1;
 }
-
-
diff --git a/tools/aidl/aidl_language.h b/tools/aidl/aidl_language.h
index 9ca5deb..f203dbb0 100644
--- a/tools/aidl/aidl_language.h
+++ b/tools/aidl/aidl_language.h
@@ -63,8 +63,9 @@
 } method_type;
 
 enum {
-    PARCELABLE_TYPE = 12,
-    INTERFACE_TYPE
+    USER_DATA_TYPE = 12,
+    INTERFACE_TYPE_BINDER,
+    INTERFACE_TYPE_RPC
 };
 
 typedef struct document_item_type {
@@ -72,13 +73,21 @@
     struct document_item_type* next;
 } document_item_type;
 
-typedef struct parcelable_type {
+
+// for user_data_type.flattening_methods
+enum {
+    PARCELABLE_DATA = 0x1,
+    RPC_DATA = 0x2
+};
+
+typedef struct user_data_type {
     document_item_type document_item;
-    buffer_type parcelable_token;
+    buffer_type keyword_token; // only the first one
     char* package;
     buffer_type name;
     buffer_type semicolon_token;
-} parcelable_type;
+    int flattening_methods;
+} user_data_type;
 
 typedef struct interface_type {
     document_item_type document_item;
@@ -100,7 +109,7 @@
     method_type* method;
     interface_item_type* interface_item;
     interface_type* interface_obj;
-    parcelable_type* parcelable;
+    user_data_type* user_data;
     document_item_type* document_item;
 } lexer_type;
 
diff --git a/tools/aidl/aidl_language_l.l b/tools/aidl/aidl_language_l.l
index 567b1cf..7c5290c 100644
--- a/tools/aidl/aidl_language_l.l
+++ b/tools/aidl/aidl_language_l.l
@@ -81,6 +81,8 @@
     /* keywords */
 parcelable      { SET_BUFFER(PARCELABLE); return PARCELABLE; }
 interface       { SET_BUFFER(INTERFACE); return INTERFACE; }
+flattenable     { SET_BUFFER(FLATTENABLE); return FLATTENABLE; }
+rpc             { SET_BUFFER(INTERFACE); return RPC; }
 in              { SET_BUFFER(IN); return IN; }
 out             { SET_BUFFER(OUT); return OUT; }
 inout           { SET_BUFFER(INOUT); return INOUT; }
diff --git a/tools/aidl/aidl_language_y.y b/tools/aidl/aidl_language_y.y
index 3d65f17..cc04d15 100644
--- a/tools/aidl/aidl_language_y.y
+++ b/tools/aidl/aidl_language_y.y
@@ -19,6 +19,8 @@
 %token ARRAY
 %token PARCELABLE
 %token INTERFACE
+%token FLATTENABLE
+%token RPC
 %token IN
 %token OUT
 %token INOUT
@@ -72,36 +74,61 @@
     ;
 
 declaration:
-        parcelable_decl                            { $$.document_item = (document_item_type*)$1.parcelable; }
+        parcelable_decl                            { $$.document_item = (document_item_type*)$1.user_data; }
     |   interface_decl                             { $$.document_item = (document_item_type*)$1.interface_item; }
     ;
 
 parcelable_decl:
-        PARCELABLE IDENTIFIER ';'                  { 
-                                                        parcelable_type* b = (parcelable_type*)malloc(sizeof(parcelable_type));
-                                                        b->document_item.item_type = PARCELABLE_TYPE;
+        PARCELABLE IDENTIFIER ';'                   {
+                                                        user_data_type* b = (user_data_type*)malloc(sizeof(user_data_type));
+                                                        b->document_item.item_type = USER_DATA_TYPE;
                                                         b->document_item.next = NULL;
-                                                        b->parcelable_token = $1.buffer;
+                                                        b->keyword_token = $1.buffer;
                                                         b->name = $2.buffer;
                                                         b->package = g_currentPackage ? strdup(g_currentPackage) : NULL;
                                                         b->semicolon_token = $3.buffer;
-                                                        $$.parcelable = b;
+                                                        b->flattening_methods = PARCELABLE_DATA;
+                                                        $$.user_data = b;
                                                     }
     |   PARCELABLE ';'                              {
                                                         fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name.\n",
                                                                      g_currentFilename, $1.buffer.lineno);
-                                                        $$.parcelable = NULL;
+                                                        $$.user_data = NULL;
                                                     }
     |   PARCELABLE error ';'                        {
                                                         fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name, saw \"%s\".\n",
                                                                      g_currentFilename, $2.buffer.lineno, $2.buffer.data);
-                                                        $$.parcelable = NULL;
+                                                        $$.user_data = NULL;
                                                     }
+    |   FLATTENABLE IDENTIFIER ';'                  {
+                                                        user_data_type* b = (user_data_type*)malloc(sizeof(user_data_type));
+                                                        b->document_item.item_type = USER_DATA_TYPE;
+                                                        b->document_item.next = NULL;
+                                                        b->keyword_token = $1.buffer;
+                                                        b->name = $2.buffer;
+                                                        b->package = g_currentPackage ? strdup(g_currentPackage) : NULL;
+                                                        b->semicolon_token = $3.buffer;
+                                                        b->flattening_methods = PARCELABLE_DATA | RPC_DATA;
+                                                        $$.user_data = b;
+                                                    }
+    |   FLATTENABLE ';'                             {
+                                                        fprintf(stderr, "%s:%d syntax error in flattenable declaration. Expected type name.\n",
+                                                                     g_currentFilename, $1.buffer.lineno);
+                                                        $$.user_data = NULL;
+                                                    }
+    |   FLATTENABLE error ';'                       {
+                                                        fprintf(stderr, "%s:%d syntax error in flattenable declaration. Expected type name, saw \"%s\".\n",
+                                                                     g_currentFilename, $2.buffer.lineno, $2.buffer.data);
+                                                        $$.user_data = NULL;
+                                                    }
+
     ;
 
 interface_header:
         INTERFACE                                  {
                                                         interface_type* c = (interface_type*)malloc(sizeof(interface_type));
+                                                        c->document_item.item_type = INTERFACE_TYPE_BINDER;
+                                                        c->document_item.next = NULL;
                                                         c->interface_token = $1.buffer;
                                                         c->oneway = false;
                                                         memset(&c->oneway_token, 0, sizeof(buffer_type));
@@ -110,19 +137,34 @@
                                                    }
     |   ONEWAY INTERFACE                           {
                                                         interface_type* c = (interface_type*)malloc(sizeof(interface_type));
+                                                        c->document_item.item_type = INTERFACE_TYPE_BINDER;
+                                                        c->document_item.next = NULL;
                                                         c->interface_token = $2.buffer;
                                                         c->oneway = true;
                                                         c->oneway_token = $1.buffer;
                                                         c->comments_token = &c->oneway_token;
                                                         $$.interface_obj = c;
                                                    }
+    |   RPC                                        {
+                                                        interface_type* c = (interface_type*)malloc(sizeof(interface_type));
+                                                        c->document_item.item_type = INTERFACE_TYPE_RPC;
+                                                        c->document_item.next = NULL;
+                                                        c->interface_token = $1.buffer;
+                                                        c->oneway = false;
+                                                        memset(&c->oneway_token, 0, sizeof(buffer_type));
+                                                        c->comments_token = &c->interface_token;
+                                                        $$.interface_obj = c;
+                                                   }
+    ;
+
+interface_keywords:
+        INTERFACE
+    |   RPC
     ;
 
 interface_decl:
         interface_header IDENTIFIER '{' interface_items '}' { 
                                                         interface_type* c = $1.interface_obj;
-                                                        c->document_item.item_type = INTERFACE_TYPE;
-                                                        c->document_item.next = NULL;
                                                         c->name = $2.buffer;
                                                         c->package = g_currentPackage ? strdup(g_currentPackage) : NULL;
                                                         c->open_brace_token = $3.buffer;
@@ -130,12 +172,12 @@
                                                         c->close_brace_token = $5.buffer;
                                                         $$.interface_obj = c;
                                                     }
-    |   INTERFACE error '{' interface_items '}'     {
+    |   interface_keywords error '{' interface_items '}'     {
                                                         fprintf(stderr, "%s:%d: syntax error in interface declaration.  Expected type name, saw \"%s\"\n",
                                                                     g_currentFilename, $2.buffer.lineno, $2.buffer.data);
                                                         $$.document_item = NULL;
                                                     }
-    |   INTERFACE error '}'                             {
+    |   interface_keywords error '}'                {
                                                         fprintf(stderr, "%s:%d: syntax error in interface declaration.  Expected type name, saw \"%s\"\n",
                                                                     g_currentFilename, $2.buffer.lineno, $2.buffer.data);
                                                         $$.document_item = NULL;
diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp
index 83e3bbc..9e57407 100644
--- a/tools/aidl/generate_java.cpp
+++ b/tools/aidl/generate_java.cpp
@@ -1,5 +1,4 @@
 #include "generate_java.h"
-#include "AST.h"
 #include "Type.h"
 #include <string.h>
 #include <stdio.h>
@@ -7,18 +6,6 @@
 #include <string.h>
 
 // =================================================
-class VariableFactory
-{
-public:
-    VariableFactory(const string& base); // base must be short
-    Variable* Get(Type* type);
-    Variable* Get(int index);
-private:
-    vector<Variable*> m_vars;
-    string m_base;
-    int m_index;
-};
-
 VariableFactory::VariableFactory(const string& base)
     :m_base(base),
      m_index(0)
@@ -43,195 +30,7 @@
 }
 
 // =================================================
-class StubClass : public Class
-{
-public:
-    StubClass(Type* type, Type* interfaceType);
-    virtual ~StubClass();
-
-    Variable* transact_code;
-    Variable* transact_data;
-    Variable* transact_reply;
-    Variable* transact_flags;
-    SwitchStatement* transact_switch;
-private:
-    void make_as_interface(Type* interfaceType);
-};
-
-StubClass::StubClass(Type* type, Type* interfaceType)
-    :Class()
-{
-    this->comment = "/** Local-side IPC implementation stub class. */";
-    this->modifiers = PUBLIC | ABSTRACT | STATIC;
-    this->what = Class::CLASS;
-    this->type = type;
-    this->extends = BINDER_NATIVE_TYPE;
-    this->interfaces.push_back(interfaceType);
-
-    // descriptor
-    Field* descriptor = new Field(STATIC | FINAL | PRIVATE,
-                            new Variable(STRING_TYPE, "DESCRIPTOR"));
-    descriptor->value = "\"" + interfaceType->QualifiedName() + "\"";
-    this->elements.push_back(descriptor);
-
-    // ctor
-    Method* ctor = new Method;
-        ctor->modifiers = PUBLIC;
-        ctor->comment = "/** Construct the stub at attach it to the "
-                        "interface. */";
-        ctor->name = "Stub";
-        ctor->statements = new StatementBlock;
-    MethodCall* attach = new MethodCall(THIS_VALUE, "attachInterface",
-                            2, THIS_VALUE, new LiteralExpression("DESCRIPTOR"));
-    ctor->statements->Add(attach);
-    this->elements.push_back(ctor);
-
-    // asInterface
-    make_as_interface(interfaceType);
-
-    // asBinder
-    Method* asBinder = new Method;
-        asBinder->modifiers = PUBLIC;
-        asBinder->returnType = IBINDER_TYPE;
-        asBinder->name = "asBinder";
-        asBinder->statements = new StatementBlock;
-    asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
-    this->elements.push_back(asBinder);
-
-    // onTransact
-    this->transact_code = new Variable(INT_TYPE, "code");
-    this->transact_data = new Variable(PARCEL_TYPE, "data");
-    this->transact_reply = new Variable(PARCEL_TYPE, "reply");
-    this->transact_flags = new Variable(INT_TYPE, "flags");
-    Method* onTransact = new Method;
-        onTransact->modifiers = PUBLIC | OVERRIDE;
-        onTransact->returnType = BOOLEAN_TYPE;
-        onTransact->name = "onTransact";
-        onTransact->parameters.push_back(this->transact_code);
-        onTransact->parameters.push_back(this->transact_data);
-        onTransact->parameters.push_back(this->transact_reply);
-        onTransact->parameters.push_back(this->transact_flags);
-        onTransact->statements = new StatementBlock;
-        onTransact->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
-    this->elements.push_back(onTransact);
-    this->transact_switch = new SwitchStatement(this->transact_code);
-
-    onTransact->statements->Add(this->transact_switch);
-    MethodCall* superCall = new MethodCall(SUPER_VALUE, "onTransact", 4,
-                                    this->transact_code, this->transact_data,
-                                    this->transact_reply, this->transact_flags);
-    onTransact->statements->Add(new ReturnStatement(superCall));
-}
-
-StubClass::~StubClass()
-{
-}
-
-void
-StubClass::make_as_interface(Type *interfaceType)
-{
-    Variable* obj = new Variable(IBINDER_TYPE, "obj");
-
-    Method* m = new Method;
-        m->comment = "/**\n * Cast an IBinder object into an ";
-        m->comment += interfaceType->QualifiedName();
-        m->comment += " interface,\n";
-        m->comment += " * generating a proxy if needed.\n */";
-        m->modifiers = PUBLIC | STATIC;
-        m->returnType = interfaceType;
-        m->name = "asInterface";
-        m->parameters.push_back(obj);
-        m->statements = new StatementBlock;
-
-    IfStatement* ifstatement = new IfStatement();
-        ifstatement->expression = new Comparison(obj, "==", NULL_VALUE);
-        ifstatement->statements = new StatementBlock;
-        ifstatement->statements->Add(new ReturnStatement(NULL_VALUE));
-    m->statements->Add(ifstatement);
-
-    // IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
-    MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface");
-    queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR"));
-    IInterfaceType* iinType = new IInterfaceType();
-    Variable *iin = new Variable(iinType, "iin");
-    VariableDeclaration* iinVd = new VariableDeclaration(iin, queryLocalInterface, iinType);
-    m->statements->Add(iinVd);
-
-    // Ensure the instance type of the local object is as expected.
-    // One scenario where this is needed is if another package (with a
-    // different class loader) runs in the same process as the service.
-
-    // if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>) iin;
-    Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE);
-    Comparison* instOfCheck = new Comparison(iin, " instanceof ",
-            new LiteralExpression(interfaceType->QualifiedName()));
-    IfStatement* instOfStatement = new IfStatement();
-        instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck);
-        instOfStatement->statements = new StatementBlock;
-        instOfStatement->statements->Add(new ReturnStatement(new Cast(interfaceType, iin)));
-    m->statements->Add(instOfStatement);
-
-    string proxyType = interfaceType->QualifiedName();
-    proxyType += ".Stub.Proxy";
-    NewExpression* ne = new NewExpression(NAMES.Find(proxyType));
-    ne->arguments.push_back(obj);
-    m->statements->Add(new ReturnStatement(ne));
-
-    this->elements.push_back(m);
-}
-
-
-
-// =================================================
-class ProxyClass : public Class
-{
-public:
-    ProxyClass(Type* type, InterfaceType* interfaceType);
-    virtual ~ProxyClass();
-
-    Variable* mRemote;
-    bool mOneWay;
-};
-
-ProxyClass::ProxyClass(Type* type, InterfaceType* interfaceType)
-    :Class()
-{
-    this->modifiers = PRIVATE | STATIC;
-    this->what = Class::CLASS;
-    this->type = type;
-    this->interfaces.push_back(interfaceType);
-
-    mOneWay = interfaceType->OneWay();
-
-    // IBinder mRemote
-    mRemote = new Variable(IBINDER_TYPE, "mRemote");
-    this->elements.push_back(new Field(PRIVATE, mRemote));
-
-    // Proxy()
-    Variable* remote = new Variable(IBINDER_TYPE, "remote");
-    Method* ctor = new Method;
-        ctor->name = "Proxy";
-        ctor->statements = new StatementBlock;
-        ctor->parameters.push_back(remote);
-    ctor->statements->Add(new Assignment(mRemote, remote));
-    this->elements.push_back(ctor);
-
-    // IBinder asBinder()
-    Method* asBinder = new Method;
-        asBinder->modifiers = PUBLIC;
-        asBinder->returnType = IBINDER_TYPE;
-        asBinder->name = "asBinder";
-        asBinder->statements = new StatementBlock;
-    asBinder->statements->Add(new ReturnStatement(mRemote));
-    this->elements.push_back(asBinder);
-}
-
-ProxyClass::~ProxyClass()
-{
-}
-
-// =================================================
-static string
+string
 gather_comments(extra_text_type* extra)
 {
     string s;
@@ -249,7 +48,7 @@
     return s;
 }
 
-static string
+string
 append(const char* a, const char* b)
 {
     string s = a;
@@ -257,379 +56,25 @@
     return s;
 }
 
-static void
-generate_new_array(Type* t, StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
-{
-    Variable* len = new Variable(INT_TYPE, v->name + "_length");
-    addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt")));
-    IfStatement* lencheck = new IfStatement();
-    lencheck->expression = new Comparison(len, "<", new LiteralExpression("0"));
-    lencheck->statements->Add(new Assignment(v, NULL_VALUE));
-    lencheck->elseif = new IfStatement();
-    lencheck->elseif->statements->Add(new Assignment(v,
-                new NewArrayExpression(t, len)));
-    addTo->Add(lencheck);
-}
-
-static void
-generate_write_to_parcel(Type* t, StatementBlock* addTo, Variable* v,
-                            Variable* parcel, int flags)
-{
-    if (v->dimension == 0) {
-        t->WriteToParcel(addTo, v, parcel, flags);
-    }
-    if (v->dimension == 1) {
-        t->WriteArrayToParcel(addTo, v, parcel, flags);
-    }
-}
-
-static void
-generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
-                            Variable* parcel, Variable** cl)
-{
-    if (v->dimension == 0) {
-        t->CreateFromParcel(addTo, v, parcel, cl);
-    }
-    if (v->dimension == 1) {
-        t->CreateArrayFromParcel(addTo, v, parcel, cl);
-    }
-}
-
-static void
-generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
-                            Variable* parcel, Variable** cl)
-{
-    if (v->dimension == 0) {
-        t->ReadFromParcel(addTo, v, parcel, cl);
-    }
-    if (v->dimension == 1) {
-        t->ReadArrayFromParcel(addTo, v, parcel, cl);
-    }
-}
-
-
-static void
-generate_method(const method_type* method, Class* interface,
-                    StubClass* stubClass, ProxyClass* proxyClass, int index)
-{
-    arg_type* arg;
-    int i;
-    bool hasOutParams = false;
-
-    const bool oneway = proxyClass->mOneWay || method->oneway;
-
-    // == the TRANSACT_ constant =============================================
-    string transactCodeName = "TRANSACTION_";
-    transactCodeName += method->name.data;
-
-    char transactCodeValue[50];
-    sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
-
-    Field* transactCode = new Field(STATIC | FINAL,
-                            new Variable(INT_TYPE, transactCodeName));
-    transactCode->value = transactCodeValue;
-    stubClass->elements.push_back(transactCode);
-
-    // == the declaration in the interface ===================================
-    Method* decl = new Method;
-        decl->comment = gather_comments(method->comments_token->extra);
-        decl->modifiers = PUBLIC;
-        decl->returnType = NAMES.Search(method->type.type.data);
-        decl->returnTypeDimension = method->type.dimension;
-        decl->name = method->name.data;
-
-    arg = method->args;
-    while (arg != NULL) {
-        decl->parameters.push_back(new Variable(
-                            NAMES.Search(arg->type.type.data), arg->name.data,
-                            arg->type.dimension));
-        arg = arg->next;
-    }
-
-    decl->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
-
-    interface->elements.push_back(decl);
-
-    // == the stub method ====================================================
-
-    Case* c = new Case(transactCodeName);
-
-    MethodCall* realCall = new MethodCall(THIS_VALUE, method->name.data);
-
-    // interface token validation is the very first thing we do
-    c->statements->Add(new MethodCall(stubClass->transact_data,
-            "enforceInterface", 1, new LiteralExpression("DESCRIPTOR")));
-
-    // args
-    Variable* cl = NULL;
-    VariableFactory stubArgs("_arg");
-    arg = method->args;
-    while (arg != NULL) {
-        Type* t = NAMES.Search(arg->type.type.data);
-        Variable* v = stubArgs.Get(t);
-        v->dimension = arg->type.dimension;
-
-        c->statements->Add(new VariableDeclaration(v));
-
-        if (convert_direction(arg->direction.data) & IN_PARAMETER) {
-            generate_create_from_parcel(t, c->statements, v,
-                    stubClass->transact_data, &cl);
-        } else {
-            if (arg->type.dimension == 0) {
-                c->statements->Add(new Assignment(
-                                                v, new NewExpression(v->type)));
-            }
-            else if (arg->type.dimension == 1) {
-                generate_new_array(v->type, c->statements, v,
-                        stubClass->transact_data);
-            }
-            else {
-                fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__,
-                        __LINE__);
-            }
-        }
-
-        realCall->arguments.push_back(v);
-
-        arg = arg->next;
-    }
-
-    // the real call
-    Variable* _result = NULL;
-    if (0 == strcmp(method->type.type.data, "void")) {
-        c->statements->Add(realCall);
-
-        if (!oneway) {
-            // report that there were no exceptions
-            MethodCall* ex = new MethodCall(stubClass->transact_reply,
-                    "writeNoException", 0);
-            c->statements->Add(ex);
-        }
-    } else {
-        _result = new Variable(decl->returnType, "_result",
-                                decl->returnTypeDimension);
-        c->statements->Add(new VariableDeclaration(_result, realCall));
-
-        if (!oneway) {
-            // report that there were no exceptions
-            MethodCall* ex = new MethodCall(stubClass->transact_reply,
-                    "writeNoException", 0);
-            c->statements->Add(ex);
-        }
-
-        // marshall the return value
-        generate_write_to_parcel(decl->returnType, c->statements, _result,
-                                    stubClass->transact_reply,
-                                    Type::PARCELABLE_WRITE_RETURN_VALUE);
-    }
-
-    // out parameters
-    i = 0;
-    arg = method->args;
-    while (arg != NULL) {
-        Type* t = NAMES.Search(arg->type.type.data);
-        Variable* v = stubArgs.Get(i++);
-
-        if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
-            generate_write_to_parcel(t, c->statements, v,
-                                stubClass->transact_reply,
-                                Type::PARCELABLE_WRITE_RETURN_VALUE);
-            hasOutParams = true;
-        }
-
-        arg = arg->next;
-    }
-
-    // return true
-    c->statements->Add(new ReturnStatement(TRUE_VALUE));
-    stubClass->transact_switch->cases.push_back(c);
-
-    // == the proxy method ===================================================
-    Method* proxy = new Method;
-        proxy->comment = gather_comments(method->comments_token->extra);
-        proxy->modifiers = PUBLIC;
-        proxy->returnType = NAMES.Search(method->type.type.data);
-        proxy->returnTypeDimension = method->type.dimension;
-        proxy->name = method->name.data;
-        proxy->statements = new StatementBlock;
-        arg = method->args;
-        while (arg != NULL) {
-            proxy->parameters.push_back(new Variable(
-                            NAMES.Search(arg->type.type.data), arg->name.data,
-                            arg->type.dimension));
-            arg = arg->next;
-        }
-        proxy->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
-    proxyClass->elements.push_back(proxy);
-
-    // the parcels
-    Variable* _data = new Variable(PARCEL_TYPE, "_data");
-    proxy->statements->Add(new VariableDeclaration(_data,
-                                new MethodCall(PARCEL_TYPE, "obtain")));
-    Variable* _reply = NULL;
-    if (!oneway) {
-        _reply = new Variable(PARCEL_TYPE, "_reply");
-        proxy->statements->Add(new VariableDeclaration(_reply,
-                                    new MethodCall(PARCEL_TYPE, "obtain")));
-    }
-
-    // the return value
-    _result = NULL;
-    if (0 != strcmp(method->type.type.data, "void")) {
-        _result = new Variable(proxy->returnType, "_result",
-                method->type.dimension);
-        proxy->statements->Add(new VariableDeclaration(_result));
-    }
-
-    // try and finally
-    TryStatement* tryStatement = new TryStatement();
-    proxy->statements->Add(tryStatement);
-    FinallyStatement* finallyStatement = new FinallyStatement();
-    proxy->statements->Add(finallyStatement);
-
-    // the interface identifier token: the DESCRIPTOR constant, marshalled as a string
-    tryStatement->statements->Add(new MethodCall(_data, "writeInterfaceToken",
-            1, new LiteralExpression("DESCRIPTOR")));
-
-    // the parameters
-    arg = method->args;
-    while (arg != NULL) {
-        Type* t = NAMES.Search(arg->type.type.data);
-        Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
-        int dir = convert_direction(arg->direction.data);
-        if (dir == OUT_PARAMETER && arg->type.dimension != 0) {
-            IfStatement* checklen = new IfStatement();
-            checklen->expression = new Comparison(v, "==", NULL_VALUE);
-            checklen->statements->Add(new MethodCall(_data, "writeInt", 1,
-                        new LiteralExpression("-1")));
-            checklen->elseif = new IfStatement();
-            checklen->elseif->statements->Add(new MethodCall(_data, "writeInt",
-                        1, new FieldVariable(v, "length")));
-            tryStatement->statements->Add(checklen);
-        }
-        else if (dir & IN_PARAMETER) {
-            generate_write_to_parcel(t, tryStatement->statements, v, _data, 0);
-        }
-        arg = arg->next;
-    }
-
-    // the transact call
-    MethodCall* call = new MethodCall(proxyClass->mRemote, "transact", 4,
-                            new LiteralExpression("Stub." + transactCodeName),
-                            _data, _reply ? _reply : NULL_VALUE,
-                            new LiteralExpression(
-                                oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
-    tryStatement->statements->Add(call);
-
-    // throw back exceptions.
-    if (_reply) {
-        MethodCall* ex = new MethodCall(_reply, "readException", 0);
-        tryStatement->statements->Add(ex);
-    }
-
-    // returning and cleanup
-    if (_reply != NULL) {
-        if (_result != NULL) {
-            generate_create_from_parcel(proxy->returnType,
-                    tryStatement->statements, _result, _reply, &cl);
-        }
-
-        // the out/inout parameters
-        arg = method->args;
-        while (arg != NULL) {
-            Type* t = NAMES.Search(arg->type.type.data);
-            Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
-            if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
-                generate_read_from_parcel(t, tryStatement->statements,
-                                            v, _reply, &cl);
-            }
-            arg = arg->next;
-        }
-
-        finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
-    }
-    finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
-
-    if (_result != NULL) {
-        proxy->statements->Add(new ReturnStatement(_result));
-    }
-}
-
-static void
-generate_interface_descriptors(StubClass* stub, ProxyClass* proxy)
-{
-    // the interface descriptor transaction handler
-    Case* c = new Case("INTERFACE_TRANSACTION");
-    c->statements->Add(new MethodCall(stub->transact_reply, "writeString",
-            1, new LiteralExpression("DESCRIPTOR")));
-    c->statements->Add(new ReturnStatement(TRUE_VALUE));
-    stub->transact_switch->cases.push_back(c);
-
-    // and the proxy-side method returning the descriptor directly
-    Method* getDesc = new Method;
-    getDesc->modifiers = PUBLIC;
-    getDesc->returnType = STRING_TYPE;
-    getDesc->returnTypeDimension = 0;
-    getDesc->name = "getInterfaceDescriptor";
-    getDesc->statements = new StatementBlock;
-    getDesc->statements->Add(new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
-    proxy->elements.push_back(getDesc);
-}
-
-static Class*
-generate_interface_class(const interface_type* iface)
-{
-    InterfaceType* interfaceType = static_cast<InterfaceType*>(
-        NAMES.Find(iface->package, iface->name.data));
-
-    // the interface class
-    Class* interface = new Class;
-        interface->comment = gather_comments(iface->comments_token->extra);
-        interface->modifiers = PUBLIC;
-        interface->what = Class::INTERFACE;
-        interface->type = interfaceType;
-        interface->interfaces.push_back(IINTERFACE_TYPE);
-
-    // the stub inner class
-    StubClass* stub = new StubClass(
-        NAMES.Find(iface->package, append(iface->name.data, ".Stub").c_str()),
-        interfaceType);
-    interface->elements.push_back(stub);
-
-    // the proxy inner class
-    ProxyClass* proxy = new ProxyClass(
-        NAMES.Find(iface->package,
-                         append(iface->name.data, ".Stub.Proxy").c_str()),
-        interfaceType);
-    stub->elements.push_back(proxy);
-
-    // stub and proxy support for getInterfaceDescriptor()
-    generate_interface_descriptors(stub, proxy);
-
-    // all the declared methods of the interface
-    int index = 0;
-    interface_item_type* item = iface->interface_items;
-    while (item != NULL) {
-        if (item->item_type == METHOD_TYPE) {
-            generate_method((method_type*)item, interface, stub, proxy, index);
-        }
-        item = item->next;
-        index++;
-    }
-
-    return interface;
-}
-
+// =================================================
 int
 generate_java(const string& filename, const string& originalSrc,
                 interface_type* iface)
 {
+    Class* cl;
+
+    if (iface->document_item.item_type == INTERFACE_TYPE_BINDER) {
+        cl = generate_binder_interface_class(iface);
+    }
+    else if (iface->document_item.item_type == INTERFACE_TYPE_RPC) {
+        cl = generate_rpc_interface_class(iface);
+    }
+
     Document* document = new Document;
         document->comment = "";
         if (iface->package) document->package = iface->package;
         document->originalSrc = originalSrc;
-        document->classes.push_back(generate_interface_class(iface));
+        document->classes.push_back(cl);
 
 //    printf("outputting... filename=%s\n", filename.c_str());
     FILE* to;
diff --git a/tools/aidl/generate_java.h b/tools/aidl/generate_java.h
index 203fe23..4bfcfeb 100644
--- a/tools/aidl/generate_java.h
+++ b/tools/aidl/generate_java.h
@@ -2,6 +2,7 @@
 #define GENERATE_JAVA_H
 
 #include "aidl_language.h"
+#include "AST.h"
 
 #include <string>
 
@@ -10,5 +11,23 @@
 int generate_java(const string& filename, const string& originalSrc,
                 interface_type* iface);
 
+Class* generate_binder_interface_class(const interface_type* iface);
+Class* generate_rpc_interface_class(const interface_type* iface);
+
+string gather_comments(extra_text_type* extra);
+string append(const char* a, const char* b);
+
+class VariableFactory
+{
+public:
+    VariableFactory(const string& base); // base must be short
+    Variable* Get(Type* type);
+    Variable* Get(int index);
+private:
+    vector<Variable*> m_vars;
+    string m_base;
+    int m_index;
+};
+
 #endif // GENERATE_JAVA_H
 
diff --git a/tools/aidl/generate_java_binder.cpp b/tools/aidl/generate_java_binder.cpp
new file mode 100644
index 0000000..2e459a8
--- /dev/null
+++ b/tools/aidl/generate_java_binder.cpp
@@ -0,0 +1,559 @@
+#include "generate_java.h"
+#include "Type.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// =================================================
+class StubClass : public Class
+{
+public:
+    StubClass(Type* type, Type* interfaceType);
+    virtual ~StubClass();
+
+    Variable* transact_code;
+    Variable* transact_data;
+    Variable* transact_reply;
+    Variable* transact_flags;
+    SwitchStatement* transact_switch;
+private:
+    void make_as_interface(Type* interfaceType);
+};
+
+StubClass::StubClass(Type* type, Type* interfaceType)
+    :Class()
+{
+    this->comment = "/** Local-side IPC implementation stub class. */";
+    this->modifiers = PUBLIC | ABSTRACT | STATIC;
+    this->what = Class::CLASS;
+    this->type = type;
+    this->extends = BINDER_NATIVE_TYPE;
+    this->interfaces.push_back(interfaceType);
+
+    // descriptor
+    Field* descriptor = new Field(STATIC | FINAL | PRIVATE,
+                            new Variable(STRING_TYPE, "DESCRIPTOR"));
+    descriptor->value = "\"" + interfaceType->QualifiedName() + "\"";
+    this->elements.push_back(descriptor);
+
+    // ctor
+    Method* ctor = new Method;
+        ctor->modifiers = PUBLIC;
+        ctor->comment = "/** Construct the stub at attach it to the "
+                        "interface. */";
+        ctor->name = "Stub";
+        ctor->statements = new StatementBlock;
+    MethodCall* attach = new MethodCall(THIS_VALUE, "attachInterface",
+                            2, THIS_VALUE, new LiteralExpression("DESCRIPTOR"));
+    ctor->statements->Add(attach);
+    this->elements.push_back(ctor);
+
+    // asInterface
+    make_as_interface(interfaceType);
+
+    // asBinder
+    Method* asBinder = new Method;
+        asBinder->modifiers = PUBLIC;
+        asBinder->returnType = IBINDER_TYPE;
+        asBinder->name = "asBinder";
+        asBinder->statements = new StatementBlock;
+    asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
+    this->elements.push_back(asBinder);
+
+    // onTransact
+    this->transact_code = new Variable(INT_TYPE, "code");
+    this->transact_data = new Variable(PARCEL_TYPE, "data");
+    this->transact_reply = new Variable(PARCEL_TYPE, "reply");
+    this->transact_flags = new Variable(INT_TYPE, "flags");
+    Method* onTransact = new Method;
+        onTransact->modifiers = PUBLIC | OVERRIDE;
+        onTransact->returnType = BOOLEAN_TYPE;
+        onTransact->name = "onTransact";
+        onTransact->parameters.push_back(this->transact_code);
+        onTransact->parameters.push_back(this->transact_data);
+        onTransact->parameters.push_back(this->transact_reply);
+        onTransact->parameters.push_back(this->transact_flags);
+        onTransact->statements = new StatementBlock;
+        onTransact->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
+    this->elements.push_back(onTransact);
+    this->transact_switch = new SwitchStatement(this->transact_code);
+
+    onTransact->statements->Add(this->transact_switch);
+    MethodCall* superCall = new MethodCall(SUPER_VALUE, "onTransact", 4,
+                                    this->transact_code, this->transact_data,
+                                    this->transact_reply, this->transact_flags);
+    onTransact->statements->Add(new ReturnStatement(superCall));
+}
+
+StubClass::~StubClass()
+{
+}
+
+void
+StubClass::make_as_interface(Type *interfaceType)
+{
+    Variable* obj = new Variable(IBINDER_TYPE, "obj");
+
+    Method* m = new Method;
+        m->comment = "/**\n * Cast an IBinder object into an ";
+        m->comment += interfaceType->QualifiedName();
+        m->comment += " interface,\n";
+        m->comment += " * generating a proxy if needed.\n */";
+        m->modifiers = PUBLIC | STATIC;
+        m->returnType = interfaceType;
+        m->name = "asInterface";
+        m->parameters.push_back(obj);
+        m->statements = new StatementBlock;
+
+    IfStatement* ifstatement = new IfStatement();
+        ifstatement->expression = new Comparison(obj, "==", NULL_VALUE);
+        ifstatement->statements = new StatementBlock;
+        ifstatement->statements->Add(new ReturnStatement(NULL_VALUE));
+    m->statements->Add(ifstatement);
+
+    // IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
+    MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface");
+    queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR"));
+    IInterfaceType* iinType = new IInterfaceType();
+    Variable *iin = new Variable(iinType, "iin");
+    VariableDeclaration* iinVd = new VariableDeclaration(iin, queryLocalInterface, iinType);
+    m->statements->Add(iinVd);
+
+    // Ensure the instance type of the local object is as expected.
+    // One scenario where this is needed is if another package (with a
+    // different class loader) runs in the same process as the service.
+
+    // if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>) iin;
+    Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE);
+    Comparison* instOfCheck = new Comparison(iin, " instanceof ",
+            new LiteralExpression(interfaceType->QualifiedName()));
+    IfStatement* instOfStatement = new IfStatement();
+        instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck);
+        instOfStatement->statements = new StatementBlock;
+        instOfStatement->statements->Add(new ReturnStatement(new Cast(interfaceType, iin)));
+    m->statements->Add(instOfStatement);
+
+    string proxyType = interfaceType->QualifiedName();
+    proxyType += ".Stub.Proxy";
+    NewExpression* ne = new NewExpression(NAMES.Find(proxyType));
+    ne->arguments.push_back(obj);
+    m->statements->Add(new ReturnStatement(ne));
+
+    this->elements.push_back(m);
+}
+
+
+
+// =================================================
+class ProxyClass : public Class
+{
+public:
+    ProxyClass(Type* type, InterfaceType* interfaceType);
+    virtual ~ProxyClass();
+
+    Variable* mRemote;
+    bool mOneWay;
+};
+
+ProxyClass::ProxyClass(Type* type, InterfaceType* interfaceType)
+    :Class()
+{
+    this->modifiers = PRIVATE | STATIC;
+    this->what = Class::CLASS;
+    this->type = type;
+    this->interfaces.push_back(interfaceType);
+
+    mOneWay = interfaceType->OneWay();
+
+    // IBinder mRemote
+    mRemote = new Variable(IBINDER_TYPE, "mRemote");
+    this->elements.push_back(new Field(PRIVATE, mRemote));
+
+    // Proxy()
+    Variable* remote = new Variable(IBINDER_TYPE, "remote");
+    Method* ctor = new Method;
+        ctor->name = "Proxy";
+        ctor->statements = new StatementBlock;
+        ctor->parameters.push_back(remote);
+    ctor->statements->Add(new Assignment(mRemote, remote));
+    this->elements.push_back(ctor);
+
+    // IBinder asBinder()
+    Method* asBinder = new Method;
+        asBinder->modifiers = PUBLIC;
+        asBinder->returnType = IBINDER_TYPE;
+        asBinder->name = "asBinder";
+        asBinder->statements = new StatementBlock;
+    asBinder->statements->Add(new ReturnStatement(mRemote));
+    this->elements.push_back(asBinder);
+}
+
+ProxyClass::~ProxyClass()
+{
+}
+
+// =================================================
+static void
+generate_new_array(Type* t, StatementBlock* addTo, Variable* v,
+                            Variable* parcel)
+{
+    Variable* len = new Variable(INT_TYPE, v->name + "_length");
+    addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt")));
+    IfStatement* lencheck = new IfStatement();
+    lencheck->expression = new Comparison(len, "<", new LiteralExpression("0"));
+    lencheck->statements->Add(new Assignment(v, NULL_VALUE));
+    lencheck->elseif = new IfStatement();
+    lencheck->elseif->statements->Add(new Assignment(v,
+                new NewArrayExpression(t, len)));
+    addTo->Add(lencheck);
+}
+
+static void
+generate_write_to_parcel(Type* t, StatementBlock* addTo, Variable* v,
+                            Variable* parcel, int flags)
+{
+    if (v->dimension == 0) {
+        t->WriteToParcel(addTo, v, parcel, flags);
+    }
+    if (v->dimension == 1) {
+        t->WriteArrayToParcel(addTo, v, parcel, flags);
+    }
+}
+
+static void
+generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
+                            Variable* parcel, Variable** cl)
+{
+    if (v->dimension == 0) {
+        t->CreateFromParcel(addTo, v, parcel, cl);
+    }
+    if (v->dimension == 1) {
+        t->CreateArrayFromParcel(addTo, v, parcel, cl);
+    }
+}
+
+static void
+generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
+                            Variable* parcel, Variable** cl)
+{
+    if (v->dimension == 0) {
+        t->ReadFromParcel(addTo, v, parcel, cl);
+    }
+    if (v->dimension == 1) {
+        t->ReadArrayFromParcel(addTo, v, parcel, cl);
+    }
+}
+
+
+static void
+generate_method(const method_type* method, Class* interface,
+                    StubClass* stubClass, ProxyClass* proxyClass, int index)
+{
+    arg_type* arg;
+    int i;
+    bool hasOutParams = false;
+
+    const bool oneway = proxyClass->mOneWay || method->oneway;
+
+    // == the TRANSACT_ constant =============================================
+    string transactCodeName = "TRANSACTION_";
+    transactCodeName += method->name.data;
+
+    char transactCodeValue[50];
+    sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
+
+    Field* transactCode = new Field(STATIC | FINAL,
+                            new Variable(INT_TYPE, transactCodeName));
+    transactCode->value = transactCodeValue;
+    stubClass->elements.push_back(transactCode);
+
+    // == the declaration in the interface ===================================
+    Method* decl = new Method;
+        decl->comment = gather_comments(method->comments_token->extra);
+        decl->modifiers = PUBLIC;
+        decl->returnType = NAMES.Search(method->type.type.data);
+        decl->returnTypeDimension = method->type.dimension;
+        decl->name = method->name.data;
+
+    arg = method->args;
+    while (arg != NULL) {
+        decl->parameters.push_back(new Variable(
+                            NAMES.Search(arg->type.type.data), arg->name.data,
+                            arg->type.dimension));
+        arg = arg->next;
+    }
+
+    decl->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
+
+    interface->elements.push_back(decl);
+
+    // == the stub method ====================================================
+
+    Case* c = new Case(transactCodeName);
+
+    MethodCall* realCall = new MethodCall(THIS_VALUE, method->name.data);
+
+    // interface token validation is the very first thing we do
+    c->statements->Add(new MethodCall(stubClass->transact_data,
+            "enforceInterface", 1, new LiteralExpression("DESCRIPTOR")));
+
+    // args
+    Variable* cl = NULL;
+    VariableFactory stubArgs("_arg");
+    arg = method->args;
+    while (arg != NULL) {
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = stubArgs.Get(t);
+        v->dimension = arg->type.dimension;
+
+        c->statements->Add(new VariableDeclaration(v));
+
+        if (convert_direction(arg->direction.data) & IN_PARAMETER) {
+            generate_create_from_parcel(t, c->statements, v,
+                    stubClass->transact_data, &cl);
+        } else {
+            if (arg->type.dimension == 0) {
+                c->statements->Add(new Assignment(v, new NewExpression(v->type)));
+            }
+            else if (arg->type.dimension == 1) {
+                generate_new_array(v->type, c->statements, v,
+                        stubClass->transact_data);
+            }
+            else {
+                fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__,
+                        __LINE__);
+            }
+        }
+
+        realCall->arguments.push_back(v);
+
+        arg = arg->next;
+    }
+
+    // the real call
+    Variable* _result = NULL;
+    if (0 == strcmp(method->type.type.data, "void")) {
+        c->statements->Add(realCall);
+
+        if (!oneway) {
+            // report that there were no exceptions
+            MethodCall* ex = new MethodCall(stubClass->transact_reply,
+                    "writeNoException", 0);
+            c->statements->Add(ex);
+        }
+    } else {
+        _result = new Variable(decl->returnType, "_result",
+                                decl->returnTypeDimension);
+        c->statements->Add(new VariableDeclaration(_result, realCall));
+
+        if (!oneway) {
+            // report that there were no exceptions
+            MethodCall* ex = new MethodCall(stubClass->transact_reply,
+                    "writeNoException", 0);
+            c->statements->Add(ex);
+        }
+
+        // marshall the return value
+        generate_write_to_parcel(decl->returnType, c->statements, _result,
+                                    stubClass->transact_reply,
+                                    Type::PARCELABLE_WRITE_RETURN_VALUE);
+    }
+
+    // out parameters
+    i = 0;
+    arg = method->args;
+    while (arg != NULL) {
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = stubArgs.Get(i++);
+
+        if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
+            generate_write_to_parcel(t, c->statements, v,
+                                stubClass->transact_reply,
+                                Type::PARCELABLE_WRITE_RETURN_VALUE);
+            hasOutParams = true;
+        }
+
+        arg = arg->next;
+    }
+
+    // return true
+    c->statements->Add(new ReturnStatement(TRUE_VALUE));
+    stubClass->transact_switch->cases.push_back(c);
+
+    // == the proxy method ===================================================
+    Method* proxy = new Method;
+        proxy->comment = gather_comments(method->comments_token->extra);
+        proxy->modifiers = PUBLIC;
+        proxy->returnType = NAMES.Search(method->type.type.data);
+        proxy->returnTypeDimension = method->type.dimension;
+        proxy->name = method->name.data;
+        proxy->statements = new StatementBlock;
+        arg = method->args;
+        while (arg != NULL) {
+            proxy->parameters.push_back(new Variable(
+                            NAMES.Search(arg->type.type.data), arg->name.data,
+                            arg->type.dimension));
+            arg = arg->next;
+        }
+        proxy->exceptions.push_back(REMOTE_EXCEPTION_TYPE);
+    proxyClass->elements.push_back(proxy);
+
+    // the parcels
+    Variable* _data = new Variable(PARCEL_TYPE, "_data");
+    proxy->statements->Add(new VariableDeclaration(_data,
+                                new MethodCall(PARCEL_TYPE, "obtain")));
+    Variable* _reply = NULL;
+    if (!oneway) {
+        _reply = new Variable(PARCEL_TYPE, "_reply");
+        proxy->statements->Add(new VariableDeclaration(_reply,
+                                    new MethodCall(PARCEL_TYPE, "obtain")));
+    }
+
+    // the return value
+    _result = NULL;
+    if (0 != strcmp(method->type.type.data, "void")) {
+        _result = new Variable(proxy->returnType, "_result",
+                method->type.dimension);
+        proxy->statements->Add(new VariableDeclaration(_result));
+    }
+
+    // try and finally
+    TryStatement* tryStatement = new TryStatement();
+    proxy->statements->Add(tryStatement);
+    FinallyStatement* finallyStatement = new FinallyStatement();
+    proxy->statements->Add(finallyStatement);
+
+    // the interface identifier token: the DESCRIPTOR constant, marshalled as a string
+    tryStatement->statements->Add(new MethodCall(_data, "writeInterfaceToken",
+            1, new LiteralExpression("DESCRIPTOR")));
+
+    // the parameters
+    arg = method->args;
+    while (arg != NULL) {
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
+        int dir = convert_direction(arg->direction.data);
+        if (dir == OUT_PARAMETER && arg->type.dimension != 0) {
+            IfStatement* checklen = new IfStatement();
+            checklen->expression = new Comparison(v, "==", NULL_VALUE);
+            checklen->statements->Add(new MethodCall(_data, "writeInt", 1,
+                        new LiteralExpression("-1")));
+            checklen->elseif = new IfStatement();
+            checklen->elseif->statements->Add(new MethodCall(_data, "writeInt",
+                        1, new FieldVariable(v, "length")));
+            tryStatement->statements->Add(checklen);
+        }
+        else if (dir & IN_PARAMETER) {
+            generate_write_to_parcel(t, tryStatement->statements, v, _data, 0);
+        }
+        arg = arg->next;
+    }
+
+    // the transact call
+    MethodCall* call = new MethodCall(proxyClass->mRemote, "transact", 4,
+                            new LiteralExpression("Stub." + transactCodeName),
+                            _data, _reply ? _reply : NULL_VALUE,
+                            new LiteralExpression(
+                                oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
+    tryStatement->statements->Add(call);
+
+    // throw back exceptions.
+    if (_reply) {
+        MethodCall* ex = new MethodCall(_reply, "readException", 0);
+        tryStatement->statements->Add(ex);
+    }
+
+    // returning and cleanup
+    if (_reply != NULL) {
+        if (_result != NULL) {
+            generate_create_from_parcel(proxy->returnType,
+                    tryStatement->statements, _result, _reply, &cl);
+        }
+
+        // the out/inout parameters
+        arg = method->args;
+        while (arg != NULL) {
+            Type* t = NAMES.Search(arg->type.type.data);
+            Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
+            if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
+                generate_read_from_parcel(t, tryStatement->statements,
+                                            v, _reply, &cl);
+            }
+            arg = arg->next;
+        }
+
+        finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
+    }
+    finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
+
+    if (_result != NULL) {
+        proxy->statements->Add(new ReturnStatement(_result));
+    }
+}
+
+static void
+generate_interface_descriptors(StubClass* stub, ProxyClass* proxy)
+{
+    // the interface descriptor transaction handler
+    Case* c = new Case("INTERFACE_TRANSACTION");
+    c->statements->Add(new MethodCall(stub->transact_reply, "writeString",
+            1, new LiteralExpression("DESCRIPTOR")));
+    c->statements->Add(new ReturnStatement(TRUE_VALUE));
+    stub->transact_switch->cases.push_back(c);
+
+    // and the proxy-side method returning the descriptor directly
+    Method* getDesc = new Method;
+    getDesc->modifiers = PUBLIC;
+    getDesc->returnType = STRING_TYPE;
+    getDesc->returnTypeDimension = 0;
+    getDesc->name = "getInterfaceDescriptor";
+    getDesc->statements = new StatementBlock;
+    getDesc->statements->Add(new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
+    proxy->elements.push_back(getDesc);
+}
+
+Class*
+generate_binder_interface_class(const interface_type* iface)
+{
+    InterfaceType* interfaceType = static_cast<InterfaceType*>(
+        NAMES.Find(iface->package, iface->name.data));
+
+    // the interface class
+    Class* interface = new Class;
+        interface->comment = gather_comments(iface->comments_token->extra);
+        interface->modifiers = PUBLIC;
+        interface->what = Class::INTERFACE;
+        interface->type = interfaceType;
+        interface->interfaces.push_back(IINTERFACE_TYPE);
+
+    // the stub inner class
+    StubClass* stub = new StubClass(
+        NAMES.Find(iface->package, append(iface->name.data, ".Stub").c_str()),
+        interfaceType);
+    interface->elements.push_back(stub);
+
+    // the proxy inner class
+    ProxyClass* proxy = new ProxyClass(
+        NAMES.Find(iface->package,
+                         append(iface->name.data, ".Stub.Proxy").c_str()),
+        interfaceType);
+    stub->elements.push_back(proxy);
+
+    // stub and proxy support for getInterfaceDescriptor()
+    generate_interface_descriptors(stub, proxy);
+
+    // all the declared methods of the interface
+    int index = 0;
+    interface_item_type* item = iface->interface_items;
+    while (item != NULL) {
+        if (item->item_type == METHOD_TYPE) {
+            generate_method((method_type*)item, interface, stub, proxy, index);
+        }
+        item = item->next;
+        index++;
+    }
+
+    return interface;
+}
+
diff --git a/tools/aidl/generate_java_rpc.cpp b/tools/aidl/generate_java_rpc.cpp
new file mode 100644
index 0000000..e5fa076
--- /dev/null
+++ b/tools/aidl/generate_java_rpc.cpp
@@ -0,0 +1,998 @@
+#include "generate_java.h"
+#include "Type.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+Type* SERVICE_CONTEXT_TYPE = new Type("android.content",
+        "Context", Type::BUILT_IN, false, false, false);
+Type* PRESENTER_BASE_TYPE = new Type("android.support.place.connector",
+        "EventListener", Type::BUILT_IN, false, false, false);
+Type* PRESENTER_LISTENER_BASE_TYPE = new Type("android.support.place.connector",
+        "EventListener.Listener", Type::BUILT_IN, false, false, false);
+Type* RPC_BROKER_TYPE = new Type("android.support.place.connector", "Broker",
+        Type::BUILT_IN, false, false, false);
+Type* RPC_CONTAINER_TYPE = new Type("com.android.athome.connector", "ConnectorContainer",
+        Type::BUILT_IN, false, false, false);
+Type* PLACE_INFO_TYPE = new Type("android.support.place.connector", "PlaceInfo",
+        Type::BUILT_IN, false, false, false);
+// TODO: Just use Endpoint, so this works for all endpoints.
+Type* RPC_CONNECTOR_TYPE = new Type("android.support.place.connector", "Connector",
+        Type::BUILT_IN, false, false, false);
+Type* RPC_ENDPOINT_INFO_TYPE = new UserDataType("android.support.place.rpc",
+        "EndpointInfo", true, __FILE__, __LINE__);
+Type* RPC_RESULT_HANDLER_TYPE = new UserDataType("android.support.place.rpc", "RpcResultHandler",
+        true, __FILE__, __LINE__);
+Type* RPC_ERROR_LISTENER_TYPE = new Type("android.support.place.rpc", "RpcErrorHandler",
+        Type::BUILT_IN, false, false, false);
+Type* RPC_CONTEXT_TYPE = new UserDataType("android.support.place.rpc", "RpcContext", true,
+        __FILE__, __LINE__);
+
+static void generate_create_from_data(Type* t, StatementBlock* addTo, const string& key,
+        Variable* v, Variable* data, Variable** cl);
+static void generate_new_array(Type* t, StatementBlock* addTo, Variable* v, Variable* from);
+static void generate_write_to_data(Type* t, StatementBlock* addTo, Expression* k, Variable* v,
+        Variable* data);
+
+static string
+format_int(int n)
+{
+    char str[20];
+    sprintf(str, "%d", n);
+    return string(str);
+}
+
+static string
+class_name_leaf(const string& str)
+{
+    string::size_type pos = str.rfind('.');
+    if (pos == string::npos) {
+        return str;
+    } else {
+        return string(str, pos+1);
+    }
+}
+
+static string
+results_class_name(const string& n)
+{
+    string str = n;
+    str[0] = toupper(str[0]);
+    str.insert(0, "On");
+    return str;
+}
+
+static string
+results_method_name(const string& n)
+{
+    string str = n;
+    str[0] = toupper(str[0]);
+    str.insert(0, "on");
+    return str;
+}
+
+static string
+push_method_name(const string& n)
+{
+    string str = n;
+    str[0] = toupper(str[0]);
+    str.insert(0, "push");
+    return str;
+}
+
+// =================================================
+class DispatcherClass : public Class
+{
+public:
+    DispatcherClass(const interface_type* iface, Expression* target);
+    virtual ~DispatcherClass();
+
+    void AddMethod(const method_type* method);
+    void DoneWithMethods();
+
+    Method* processMethod;
+    Variable* actionParam;
+    Variable* requestParam;
+    Variable* rpcContextParam;
+    Variable* errorParam;
+    Variable* requestData;
+    Variable* resultData;
+    IfStatement* dispatchIfStatement;
+    Expression* targetExpression;
+
+private:
+    void generate_process();
+};
+
+DispatcherClass::DispatcherClass(const interface_type* iface, Expression* target)
+    :Class(),
+     dispatchIfStatement(NULL),
+     targetExpression(target)
+{
+    generate_process();
+}
+
+DispatcherClass::~DispatcherClass()
+{
+}
+
+void
+DispatcherClass::generate_process()
+{
+    // byte[] process(String action, byte[] params, RpcContext context, RpcError status)
+    this->processMethod = new Method;
+        this->processMethod->modifiers = PUBLIC;
+        this->processMethod->returnType = BYTE_TYPE;
+        this->processMethod->returnTypeDimension = 1;
+        this->processMethod->name = "process";
+        this->processMethod->statements = new StatementBlock;
+
+    this->actionParam = new Variable(STRING_TYPE, "action");
+    this->processMethod->parameters.push_back(this->actionParam);
+
+    this->requestParam = new Variable(BYTE_TYPE, "requestParam", 1);
+    this->processMethod->parameters.push_back(this->requestParam);
+
+    this->rpcContextParam = new Variable(RPC_CONTEXT_TYPE, "context", 0);
+    this->processMethod->parameters.push_back(this->rpcContextParam);    
+
+    this->errorParam = new Variable(RPC_ERROR_TYPE, "errorParam", 0);
+    this->processMethod->parameters.push_back(this->errorParam);
+
+    this->requestData = new Variable(RPC_DATA_TYPE, "request");
+    this->processMethod->statements->Add(new VariableDeclaration(requestData,
+                new NewExpression(RPC_DATA_TYPE, 1, this->requestParam)));
+
+    this->resultData = new Variable(RPC_DATA_TYPE, "resultData");
+    this->processMethod->statements->Add(new VariableDeclaration(this->resultData,
+                NULL_VALUE));
+}
+
+void
+DispatcherClass::AddMethod(const method_type* method)
+{
+    arg_type* arg;
+
+    // The if/switch statement
+    IfStatement* ifs = new IfStatement();
+        ifs->expression = new MethodCall(new StringLiteralExpression(method->name.data), "equals",
+                1, this->actionParam);
+    StatementBlock* block = ifs->statements = new StatementBlock;
+    if (this->dispatchIfStatement == NULL) {
+        this->dispatchIfStatement = ifs;
+        this->processMethod->statements->Add(dispatchIfStatement);
+    } else {
+        this->dispatchIfStatement->elseif = ifs;
+        this->dispatchIfStatement = ifs;
+    }
+    
+    // The call to decl (from above)
+    MethodCall* realCall = new MethodCall(this->targetExpression, method->name.data);
+
+    // args
+    Variable* classLoader = NULL;
+    VariableFactory stubArgs("_arg");
+    arg = method->args;
+    while (arg != NULL) {
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = stubArgs.Get(t);
+        v->dimension = arg->type.dimension;
+
+        // Unmarshall the parameter
+        block->Add(new VariableDeclaration(v));
+        if (convert_direction(arg->direction.data) & IN_PARAMETER) {
+            generate_create_from_data(t, block, arg->name.data, v,
+                    this->requestData, &classLoader);
+        } else {
+            if (arg->type.dimension == 0) {
+                block->Add(new Assignment(v, new NewExpression(v->type)));
+            }
+            else if (arg->type.dimension == 1) {
+                generate_new_array(v->type, block, v, this->requestData);
+            }
+            else {
+                fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__,
+                        __LINE__);
+            }
+        }
+
+        // Add that parameter to the method call
+        realCall->arguments.push_back(v);
+
+        arg = arg->next;
+    }
+
+    // Add a final parameter: RpcContext. Contains data about
+    // incoming request (e.g., certificate)
+    realCall->arguments.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0));
+
+    Type* returnType = NAMES.Search(method->type.type.data);
+    if (returnType == EVENT_FAKE_TYPE) {
+        returnType = VOID_TYPE;
+    }
+    
+    // the real call
+    bool first = true;
+    Variable* _result = NULL;
+    if (returnType == VOID_TYPE) {
+        block->Add(realCall);
+    } else {
+        _result = new Variable(returnType, "_result",
+                                method->type.dimension);
+        block->Add(new VariableDeclaration(_result, realCall));
+
+        // need the result RpcData
+        if (first) {
+            block->Add(new Assignment(this->resultData,
+                        new NewExpression(RPC_DATA_TYPE)));
+            first = false;
+        }
+
+        // marshall the return value
+        generate_write_to_data(returnType, block,
+                new StringLiteralExpression("_result"), _result, this->resultData);
+    }
+
+    // out parameters
+    int i = 0;
+    arg = method->args;
+    while (arg != NULL) {
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = stubArgs.Get(i++);
+
+        if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
+            // need the result RpcData
+            if (first) {
+                block->Add(new Assignment(this->resultData, new NewExpression(RPC_DATA_TYPE)));
+                first = false;
+            }
+
+            generate_write_to_data(t, block, new StringLiteralExpression(arg->name.data),
+                    v, this->resultData);
+        }
+
+        arg = arg->next;
+    }
+}
+
+void
+DispatcherClass::DoneWithMethods()
+{
+    if (this->dispatchIfStatement == NULL) {
+        return;
+    }
+
+    this->elements.push_back(this->processMethod);
+
+    IfStatement* fallthrough = new IfStatement();
+        fallthrough->statements = new StatementBlock;
+        fallthrough->statements->Add(new ReturnStatement(
+                    new MethodCall(SUPER_VALUE, "process", 4, 
+                    this->actionParam, this->requestParam, 
+                    this->rpcContextParam,
+                    this->errorParam)));
+    this->dispatchIfStatement->elseif = fallthrough;
+    IfStatement* s = new IfStatement;
+        s->statements = new StatementBlock;
+    this->processMethod->statements->Add(s);
+    s->expression = new Comparison(this->resultData, "!=", NULL_VALUE);
+    s->statements->Add(new ReturnStatement(new MethodCall(this->resultData, "serialize")));
+    s->elseif = new IfStatement;
+    s = s->elseif;
+    s->statements->Add(new ReturnStatement(NULL_VALUE));
+}
+
+// =================================================
+class RpcProxyClass : public Class
+{
+public:
+    RpcProxyClass(const interface_type* iface, InterfaceType* interfaceType);
+    virtual ~RpcProxyClass();
+
+    Variable* endpoint;
+    Variable* broker;
+
+private:
+    void generate_ctor();
+    void generate_get_endpoint_info();
+};
+
+RpcProxyClass::RpcProxyClass(const interface_type* iface, InterfaceType* interfaceType)
+    :Class()
+{
+    this->comment = gather_comments(iface->comments_token->extra);
+    this->modifiers = PUBLIC;
+    this->what = Class::CLASS;
+    this->type = interfaceType;
+
+    // broker
+    this->broker = new Variable(RPC_BROKER_TYPE, "_broker");
+    this->elements.push_back(new Field(PRIVATE, this->broker));
+    // endpoint
+    this->endpoint = new Variable(RPC_ENDPOINT_INFO_TYPE, "_endpoint");
+    this->elements.push_back(new Field(PRIVATE, this->endpoint));
+
+    // methods
+    generate_ctor();
+    generate_get_endpoint_info();
+}
+
+RpcProxyClass::~RpcProxyClass()
+{
+}
+
+void
+RpcProxyClass::generate_ctor()
+{
+    Variable* broker = new Variable(RPC_BROKER_TYPE, "broker");
+    Variable* endpoint = new Variable(RPC_ENDPOINT_INFO_TYPE, "endpoint");
+    Method* ctor = new Method;
+        ctor->modifiers = PUBLIC;
+        ctor->name = class_name_leaf(this->type->Name());
+        ctor->statements = new StatementBlock;
+        ctor->parameters.push_back(broker);
+        ctor->parameters.push_back(endpoint);
+    this->elements.push_back(ctor);
+
+    ctor->statements->Add(new Assignment(this->broker, broker));
+    ctor->statements->Add(new Assignment(this->endpoint, endpoint));
+}
+
+void
+RpcProxyClass::generate_get_endpoint_info()
+{
+    Method* get = new Method;
+    get->modifiers = PUBLIC;
+    get->returnType = RPC_ENDPOINT_INFO_TYPE;
+    get->name = "getEndpointInfo";
+    get->statements = new StatementBlock;
+    this->elements.push_back(get);
+
+    get->statements->Add(new ReturnStatement(this->endpoint));
+}
+
+// =================================================
+class EventListenerClass : public DispatcherClass
+{
+public:
+    EventListenerClass(const interface_type* iface, Type* listenerType);
+    virtual ~EventListenerClass();
+
+    Variable* _listener;
+
+private:
+    void generate_ctor();
+};
+
+Expression*
+generate_get_listener_expression(Type* cast)
+{
+    return new Cast(cast, new MethodCall(THIS_VALUE, "getView"));
+}
+
+EventListenerClass::EventListenerClass(const interface_type* iface, Type* listenerType)
+    :DispatcherClass(iface, new FieldVariable(THIS_VALUE, "_listener"))
+{
+    this->modifiers = PRIVATE;
+    this->what = Class::CLASS;
+    this->type = new Type(iface->package ? iface->package : "",
+                        append(iface->name.data, ".Presenter"),
+                        Type::GENERATED, false, false, false);
+    this->extends = PRESENTER_BASE_TYPE;
+
+    this->_listener = new Variable(listenerType, "_listener");
+    this->elements.push_back(new Field(PRIVATE, this->_listener));
+
+    // methods
+    generate_ctor();
+}
+
+EventListenerClass::~EventListenerClass()
+{
+}
+
+void
+EventListenerClass::generate_ctor()
+{
+    Variable* broker = new Variable(RPC_BROKER_TYPE, "broker");
+    Variable* listener = new Variable(this->_listener->type, "listener");
+    Method* ctor = new Method;
+        ctor->modifiers = PUBLIC;
+        ctor->name = class_name_leaf(this->type->Name());
+        ctor->statements = new StatementBlock;
+        ctor->parameters.push_back(broker);
+        ctor->parameters.push_back(listener);
+    this->elements.push_back(ctor);
+
+    ctor->statements->Add(new MethodCall("super", 2, broker, listener));
+    ctor->statements->Add(new Assignment(this->_listener, listener));
+}
+
+// =================================================
+class ListenerClass : public Class
+{
+public:
+    ListenerClass(const interface_type* iface);
+    virtual ~ListenerClass();
+
+    bool needed;
+
+private:
+    void generate_ctor();
+};
+
+ListenerClass::ListenerClass(const interface_type* iface)
+    :Class(),
+     needed(false)
+{
+    this->comment = "/** Extend this to listen to the events from this class. */";
+    this->modifiers = STATIC | PUBLIC ;
+    this->what = Class::CLASS;
+    this->type = new Type(iface->package ? iface->package : "",
+                        append(iface->name.data, ".Listener"),
+                        Type::GENERATED, false, false, false);
+    this->extends = PRESENTER_LISTENER_BASE_TYPE;
+}
+
+ListenerClass::~ListenerClass()
+{
+}
+
+// =================================================
+class EndpointBaseClass : public DispatcherClass
+{
+public:
+    EndpointBaseClass(const interface_type* iface);
+    virtual ~EndpointBaseClass();
+
+    bool needed;
+
+private:
+    void generate_ctor();
+};
+
+EndpointBaseClass::EndpointBaseClass(const interface_type* iface)
+    :DispatcherClass(iface, THIS_VALUE),
+     needed(false)
+{
+    this->comment = "/** Extend this to implement a link service. */";
+    this->modifiers = STATIC | PUBLIC | ABSTRACT;
+    this->what = Class::CLASS;
+    this->type = new Type(iface->package ? iface->package : "",
+                        append(iface->name.data, ".EndpointBase"),
+                        Type::GENERATED, false, false, false);
+    this->extends = RPC_CONNECTOR_TYPE;
+
+    // methods
+    generate_ctor();
+}
+
+EndpointBaseClass::~EndpointBaseClass()
+{
+}
+
+void
+EndpointBaseClass::generate_ctor()
+{
+    Variable* container = new Variable(RPC_CONTAINER_TYPE, "container");
+    Variable* broker = new Variable(RPC_BROKER_TYPE, "broker");
+	Variable* place = new Variable(PLACE_INFO_TYPE, "placeInfo");
+    Method* ctor = new Method;
+        ctor->modifiers = PUBLIC;
+        ctor->name = class_name_leaf(this->type->Name());
+        ctor->statements = new StatementBlock;
+        ctor->parameters.push_back(container);
+        ctor->parameters.push_back(broker);
+        ctor->parameters.push_back(place);
+    this->elements.push_back(ctor);
+
+    ctor->statements->Add(new MethodCall("super", 3, container, broker, place));
+}
+
+// =================================================
+class ResultDispatcherClass : public Class
+{
+public:
+    ResultDispatcherClass();
+    virtual ~ResultDispatcherClass();
+
+    void AddMethod(int index, const string& name, Method** method, Variable** param);
+
+    bool needed;
+    Variable* methodId;
+    Variable* callback;
+    Method* onResultMethod;
+    Variable* resultParam;
+    SwitchStatement* methodSwitch;
+
+private:
+    void generate_ctor();
+    void generate_onResult();
+};
+
+ResultDispatcherClass::ResultDispatcherClass()
+    :Class(),
+     needed(false)
+{
+    this->modifiers = PRIVATE | FINAL;
+    this->what = Class::CLASS;
+    this->type = new Type("_ResultDispatcher", Type::GENERATED, false, false, false);
+    this->interfaces.push_back(RPC_RESULT_HANDLER_TYPE);
+
+    // methodId
+    this->methodId = new Variable(INT_TYPE, "methodId");
+    this->elements.push_back(new Field(PRIVATE, this->methodId));
+    this->callback = new Variable(OBJECT_TYPE, "callback");
+    this->elements.push_back(new Field(PRIVATE, this->callback));
+
+    // methods
+    generate_ctor();
+    generate_onResult();
+}
+
+ResultDispatcherClass::~ResultDispatcherClass()
+{
+}
+
+void
+ResultDispatcherClass::generate_ctor()
+{
+    Variable* methodIdParam = new Variable(INT_TYPE, "methId");
+    Variable* callbackParam = new Variable(OBJECT_TYPE, "cbObj");
+    Method* ctor = new Method;
+        ctor->modifiers = PUBLIC;
+        ctor->name = class_name_leaf(this->type->Name());
+        ctor->statements = new StatementBlock;
+        ctor->parameters.push_back(methodIdParam);
+        ctor->parameters.push_back(callbackParam);
+    this->elements.push_back(ctor);
+
+    ctor->statements->Add(new Assignment(this->methodId, methodIdParam));
+    ctor->statements->Add(new Assignment(this->callback, callbackParam));
+}
+
+void
+ResultDispatcherClass::generate_onResult()
+{
+    this->onResultMethod = new Method;
+        this->onResultMethod->modifiers = PUBLIC;
+        this->onResultMethod->returnType = VOID_TYPE;
+        this->onResultMethod->returnTypeDimension = 0;
+        this->onResultMethod->name = "onResult";
+        this->onResultMethod->statements = new StatementBlock;
+    this->elements.push_back(this->onResultMethod);
+
+    this->resultParam = new Variable(BYTE_TYPE, "result", 1);
+    this->onResultMethod->parameters.push_back(this->resultParam);
+
+    this->methodSwitch = new SwitchStatement(this->methodId);
+    this->onResultMethod->statements->Add(this->methodSwitch);
+}
+
+void
+ResultDispatcherClass::AddMethod(int index, const string& name, Method** method, Variable** param)
+{
+    Method* m = new Method;
+        m->modifiers = PUBLIC;
+        m->returnType = VOID_TYPE;
+        m->returnTypeDimension = 0;
+        m->name = name;
+        m->statements = new StatementBlock;
+    *param = new Variable(BYTE_TYPE, "result", 1);
+    m->parameters.push_back(*param);
+    this->elements.push_back(m);
+    *method = m;
+
+    Case* c = new Case(format_int(index));
+    c->statements->Add(new MethodCall(new LiteralExpression("this"), name, 1, this->resultParam));
+    c->statements->Add(new Break());
+
+    this->methodSwitch->cases.push_back(c);
+}
+
+// =================================================
+static void
+generate_new_array(Type* t, StatementBlock* addTo, Variable* v, Variable* from)
+{
+    fprintf(stderr, "aidl: implement generate_new_array %s:%d\n", __FILE__, __LINE__);
+    exit(1);
+}
+
+static void
+generate_create_from_data(Type* t, StatementBlock* addTo, const string& key, Variable* v,
+                            Variable* data, Variable** cl)
+{
+    Expression* k = new StringLiteralExpression(key);
+    if (v->dimension == 0) {
+        t->CreateFromRpcData(addTo, k, v, data, cl);
+    }
+    if (v->dimension == 1) {
+        //t->ReadArrayFromRpcData(addTo, v, data, cl);
+        fprintf(stderr, "aidl: implement generate_create_from_data for arrays%s:%d\n",
+                __FILE__, __LINE__);
+    }
+}
+
+static void
+generate_write_to_data(Type* t, StatementBlock* addTo, Expression* k, Variable* v, Variable* data)
+{
+    if (v->dimension == 0) {
+        t->WriteToRpcData(addTo, k, v, data, 0);
+    }
+    if (v->dimension == 1) {
+        //t->WriteArrayToParcel(addTo, v, data);
+        fprintf(stderr, "aidl: implement generate_write_to_data for arrays%s:%d\n",
+                __FILE__, __LINE__);
+    }
+}
+
+// =================================================
+static Type*
+generate_results_method(const method_type* method, RpcProxyClass* proxyClass)
+{
+    arg_type* arg;
+
+    string resultsMethodName = results_method_name(method->name.data);
+    Type* resultsInterfaceType = new Type(results_class_name(method->name.data),
+            Type::GENERATED, false, false, false);
+
+    if (!method->oneway) {
+        Class* resultsClass = new Class;
+            resultsClass->modifiers = STATIC | PUBLIC;
+            resultsClass->what = Class::INTERFACE;
+            resultsClass->type = resultsInterfaceType;
+
+        Method* resultMethod = new Method;
+            resultMethod->comment = gather_comments(method->comments_token->extra);
+            resultMethod->modifiers = PUBLIC;
+            resultMethod->returnType = VOID_TYPE;
+            resultMethod->returnTypeDimension = 0;
+            resultMethod->name = resultsMethodName;
+        if (0 != strcmp("void", method->type.type.data)) {
+            resultMethod->parameters.push_back(new Variable(NAMES.Search(method->type.type.data),
+                        "_result", method->type.dimension));
+        }
+        arg = method->args;
+        while (arg != NULL) {
+            if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
+                resultMethod->parameters.push_back(new Variable(
+                                    NAMES.Search(arg->type.type.data), arg->name.data,
+                                    arg->type.dimension));
+            }
+            arg = arg->next;
+        }
+        resultsClass->elements.push_back(resultMethod);
+
+        if (resultMethod->parameters.size() > 0) {
+            proxyClass->elements.push_back(resultsClass);
+            return resultsInterfaceType;
+        } 
+    }
+    //delete resultsInterfaceType;
+    return NULL;
+}
+
+static void
+generate_proxy_method(const method_type* method, RpcProxyClass* proxyClass,
+        ResultDispatcherClass* resultsDispatcherClass, Type* resultsInterfaceType, int index)
+{
+    arg_type* arg;
+    Method* proxyMethod = new Method;
+        proxyMethod->comment = gather_comments(method->comments_token->extra);
+        proxyMethod->modifiers = PUBLIC;
+        proxyMethod->returnType = VOID_TYPE;
+        proxyMethod->returnTypeDimension = 0;
+        proxyMethod->name = method->name.data;
+        proxyMethod->statements = new StatementBlock;
+    proxyClass->elements.push_back(proxyMethod);
+
+    // The local variables
+    Variable* _data = new Variable(RPC_DATA_TYPE, "_data");
+    proxyMethod->statements->Add(new VariableDeclaration(_data, new NewExpression(RPC_DATA_TYPE)));
+
+    // Add the arguments
+    arg = method->args;
+    while (arg != NULL) {
+        if (convert_direction(arg->direction.data) & IN_PARAMETER) {
+            // Function signature
+            Type* t = NAMES.Search(arg->type.type.data);
+            Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
+            proxyMethod->parameters.push_back(v);
+
+            // Input parameter marshalling
+            generate_write_to_data(t, proxyMethod->statements,
+                    new StringLiteralExpression(arg->name.data), v, _data);
+        }
+        arg = arg->next;
+    }
+
+    // If there is a results interface for this class
+    Expression* resultParameter;
+    if (resultsInterfaceType != NULL) {
+        // Result interface parameter
+        Variable* resultListener = new Variable(resultsInterfaceType, "_result");
+        proxyMethod->parameters.push_back(resultListener);
+
+        // Add the results dispatcher callback
+        resultsDispatcherClass->needed = true;
+        resultParameter = new NewExpression(resultsDispatcherClass->type, 2,
+                new LiteralExpression(format_int(index)), resultListener);
+    } else {
+        resultParameter = NULL_VALUE;
+    }
+
+    // All proxy methods take an error parameter
+    Variable* errorListener = new Variable(RPC_ERROR_LISTENER_TYPE, "_errors");
+    proxyMethod->parameters.push_back(errorListener);
+
+    // Call the broker
+    proxyMethod->statements->Add(new MethodCall(new FieldVariable(THIS_VALUE, "_broker"),
+                "sendRpc", 5,
+                proxyClass->endpoint,
+                new StringLiteralExpression(method->name.data),
+                new MethodCall(_data, "serialize"),
+                resultParameter,
+                errorListener));
+}
+
+static void
+generate_result_dispatcher_method(const method_type* method,
+        ResultDispatcherClass* resultsDispatcherClass, Type* resultsInterfaceType, int index)
+{
+    arg_type* arg;
+    Method* dispatchMethod;
+    Variable* dispatchParam;
+    resultsDispatcherClass->AddMethod(index, method->name.data, &dispatchMethod, &dispatchParam);
+
+    Variable* classLoader = NULL;
+    Variable* resultData = new Variable(RPC_DATA_TYPE, "resultData");
+    dispatchMethod->statements->Add(new VariableDeclaration(resultData,
+                new NewExpression(RPC_DATA_TYPE, 1, dispatchParam)));
+
+    // The callback method itself
+    MethodCall* realCall = new MethodCall(
+            new Cast(resultsInterfaceType, new FieldVariable(THIS_VALUE, "callback")),
+            results_method_name(method->name.data));
+
+    // The return value
+    {
+        Type* t = NAMES.Search(method->type.type.data);
+        if (t != VOID_TYPE) {
+            Variable* rv = new Variable(t, "rv");
+            dispatchMethod->statements->Add(new VariableDeclaration(rv));
+            generate_create_from_data(t, dispatchMethod->statements, "_result", rv,
+                    resultData, &classLoader);
+            realCall->arguments.push_back(rv);
+        }
+    }
+
+    VariableFactory stubArgs("arg");
+    arg = method->args;
+    while (arg != NULL) {
+        if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
+            // Unmarshall the results
+            Type* t = NAMES.Search(arg->type.type.data);
+            Variable* v = stubArgs.Get(t);
+            dispatchMethod->statements->Add(new VariableDeclaration(v));
+
+            generate_create_from_data(t, dispatchMethod->statements, arg->name.data, v,
+                    resultData, &classLoader);
+
+            // Add the argument to the callback
+            realCall->arguments.push_back(v);
+        }
+        arg = arg->next;
+    }
+
+    // Call the callback method
+    dispatchMethod->statements->Add(realCall);
+}
+
+static void
+generate_regular_method(const method_type* method, RpcProxyClass* proxyClass,
+        EndpointBaseClass* serviceBaseClass, ResultDispatcherClass* resultsDispatcherClass,
+        int index)
+{
+    arg_type* arg;
+
+    // == the callback interface for results ================================
+    // the service base class
+    Type* resultsInterfaceType = generate_results_method(method, proxyClass);
+    
+    // == the method in the proxy class =====================================
+    generate_proxy_method(method, proxyClass, resultsDispatcherClass, resultsInterfaceType, index);
+
+    // == the method in the result dispatcher class =========================
+    if (resultsInterfaceType != NULL) {
+        generate_result_dispatcher_method(method, resultsDispatcherClass, resultsInterfaceType,
+                index);
+    }
+
+    // == The abstract method that the service developers implement ==========
+    Method* decl = new Method;
+        decl->comment = gather_comments(method->comments_token->extra);
+        decl->modifiers = PUBLIC | ABSTRACT;
+        decl->returnType = NAMES.Search(method->type.type.data);
+        decl->returnTypeDimension = method->type.dimension;
+        decl->name = method->name.data;
+    arg = method->args;
+    while (arg != NULL) {
+        decl->parameters.push_back(new Variable(
+                            NAMES.Search(arg->type.type.data), arg->name.data,
+                            arg->type.dimension));
+        arg = arg->next;
+    }
+
+    // Add the default RpcContext param to all methods
+    decl->parameters.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0));
+	
+    serviceBaseClass->elements.push_back(decl);
+    
+
+    // == the dispatch method in the service base class ======================
+    serviceBaseClass->AddMethod(method);
+}
+
+static void
+generate_event_method(const method_type* method, RpcProxyClass* proxyClass,
+        EndpointBaseClass* serviceBaseClass, ListenerClass* listenerClass,
+        EventListenerClass* presenterClass, int index)
+{
+    arg_type* arg;
+    listenerClass->needed = true;
+
+    // == the push method in the service base class =========================
+    Method* push = new Method;
+        push->modifiers = PUBLIC;
+        push->name = push_method_name(method->name.data);
+        push->statements = new StatementBlock;
+        push->returnType = VOID_TYPE;
+    serviceBaseClass->elements.push_back(push);
+
+    // The local variables
+    Variable* _data = new Variable(RPC_DATA_TYPE, "_data");
+    push->statements->Add(new VariableDeclaration(_data, new NewExpression(RPC_DATA_TYPE)));
+
+    // Add the arguments
+    arg = method->args;
+    while (arg != NULL) {
+        // Function signature
+        Type* t = NAMES.Search(arg->type.type.data);
+        Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
+        push->parameters.push_back(v);
+
+        // Input parameter marshalling
+        generate_write_to_data(t, push->statements,
+                new StringLiteralExpression(arg->name.data), v, _data);
+
+        arg = arg->next;
+    }
+
+    // Send the notifications
+    push->statements->Add(new MethodCall("pushEvent", 2,
+                new StringLiteralExpression(method->name.data),
+                new MethodCall(_data, "serialize")));
+
+    // == the event callback dispatcher method  ====================================
+    presenterClass->AddMethod(method);
+
+    // == the event method in the listener base class =====================
+    Method* event = new Method;
+        event->modifiers = PUBLIC;
+        event->name = method->name.data;
+        event->statements = new StatementBlock;
+        event->returnType = VOID_TYPE;
+    listenerClass->elements.push_back(event);
+    arg = method->args;
+    while (arg != NULL) {
+        event->parameters.push_back(new Variable(
+                            NAMES.Search(arg->type.type.data), arg->name.data,
+                            arg->type.dimension));
+        arg = arg->next;
+    }
+
+    // Add a final parameter: RpcContext. Contains data about
+    // incoming request (e.g., certificate)
+    event->parameters.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0));
+}
+
+static void
+generate_listener_methods(RpcProxyClass* proxyClass, Type* presenterType, Type* listenerType)
+{
+    // AndroidAtHomePresenter _presenter;
+    // void startListening(Listener listener) {
+    //     stopListening();
+    //     _presenter = new Presenter(_broker, listener);
+    //     _presenter.startListening(_endpoint);
+    // }
+    // void stopListening() {
+    //     if (_presenter != null) {
+    //         _presenter.stopListening();
+    //     }
+    // }
+
+    Variable* _presenter = new Variable(presenterType, "_presenter");
+    proxyClass->elements.push_back(new Field(PRIVATE, _presenter));
+
+    Variable* listener = new Variable(listenerType, "listener");
+
+    Method* startListeningMethod = new Method;
+        startListeningMethod->modifiers = PUBLIC;
+        startListeningMethod->returnType = VOID_TYPE;
+        startListeningMethod->name = "startListening";
+        startListeningMethod->statements = new StatementBlock;
+        startListeningMethod->parameters.push_back(listener);
+    proxyClass->elements.push_back(startListeningMethod);
+
+    startListeningMethod->statements->Add(new MethodCall(THIS_VALUE, "stopListening"));
+    startListeningMethod->statements->Add(new Assignment(_presenter,
+                new NewExpression(presenterType, 2, proxyClass->broker, listener)));
+    startListeningMethod->statements->Add(new MethodCall(_presenter,
+                "startListening", 1, proxyClass->endpoint));
+
+    Method* stopListeningMethod = new Method;
+        stopListeningMethod->modifiers = PUBLIC;
+        stopListeningMethod->returnType = VOID_TYPE;
+        stopListeningMethod->name = "stopListening";
+        stopListeningMethod->statements = new StatementBlock;
+    proxyClass->elements.push_back(stopListeningMethod);
+
+    IfStatement* ifst = new IfStatement;
+        ifst->expression = new Comparison(_presenter, "!=", NULL_VALUE);
+    stopListeningMethod->statements->Add(ifst);
+
+    ifst->statements->Add(new MethodCall(_presenter, "stopListening"));
+    ifst->statements->Add(new Assignment(_presenter, NULL_VALUE));
+}
+
+Class*
+generate_rpc_interface_class(const interface_type* iface)
+{
+    // the proxy class
+    InterfaceType* interfaceType = static_cast<InterfaceType*>(
+        NAMES.Find(iface->package, iface->name.data));
+    RpcProxyClass* proxy = new RpcProxyClass(iface, interfaceType);
+
+    // the listener class
+    ListenerClass* listener = new ListenerClass(iface);
+
+    // the presenter class
+    EventListenerClass* presenter = new EventListenerClass(iface, listener->type);
+
+    // the service base class
+    EndpointBaseClass* base = new EndpointBaseClass(iface);
+    proxy->elements.push_back(base);
+
+    // the result dispatcher
+    ResultDispatcherClass* results = new ResultDispatcherClass();
+
+    // all the declared methods of the proxy
+    int index = 0;
+    interface_item_type* item = iface->interface_items;
+    while (item != NULL) {
+        if (item->item_type == METHOD_TYPE) {
+            if (NAMES.Search(((method_type*)item)->type.type.data) == EVENT_FAKE_TYPE) {
+                generate_event_method((method_type*)item, proxy, base, listener, presenter, index);
+            } else {
+                generate_regular_method((method_type*)item, proxy, base, results, index);
+            }
+        }
+        item = item->next;
+        index++;
+    }
+    presenter->DoneWithMethods();
+    base->DoneWithMethods();
+
+    // only add this if there are methods with results / out parameters
+    if (results->needed) {
+        proxy->elements.push_back(results);
+    }
+    if (listener->needed) {
+        proxy->elements.push_back(listener);
+        proxy->elements.push_back(presenter);
+        generate_listener_methods(proxy, presenter->type, listener->type);
+    }
+
+    return proxy;
+}
diff --git a/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
index 0a3cdc6..6ac5b02 100644
--- a/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
+++ b/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
@@ -18,6 +18,7 @@
 
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.internal.util.XmlUtils;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.BridgeConstants;
 import com.android.layoutlib.bridge.android.BridgeContext;
@@ -25,9 +26,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 
-import android.util.AttributeSet;
-import android.util.XmlPullAttributes;
-
 /**
  * A correct implementation of the {@link AttributeSet} interface on top of a XmlPullParser
  */
@@ -80,21 +78,40 @@
         return 0;
     }
 
-    /*
-     * (non-Javadoc)
-     * @see android.util.XmlPullAttributes#getAttributeResourceValue(int, int)
-     */
     @Override
-    public int getAttributeResourceValue(int index, int defaultValue) {
-        String value = getAttributeValue(index);
+    public int getAttributeListValue(String namespace, String attribute,
+            String[] options, int defaultValue) {
+        String value = getAttributeValue(namespace, attribute);
+        if (value != null) {
+            ResourceValue r = getResourceValue(value);
 
-        return resolveResourceValue(value, defaultValue);
+            if (r != null) {
+                value = r.getValue();
+            }
+
+            return XmlUtils.convertValueToList(value, options, defaultValue);
+        }
+
+        return defaultValue;
     }
 
-    /*
-     * (non-Javadoc)
-     * @see android.util.XmlPullAttributes#getAttributeResourceValue(java.lang.String, java.lang.String, int)
-     */
+    @Override
+    public boolean getAttributeBooleanValue(String namespace, String attribute,
+            boolean defaultValue) {
+        String value = getAttributeValue(namespace, attribute);
+        if (value != null) {
+            ResourceValue r = getResourceValue(value);
+
+            if (r != null) {
+                value = r.getValue();
+            }
+
+            return XmlUtils.convertValueToBoolean(value, defaultValue);
+        }
+
+        return defaultValue;
+    }
+
     @Override
     public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
         String value = getAttributeValue(namespace, attribute);
@@ -102,12 +119,151 @@
         return resolveResourceValue(value, defaultValue);
     }
 
-    private int resolveResourceValue(String value, int defaultValue) {
+    @Override
+    public int getAttributeIntValue(String namespace, String attribute,
+            int defaultValue) {
+        String value = getAttributeValue(namespace, attribute);
+        if (value != null) {
+            ResourceValue r = getResourceValue(value);
+
+            if (r != null) {
+                value = r.getValue();
+            }
+
+            return XmlUtils.convertValueToInt(value, defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public int getAttributeUnsignedIntValue(String namespace, String attribute,
+            int defaultValue) {
+        String value = getAttributeValue(namespace, attribute);
+        if (value != null) {
+            ResourceValue r = getResourceValue(value);
+
+            if (r != null) {
+                value = r.getValue();
+            }
+
+            return XmlUtils.convertValueToUnsignedInt(value, defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public float getAttributeFloatValue(String namespace, String attribute,
+            float defaultValue) {
+        String s = getAttributeValue(namespace, attribute);
+        if (s != null) {
+            ResourceValue r = getResourceValue(s);
+
+            if (r != null) {
+                s = r.getValue();
+            }
+
+            return Float.parseFloat(s);
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public int getAttributeListValue(int index,
+            String[] options, int defaultValue) {
+        return XmlUtils.convertValueToList(
+            getAttributeValue(index), options, defaultValue);
+    }
+
+    @Override
+    public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
+        String value = getAttributeValue(index);
+        if (value != null) {
+            ResourceValue r = getResourceValue(value);
+
+            if (r != null) {
+                value = r.getValue();
+            }
+
+            return XmlUtils.convertValueToBoolean(value, defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public int getAttributeResourceValue(int index, int defaultValue) {
+        String value = getAttributeValue(index);
+
+        return resolveResourceValue(value, defaultValue);
+    }
+
+    @Override
+    public int getAttributeIntValue(int index, int defaultValue) {
+        String value = getAttributeValue(index);
+        if (value != null) {
+            ResourceValue r = getResourceValue(value);
+
+            if (r != null) {
+                value = r.getValue();
+            }
+
+            return XmlUtils.convertValueToInt(value, defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public int getAttributeUnsignedIntValue(int index, int defaultValue) {
+        String value = getAttributeValue(index);
+        if (value != null) {
+            ResourceValue r = getResourceValue(value);
+
+            if (r != null) {
+                value = r.getValue();
+            }
+
+            return XmlUtils.convertValueToUnsignedInt(value, defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public float getAttributeFloatValue(int index, float defaultValue) {
+        String s = getAttributeValue(index);
+        if (s != null) {
+            ResourceValue r = getResourceValue(s);
+
+            if (r != null) {
+                s = r.getValue();
+            }
+
+            return Float.parseFloat(s);
+        }
+
+        return defaultValue;
+    }
+
+    // -- private helper methods
+
+    /**
+     * Returns a resolved {@link ResourceValue} from a given value.
+     */
+    private ResourceValue getResourceValue(String value) {
         // now look for this particular value
         RenderResources resources = mContext.getRenderResources();
-        ResourceValue resource = resources.resolveResValue(
-                resources.findResValue(value, mPlatformFile));
+        return resources.resolveResValue(resources.findResValue(value, mPlatformFile));
+    }
 
+    /**
+     * Resolves and return a value to its associated integer.
+     */
+    private int resolveResourceValue(String value, int defaultValue) {
+        ResourceValue resource = getResourceValue(value);
         if (resource != null) {
             Integer id = null;
             if (mPlatformFile || resource.isFramework()) {
@@ -124,5 +280,4 @@
 
         return defaultValue;
     }
-
 }
diff --git a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
index 96de51c..97d9969 100644
--- a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
@@ -29,7 +29,7 @@
 
     public static void setAttachInfo(View view) {
         AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
-                new Handler(), null);
+                new ViewRootImpl(view.getContext()), new Handler(), null);
         info.mHasWindowFocus = true;
         info.mWindowVisibility = View.VISIBLE;
         info.mInTouchMode = false; // this is so that we can display selections.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 33bf7bc..ff88209 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -24,8 +24,8 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.Result.Status;
+import com.android.ide.common.rendering.api.SessionParams;
 import com.android.layoutlib.bridge.impl.FontLoader;
 import com.android.layoutlib.bridge.impl.RenderDrawable;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
@@ -242,6 +242,8 @@
         if (fontLoader != null) {
             Typeface_Delegate.init(fontLoader);
         } else {
+            log.error(LayoutLog.TAG_BROKEN,
+                    "Failed create FontLoader in layout lib.", null);
             return false;
         }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index db0694c..6afdfbe 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -197,6 +197,12 @@
     }
 
     @Override
+        public boolean switchToNextInputMethod(IBinder arg0, boolean arg1) throws RemoteException {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
     public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException {
         // TODO Auto-generated method stub
 
diff --git a/tools/makekeycodes/makekeycodes.cpp b/tools/makekeycodes/makekeycodes.cpp
index 16df774..6ffbfb8 100644
--- a/tools/makekeycodes/makekeycodes.cpp
+++ b/tools/makekeycodes/makekeycodes.cpp
@@ -1,5 +1,21 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
 #include <stdio.h>
-#include <ui/KeycodeLabels.h>
+#include <androidfw/KeycodeLabels.h>
 
 int
 main(int argc, char** argv)
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index 72a9858..dd57ae6 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_STATIC_LIBRARIES := \
 	libutils \
+	libandroidfw \
 	libcutils
 
 ifeq ($(HOST_OS),linux)
diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp
index 932dbec..b2152e8 100644
--- a/tools/obbtool/Main.cpp
+++ b/tools/obbtool/Main.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <utils/ObbFile.h>
+#include <androidfw/ObbFile.h>
 #include <utils/String8.h>
 
 #include <getopt.h>
diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk
index 1368a07..fce2e93 100644
--- a/tools/validatekeymaps/Android.mk
+++ b/tools/validatekeymaps/Android.mk
@@ -18,7 +18,7 @@
 #LOCAL_C_INCLUDES +=
 
 LOCAL_STATIC_LIBRARIES := \
-	libui \
+	libandroidfw \
 	libutils \
 	libcutils
 
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 8ab9b6a..3cc2467 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#include <ui/KeyCharacterMap.h>
-#include <ui/KeyLayoutMap.h>
-#include <ui/VirtualKeyMap.h>
+#include <androidfw/KeyCharacterMap.h>
+#include <androidfw/KeyLayoutMap.h>
+#include <androidfw/VirtualKeyMap.h>
 #include <utils/PropertyMap.h>
 #include <utils/String8.h>
 
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index bbb74d1..d05e0b8 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -366,17 +366,6 @@
                     handleDriverEvent(eventData);
                 } else if (event == TERMINATING) {
                     /**
-                     * If monitor socket is closed, we have already
-                     * stopped the supplicant, simply exit the monitor thread
-                     */
-                    if (eventData.startsWith(MONITOR_SOCKET_CLOSED_STR)) {
-                        if (false) {
-                            Log.d(TAG, "Monitor socket is closed, exiting thread");
-                        }
-                        break;
-                    }
-
-                    /**
                      * Close the supplicant connection if we see
                      * too many recv errors
                      */
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 48a785c..e3dd3a6 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -39,6 +39,8 @@
  */
 public class WifiNative {
 
+    private static final boolean DBG = false;
+    private final String mTAG;
     private static final int DEFAULT_GROUP_OWNER_INTENT = 7;
 
     static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
@@ -53,9 +55,7 @@
 
     public native static boolean unloadDriver();
 
-    public native static boolean startSupplicant();
-
-    public native static boolean startP2pSupplicant();
+    public native static boolean startSupplicant(boolean p2pSupported);
 
     /* Sends a kill signal to supplicant. To be used when we have lost connection
        or when the supplicant is hung */
@@ -79,6 +79,7 @@
 
     public WifiNative(String iface) {
         mInterface = iface;
+        mTAG = "WifiNative-" + iface;
     }
 
     public boolean connectToSupplicant() {
@@ -94,14 +95,17 @@
     }
 
     private boolean doBooleanCommand(String command) {
+        if (DBG) Log.d(mTAG, "doBoolean: " + command);
         return doBooleanCommand(mInterface, command);
     }
 
     private int doIntCommand(String command) {
+        if (DBG) Log.d(mTAG, "doInt: " + command);
         return doIntCommand(mInterface, command);
     }
 
     private String doStringCommand(String command) {
+        if (DBG) Log.d(mTAG, "doString: " + command);
         return doStringCommand(mInterface, command);
     }
 
@@ -437,6 +441,10 @@
         return doBooleanCommand("P2P_FIND " + timeout);
     }
 
+    public boolean p2pStopFind() {
+       return doBooleanCommand("P2P_STOP_FIND");
+    }
+
     public boolean p2pListen() {
         return doBooleanCommand("P2P_LISTEN");
     }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index b8b7c0e..1b64f3e 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -45,6 +45,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.DhcpInfoInternal;
@@ -118,6 +119,8 @@
     private INetworkManagementService mNwService;
     private ConnectivityManager mCm;
 
+    private final boolean mP2pSupported;
+
     /* Scan results handling */
     private List<ScanResult> mScanResults;
     private static final Pattern scanResultPattern = Pattern.compile("\t+");
@@ -361,9 +364,9 @@
     /* Reset the WPS state machine */
     static final int CMD_RESET_WPS_STATE                  = BASE + 122;
 
-    /* Interaction with WifiP2pService */
-    public static final int WIFI_ENABLE_PENDING           = BASE + 131;
-    public static final int P2P_ENABLE_PROCEED            = BASE + 132;
+    /* P2p commands */
+    public static final int CMD_ENABLE_P2P                = BASE + 131;
+    public static final int CMD_DISABLE_P2P               = BASE + 132;
 
     private static final int CONNECT_MODE   = 1;
     private static final int SCAN_ONLY_MODE = 2;
@@ -482,9 +485,6 @@
     /* Waiting for untether confirmation to stop soft Ap */
     private State mSoftApStoppingState = new SoftApStoppingState();
 
-    /* Wait till p2p is disabled */
-    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
-
     private class TetherStateChange {
         ArrayList<String> available;
         ArrayList<String> active;
@@ -556,6 +556,9 @@
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         mNwService = INetworkManagementService.Stub.asInterface(b);
 
+        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WIFI_DIRECT);
+
         mWifiNative = new WifiNative(mInterfaceName);
         mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
         mWifiMonitor = new WifiMonitor(this, mWifiNative);
@@ -639,7 +642,6 @@
                 addState(mTetheringState, mSoftApStartedState);
                 addState(mTetheredState, mSoftApStartedState);
             addState(mSoftApStoppingState, mDefaultState);
-            addState(mWaitForP2pDisableState, mDefaultState);
 
         setInitialState(mInitialState);
 
@@ -1625,14 +1627,14 @@
     private void handleNetworkDisconnect() {
         if (DBG) log("Stopping DHCP and clearing IP");
 
-        /* In case we were in middle of DHCP operation
-           restore back powermode */
-        handlePostDhcpSetup();
-
         /*
          * stop DHCP
          */
         if (mDhcpStateMachine != null) {
+            /* In case we were in middle of DHCP operation
+               restore back powermode */
+            handlePostDhcpSetup();
+
             mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
             mDhcpStateMachine.quit();
             mDhcpStateMachine = null;
@@ -1752,7 +1754,9 @@
          * If we've exceeded the maximum number of retries for DHCP
          * to a given network, disable the network
          */
-        if (++mReconnectCount > getMaxDhcpRetries()) {
+        int maxRetries = getMaxDhcpRetries();
+        // maxRetries == 0 means keep trying forever
+        if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
             loge("Failed " +
                     mReconnectCount + " times, Disabling " + mLastNetworkId);
             mWifiConfigStore.disableNetwork(mLastNetworkId,
@@ -1896,11 +1900,6 @@
                     mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
                                 new WpsResult(Status.FAILURE));
                     break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    // turn off wifi and defer to be handled in DriverUnloadedState
-                    setWifiEnabled(false);
-                    deferMessage(message);
-                    break;
                 default:
                     loge("Error! unhandled message" + message);
                     break;
@@ -2060,7 +2059,7 @@
                         loge("Unable to change interface settings: " + ie);
                     }
 
-                    if(mWifiNative.startSupplicant()) {
+                    if(mWifiNative.startSupplicant(mP2pSupported)) {
                         if (DBG) log("Supplicant start successful");
                         mWifiMonitor.startMonitoring();
                         transitionTo(mSupplicantStartingState);
@@ -2172,11 +2171,7 @@
             if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
                 case CMD_LOAD_DRIVER:
-                    mWifiP2pChannel.sendMessage(WIFI_ENABLE_PENDING);
-                    transitionTo(mWaitForP2pDisableState);
-                    break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    mReplyChannel.replyToMessage(message, P2P_ENABLE_PROCEED);
+                    transitionTo(mDriverLoadingState);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -2395,6 +2390,10 @@
         public void enter() {
             if (DBG) log(getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            /* Send any reset commands to supplicant before shutting it down */
+            handleNetworkDisconnect();
+
             if (DBG) log("stopping supplicant");
             if (!mWifiNative.stopSupplicant()) {
                 loge("Failed to stop supplicant");
@@ -2405,7 +2404,6 @@
                     ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
 
             mNetworkInfo.setIsAvailable(false);
-            handleNetworkDisconnect();
             setWifiState(WIFI_STATE_DISABLING);
             sendSupplicantConnectionChangedBroadcast(false);
             mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
@@ -2553,13 +2551,15 @@
                 mWifiNative.status();
                 transitionTo(mDisconnectedState);
             }
+
+            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
         }
         @Override
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
             boolean eventLoggingEnabled = true;
             switch(message.what) {
-                case CMD_SET_SCAN_TYPE:
+               case CMD_SET_SCAN_TYPE:
                     mSetScanActive = (message.arg1 == SCAN_ACTIVE);
                     mWifiNative.setScanMode(mSetScanActive);
                     break;
@@ -2672,6 +2672,8 @@
             mIsRunning = false;
             updateBatteryWorkSource(null);
             mScanResults = null;
+
+            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P);
         }
     }
 
@@ -3345,7 +3347,6 @@
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
                 case CMD_TETHER_STATE_CHANGE:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
@@ -3409,55 +3410,6 @@
                         transitionTo(mTetheringState);
                     }
                     break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    // turn of soft Ap and defer to be handled in DriverUnloadedState
-                    setWifiApEnabled(null, false);
-                    deferMessage(message);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-            return HANDLED;
-        }
-    }
-
-    class WaitForP2pDisableState extends State {
-        private int mSavedArg;
-        @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
-            //Preserve the argument arg1 that has information used in DriverLoadingState
-            mSavedArg = getCurrentMessage().arg1;
-        }
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
-            switch(message.what) {
-                case WifiP2pService.WIFI_ENABLE_PROCEED:
-                    //restore argument from original message (CMD_LOAD_DRIVER)
-                    message.arg1 = mSavedArg;
-                    transitionTo(mDriverLoadingState);
-                    break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
-                case CMD_START_SUPPLICANT:
-                case CMD_STOP_SUPPLICANT:
-                case CMD_START_AP:
-                case CMD_STOP_AP:
-                case CMD_START_DRIVER:
-                case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
-                case CMD_SET_SCAN_TYPE:
-                case CMD_SET_HIGH_PERF_MODE:
-                case CMD_SET_COUNTRY_CODE:
-                case CMD_SET_FREQUENCY_BAND:
-                case CMD_START_PACKET_FILTERING:
-                case CMD_STOP_PACKET_FILTERING:
-                    deferMessage(message);
-                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -3507,7 +3459,6 @@
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 default:
@@ -3603,7 +3554,6 @@
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 default:
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 7471a2d..b0cde64 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -301,7 +301,8 @@
     private String trimQuotes(String str) {
         str = str.trim();
         if (str.startsWith("'") && str.endsWith("'")) {
-            return str.substring(1, str.length()-1);
+            if (str.length() <= 2) return "";
+            else return str.substring(1, str.length()-1);
         }
         return str;
     }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 9205300..4fd0a57 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -199,68 +199,61 @@
     private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
 
     /** @hide */
-    public static final int ENABLE_P2P                              = BASE + 1;
+    public static final int DISCOVER_PEERS                          = BASE + 1;
     /** @hide */
-    public static final int ENABLE_P2P_FAILED                       = BASE + 2;
+    public static final int DISCOVER_PEERS_FAILED                   = BASE + 2;
     /** @hide */
-    public static final int ENABLE_P2P_SUCCEEDED                    = BASE + 3;
+    public static final int DISCOVER_PEERS_SUCCEEDED                = BASE + 3;
 
     /** @hide */
-    public static final int DISABLE_P2P                             = BASE + 4;
+    public static final int STOP_DISCOVERY                          = BASE + 4;
     /** @hide */
-    public static final int DISABLE_P2P_FAILED                      = BASE + 5;
+    public static final int STOP_DISCOVERY_FAILED                   = BASE + 5;
     /** @hide */
-    public static final int DISABLE_P2P_SUCCEEDED                   = BASE + 6;
+    public static final int STOP_DISCOVERY_SUCCEEDED                = BASE + 6;
 
     /** @hide */
-    public static final int DISCOVER_PEERS                          = BASE + 7;
+    public static final int CONNECT                                 = BASE + 7;
     /** @hide */
-    public static final int DISCOVER_PEERS_FAILED                   = BASE + 8;
+    public static final int CONNECT_FAILED                          = BASE + 8;
     /** @hide */
-    public static final int DISCOVER_PEERS_SUCCEEDED                = BASE + 9;
+    public static final int CONNECT_SUCCEEDED                       = BASE + 9;
 
     /** @hide */
-    public static final int CONNECT                                 = BASE + 10;
+    public static final int CANCEL_CONNECT                          = BASE + 10;
     /** @hide */
-    public static final int CONNECT_FAILED                          = BASE + 11;
+    public static final int CANCEL_CONNECT_FAILED                   = BASE + 11;
     /** @hide */
-    public static final int CONNECT_SUCCEEDED                       = BASE + 12;
+    public static final int CANCEL_CONNECT_SUCCEEDED                = BASE + 12;
 
     /** @hide */
-    public static final int CANCEL_CONNECT                          = BASE + 13;
+    public static final int CREATE_GROUP                            = BASE + 13;
     /** @hide */
-    public static final int CANCEL_CONNECT_FAILED                   = BASE + 14;
+    public static final int CREATE_GROUP_FAILED                     = BASE + 14;
     /** @hide */
-    public static final int CANCEL_CONNECT_SUCCEEDED                = BASE + 15;
+    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 15;
 
     /** @hide */
-    public static final int CREATE_GROUP                            = BASE + 16;
+    public static final int REMOVE_GROUP                            = BASE + 16;
     /** @hide */
-    public static final int CREATE_GROUP_FAILED                     = BASE + 17;
+    public static final int REMOVE_GROUP_FAILED                     = BASE + 17;
     /** @hide */
-    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 18;
+    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 18;
 
     /** @hide */
-    public static final int REMOVE_GROUP                            = BASE + 19;
+    public static final int REQUEST_PEERS                           = BASE + 19;
     /** @hide */
-    public static final int REMOVE_GROUP_FAILED                     = BASE + 20;
-    /** @hide */
-    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 21;
+    public static final int RESPONSE_PEERS                          = BASE + 20;
 
     /** @hide */
-    public static final int REQUEST_PEERS                           = BASE + 22;
+    public static final int REQUEST_CONNECTION_INFO                 = BASE + 21;
     /** @hide */
-    public static final int RESPONSE_PEERS                          = BASE + 23;
+    public static final int RESPONSE_CONNECTION_INFO                = BASE + 22;
 
     /** @hide */
-    public static final int REQUEST_CONNECTION_INFO                 = BASE + 24;
+    public static final int REQUEST_GROUP_INFO                      = BASE + 23;
     /** @hide */
-    public static final int RESPONSE_CONNECTION_INFO                = BASE + 25;
-
-    /** @hide */
-    public static final int REQUEST_GROUP_INFO                      = BASE + 26;
-    /** @hide */
-    public static final int RESPONSE_GROUP_INFO                     = BASE + 27;
+    public static final int RESPONSE_GROUP_INFO                     = BASE + 24;
 
     /**
      * Create a new WifiP2pManager instance. Applications use
@@ -376,6 +369,7 @@
                         break;
                     /* ActionListeners grouped together */
                     case WifiP2pManager.DISCOVER_PEERS_FAILED:
+                    case WifiP2pManager.STOP_DISCOVERY_FAILED:
                     case WifiP2pManager.CONNECT_FAILED:
                     case WifiP2pManager.CANCEL_CONNECT_FAILED:
                     case WifiP2pManager.CREATE_GROUP_FAILED:
@@ -386,6 +380,7 @@
                         break;
                     /* ActionListeners grouped together */
                     case WifiP2pManager.DISCOVER_PEERS_SUCCEEDED:
+                    case WifiP2pManager.STOP_DISCOVERY_SUCCEEDED:
                     case WifiP2pManager.CONNECT_SUCCEEDED:
                     case WifiP2pManager.CANCEL_CONNECT_SUCCEEDED:
                     case WifiP2pManager.CREATE_GROUP_SUCCEEDED:
@@ -459,26 +454,6 @@
     }
 
     /**
-     * Sends in a request to the system to enable p2p. This will pop up a dialog
-     * to the user and upon authorization will enable p2p.
-     * @hide
-     */
-    public void enableP2p(Channel c) {
-        if (c == null) return;
-        c.mAsyncChannel.sendMessage(ENABLE_P2P);
-    }
-
-    /**
-     * Sends in a request to the system to disable p2p. This will pop up a dialog
-     * to the user and upon authorization will enable p2p.
-     * @hide
-     */
-    public void disableP2p(Channel c) {
-        if (c == null) return;
-        c.mAsyncChannel.sendMessage(DISABLE_P2P);
-    }
-
-    /**
      * Initiate peer discovery. A discovery process involves scanning for available Wi-Fi peers
      * for the purpose of establishing a connection.
      *
@@ -503,6 +478,16 @@
     }
 
     /**
+     * TODO: Add more documentation before opening up
+     * Cancel peer discovery
+     * @hide
+     */
+    public void stopPeerDiscovery(Channel c, ActionListener listener) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, c.putListener(listener));
+    }
+
+    /**
      * Start a p2p connection to a device with the specified configuration.
      *
      * <p> The function call immediately returns after sending a connection request
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 69cbb5c..5b0e424 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -49,6 +49,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
@@ -84,7 +85,7 @@
  */
 public class WifiP2pService extends IWifiP2pManager.Stub {
     private static final String TAG = "WifiP2pService";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final String NETWORKTYPE = "WIFI_P2P";
 
     private Context mContext;
@@ -94,11 +95,6 @@
     INetworkManagementService mNwService;
     private DhcpStateMachine mDhcpStateMachine;
 
-    //Tracked to notify the user about wifi client/hotspot being shut down
-    //during p2p bring up
-    private int mWifiState = WifiManager.WIFI_STATE_DISABLED;
-    private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
-
     private P2pStateMachine mP2pStateMachine;
     private AsyncChannel mReplyChannel = new AsyncChannel();
     private AsyncChannel mWifiChannel;
@@ -110,6 +106,9 @@
     private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
     private static int mGroupCreatingTimeoutIndex = 0;
 
+    /* Set a two minute discover timeout to avoid STA scans from being blocked */
+    private static final int DISCOVER_TIMEOUT_S = 120;
+
     /**
      * Delay between restarts upon failure to setup connection with supplicant
      */
@@ -124,28 +123,13 @@
 
     private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
 
-    /* Message sent to WifiStateMachine to indicate p2p enable is pending */
-    public static final int P2P_ENABLE_PENDING              =   BASE + 1;
-    /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */
-    public static final int WIFI_ENABLE_PROCEED             =   BASE + 2;
-
     /* Delayed message to timeout group creation */
-    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 3;
-
-    /* User accepted to disable Wi-Fi in order to enable p2p */
-    private static final int WIFI_DISABLE_USER_ACCEPT       =   BASE + 4;
-    /* User rejected to disable Wi-Fi in order to enable p2p */
-    private static final int WIFI_DISABLE_USER_REJECT       =   BASE + 5;
+    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 1;
 
     /* User accepted a peer request */
-    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 6;
+    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 2;
     /* User rejected a peer request */
-    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 7;
-
-    /* Airplane mode changed */
-    private static final int AIRPLANE_MODE_CHANGED          =   BASE + 8;
-    /* Emergency callback mode */
-    private static final int EMERGENCY_CALLBACK_MODE        =   BASE + 9;
+    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 3;
 
     private final boolean mP2pSupported;
 
@@ -166,7 +150,7 @@
     public WifiP2pService(Context context) {
         mContext = context;
 
-        //STOPSHIP: fix this
+        //STOPSHIP: get this from native side
         mInterface = "p2p0";
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
 
@@ -179,15 +163,6 @@
 
         mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
         mP2pStateMachine.start();
-
-        // broadcasts
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
-        filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
-        mContext.registerReceiver(new WifiStateReceiver(), filter);
-
     }
 
     public void connectivityServiceReady() {
@@ -195,26 +170,6 @@
         mNwService = INetworkManagementService.Stub.asInterface(b);
     }
 
-    private class WifiStateReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_DISABLED);
-            } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
-                mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
-                        WifiManager.WIFI_AP_STATE_DISABLED);
-            } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
-                mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED);
-            } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
-                if (intent.getBooleanExtra("phoneinECMState", false) == true) {
-                    mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE);
-                }
-            }
-        }
-    }
-
     private void enforceAccessPermission() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
                 "WifiP2pService");
@@ -264,8 +219,6 @@
         private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
         private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
         private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
-        private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState();
-        private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
         private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
         private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
         // Inactive is when p2p is enabled with no connectivity
@@ -299,8 +252,6 @@
                 addState(mP2pNotSupportedState, mDefaultState);
                 addState(mP2pDisablingState, mDefaultState);
                 addState(mP2pDisabledState, mDefaultState);
-                    addState(mWaitForUserActionState, mP2pDisabledState);
-                    addState(mWaitForWifiDisableState, mP2pDisabledState);
                 addState(mP2pEnablingState, mDefaultState);
                 addState(mP2pEnabledState, mDefaultState);
                     addState(mInactiveState, mP2pEnabledState);
@@ -346,23 +297,14 @@
                     AsyncChannel ac = new AsyncChannel();
                     ac.connect(mContext, getHandler(), message.replyTo);
                     break;
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    // Disable p2p operation before we can respond
-                    sendMessage(WifiP2pManager.DISABLE_P2P);
-                    deferMessage(message);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
-                            WifiP2pManager.BUSY);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
-                            WifiP2pManager.BUSY);
-                    break;
                 case WifiP2pManager.DISCOVER_PEERS:
                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                             WifiP2pManager.BUSY);
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                            WifiP2pManager.BUSY);
+                    break;
                 case WifiP2pManager.CONNECT:
                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
                             WifiP2pManager.BUSY);
@@ -388,16 +330,14 @@
                 case WifiP2pManager.REQUEST_GROUP_INFO:
                     replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
                     break;
-                case AIRPLANE_MODE_CHANGED:
-                    if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P);
-                    break;
-                case EMERGENCY_CALLBACK_MODE:
-                    sendMessage(WifiP2pManager.DISABLE_P2P);
-                    break;
                     // Ignore
                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
-                case WIFI_DISABLE_USER_ACCEPT:
-                case WIFI_DISABLE_USER_REJECT:
+                case WifiMonitor.SCAN_RESULTS_EVENT:
+                case WifiMonitor.SUP_CONNECTION_EVENT:
+                case WifiMonitor.SUP_DISCONNECTION_EVENT:
+                case WifiMonitor.NETWORK_CONNECTION_EVENT:
+                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                 case PEER_CONNECTION_USER_ACCEPT:
                 case PEER_CONNECTION_USER_REJECT:
                 case GROUP_CREATING_TIMED_OUT:
@@ -414,22 +354,14 @@
         @Override
         public boolean processMessage(Message message) {
             switch (message.what) {
-                // Allow Wi-Fi to proceed
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    replyToMessage(message, WIFI_ENABLE_PROCEED);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
-                            WifiP2pManager.P2P_UNSUPPORTED);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
-                            WifiP2pManager.P2P_UNSUPPORTED);
-                    break;
-                case WifiP2pManager.DISCOVER_PEERS:
+               case WifiP2pManager.DISCOVER_PEERS:
                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
                 case WifiP2pManager.CONNECT:
                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
@@ -438,7 +370,7 @@
                     replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
-                case WifiP2pManager.CREATE_GROUP:
+               case WifiP2pManager.CREATE_GROUP:
                     replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
@@ -455,26 +387,15 @@
 
     class P2pDisablingState extends State {
         @Override
-        public void enter() {
-            if (DBG) logd(getName());
-            logd("stopping supplicant");
-            if (!mWifiNative.stopSupplicant()) {
-                loge("Failed to stop supplicant, issue kill");
-                mWifiNative.killSupplicant();
-            }
-        }
-
-        @Override
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
-                    logd("Supplicant connection lost");
-                    mWifiNative.closeSupplicantConnection();
+                    if (DBG) logd("p2p socket connection lost");
                     transitionTo(mP2pDisabledState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     deferMessage(message);
                     break;
                 default:
@@ -484,7 +405,6 @@
         }
     }
 
-
     class P2pDisabledState extends State {
        @Override
         public void enter() {
@@ -495,118 +415,19 @@
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
-                case WifiP2pManager.ENABLE_P2P:
-                    OnClickListener listener = new OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            if (which == DialogInterface.BUTTON_POSITIVE) {
-                                sendMessage(WIFI_DISABLE_USER_ACCEPT);
-                            } else {
-                                sendMessage(WIFI_DISABLE_USER_REJECT);
-                            }
-                        }
-                    };
-
-                    // Show a user request dialog if we know Wi-Fi client/hotspot is in operation
-                    if (mWifiState != WifiManager.WIFI_STATE_DISABLED ||
-                            mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
-                        Resources r = Resources.getSystem();
-                        AlertDialog dialog = new AlertDialog.Builder(mContext)
-                            .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
-                            .setMessage(r.getString(R.string.wifi_p2p_turnon_message))
-                            .setPositiveButton(r.getString(R.string.ok), listener)
-                            .setNegativeButton(r.getString(R.string.cancel), listener)
-                            .create();
-                        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-                        dialog.show();
-                        transitionTo(mWaitForUserActionState);
-                    } else {
-                        mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
-                        transitionTo(mWaitForWifiDisableState);
-                    }
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
-                    break;
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    replyToMessage(message, WIFI_ENABLE_PROCEED);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class WaitForUserActionState extends State {
-        @Override
-        public void enter() {
-            if (DBG) logd(getName());
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) logd(getName() + message.toString());
-            switch (message.what) {
-                case WIFI_DISABLE_USER_ACCEPT:
-                    mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
-                    transitionTo(mWaitForWifiDisableState);
-                    break;
-                case WIFI_DISABLE_USER_REJECT:
-                    logd("User rejected enabling p2p");
-                    sendP2pStateChangedBroadcast(false);
-                    transitionTo(mP2pDisabledState);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
-                    deferMessage(message);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class WaitForWifiDisableState extends State {
-        @Override
-        public void enter() {
-            if (DBG) logd(getName());
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) logd(getName() + message.toString());
-            switch (message.what) {
-                case WifiStateMachine.P2P_ENABLE_PROCEED:
+                case WifiStateMachine.CMD_ENABLE_P2P:
                     try {
-                        mNwService.wifiFirmwareReload(mInterface, "P2P");
-                    } catch (Exception e) {
-                        loge("Failed to reload p2p firmware " + e);
-                        // continue
+                        mNwService.setInterfaceUp(mInterface);
+                    } catch (RemoteException re) {
+                        loge("Unable to change interface settings: " + re);
+                    } catch (IllegalStateException ie) {
+                        loge("Unable to change interface settings: " + ie);
                     }
-
-                    //A runtime crash can leave the interface up and
-                    //this affects p2p when supplicant starts up.
-                    //Ensure interface is down before a supplicant start.
-                    try {
-                        mNwService.setInterfaceDown(mInterface);
-                    } catch (Exception e) {
-                        if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
-                    }
-
-                    if (mWifiNative.startP2pSupplicant()) {
-                        mWifiMonitor.startMonitoring();
-                        transitionTo(mP2pEnablingState);
-                    } else {
-                        notifyP2pEnableFailure();
-                        transitionTo(mP2pDisabledState);
-                    }
+                    mWifiMonitor.startMonitoring();
+                    transitionTo(mP2pEnablingState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
-                    deferMessage(message);
+                case WifiStateMachine.CMD_DISABLE_P2P:
+                    //Nothing to do
                     break;
                 default:
                     return NOT_HANDLED;
@@ -626,22 +447,15 @@
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.SUP_CONNECTION_EVENT:
-                    logd("P2p start successful");
+                    if (DBG) logd("P2p socket connection successful");
                     transitionTo(mInactiveState);
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
-                    if (++mP2pRestartCount <= P2P_RESTART_TRIES) {
-                        loge("Failed to start p2p, retry");
-                        mWifiNative.killSupplicant();
-                        sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
-                    } else {
-                        loge("Failed " + mP2pRestartCount + " times to start p2p, quit ");
-                        mP2pRestartCount = 0;
-                    }
+                    loge("P2p socket connection failed");
                     transitionTo(mP2pDisabledState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     deferMessage(message);
                     break;
                 default:
@@ -658,30 +472,36 @@
             sendP2pStateChangedBroadcast(true);
             mNetworkInfo.setIsAvailable(true);
             initializeP2pSettings();
-            showNotification();
         }
 
         @Override
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                    //Nothing to do
                     break;
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
+                    mWifiNative.closeSupplicantConnection();
                     transitionTo(mP2pDisablingState);
                     break;
                 case WifiP2pManager.DISCOVER_PEERS:
-                    int timeout = message.arg1;
-                    if (mWifiNative.p2pFind(timeout)) {
+                    if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
                     } else {
                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                                 WifiP2pManager.ERROR);
                     }
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    if (mWifiNative.p2pStopFind()) {
+                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                                WifiP2pManager.ERROR);
+                    }
+                    break;
                 case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
                     if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
@@ -692,15 +512,7 @@
                     device = (WifiP2pDevice) message.obj;
                     if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
                     break;
-               case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant died */
-                    loge("Connection lost, restart p2p");
-                    mWifiNative.killSupplicant();
-                    mWifiNative.closeSupplicantConnection();
-                    if (mPeers.clear()) sendP2pPeersChangedBroadcast();
-                    transitionTo(mP2pDisabledState);
-                    sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
-                    break;
-                default:
+               default:
                     return NOT_HANDLED;
             }
             return HANDLED;
@@ -710,7 +522,6 @@
         public void exit() {
             sendP2pStateChangedBroadcast(false);
             mNetworkInfo.setIsAvailable(false);
-            clearNotification();
         }
     }
 
@@ -719,7 +530,8 @@
         public void enter() {
             if (DBG) logd(getName());
             //Start listening every time we get inactive
-            mWifiNative.p2pListen();
+            //TODO: Fix listen after driver behavior is fixed
+            //mWifiNative.p2pListen();
         }
 
         @Override
@@ -737,6 +549,8 @@
                         //TODO: if failure, remove config and do a regular p2pConnect()
                         mWifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
                     } else {
+                        //Stop discovery before issuing connect
+                        mWifiNative.p2pStopFind();
                         //If peer is a GO, we do not need to send provisional discovery,
                         //the supplicant takes care of it.
                         if (isGroupOwner(mSavedPeerConfig.deviceAddress)) {
@@ -1114,7 +928,7 @@
                     }
                     // Do the regular device lost handling
                     return NOT_HANDLED;
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     sendMessage(WifiP2pManager.REMOVE_GROUP);
                     deferMessage(message);
                     break;
@@ -1494,54 +1308,5 @@
         Slog.e(TAG, s);
     }
 
-    private void showNotification() {
-        NotificationManager notificationManager =
-            (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager == null || mNotification != null) {
-            return;
-        }
-
-        Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
-
-        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
-        Resources r = Resources.getSystem();
-        CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title);
-        CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message);
-
-        mNotification = new Notification();
-        mNotification.when = 0;
-        //TODO: might change to be a seperate icon
-        mNotification.icon = R.drawable.stat_sys_tether_wifi;
-        mNotification.defaults &= ~Notification.DEFAULT_SOUND;
-        mNotification.flags = Notification.FLAG_ONGOING_EVENT;
-        mNotification.tickerText = title;
-        mNotification.setLatestEventInfo(mContext, title, message, pi);
-
-        notificationManager.notify(mNotification.icon, mNotification);
-    }
-
-    private void clearNotification() {
-        NotificationManager notificationManager =
-            (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager != null && mNotification != null) {
-            notificationManager.cancel(mNotification.icon);
-            mNotification = null;
-        }
-    }
-
-    private boolean isAirplaneSensitive() {
-        String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
-                Settings.System.AIRPLANE_MODE_RADIOS);
-        return airplaneModeRadios == null
-            || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
-    }
-
-    private boolean isAirplaneModeOn() {
-        return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
-    }
-
     }
 }