Merge "Removed dependecies between BufferQueue and SurfaceTexture"
diff --git a/api/current.txt b/api/current.txt
index 97be454..fffa1fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -614,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
@@ -711,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
@@ -3627,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;
@@ -3643,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;
@@ -3657,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);
@@ -3672,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);
@@ -6177,6 +6195,8 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+    field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
     field public android.content.pm.ActivityInfo[] activities;
     field public android.content.pm.ApplicationInfo applicationInfo;
     field public android.content.pm.ConfigurationInfo[] configPreferences;
@@ -6190,6 +6210,7 @@
     field public android.content.pm.ActivityInfo[] receivers;
     field public android.content.pm.FeatureInfo[] reqFeatures;
     field public java.lang.String[] requestedPermissions;
+    field public int[] requestedPermissionsFlags;
     field public android.content.pm.ServiceInfo[] services;
     field public java.lang.String sharedUserId;
     field public int sharedUserLabel;
@@ -6412,6 +6433,10 @@
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int PROTECTION_DANGEROUS = 1; // 0x1
+    field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
+    field public static final int PROTECTION_FLAG_SYSTEM = 16; // 0x10
+    field public static final int PROTECTION_MASK_BASE = 15; // 0xf
+    field public static final int PROTECTION_MASK_FLAGS = 240; // 0xf0
     field public static final int PROTECTION_NORMAL = 0; // 0x0
     field public static final int PROTECTION_SIGNATURE = 2; // 0x2
     field public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3; // 0x3
@@ -8611,14 +8636,14 @@
     method public static void getPixelFormatInfo(int, android.graphics.PixelFormat);
     field public static final int A_8 = 8; // 0x8
     field public static final deprecated int JPEG = 256; // 0x100
-    field public static final int LA_88 = 10; // 0xa
+    field public static final deprecated int LA_88 = 10; // 0xa
     field public static final int L_8 = 9; // 0x9
     field public static final int OPAQUE = -1; // 0xffffffff
-    field public static final int RGBA_4444 = 7; // 0x7
-    field public static final int RGBA_5551 = 6; // 0x6
+    field public static final deprecated int RGBA_4444 = 7; // 0x7
+    field public static final deprecated int RGBA_5551 = 6; // 0x6
     field public static final int RGBA_8888 = 1; // 0x1
     field public static final int RGBX_8888 = 2; // 0x2
-    field public static final int RGB_332 = 11; // 0xb
+    field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
     field public static final int TRANSLUCENT = -3; // 0xfffffffd
@@ -14880,7 +14905,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();
@@ -21917,12 +21942,14 @@
     method public java.lang.Object getTag();
     method public abstract java.lang.CharSequence getTitle();
     method public abstract void invalidate();
+    method public boolean isTitleOptional();
     method public abstract void setCustomView(android.view.View);
     method public abstract void setSubtitle(java.lang.CharSequence);
     method public abstract void setSubtitle(int);
     method public void setTag(java.lang.Object);
     method public abstract void setTitle(java.lang.CharSequence);
     method public abstract void setTitle(int);
+    method public void setTitleOptionalHint(boolean);
   }
 
   public static abstract interface ActionMode.Callback {
@@ -23179,8 +23206,10 @@
     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();
@@ -23256,6 +23285,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();
@@ -23303,6 +23333,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);
@@ -23337,10 +23370,11 @@
     method public void requestLayout();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
-    method protected void resetResolvedTextDirection();
+    method public void resetResolvedTextDirection();
+    method public void resolvePadding();
     method public static int resolveSize(int, int);
     method public static int resolveSizeAndState(int, int, int);
-    method protected void resolveTextDirection();
+    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);
@@ -23399,6 +23433,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);
@@ -23843,10 +23878,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;
   }
 
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 2c9cb35..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, NULL, 0);
-    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/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index f457842..ac5bffe 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -126,6 +126,16 @@
             return;
         }
 
+        if ("grant".equals(op)) {
+            runGrantRevokePermission(true);
+            return;
+        }
+
+        if ("revoke".equals(op)) {
+            runGrantRevokePermission(false);
+            return;
+        }
+
         if ("set-install-location".equals(op)) {
             runSetInstallLocation();
             return;
@@ -596,8 +606,9 @@
                 if (groups && groupName == null && pi.group != null) {
                     continue;
                 }
-                if (pi.protectionLevel < startProtectionLevel
-                        || pi.protectionLevel > endProtectionLevel) {
+                final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+                if (base < startProtectionLevel
+                        || base > endProtectionLevel) {
                     continue;
                 }
                 if (summary) {
@@ -627,22 +638,8 @@
                                     + loadText(pi, pi.descriptionRes,
                                             pi.nonLocalizedDescription));
                         }
-                        String protLevel = "unknown";
-                        switch(pi.protectionLevel) {
-                            case PermissionInfo.PROTECTION_DANGEROUS:
-                                protLevel = "dangerous";
-                                break;
-                            case PermissionInfo.PROTECTION_NORMAL:
-                                protLevel = "normal";
-                                break;
-                            case PermissionInfo.PROTECTION_SIGNATURE:
-                                protLevel = "signature";
-                                break;
-                            case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
-                                protLevel = "signatureOrSystem";
-                                break;
-                        }
-                        System.out.println(prefix + "  protectionLevel:" + protLevel);
+                        System.out.println(prefix + "  protectionLevel:"
+                                + PermissionInfo.protectionToString(pi.protectionLevel));
                     }
                 }
             }
@@ -1063,6 +1060,36 @@
         }
     }
 
+    private void runGrantRevokePermission(boolean grant) {
+        String pkg = nextArg();
+        if (pkg == null) {
+            System.err.println("Error: no package specified");
+            showUsage();
+            return;
+        }
+        String perm = nextArg();
+        if (perm == null) {
+            System.err.println("Error: no permission specified");
+            showUsage();
+            return;
+        }
+        try {
+            if (grant) {
+                mPm.grantPermission(pkg, perm);
+            } else {
+                mPm.revokePermission(pkg, perm);
+            }
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(PM_NOT_RUNNING_ERR);
+        } catch (IllegalArgumentException e) {
+            System.err.println("Bad argument: " + e.toString());
+            showUsage();
+        } catch (SecurityException e) {
+            System.err.println("Operation not allowed: " + e.toString());
+        }
+    }
+
     /**
      * Displays the package file for a package.
      * @param pckg
@@ -1158,6 +1185,8 @@
         System.err.println("       pm enable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable-user PACKAGE_OR_COMPONENT");
+        System.err.println("       pm grant PACKAGE PERMISSION");
+        System.err.println("       pm revoke PACKAGE PERMISSION");
         System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
         System.err.println("       pm get-install-location");
         System.err.println("       pm create-profile USER_NAME");
@@ -1208,6 +1237,10 @@
         System.err.println("pm enable, disable, disable-user: these commands change the enabled state");
         System.err.println("  of a given package or component (written as \"package/class\").");
         System.err.println("");
+        System.err.println("pm grant, revoke: these commands either grant or revoke permissions");
+        System.err.println("  to applications.  Only optional permissions the application has");
+        System.err.println("  declared can be granted or revoked.");
+        System.err.println("");
         System.err.println("pm get-install-location: returns the current install location.");
         System.err.println("    0 [auto]: Let system decide the best location");
         System.err.println("    1 [internal]: Install on internal device storage");
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 cfc2d16..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;
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 11e94e8..f26747b 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -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/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/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/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 5a36466..24079a5d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -581,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();
@@ -601,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);
@@ -2178,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
     {
@@ -2204,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
     {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bf632a9..5d1d461 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -78,6 +78,7 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
+import android.renderscript.RenderScript;
 
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.RuntimeInit;
@@ -3779,6 +3780,7 @@
                 appContext.init(info, null, this);
 
                 HardwareRenderer.setupDiskCache(appContext.getCacheDir());
+                RenderScript.setupDiskCache(appContext.getCacheDir());
             }
         } catch (RemoteException e) {
             // Ignore
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index fee2beb..758ce09 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -332,6 +332,24 @@
     }
 
     @Override
+    public void grantPermission(String packageName, String permissionName) {
+        try {
+            mPM.grantPermission(packageName, permissionName);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
+    public void revokePermission(String packageName, String permissionName) {
+        try {
+            mPM.revokePermission(packageName, permissionName);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
     public int checkSignatures(String pkg1, String pkg2) {
         try {
             return mPM.checkSignatures(pkg1, pkg2);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ebf692a..6d5cce5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -911,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());
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 7deb615..53a71db 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -150,8 +150,11 @@
             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)
@@ -415,7 +418,7 @@
                 source.readStrongBinder());
             noReleaseNeeded = source.readInt() != 0;
         }
-    };
+    }
 
     /** Information returned after waiting for an activity start. */
     public static class WaitResult implements Parcelable {
@@ -601,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/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/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/content/Context.java b/core/java/android/content/Context.java
index 6d4cdae..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
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index cd8d87f..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);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bb35c29..95b6fee 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -81,7 +81,11 @@
     boolean addPermission(in PermissionInfo info);
     
     void removePermission(String name);
-    
+
+    void grantPermission(String packageName, String permissionName);
+
+    void revokePermission(String packageName, String permissionName);
+
     boolean isProtectedBroadcast(String actionName);
     
     int checkSignatures(String pkg1, String pkg2);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index eb05d76..415d58a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -141,6 +141,30 @@
     public String[] requestedPermissions;
     
     /**
+     * Array of flags of all {@link android.R.styleable#AndroidManifestUsesPermission
+     * &lt;uses-permission&gt;} tags included under &lt;manifest&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_PERMISSIONS} was set.  Each value matches
+     * the corresponding entry in {@link #requestedPermissions}, and will have
+     * the flags {@link #REQUESTED_PERMISSION_REQUIRED} and
+     * {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+     */
+    public int[] requestedPermissionsFlags;
+
+    /**
+     * Flag for {@link #requestedPermissionsFlags}: the requested permission
+     * is required for the application to run; the user can not optionally
+     * disable it.
+     */
+    public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
+
+    /**
+     * Flag for {@link #requestedPermissionsFlags}: the requested permission
+     * is currently granted to the application.
+     */
+    public static final int REQUESTED_PERMISSION_GRANTED = 1<<1;
+
+    /**
      * Array of all signatures read from the package file.  This is only filled
      * in if the flag {@link PackageManager#GET_SIGNATURES} was set.
      */
@@ -229,6 +253,7 @@
         dest.writeTypedArray(instrumentation, parcelableFlags);
         dest.writeTypedArray(permissions, parcelableFlags);
         dest.writeStringArray(requestedPermissions);
+        dest.writeIntArray(requestedPermissionsFlags);
         dest.writeTypedArray(signatures, parcelableFlags);
         dest.writeTypedArray(configPreferences, parcelableFlags);
         dest.writeTypedArray(reqFeatures, parcelableFlags);
@@ -266,6 +291,7 @@
         instrumentation = source.createTypedArray(InstrumentationInfo.CREATOR);
         permissions = source.createTypedArray(PermissionInfo.CREATOR);
         requestedPermissions = source.createStringArray();
+        requestedPermissionsFlags = source.createIntArray();
         signatures = source.createTypedArray(Signature.CREATOR);
         configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
         reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 26a9181..f2133d8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1475,6 +1475,29 @@
     public abstract void removePermission(String name);
 
     /**
+     * Grant a permission to an application which the application does not
+     * already have.  The permission must have been requested by the application,
+     * but as an optional permission.  If the application is not allowed to
+     * hold the permission, a SecurityException is thrown.
+     * @hide
+     *
+     * @param packageName The name of the package that the permission will be
+     * granted to.
+     * @param permissionName The name of the permission.
+     */
+    public abstract void grantPermission(String packageName, String permissionName);
+
+    /**
+     * Revoke a permission that was previously granted by {@link #grantPermission}.
+     * @hide
+     *
+     * @param packageName The name of the package that the permission will be
+     * granted to.
+     * @param permissionName The name of the permission.
+     */
+    public abstract void revokePermission(String packageName, String permissionName);
+
+    /**
      * Compare the signatures of two packages to determine if the same
      * signature appears in both of them.  If they do contain the same
      * signature, then they are allowed special privileges when working
@@ -2125,7 +2148,7 @@
         if ((flags & GET_SIGNATURES) != 0) {
             packageParser.collectCertificates(pkg, 0);
         }
-        return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0);
+        return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null);
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8029bd5..7b4a0ad 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -51,6 +51,7 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.jar.Attributes;
@@ -211,7 +212,8 @@
      * @param flags indicating which optional information is included.
      */
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
-            int gids[], int flags, long firstInstallTime, long lastUpdateTime) {
+            int gids[], int flags, long firstInstallTime, long lastUpdateTime,
+            HashSet<String> grantedPermissions) {
 
         final int userId = Binder.getOrigCallingUser();
 
@@ -346,8 +348,16 @@
             N = p.requestedPermissions.size();
             if (N > 0) {
                 pi.requestedPermissions = new String[N];
+                pi.requestedPermissionsFlags = new int[N];
                 for (int i=0; i<N; i++) {
-                    pi.requestedPermissions[i] = p.requestedPermissions.get(i);
+                    final String perm = p.requestedPermissions.get(i);
+                    pi.requestedPermissions[i] = perm;
+                    if (p.requestedPermissionsRequired.get(i)) {
+                        pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+                    }
+                    if (grantedPermissions != null && grantedPermissions.contains(perm)) {
+                        pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+                    }
                 }
             }
         }
@@ -927,11 +937,14 @@
                 // that may change.
                 String name = sa.getNonResourceString(
                         com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
+                boolean required = sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
 
                 sa.recycle();
 
                 if (name != null && !pkg.requestedPermissions.contains(name)) {
                     pkg.requestedPermissions.add(name.intern());
+                    pkg.requestedPermissionsRequired.add(required);
                 }
 
                 XmlUtils.skipCurrentTag(parser);
@@ -1419,12 +1432,24 @@
                 PermissionInfo.PROTECTION_NORMAL);
 
         sa.recycle();
-        
+
         if (perm.info.protectionLevel == -1) {
             outError[0] = "<permission> does not specify protectionLevel";
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
         }
+
+        perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
+
+        if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
+            if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
+                    PermissionInfo.PROTECTION_SIGNATURE) {
+                outError[0] = "<permission>  protectionLevel specifies a flag but is "
+                        + "not based on signature type";
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                return null;
+            }
+        }
         
         if (!parseAllMetaData(res, parser, attrs, "<permission>", perm,
                 outError)) {
@@ -2951,6 +2976,7 @@
         public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
 
         public final ArrayList<String> requestedPermissions = new ArrayList<String>();
+        public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();
 
         public ArrayList<String> protectedBroadcasts;
         
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 3cc884b..69b812c 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -55,6 +55,30 @@
     public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
 
     /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>system</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_SYSTEM = 0x10;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>development</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_DEVELOPMENT = 0x20;
+
+    /**
+     * Mask for {@link #protectionLevel}: the basic protection type.
+     */
+    public static final int PROTECTION_MASK_BASE = 0xf;
+
+    /**
+     * Mask for {@link #protectionLevel}: additional flag bits.
+     */
+    public static final int PROTECTION_MASK_FLAGS = 0xf0;
+
+    /**
      * The group this permission is a part of, as per
      * {@link android.R.attr#permissionGroup}.
      */
@@ -79,10 +103,47 @@
      * The level of access this permission is protecting, as per
      * {@link android.R.attr#protectionLevel}.  Values may be
      * {@link #PROTECTION_NORMAL}, {@link #PROTECTION_DANGEROUS}, or
+     * {@link #PROTECTION_SIGNATURE}.  May also include the additional
+     * flags {@link #PROTECTION_FLAG_SYSTEM} or {@link #PROTECTION_FLAG_DEVELOPMENT}
+     * (which only make sense in combination with the base
      * {@link #PROTECTION_SIGNATURE}.
      */
     public int protectionLevel;
 
+    /** @hide */
+    public static int fixProtectionLevel(int level) {
+        if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
+            level = PROTECTION_SIGNATURE | PROTECTION_FLAG_SYSTEM;
+        }
+        return level;
+    }
+
+    /** @hide */
+    public static String protectionToString(int level) {
+        String protLevel = "????";
+        switch (level&PROTECTION_MASK_BASE) {
+            case PermissionInfo.PROTECTION_DANGEROUS:
+                protLevel = "dangerous";
+                break;
+            case PermissionInfo.PROTECTION_NORMAL:
+                protLevel = "normal";
+                break;
+            case PermissionInfo.PROTECTION_SIGNATURE:
+                protLevel = "signature";
+                break;
+            case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
+                protLevel = "signatureOrSystem";
+                break;
+        }
+        if ((level&PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) {
+            protLevel += "|system";
+        }
+        if ((level&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+            protLevel += "|development";
+        }
+        return protLevel;
+    }
+
     public PermissionInfo() {
     }
 
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/inputmethodservice/ExtractEditLayout.java b/core/java/android/inputmethodservice/ExtractEditLayout.java
index 220214b..5696839 100644
--- a/core/java/android/inputmethodservice/ExtractEditLayout.java
+++ b/core/java/android/inputmethodservice/ExtractEditLayout.java
@@ -124,6 +124,12 @@
         }
 
         @Override
+        public boolean isTitleOptional() {
+            // Not only is it optional, it will *never* be shown.
+            return true;
+        }
+
+        @Override
         public void setCustomView(View view) {
             // Custom view is not supported here.
         }
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/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/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 11dc124..64027ef 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -30,21 +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;
 
-    // Indicates the barrier nesting level.
-    private int mBarrierNestCount;
+    // The next barrier token.
+    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
+    private int mNextBarrierToken;
 
-    @SuppressWarnings("unused")
-    private int mPtr; // used by native code
-    
     private native void nativeInit();
     private native void nativeDestroy();
     private native void nativePollOnce(int ptr, int timeoutMillis);
@@ -97,56 +100,11 @@
         }
     }
 
-    /**
-     * Acquires a synchronization barrier.
-     *
-     * While a synchronization barrier is active, only asynchronous messages are
-     * permitted to execute.  Synchronous messages are retained but are not executed
-     * until the synchronization barrier is released.
-     *
-     * This method is used to immediately postpone execution of all synchronous messages
-     * until a condition is met that releases the barrier.  Asynchronous messages are
-     * exempt from the barrier and continue to be executed as usual.
-     *
-     * This call nests and must be matched by an equal number of calls to
-     * {@link #releaseSyncBarrier}.
-     *
-     * @hide
-     */
-    public final void acquireSyncBarrier() {
-        synchronized (this) {
-            mBarrierNestCount += 1;
-        }
-    }
-
-    /**
-     * Releases a synchronization barrier.
-     *
-     * This class undoes one invocation of {@link #acquireSyncBarrier}.
-     *
-     * @throws IllegalStateException if the barrier is not acquired.
-     *
-     * @hide
-     */
-    public final void releaseSyncBarrier() {
-        synchronized (this) {
-            if (mBarrierNestCount == 0) {
-                throw new IllegalStateException("The message queue synchronization barrier "
-                        + "has not been acquired.");
-            }
-
-            mBarrierNestCount -= 1;
-            if (!mBlocked || mMessages == null) {
-                return;
-            }
-        }
-        nativeWake(mPtr);
-    }
-
-    MessageQueue() {
+    MessageQueue(boolean quitAllowed) {
+        mQuitAllowed = quitAllowed;
         nativeInit();
     }
-    
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -167,26 +125,26 @@
             nativePollOnce(mPtr, nextPollTimeoutMillis);
 
             synchronized (this) {
+                if (mQuiting) {
+                    return null;
+                }
+
                 // Try to retrieve the next message.  Return if found.
                 final long now = SystemClock.uptimeMillis();
-
                 Message prevMsg = null;
                 Message msg = mMessages;
-                for (;;) {
-                    if (msg == null) {
-                        // No more messages.
-                        nextPollTimeoutMillis = -1;
-                        break;
-                    }
-
-                    final long when = msg.when;
-                    if (now < when) {
+                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) {
+                    if (now < msg.when) {
                         // Next message is not ready.  Set a timeout to wake up when it is ready.
-                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
-                        break;
-                    }
-
-                    if (mBarrierNestCount == 0 || msg.isAsynchronous()) {
+                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
+                    } else {
                         // Got a message.
                         mBlocked = false;
                         if (prevMsg != null) {
@@ -199,16 +157,16 @@
                         msg.markInUse();
                         return msg;
                     }
-
-                    // We have a message that we could return except that it is
-                    // blocked by the sync barrier.  In particular, this means that
-                    // we are not idle yet, so we do not want to run the idle handlers.
-                    prevMsg = msg;
-                    msg = msg.next;
+                } else {
+                    // No more messages.
+                    nextPollTimeoutMillis = -1;
                 }
 
                 // If first time idle, then get the number of idlers to run.
-                if (pendingIdleHandlerCount < 0 && msg == mMessages) {
+                // 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) {
@@ -252,27 +210,94 @@
         }
     }
 
+    final void quit() {
+        if (!mQuitAllowed) {
+            throw new RuntimeException("Main thread not allowed to quit.");
+        }
+
+        synchronized (this) {
+            if (mQuiting) {
+                return;
+            }
+            mQuiting = true;
+        }
+        nativeWake(mPtr);
+    }
+
+    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 (when != 0) {
+                while (p != null && p.when <= when) {
+                    prev = p;
+                    p = p.next;
+                }
+            }
+            if (prev != null) { // invariant: p == prev.next
+                msg.next = p;
+                prev.next = msg;
+            } 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.");
+            throw new AndroidRuntimeException(msg + " This message is already in use.");
         }
-        if (msg.target == null && !mQuitAllowed) {
-            throw new RuntimeException("Main thread not allowed to quit");
+        if (msg.target == null) {
+            throw new AndroidRuntimeException("Message must have a target.");
         }
-        final boolean needWake;
+
+        boolean needWake;
         synchronized (this) {
             if (mQuiting) {
                 RuntimeException e = new RuntimeException(
-                    msg.target + " sending message to a Handler on a dead thread");
+                        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;
             }
 
             msg.when = when;
-            //Log.d("MessageQueue", "Enqueing: " + msg);
             Message p = mMessages;
             if (p == null || when == 0 || when < p.when) {
                 // New head, wake up the event queue if blocked.
@@ -281,18 +306,22 @@
                 needWake = mBlocked;
             } else {
                 // Inserted within the middle of the queue.  Usually we don't have to wake
-                // up the event queue unless the message is asynchronous and it might be
-                // possible for it to be returned out of sequence relative to an earlier
-                // synchronous message at the head of the queue.
-                Message prev = null;
-                while (p != null && p.when <= when) {
+                // 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 = prev.next;
+                msg.next = p; // invariant: p == prev.next
                 prev.next = msg;
-                needWake = mBlocked && mBarrierNestCount != 0 && msg.isAsynchronous()
-                        && !mMessages.isAsynchronous();
             }
         }
         if (needWake) {
@@ -301,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();
@@ -324,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;
@@ -334,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;
         }
 
@@ -374,6 +416,10 @@
     }
 
     final void removeCallbacksAndMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
         synchronized (this) {
             Message p = mMessages;
 
@@ -401,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/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/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/view/ActionMode.java b/core/java/android/view/ActionMode.java
index 0349a2b..0ba5fdb 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>
  */
@@ -104,6 +104,32 @@
     public abstract void setSubtitle(int resId);
 
     /**
+     * Set whether or not the title/subtitle display for this action mode
+     * is optional.
+     *
+     * <p>In many cases the supplied title for an action mode is merely
+     * meant to add context and is not strictly required for the action
+     * mode to be useful. If the title is optional, the system may choose
+     * to hide the title entirely rather than truncate it due to a lack
+     * of available space.</p>
+     *
+     * <p>Note that this is merely a hint; the underlying implementation
+     * may choose to ignore this setting under some circumstances.</p>
+     *
+     * @param titleOptional true if the title only presents optional information.
+     */
+    public void setTitleOptionalHint(boolean titleOptional) {
+    }
+
+    /**
+     * @return true if this action mode considers the title and subtitle fields
+     *         as optional. Optional titles may not be displayed to the user.
+     */
+    public boolean isTitleOptional() {
+        return false;
+    }
+
+    /**
      * Set a custom view for this action mode. The custom view will take the place of
      * the title and subtitle. Useful for things like search boxes.
      *
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 658da2f..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,8 +26,8 @@
 /**
  * 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
@@ -41,10 +39,6 @@
     private static final String TAG = "Choreographer";
     private static final boolean DEBUG = false;
 
-    // Amount of time in ms to wait before actually disposing of the display event
-    // receiver after all listeners have been removed.
-    private static final long DISPOSE_RECEIVER_DELAY = 200;
-
     // The default amount of time in ms between animation frames.
     // When vsync is not enabled, we want to have some idea of how long we should
     // wait before posting the next animation message.  It is important that the
@@ -87,31 +81,27 @@
     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 static final int MSG_DO_DISPOSE_RECEIVER = 3;
 
     private final Object mLock = new Object();
 
     private final Looper mLooper;
     private final FrameHandler mHandler;
+    private final FrameDisplayEventReceiver mDisplayEventReceiver;
 
     private Callback mCallbackPool;
 
-    private OnAnimateListener[] mOnAnimateListeners;
-    private OnDrawListener[] mOnDrawListeners;
-
-    private Callback mOnAnimateCallbacks;
-    private Callback mOnDrawCallbacks;
+    private Callback mAnimationCallbacks;
+    private Callback mDrawCallbacks;
 
     private boolean mAnimationScheduled;
     private boolean mDrawScheduled;
-    private boolean mFrameDisplayEventReceiverNeeded;
-    private FrameDisplayEventReceiver mFrameDisplayEventReceiver;
     private long mLastAnimationTime;
     private long mLastDrawTime;
 
     private Choreographer(Looper looper) {
         mLooper = looper;
         mHandler = new FrameHandler(looper);
+        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
         mLastAnimationTime = Long.MIN_VALUE;
         mLastDrawTime = Long.MIN_VALUE;
     }
@@ -160,195 +150,20 @@
     }
 
     /**
-     * Schedules animation (and drawing) to occur on the next frame synchronization boundary.
-     */
-    public void scheduleAnimation() {
-        synchronized (mLock) {
-            scheduleAnimationLocked(false);
-        }
-    }
-
-    private void scheduleAnimationLocked(boolean force) {
-        if (!mAnimationScheduled
-                && (force || mOnAnimateListeners != null || mOnAnimateCallbacks != null)) {
-            mAnimationScheduled = true;
-            if (USE_VSYNC) {
-                if (DEBUG) {
-                    Log.d(TAG, "Scheduling vsync for animation.");
-                }
-
-                // 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 (!mFrameDisplayEventReceiverNeeded) {
-                    mFrameDisplayEventReceiverNeeded = true;
-                    if (mFrameDisplayEventReceiver != null) {
-                        mHandler.removeMessages(MSG_DO_DISPOSE_RECEIVER);
-                    }
-                }
-                if (isRunningOnLooperThreadLocked()) {
-                    doScheduleVsyncLocked();
-                } else {
-                    mHandler.sendMessageAtFrontOfQueue(
-                            mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC));
-                }
-            } 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.");
-                }
-                mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, 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) {
-            scheduleDrawLocked();
-        }
-    }
-
-    private void scheduleDrawLocked() {
-        if (!mDrawScheduled
-                && (mOnDrawListeners != null || mOnDrawCallbacks != null)) {
-            mDrawScheduled = true;
-            if (USE_ANIMATION_TIMER_FOR_DRAW) {
-                scheduleAnimationLocked(true);
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "Scheduling draw immediately.");
-                }
-                mHandler.sendEmptyMessage(MSG_DO_DRAW);
-            }
-        }
-    }
-
-    /**
-     * 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;
-        }
-    }
-
-    /**
-     * 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);
-        }
-
-        synchronized (mLock) {
-            mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
-                    mOnAnimateListeners, listener);
-        }
-    }
-
-    /**
-     * 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);
-            stopTimingLoopIfNoListenersOrCallbacksLocked();
-        }
-    }
-
-    /**
-     * 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);
-        }
-    }
-
-    /**
-     * 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");
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "Removing onDraw listener: " + listener);
-        }
-
-        synchronized (mLock) {
-            mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
-                    mOnDrawListeners, listener);
-            stopTimingLoopIfNoListenersOrCallbacksLocked();
-        }
-    }
-
-
-    /**
      * 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 #removeOnAnimateCallback
+     * @see #removeAnimationCallback
      */
-    public void postOnAnimateCallback(Runnable runnable) {
+    public void postAnimationCallback(Runnable runnable) {
         if (runnable == null) {
             throw new IllegalArgumentException("runnable must not be null");
         }
         synchronized (mLock) {
-            mOnAnimateCallbacks = addCallbackLocked(mOnAnimateCallbacks, runnable);
-            scheduleAnimationLocked(false);
+            mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, runnable);
+            scheduleAnimationLocked();
         }
     }
 
@@ -359,15 +174,14 @@
      *
      * @param runnable The animation callback to remove.
      *
-     * @see #postOnAnimateCallback
+     * @see #postAnimationCallback
      */
-    public void removeOnAnimateCallback(Runnable runnable) {
+    public void removeAnimationCallback(Runnable runnable) {
         if (runnable == null) {
             throw new IllegalArgumentException("runnable must not be null");
         }
         synchronized (mLock) {
-            mOnAnimateCallbacks = removeCallbackLocked(mOnAnimateCallbacks, runnable);
-            stopTimingLoopIfNoListenersOrCallbacksLocked();
+            mAnimationCallbacks = removeCallbackLocked(mAnimationCallbacks, runnable);
         }
     }
 
@@ -377,14 +191,14 @@
      *
      * @param runnable The callback to run during the next draw cycle.
      *
-     * @see #removeOnDrawCallback
+     * @see #removeDrawCallback
      */
-    public void postOnDrawCallback(Runnable runnable) {
+    public void postDrawCallback(Runnable runnable) {
         if (runnable == null) {
             throw new IllegalArgumentException("runnable must not be null");
         }
         synchronized (mLock) {
-            mOnDrawCallbacks = addCallbackLocked(mOnDrawCallbacks, runnable);
+            mDrawCallbacks = addCallbackLocked(mDrawCallbacks, runnable);
             scheduleDrawLocked();
         }
     }
@@ -396,15 +210,61 @@
      *
      * @param runnable The draw callback to remove.
      *
-     * @see #postOnDrawCallback
+     * @see #postDrawCallback
      */
-    public void removeOnDrawCallback(Runnable runnable) {
+    public void removeDrawCallback(Runnable runnable) {
         if (runnable == null) {
             throw new IllegalArgumentException("runnable must not be null");
         }
         synchronized (mLock) {
-            mOnDrawCallbacks = removeCallbackLocked(mOnDrawCallbacks, runnable);
-            stopTimingLoopIfNoListenersOrCallbacksLocked();
+            mDrawCallbacks = removeCallbackLocked(mDrawCallbacks, runnable);
+        }
+    }
+
+    private void scheduleAnimationLocked() {
+        if (!mAnimationScheduled) {
+            mAnimationScheduled = true;
+            if (USE_VSYNC) {
+                if (DEBUG) {
+                    Log.d(TAG, "Scheduling vsync for animation.");
+                }
+
+                // 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);
+                }
+            } 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.");
+                }
+                Message msg = mHandler.obtainMessage(MSG_DO_ANIMATION);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageAtTime(msg, nextAnimationTime);
+            }
+        }
+    }
+
+    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);
+            }
         }
     }
 
@@ -418,7 +278,6 @@
 
     void doAnimationInner() {
         final long start;
-        final OnAnimateListener[] listeners;
         final Callback callbacks;
         synchronized (mLock) {
             if (!mAnimationScheduled) {
@@ -433,15 +292,8 @@
             }
             mLastAnimationTime = start;
 
-            listeners = mOnAnimateListeners;
-            callbacks = mOnAnimateCallbacks;
-            mOnAnimateCallbacks = null;
-        }
-
-        if (listeners != null) {
-            for (int i = 0; i < listeners.length; i++) {
-                listeners[i].onAnimate();
-            }
+            callbacks = mAnimationCallbacks;
+            mAnimationCallbacks = null;
         }
 
         if (callbacks != null) {
@@ -458,7 +310,6 @@
 
     void doDraw() {
         final long start;
-        final OnDrawListener[] listeners;
         final Callback callbacks;
         synchronized (mLock) {
             if (!mDrawScheduled) {
@@ -473,15 +324,8 @@
             }
             mLastDrawTime = start;
 
-            listeners = mOnDrawListeners;
-            callbacks = mOnDrawCallbacks;
-            mOnDrawCallbacks = null;
-        }
-
-        if (listeners != null) {
-            for (int i = 0; i < listeners.length; i++) {
-                listeners[i].onDraw();
-            }
+            callbacks = mDrawCallbacks;
+            mDrawCallbacks = null;
         }
 
         if (callbacks != null) {
@@ -503,58 +347,8 @@
     }
 
     private void doScheduleVsyncLocked() {
-        if (mFrameDisplayEventReceiverNeeded && mAnimationScheduled) {
-            if (mFrameDisplayEventReceiver == null) {
-                mFrameDisplayEventReceiver = new FrameDisplayEventReceiver(mLooper);
-            }
-            mFrameDisplayEventReceiver.scheduleVsync();
-        }
-    }
-
-    void doDisposeReceiver() {
-        synchronized (mLock) {
-            if (!mFrameDisplayEventReceiverNeeded && mFrameDisplayEventReceiver != null) {
-                mFrameDisplayEventReceiver.dispose();
-                mFrameDisplayEventReceiver = null;
-            }
-        }
-    }
-
-    private void stopTimingLoopIfNoListenersOrCallbacksLocked() {
-        if (mOnAnimateListeners == null && mOnDrawListeners == null
-                && mOnAnimateCallbacks == null && mOnDrawCallbacks == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Stopping timing loop.");
-            }
-
-            if (mAnimationScheduled) {
-                mAnimationScheduled = false;
-                if (USE_VSYNC) {
-                    mHandler.removeMessages(MSG_DO_SCHEDULE_VSYNC);
-                } else {
-                    mHandler.removeMessages(MSG_DO_ANIMATION);
-                }
-            }
-
-            if (mDrawScheduled) {
-                mDrawScheduled = false;
-                if (!USE_ANIMATION_TIMER_FOR_DRAW) {
-                    mHandler.removeMessages(MSG_DO_DRAW);
-                }
-            }
-
-            // Post a message to dispose the display event receiver if we haven't needed
-            // it again after a certain amount of time has elapsed.  Another reason to
-            // defer disposal is that it is possible for use to attempt to dispose the
-            // receiver while handling a vsync event that it dispatched, which might
-            // cause a few problems...
-            if (mFrameDisplayEventReceiverNeeded) {
-                mFrameDisplayEventReceiverNeeded = false;
-                if (mFrameDisplayEventReceiver != null) {
-                    mHandler.sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER,
-                            DISPOSE_RECEIVER_DELAY);
-                }
-            }
+        if (mAnimationScheduled) {
+            mDisplayEventReceiver.scheduleVsync();
         }
     }
 
@@ -627,26 +421,6 @@
         mCallbackPool = callback;
     }
 
-    /**
-     * Listens for animation frame timing events.
-     */
-    public static interface OnAnimateListener {
-        /**
-         * Called to animate properties before drawing the frame.
-         */
-        public void onAnimate();
-    }
-
-    /**
-     * Listens for draw frame timing events.
-     */
-    public static interface OnDrawListener {
-        /**
-         * Called to draw the frame.
-         */
-        public void onDraw();
-    }
-
     private final class FrameHandler extends Handler {
         public FrameHandler(Looper looper) {
             super(looper);
@@ -664,9 +438,6 @@
                 case MSG_DO_SCHEDULE_VSYNC:
                     doScheduleVsync();
                     break;
-                case MSG_DO_DISPOSE_RECEIVER:
-                    doDisposeReceiver();
-                    break;
             }
         }
     }
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 1e92b43..36582af 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,
@@ -726,10 +741,13 @@
         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,
@@ -740,9 +758,12 @@
         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(
@@ -753,10 +774,13 @@
         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,
@@ -767,23 +791,26 @@
         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
@@ -791,23 +818,26 @@
         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,
@@ -819,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
@@ -854,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,
@@ -867,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,
@@ -901,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,
@@ -916,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,
@@ -933,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);
@@ -1001,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,
@@ -1047,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,
@@ -1072,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,
@@ -1151,14 +1208,38 @@
     @Override
     public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset,
             float vOffset, Paint paint) {
-        // TODO: Implement
+        if (index < 0 || index + count > text.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+        int modifiers = setupModifiers(paint);
+        try {
+            nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
+                    paint.mBidiFlags, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
+    private static native void nDrawTextOnPath(int renderer, char[] text, int index, int count,
+            int path, float hOffset, float vOffset, int bidiFlags, int nativePaint);
+
     @Override
     public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
-        // TODO: Implement
+        if (text.length() == 0) return;
+
+        int modifiers = setupModifiers(paint);
+        try {
+            nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
+                    paint.mBidiFlags, paint.mNativePaint);
+        } finally {
+            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        }
     }
 
+    private static native void nDrawTextOnPath(int renderer, String text, int start, int end,
+            int path, float hOffset, float vOffset, int bidiFlags, int nativePaint);
+
     @Override
     public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
             float x, float y, int dir, Paint paint) {
@@ -1223,17 +1304,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 1932c7f..ec95863 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -109,8 +109,12 @@
 
     /**
      * 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
@@ -491,6 +495,8 @@
         final boolean mProfileEnabled;
         final float[] mProfileData;
         int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
+        
+        final boolean mDebugDirtyRegions;
 
         final int mGlVersion;
         final boolean mTranslucent;
@@ -502,17 +508,19 @@
         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) {
-                final String profileProperty = SystemProperties.get(PROFILE_PROPERTY, "false");
-                mProfileEnabled = "true".equalsIgnoreCase(profileProperty);
+                property = SystemProperties.get(PROFILE_PROPERTY, "false");
+                mProfileEnabled = "true".equalsIgnoreCase(property);
                 if (mProfileEnabled) {
                     Log.d(LOG_TAG, "Profiling hardware renderer");
                 }
@@ -525,6 +533,12 @@
             } 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
@@ -955,7 +969,8 @@
                             }
 
                             boolean invalidateNeeded = canvas.drawDisplayList(displayList,
-                                    view.getWidth(), view.getHeight(), mRedrawClip);
+                                    view.getWidth(), view.getHeight(), mRedrawClip,
+                                    DisplayList.FLAG_CLIP_CHILDREN);
 
                             if (mProfileEnabled) {
                                 long now = System.nanoTime();
@@ -981,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);
@@ -991,10 +1010,6 @@
                                 canvas.drawRect(dirty, mDebugPaint);
                             }
                         }
-                    } finally {
-                        callbacks.onHardwarePostDraw(canvas);
-                        canvas.restoreToCount(saveCount);
-                        view.mRecreateDisplayList = false;
                     }
 
                     onPostDraw();
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 38f055f..0675a74 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -601,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
@@ -1757,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;
@@ -3619,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);
     }
 
     /**
@@ -4887,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.
@@ -4997,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 {
@@ -5440,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
@@ -6505,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.
@@ -6538,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) {
@@ -6595,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;
 
@@ -6619,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;
@@ -6823,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);
@@ -8683,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>
      * 
@@ -8696,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;
     }
 
     /**
@@ -8729,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;
     }
 
     /**
@@ -8756,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;
     }
 
@@ -8815,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);
         }
     }
 
@@ -8843,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;
@@ -8852,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);
         }
     }
 
@@ -9540,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:
@@ -9580,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
@@ -9623,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) {
     }
 
     /**
@@ -9649,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();
     }
 
     /**
@@ -9689,13 +9737,12 @@
         }
 
         if (mAttachInfo != null) {
-            mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_MSG, this);
+            mAttachInfo.mViewRootImpl.cancelInvalidate(this);
         }
 
         mCurrentAnimation = null;
 
         resetResolvedLayoutDirection();
-        resetResolvedTextDirection();
     }
 
     /**
@@ -11209,7 +11256,7 @@
                 } else {
                     mPrivateFlags &= ~DIRTY_MASK;
                     ((HardwareCanvas) canvas).drawDisplayList(displayList,
-                            mRight - mLeft, mBottom - mTop, null);
+                            mRight - mLeft, mBottom - mTop, null, flags);
                 }
             }
         } else if (cache != null) {
@@ -12249,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;
@@ -12305,8 +12350,6 @@
      * scrollbars as well.
      *
      * @return the start padding in pixels
-     *
-     * @hide
      */
     public int getPaddingStart() {
         return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
@@ -12330,8 +12373,6 @@
      * scrollbars as well.
      *
      * @return the end padding in pixels
-     *
-     * @hide
      */
     public int getPaddingEnd() {
         return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
@@ -12345,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;
@@ -13016,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;
 
@@ -14078,7 +14112,6 @@
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
-     *
      */
     public int getTextDirection() {
         return mTextDirection;
@@ -14095,7 +14128,6 @@
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
-     *
      */
     public void setTextDirection(int textDirection) {
         if (textDirection != mTextDirection) {
@@ -14115,7 +14147,6 @@
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
-     *
      */
     public int getResolvedTextDirection() {
         if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) {
@@ -14125,27 +14156,47 @@
     }
 
     /**
-     * Resolve the text direction.
-     *
+     * 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().
-     *
+     * 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() {
     }
 
     //
@@ -14449,8 +14500,7 @@
     private final class CheckForTap implements Runnable {
         public void run() {
             mPrivateFlags &= ~PREPRESSED;
-            mPrivateFlags |= PRESSED;
-            refreshDrawableState();
+            setPressed(true);
             checkForLongClick(ViewConfiguration.getTapTimeout());
         }
     }
@@ -14970,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.
          */
@@ -15015,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 a2e85a3..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;
 
@@ -168,6 +169,11 @@
      */
     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
     static final int FLAG_CLIP_CHILDREN = 0x1;
@@ -370,6 +376,10 @@
     @ViewDebug.ExportedProperty(category = "drawing")
     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);
         initViewGroup();
@@ -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
@@ -2627,6 +2669,15 @@
         return child.draw(canvas, this, drawingTime);
     }
 
+    @Override
+    public void requestLayout() {
+        if (mChildrenCount > 0 && getAccessibilityNodeProvider() != null) {
+            throw new IllegalStateException("Views with AccessibilityNodeProvider"
+                    + " can't have children.");
+        }
+        super.requestLayout();
+    }
+
     /**
      * 
      * @param enabled True if children should be drawn with layers, false otherwise.
@@ -3094,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) {
@@ -3290,6 +3345,10 @@
            view.dispatchDetachedFromWindow();
         }
 
+        if (view.hasTransientState()) {
+            childHasTransientStateChanged(view, false);
+        }
+
         onViewRemoved(view);
 
         needGlobalAttributesUpdate(false);
@@ -3361,6 +3420,10 @@
                view.dispatchDetachedFromWindow();
             }
 
+            if (view.hasTransientState()) {
+                childHasTransientStateChanged(view, false);
+            }
+
             needGlobalAttributesUpdate(false);
 
             onViewRemoved(view);
@@ -3426,6 +3489,10 @@
                view.dispatchDetachedFromWindow();
             }
 
+            if (view.hasTransientState()) {
+                childHasTransientStateChanged(view, false);
+            }
+
             onViewRemoved(view);
 
             view.mParent = null;
@@ -3466,6 +3533,10 @@
             child.dispatchDetachedFromWindow();
         }
 
+        if (child.hasTransientState()) {
+            childHasTransientStateChanged(child, false);
+        }
+
         onViewRemoved(child);
     }
 
@@ -3604,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);
         }
     }
 
@@ -4852,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++) {
@@ -5123,20 +5198,19 @@
 
         /**
          * 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.
@@ -5268,8 +5342,6 @@
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
          *
          * @return the start margin in pixels.
-         *
-         * @hide
          */
         public int getMarginStart() {
             return startMargin;
@@ -5281,8 +5353,6 @@
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
          *
          * @return the end margin in pixels.
-         *
-         * @hide
          */
         public int getMarginEnd() {
             return endMargin;
@@ -5295,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 1930a5e..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,16 +852,35 @@
     public void scheduleTraversals() {
         if (!mTraversalScheduled) {
             mTraversalScheduled = true;
-            mChoreographer.scheduleDraw();
+            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
+            scheduleFrame();
         }
     }
 
     public void unscheduleTraversals() {
-        mTraversalScheduled = false;
+        if (mTraversalScheduled) {
+            mTraversalScheduled = false;
+            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
+        }
     }
 
-    @Override
-    public void onDraw() {
+    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();
         }
@@ -858,6 +888,7 @@
 
         if (mTraversalScheduled) {
             mTraversalScheduled = false;
+            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
             doTraversal();
         }
     }
@@ -1919,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));
                 }
             }
         }
@@ -2376,7 +2407,7 @@
             mInputChannel = null;
         }
 
-        mChoreographer.removeOnDrawListener(this);
+        unscheduleFrame();
     }
 
     void updateConfiguration(Configuration config, boolean force) {
@@ -2431,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, true);
-        } 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, true);
-        } 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
@@ -3529,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,
@@ -3674,7 +3711,7 @@
         if (immediate) {
             doDie();
         } else {
-            sendEmptyMessage(DIE);
+            mHandler.sendEmptyMessage(MSG_DIE);
         }
     }
 
@@ -3711,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() {
@@ -3724,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,
@@ -3736,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);
@@ -3750,7 +3792,7 @@
         ri.visibleInsets = new Rect(visibleInsets);
         ri.newConfig = newConfig;
         msg.obj = ri;
-        sendMessage(msg);
+        mHandler.sendMessage(msg);
     }
 
     /**
@@ -3847,7 +3889,7 @@
     private void scheduleProcessInputEvents() {
         if (!mProcessInputEventsScheduled) {
             mProcessInputEventsScheduled = true;
-            sendEmptyMessage(DO_PROCESS_INPUT_EVENTS);
+            mHandler.sendEmptyMessage(MSG_PROCESS_INPUT_EVENTS);
         }
     }
 
@@ -3864,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);
         }
     }
 
@@ -3923,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);
@@ -3935,52 +3987,74 @@
 
         @Override
         public void onBatchedInputEventPending() {
-            mChoreographer.scheduleDraw();
+            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) {
-        Message msg = obtainMessage(DISPATCH_KEY, event);
-        sendMessage(msg);
+        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,
@@ -3990,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);
         }
     }
 
@@ -4021,7 +4087,7 @@
         }
         if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) {
             mSendWindowContentChangedAccessibilityEvent.mIsPending = true;
-            postDelayed(mSendWindowContentChangedAccessibilityEvent,
+            mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent,
                     ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
         }
     }
@@ -4032,7 +4098,7 @@
      */
     private void removeSendWindowContentChangedCallback() {
         if (mSendWindowContentChangedAccessibilityEvent != null) {
-            removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
+            mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
         }
     }
 
@@ -4054,7 +4120,6 @@
         if (mView == null) {
             return false;
         }
-        getAccessibilityPrefetchStrategy().onAccessibilityEvent(event);
         mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
     }
@@ -4075,6 +4140,10 @@
         return scrollToRectOrFocus(rectangle, immediate);
     }
 
+    public void childHasTransientStateChanged(View child, boolean hasTransientState) {
+        // Do nothing.
+    }
+
     class TakenSurfaceHolder extends BaseSurfaceHolder {
         @Override
         public boolean onAllowLockCanvas() {
@@ -4492,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 {
@@ -4570,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);
             }
         }
@@ -4608,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 {
@@ -4748,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);
@@ -4765,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;
@@ -4785,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 {
@@ -4808,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;
@@ -4822,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);
             }
         }
 
@@ -4841,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;
@@ -4865,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);
@@ -4880,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);
             }
         }
 
@@ -4900,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;
@@ -4910,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
@@ -4951,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();
@@ -4966,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);
             }
         }
 
@@ -4991,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()) {
@@ -5057,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/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 7171b58..b6b27c1 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -26,7 +26,6 @@
 import com.android.internal.view.InputBindResult;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -198,7 +197,31 @@
 
     static final Object mInstanceSync = new Object();
     static InputMethodManager mInstance;
-    
+
+    /**
+     * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
+     * the window has input focus.
+     */
+    public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0;
+
+    /**
+     * @hide Flag for IInputMethodManager.windowGainedFocus: the focus
+     * is a text editor.
+     */
+    public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1;
+
+    /**
+     * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first
+     * time the window has gotten focus.
+     */
+    public static final int CONTROL_WINDOW_FIRST = 1<<2;
+
+    /**
+     * @hide Flag for IInputMethodManager.startInput: this is the first
+     * time the window has gotten focus.
+     */
+    public static final int CONTROL_START_INITIAL = 1<<8;
+
     final IInputMethodManager mService;
     final Looper mMainLooper;
     
@@ -216,7 +239,7 @@
     
     /**
      * Set whenever this client becomes inactive, to know we need to reset
-     * state with the IME then next time we receive focus.
+     * state with the IME the next time we receive focus.
      */
     boolean mHasBeenInactive = true;
     
@@ -243,11 +266,6 @@
      */
     View mNextServedView;
     /**
-     * True if we should restart input in the next served view, even if the
-     * view hasn't actually changed from the current serve view.
-     */
-    boolean mNextServedNeedsStart;
-    /**
      * This is set when we are in the process of connecting, to determine
      * when we have actually finished.
      */
@@ -331,11 +349,12 @@
                         mCurId = res.id;
                         mBindSequence = res.sequence;
                     }
-                    startInputInner();
+                    startInputInner(null, 0, 0, 0);
                     return;
                 }
                 case MSG_UNBIND: {
                     final int sequence = msg.arg1;
+                    boolean startInput = false;
                     synchronized (mH) {
                         if (mBindSequence == sequence) {
                             if (false) {
@@ -355,8 +374,13 @@
                             if (mServedView != null && mServedView.isFocused()) {
                                 mServedConnecting = true;
                             }
+                            if (mActive) {
+                                startInput = true;
+                            }
                         }
-                        startInputInner();
+                    }
+                    if (startInput) {
+                        startInputInner(null, 0, 0, 0);
                     }
                     return;
                 }
@@ -669,11 +693,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);
             }
         }
     }
@@ -952,10 +975,11 @@
             mServedConnecting = true;
         }
         
-        startInputInner();
+        startInputInner(null, 0, 0, 0);
     }
     
-    void startInputInner() {
+    boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
+            int windowFlags) {
         final View view;
         synchronized (mH) {
             view = mServedView;
@@ -964,7 +988,7 @@
             if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
             if (view == null) {
                 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
-                return;
+                return false;
             }
         }
         
@@ -977,7 +1001,7 @@
             // If the view doesn't have a handler, something has changed out
             // from under us, so just bail.
             if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
-            return;
+            return false;
         }
         if (vh.getLooper() != Looper.myLooper()) {
             // The view is running on a different thread than our own, so
@@ -985,10 +1009,10 @@
             if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
             vh.post(new Runnable() {
                 public void run() {
-                    startInputInner();
+                    startInputInner(null, 0, 0, 0);
                 }
             });
-            return;
+            return false;
         }
         
         // Okay we are now ready to call into the served view and have it
@@ -1008,12 +1032,14 @@
                 if (DEBUG) Log.v(TAG, 
                         "Starting input: finished by someone else (view="
                         + mServedView + " conn=" + mServedConnecting + ")");
-                return;
+                return false;
             }
-            
+
             // If we already have a text box, then this view is already
             // connected so we want to restart it.
-            final boolean initial = mCurrentTextBoxAttribute == null;
+            if (mCurrentTextBoxAttribute == null) {
+                controlFlags |= CONTROL_START_INITIAL;
+            }
             
             // Hook 'em up and let 'er rip.
             mCurrentTextBoxAttribute = tba;
@@ -1035,9 +1061,17 @@
             
             try {
                 if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
-                        + ic + " tba=" + tba + " initial=" + initial);
-                InputBindResult res = mService.startInput(mClient,
-                        servedContext, tba, initial, true);
+                        + ic + " tba=" + tba + " controlFlags=#"
+                        + Integer.toHexString(controlFlags));
+                InputBindResult res;
+                if (windowGainingFocus != null) {
+                    res = mService.windowGainedFocus(mClient, windowGainingFocus,
+                            controlFlags, softInputMode, windowFlags,
+                            tba, servedContext);
+                } else {
+                    res = mService.startInput(mClient,
+                            servedContext, tba, controlFlags);
+                }
                 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                 if (res != null) {
                     if (res.id != null) {
@@ -1046,7 +1080,7 @@
                     } else if (mCurMethod == null) {
                         // This means there is no input method available.
                         if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
-                        return;
+                        return true;
                     }
                 }
                 if (mCurMethod != null && mCompletions != null) {
@@ -1059,6 +1093,8 @@
                 Log.w(TAG, "IME died: " + mCurId, e);
             }
         }
+
+        return true;
     }
 
     /**
@@ -1124,54 +1160,58 @@
     }
 
     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(false)) {
+            startInputInner(null, 0, 0, 0);
+        }
+    }
+
+    private boolean checkFocusNoStartInput(boolean forceNewFocus) {
         // This is called a lot, so short-circuit before locking.
-        if (mServedView == mNextServedView && !mNextServedNeedsStart) {
-            return;
+        if (mServedView == mNextServedView && !forceNewFocus) {
+            return false;
         }
 
         InputConnection ic = null;
         synchronized (mH) {
-            if (mServedView == mNextServedView && !mNextServedNeedsStart) {
-                return;
+            if (mServedView == mNextServedView && !forceNewFocus) {
+                return false;
             }
             if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
                     + " next=" + mNextServedView
-                    + " restart=" + mNextServedNeedsStart);
-            
-            mNextServedNeedsStart = false;
+                    + " forceNewFocus=" + forceNewFocus);
+
             if (mNextServedView == null) {
                 finishInputLocked();
                 // In this case, we used to have a focused view on the window,
                 // 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() {
@@ -1180,13 +1220,14 @@
         } catch (RemoteException e) {
         }
     }
-    
+
     /**
      * Called by ViewAncestor when its window gets input focus.
      * @hide
      */
     public void onWindowFocus(View rootView, View focusedView, int softInputMode,
             boolean first, int windowFlags) {
+        boolean forceNewFocus = false;
         synchronized (mH) {
             if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
                     + " softInputMode=" + softInputMode
@@ -1195,20 +1236,39 @@
             if (mHasBeenInactive) {
                 if (DEBUG) Log.v(TAG, "Has been inactive!  Starting fresh");
                 mHasBeenInactive = false;
-                mNextServedNeedsStart = true;
+                forceNewFocus = true;
             }
             focusInLocked(focusedView != null ? focusedView : rootView);
         }
+
+        int controlFlags = 0;
+        if (focusedView != null) {
+            controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
+            if (focusedView.onCheckIsTextEditor()) {
+                controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
+            }
+        }
+        if (first) {
+            controlFlags |= CONTROL_WINDOW_FIRST;
+        }
         
-        checkFocus();
+        if (checkFocusNoStartInput(forceNewFocus)) {
+            // We need to restart input on the current focus view.  This
+            // should be done in conjunction with telling the system service
+            // about the window gaining focus, to help make the transition
+            // smooth.
+            if (startInputInner(rootView.getWindowToken(),
+                    controlFlags, softInputMode, windowFlags)) {
+                return;
+            }
+        }
         
+        // For some reason we didn't do a startInput + windowFocusGain, so
+        // we'll just do a window focus gain and call it a day.
         synchronized (mH) {
             try {
-                final boolean isTextEditor = focusedView != null &&
-                        focusedView.onCheckIsTextEditor();
                 mService.windowGainedFocus(mClient, rootView.getWindowToken(),
-                        focusedView != null, isTextEditor, softInputMode, first,
-                        windowFlags);
+                        controlFlags, softInputMode, windowFlags, null, null);
             } catch (RemoteException e) {
             }
         }
@@ -1662,8 +1722,7 @@
         p.println("  mCurMethod=" + mCurMethod);
         p.println("  mCurRootView=" + mCurRootView);
         p.println("  mServedView=" + mServedView);
-        p.println("  mNextServedNeedsStart=" + mNextServedNeedsStart
-                + " mNextServedView=" + mNextServedView);
+        p.println("  mNextServedView=" + mNextServedView);
         p.println("  mServedConnecting=" + mServedConnecting);
         if (mCurrentTextBoxAttribute != null) {
             p.println("  mCurrentTextBoxAttribute:");
diff --git a/include/private/ui/android_natives_priv.h b/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl
similarity index 81%
rename from include/private/ui/android_natives_priv.h
rename to core/java/android/view/textservice/SentenceSuggestionsInfo.aidl
index 6b9f524..d0b6ba6 100644
--- a/include/private/ui/android_natives_priv.h
+++ b/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 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,4 +14,6 @@
  * limitations under the License.
  */
 
-#include <ui/android_native_buffer.h>
+package android.view.textservice;
+
+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 e21e9ef..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/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index cdf20f6..2a770f5 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -53,10 +53,8 @@
         mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_copy, menu);
 
         final Context context = mWebView.getContext();
-        boolean allowText = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);
-        mode.setTitle(allowText ?
-                context.getString(com.android.internal.R.string.textSelectionCABTitle) : null);
+        mode.setTitle(context.getString(com.android.internal.R.string.textSelectionCABTitle));
+        mode.setTitleOptionalHint(true);
 
         // If the action mode UI we're running in isn't capable of taking window focus
         // the user won't be able to type into the find on page UI. Disable this functionality.
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/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 332a0eb..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) {
@@ -4914,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
@@ -4984,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);
     }
 
     /**
@@ -5110,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;
     }
 
@@ -5438,6 +5583,9 @@
                     + "keyCode=" + keyCode
                     + ", " + event + ", unicode=" + event.getUnicodeChar());
         }
+        if (mIsCaretSelection) {
+            selectionDone();
+        }
         if (mBlockWebkitViewMessages) {
             return false;
         }
@@ -5555,7 +5703,7 @@
             return false;
         }
 
-        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+        if (isEnterActionKey(keyCode)) {
             switchOutDrawHistory();
             boolean wantsKeyEvents = nativeCursorNodePointer() == 0
                 || nativeCursorWantsKeyEvents();
@@ -5704,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;
+                }
             }
         }
 
@@ -5748,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
@@ -5768,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;
@@ -5779,6 +5934,9 @@
 
     private void updateWebkitSelection() {
         int[] handles = null;
+        if (mIsCaretSelection) {
+            mSelectCursorExtent.set(mSelectCursorBase);
+        }
         if (mSelectingText) {
             handles = new int[4];
             handles[0] = mSelectCursorBase.centerX();
@@ -5792,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.
@@ -5819,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;
@@ -6435,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(
@@ -7097,6 +7276,9 @@
 
         if (mSelectingText) {
             mSelectionStarted = false;
+            if (mIsCaretSelection) {
+                resetCaretTimer();
+            }
             syncSelectionCursors();
             invalidate();
         }
@@ -7739,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
@@ -8842,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();
@@ -8984,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:
@@ -9037,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;
 
@@ -9053,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;
@@ -9060,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) {
@@ -9083,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();
+            }
         }
     }
 
@@ -9183,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();
         }
@@ -9889,9 +10239,6 @@
     private native void     nativeUpdateDrawGLFunction(Rect rect, Rect viewRect,
             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();
@@ -9949,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/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/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 988760d..e6184d5 100755
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -26,6 +26,12 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -61,18 +67,52 @@
         BOTH
     }
 
+    static class MyPermissionInfo extends PermissionInfo {
+        /**
+         * PackageInfo.requestedPermissionsFlags for the new package being installed.
+         */
+        int mNewReqFlags;
+
+        /**
+         * PackageInfo.requestedPermissionsFlags for the currently installed
+         * package, if it is installed.
+         */
+        int mExistingReqFlags;
+
+        /**
+         * True if this should be considered a new permission.
+         */
+        boolean mNew;
+
+        MyPermissionInfo() {
+        }
+
+        MyPermissionInfo(PermissionInfo info) {
+            super(info);
+        }
+
+        MyPermissionInfo(MyPermissionInfo info) {
+            super(info);
+            mNewReqFlags = info.mNewReqFlags;
+            mExistingReqFlags = info.mExistingReqFlags;
+            mNew = info.mNew;
+        }
+    }
+
     private final static String TAG = "AppSecurityPermissions";
     private boolean localLOGV = false;
     private Context mContext;
     private LayoutInflater mInflater;
     private PackageManager mPm;
     private LinearLayout mPermsView;
-    private Map<String, String> mDangerousMap;
-    private Map<String, String> mNormalMap;
-    private List<PermissionInfo> mPermsList;
+    private Map<String, CharSequence> mNewMap;
+    private Map<String, CharSequence> mDangerousMap;
+    private Map<String, CharSequence> mNormalMap;
+    private List<MyPermissionInfo> mPermsList;
     private String mDefaultGrpLabel;
     private String mDefaultGrpName="DefaultGrp";
     private String mPermFormat;
+    private CharSequence mNewPermPrefix;
     private Drawable mNormalIcon;
     private Drawable mDangerousIcon;
     private boolean mExpanded;
@@ -84,20 +124,23 @@
     private State mCurrentState;
     private LinearLayout mNonDangerousList;
     private LinearLayout mDangerousList;
+    private LinearLayout mNewList;
     private HashMap<String, CharSequence> mGroupLabelCache;
     private View mNoPermsView;
-    
+
     public AppSecurityPermissions(Context context, List<PermissionInfo> permList) {
         mContext = context;
         mPm = mContext.getPackageManager();
-        mPermsList = permList;
+        for (PermissionInfo pi : permList) {
+            mPermsList.add(new MyPermissionInfo(pi));
+        }
     }
     
     public AppSecurityPermissions(Context context, String packageName) {
         mContext = context;
         mPm = mContext.getPackageManager();
-        mPermsList = new ArrayList<PermissionInfo>();
-        Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();
+        mPermsList = new ArrayList<MyPermissionInfo>();
+        Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         PackageInfo pkgInfo;
         try {
             pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
@@ -109,7 +152,7 @@
         if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) {
             getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet);
         }
-        for(PermissionInfo tmpInfo : permSet) {
+        for(MyPermissionInfo tmpInfo : permSet) {
             mPermsList.add(tmpInfo);
         }
     }
@@ -117,21 +160,27 @@
     public AppSecurityPermissions(Context context, PackageParser.Package pkg) {
         mContext = context;
         mPm = mContext.getPackageManager();
-        mPermsList = new ArrayList<PermissionInfo>();
-        Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();
+        mPermsList = new ArrayList<MyPermissionInfo>();
+        Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         if(pkg == null) {
             return;
         }
+
+        // Convert to a PackageInfo
+        PackageInfo info = PackageParser.generatePackageInfo(pkg, null,
+                PackageManager.GET_PERMISSIONS, 0, 0, null);
+        PackageInfo installedPkgInfo = null;
         // Get requested permissions
-        if (pkg.requestedPermissions != null) {
-            ArrayList<String> strList = pkg.requestedPermissions;
-            int size = strList.size();
-            if (size > 0) {
-                extractPerms(strList.toArray(new String[size]), permSet);
+        if (info.requestedPermissions != null) {
+            try {
+                installedPkgInfo = mPm.getPackageInfo(info.packageName,
+                        PackageManager.GET_PERMISSIONS);
+            } catch (NameNotFoundException e) {
             }
+            extractPerms(info, permSet, installedPkgInfo);
         }
         // Get permissions related to  shared user if any
-        if(pkg.mSharedUserId != null) {
+        if (pkg.mSharedUserId != null) {
             int sharedUid;
             try {
                 sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId);
@@ -141,7 +190,7 @@
             }
         }
         // Retrieve list of permissions
-        for(PermissionInfo tmpInfo : permSet) {
+        for (MyPermissionInfo tmpInfo : permSet) {
             mPermsList.add(tmpInfo);
         }
     }
@@ -159,7 +208,7 @@
                 description, dangerous, icon);
     }
     
-    private void getAllUsedPermissions(int sharedUid, Set<PermissionInfo> permSet) {
+    private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
         String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
         if(sharedPkgList == null || (sharedPkgList.length == 0)) {
             return;
@@ -170,29 +219,65 @@
     }
     
     private void getPermissionsForPackage(String packageName, 
-            Set<PermissionInfo> permSet) {
+            Set<MyPermissionInfo> permSet) {
         PackageInfo pkgInfo;
         try {
             pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
         } catch (NameNotFoundException e) {
-            Log.w(TAG, "Could'nt retrieve permissions for package:"+packageName);
+            Log.w(TAG, "Couldn't retrieve permissions for package:"+packageName);
             return;
         }
         if ((pkgInfo != null) && (pkgInfo.requestedPermissions != null)) {
-            extractPerms(pkgInfo.requestedPermissions, permSet);
+            extractPerms(pkgInfo, permSet, pkgInfo);
         }
     }
-    
-    private void extractPerms(String strList[], Set<PermissionInfo> permSet) {
-        if((strList == null) || (strList.length == 0)) {
+
+    private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet,
+            PackageInfo installedPkgInfo) {
+        String[] strList = info.requestedPermissions;
+        int[] flagsList = info.requestedPermissionsFlags;
+        if ((strList == null) || (strList.length == 0)) {
             return;
         }
-        for(String permName:strList) {
+        for (int i=0; i<strList.length; i++) {
+            String permName = strList[i];
+            // If we are only looking at an existing app, then we only
+            // care about permissions that have actually been granted to it.
+            if (installedPkgInfo != null && info == installedPkgInfo) {
+                if ((flagsList[i]&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
+                    continue;
+                }
+            }
             try {
                 PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0);
-                if(tmpPermInfo != null) {
-                    permSet.add(tmpPermInfo);
+                if (tmpPermInfo == null) {
+                    continue;
                 }
+                int existingIndex = -1;
+                if (installedPkgInfo != null
+                        && installedPkgInfo.requestedPermissions != null) {
+                    for (int j=0; j<installedPkgInfo.requestedPermissions.length; j++) {
+                        if (permName.equals(installedPkgInfo.requestedPermissions[j])) {
+                            existingIndex = j;
+                            break;
+                        }
+                    }
+                }
+                final int existingFlags = existingIndex >= 0 ?
+                        installedPkgInfo.requestedPermissionsFlags[existingIndex] : 0;
+                if (!isDisplayablePermission(tmpPermInfo, flagsList[i], existingFlags)) {
+                    // This is not a permission that is interesting for the user
+                    // to see, so skip it.
+                    continue;
+                }
+                MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo);
+                myPerm.mNewReqFlags = flagsList[i];
+                myPerm.mExistingReqFlags = existingFlags;
+                // This is a new permission if the app is already installed and
+                // doesn't currently hold this permission.
+                myPerm.mNew = installedPkgInfo != null
+                        && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
+                permSet.add(myPerm);
             } catch (NameNotFoundException e) {
                 Log.i(TAG, "Ignoring unknown permission:"+permName);
             }
@@ -210,6 +295,7 @@
         mShowMore = mPermsView.findViewById(R.id.show_more);
         mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon);
         mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text);
+        mNewList = (LinearLayout) mPermsView.findViewById(R.id.new_perms_list);
         mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list);
         mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list);
         mNoPermsView = mPermsView.findViewById(R.id.no_permissions);
@@ -222,6 +308,7 @@
         // Pick up from framework resources instead.
         mDefaultGrpLabel = mContext.getString(R.string.default_permission_group);
         mPermFormat = mContext.getString(R.string.permissions_format);
+        mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
         mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);
         mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);
         mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark);
@@ -233,39 +320,56 @@
     }
 
     /**
-     * Canonicalizes the group description before it is displayed to the user.
-     *
-     * TODO check for internationalization issues remove trailing '.' in str1
-     */
-    private String canonicalizeGroupDesc(String groupDesc) {
-        if ((groupDesc == null) || (groupDesc.length() == 0)) {
-            return null;
-        }
-        // Both str1 and str2 are non-null and are non-zero in size.
-        int len = groupDesc.length();
-        if(groupDesc.charAt(len-1) == '.') {
-            groupDesc = groupDesc.substring(0, len-1);
-        }
-        return groupDesc;
-    }
-
-    /**
      * Utility method that concatenates two strings defined by mPermFormat.
      * a null value is returned if both str1 and str2 are null, if one of the strings
      * is null the other non null value is returned without formatting
      * this is to placate initial error checks
      */
-    private String formatPermissions(String groupDesc, CharSequence permDesc) {
-        if(groupDesc == null) {
-            if(permDesc == null) {
-                return null;
-            }
-            return permDesc.toString();
-        }
-        groupDesc = canonicalizeGroupDesc(groupDesc);
-        if(permDesc == null) {
+    private CharSequence formatPermissions(CharSequence groupDesc, CharSequence permDesc,
+            boolean newPerms) {
+        if (permDesc == null) {
             return groupDesc;
         }
+        // Sometimes people write permission names with a trailing period;
+        // strip that if it appears.
+        int len = permDesc.length();
+        if (len > 0 && permDesc.charAt(len-1) == '.') {
+            permDesc = (permDesc.toString()).substring(0, len-1);
+        }
+        if (newPerms) {
+            if (true) {
+                // If this is a new permission, format it appropriately.
+                SpannableStringBuilder builder = new SpannableStringBuilder();
+                if (groupDesc != null) {
+                    // The previous permissions go in front, with a newline
+                    // separating them.
+                    builder.append(groupDesc);
+                    builder.append("\n");
+                }
+                Parcel parcel = Parcel.obtain();
+                TextUtils.writeToParcel(mNewPermPrefix, parcel, 0);
+                parcel.setDataPosition(0);
+                CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+                parcel.recycle();
+                builder.append(newStr);
+                builder.append(permDesc);
+                return builder;
+            } else {
+                // If this is a new permission, format it appropriately.
+                SpannableStringBuilder builder = new SpannableStringBuilder(permDesc);
+                builder.insert(0, mNewPermPrefix);
+                if (groupDesc != null) {
+                    // The previous permissions go in front, with a newline
+                    // separating them.
+                    builder.insert(0, "\n");
+                    builder.insert(0, groupDesc);
+                }
+                return builder;
+            }
+        }
+        if (groupDesc == null) {
+            return permDesc;
+        }
         // groupDesc and permDesc are non null
         return String.format(mPermFormat, groupDesc, permDesc.toString());
     }
@@ -295,9 +399,8 @@
      * Utility method that displays permissions from a map containing group name and
      * list of permission descriptions.
      */
-    private void displayPermissions(boolean dangerous) {
-        Map<String, String> permInfoMap = dangerous ? mDangerousMap : mNormalMap;
-        LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList;
+    private void displayPermissions(Map<String, CharSequence> permInfoMap,
+            LinearLayout permListView, boolean dangerous) {
         permListView.removeAllViews();
 
         Set<String> permInfoStrSet = permInfoMap.keySet();
@@ -349,17 +452,20 @@
             break;
 
         case DANGEROUS_ONLY:
-            displayPermissions(true);
+            displayPermissions(mNewMap, mNewList, true);
+            displayPermissions(mDangerousMap, mDangerousList, true);
             break;
 
         case NORMAL_ONLY:
-            displayPermissions(false);
+            displayPermissions(mNewMap, mNewList, true);
+            displayPermissions(mNormalMap, mNonDangerousList, false);
             break;
 
         case BOTH:
-            displayPermissions(true);
+            displayPermissions(mNewMap, mNewList, true);
+            displayPermissions(mDangerousMap, mDangerousList, true);
             if (mExpanded) {
-                displayPermissions(false);
+                displayPermissions(mNormalMap, mNonDangerousList, false);
                 mShowMoreIcon.setImageDrawable(mShowMaxIcon);
                 mShowMoreText.setText(R.string.perms_hide);
                 mNonDangerousList.setVisibility(View.VISIBLE);
@@ -372,22 +478,38 @@
             break;
         }
     }
-    
-    private boolean isDisplayablePermission(PermissionInfo pInfo) {
-        if(pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS ||
-                pInfo.protectionLevel == PermissionInfo.PROTECTION_NORMAL) {
+
+    private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
+            int existingReqFlags) {
+        final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+        // Dangerous and normal permissions are always shown to the user.
+        if (base == PermissionInfo.PROTECTION_DANGEROUS ||
+                base == PermissionInfo.PROTECTION_NORMAL) {
+            return true;
+        }
+        // Development permissions are only shown to the user if they are already
+        // granted to the app -- if we are installing an app and they are not
+        // already granted, they will not be granted as part of the install.
+        // Note we also need the app to have specified this permission is not
+        // required -- this is not technically needed, but it helps various things
+        // if we ensure apps always mark development permissions as option, so that
+        // even not knowing what a permission is we can still know whether it will
+        // be granted to the app when it is installed.
+        if ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0
+                && (newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) == 0
+                && (pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
             return true;
         }
         return false;
     }
-    
+
     /*
      * Utility method that aggregates all permission descriptions categorized by group
      * Say group1 has perm11, perm12, perm13, the group description will be
      * perm11_Desc, perm12_Desc, perm13_Desc
      */
-    private void aggregateGroupDescs(
-            Map<String, List<PermissionInfo> > map, Map<String, String> retMap) {
+    private void aggregateGroupDescs(Map<String, List<MyPermissionInfo> > map,
+            Map<String, CharSequence> retMap, boolean newPerms) {
         if(map == null) {
             return;
         }
@@ -397,20 +519,20 @@
         Set<String> grpNames = map.keySet();
         Iterator<String> grpNamesIter = grpNames.iterator();
         while(grpNamesIter.hasNext()) {
-            String grpDesc = null;
+            CharSequence grpDesc = null;
             String grpNameKey = grpNamesIter.next();
-            List<PermissionInfo> grpPermsList = map.get(grpNameKey);
+            List<MyPermissionInfo> grpPermsList = map.get(grpNameKey);
             if(grpPermsList == null) {
                 continue;
             }
             for(PermissionInfo permInfo: grpPermsList) {
                 CharSequence permDesc = permInfo.loadLabel(mPm);
-                grpDesc = formatPermissions(grpDesc, permDesc);
+                grpDesc = formatPermissions(grpDesc, permDesc, newPerms);
             }
             // Insert grpDesc into map
             if(grpDesc != null) {
                 if(localLOGV) Log.i(TAG, "Group:"+grpNameKey+" description:"+grpDesc.toString());
-                retMap.put(grpNameKey, grpDesc.toString());
+                retMap.put(grpNameKey, grpDesc);
             }
         }
     }
@@ -428,42 +550,53 @@
         }
     }
     
-    private void setPermissions(List<PermissionInfo> permList) {
+    private void setPermissions(List<MyPermissionInfo> permList) {
         mGroupLabelCache = new HashMap<String, CharSequence>();
         //add the default label so that uncategorized permissions can go here
         mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel);
         
         // Map containing group names and a list of permissions under that group
+        // that are new from the current install
+        mNewMap = new HashMap<String, CharSequence>();
+        // Map containing group names and a list of permissions under that group
         // categorized as dangerous
-        mDangerousMap = new HashMap<String, String>();
+        mDangerousMap = new HashMap<String, CharSequence>();
         // Map containing group names and a list of permissions under that group
         // categorized as normal
-        mNormalMap = new HashMap<String, String>();
+        mNormalMap = new HashMap<String, CharSequence>();
         
         // Additional structures needed to ensure that permissions are unique under 
         // each group
-        Map<String, List<PermissionInfo>> dangerousMap = 
-            new HashMap<String,  List<PermissionInfo>>();
-        Map<String, List<PermissionInfo> > normalMap = 
-            new HashMap<String,  List<PermissionInfo>>();
+        Map<String, List<MyPermissionInfo>> newMap =
+            new HashMap<String,  List<MyPermissionInfo>>();
+        Map<String, List<MyPermissionInfo>> dangerousMap = 
+            new HashMap<String,  List<MyPermissionInfo>>();
+        Map<String, List<MyPermissionInfo> > normalMap = 
+            new HashMap<String,  List<MyPermissionInfo>>();
         PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);
         
         if (permList != null) {
             // First pass to group permissions
-            for (PermissionInfo pInfo : permList) {
+            for (MyPermissionInfo pInfo : permList) {
                 if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);
-                if(!isDisplayablePermission(pInfo)) {
+                if(!isDisplayablePermission(pInfo, pInfo.mNewReqFlags, pInfo.mExistingReqFlags)) {
                     if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");
                     continue;
                 }
-                Map<String, List<PermissionInfo> > permInfoMap =
-                    (pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) ?
-                            dangerousMap : normalMap;
+                Map<String, List<MyPermissionInfo> > permInfoMap;
+                if (pInfo.mNew) {
+                    permInfoMap = newMap;
+                } else if ((pInfo.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
+                            == PermissionInfo.PROTECTION_DANGEROUS) {
+                    permInfoMap = dangerousMap;
+                } else {
+                    permInfoMap = normalMap;
+                }
                 String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group;
                 if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName);
-                List<PermissionInfo> grpPermsList = permInfoMap.get(grpName);
+                List<MyPermissionInfo> grpPermsList = permInfoMap.get(grpName);
                 if(grpPermsList == null) {
-                    grpPermsList = new ArrayList<PermissionInfo>();
+                    grpPermsList = new ArrayList<MyPermissionInfo>();
                     permInfoMap.put(grpName, grpPermsList);
                     grpPermsList.add(pInfo);
                 } else {
@@ -477,12 +610,13 @@
             }
             // Second pass to actually form the descriptions
             // Look at dangerous permissions first
-            aggregateGroupDescs(dangerousMap, mDangerousMap);
-            aggregateGroupDescs(normalMap, mNormalMap);
+            aggregateGroupDescs(newMap, mNewMap, true);
+            aggregateGroupDescs(dangerousMap, mDangerousMap, false);
+            aggregateGroupDescs(normalMap, mNormalMap, false);
         }
 
         mCurrentState = State.NO_PERMS;
-        if(mDangerousMap.size() > 0) {
+        if (mNewMap.size() > 0 || mDangerousMap.size() > 0) {
             mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY;
         } else if(mNormalMap.size() > 0) {
             mCurrentState = State.NORMAL_ONLY;
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 ea40dc2..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,7 +208,6 @@
 
     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;
@@ -779,7 +777,7 @@
         }
     }
 
-    private void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint) {
+    private static void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint) {
         canvas.drawRect(x1, y1, x2 - 1, y2 - 1, paint);
     }
 
@@ -957,10 +955,6 @@
                 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();
     }
@@ -1040,42 +1034,31 @@
             Alignment hAlign = getAlignment(columnSpec.alignment, true);
             Alignment vAlign = getAlignment(rowSpec.alignment, false);
 
-            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 gravityOffsetX = protect(hAlign.getAlignmentValue(null,
-                    cellWidth - colBounds.size(true)));
-            //noinspection NullableProblems
-            int gravityOffsetY = 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));
 
-            boolean rtl = isLayoutRtl();
-            int startMargin = getMargin(c, true, !rtl);
+            int leftMargin = getMargin(c, true, true);
             int topMargin = getMargin(c, false, true);
-            int endMargin = getMargin(c, true, rtl);
+            int rightMargin = getMargin(c, true, false);
             int bottomMargin = getMargin(c, false, false);
 
-            // Same calculation as getMeasurementIncludingMargin()
-            int mWidth = startMargin + pWidth + endMargin;
-            int mHeight = topMargin + pHeight + bottomMargin;
-
             // Alignment offsets: the location of the view relative to its alignment group.
-            int alignmentOffsetX = colBounds.getOffset(c, hAlign, mWidth);
-            int alignmentOffsetY = rowBounds.getOffset(c, vAlign, mHeight);
+            int alignmentOffsetX = boundsX.getOffset(c, hAlign, leftMargin + pWidth + rightMargin);
+            int alignmentOffsetY = boundsY.getOffset(c, vAlign, topMargin + pHeight + bottomMargin);
 
-            int dx = gravityOffsetX + alignmentOffsetX + startMargin;
-            int dy = gravityOffsetY + alignmentOffsetY + topMargin;
+            int width = hAlign.getSizeInCell(c, pWidth, cellWidth - leftMargin - rightMargin);
+            int height = vAlign.getSizeInCell(c, pHeight, cellHeight - topMargin - bottomMargin);
 
-            cellWidth -= startMargin + endMargin;
-            cellHeight -= topMargin + bottomMargin;
+            int dx = x1 + gravityOffsetX + alignmentOffsetX;
 
-            int width = hAlign.getSizeInCell(c, pWidth, cellWidth, PRF);
-            int height = vAlign.getSizeInCell(c, pHeight, cellHeight, PRF);
+            int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx :
+                    targetWidth - width - paddingRight - rightMargin - dx;
+            int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
 
-            int cx = rtl ? targetWidth - paddingRight - x1 - dx - width : paddingLeft + x1 + dx;
-            int cy = paddingTop + y1 + dy;
             if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
                 c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
             }
@@ -1694,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
@@ -1747,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
@@ -1936,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
          *
@@ -2426,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)},
@@ -2441,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
@@ -2463,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;
         }
 
@@ -2479,6 +2461,11 @@
 
     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;
         }
@@ -2490,6 +2477,11 @@
      */
     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;
         }
@@ -2501,6 +2493,11 @@
      */
     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;
         }
@@ -2530,15 +2527,16 @@
      */
     public static final Alignment END = TRAILING;
 
-    private static Alignment getAbsoluteAlignment(final Alignment a1, final Alignment a2) {
+    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) {
-                if (view == null) {
-                    return UNDEFINED;
-                }
-                Alignment alignment = view.isLayoutRtl() ? a2 : a1;
-                return alignment.getAlignmentValue(view, viewSize);
+                return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize);
             }
         };
     }
@@ -2547,13 +2545,13 @@
      * 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 = getAbsoluteAlignment(START, END);
+    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 = getAbsoluteAlignment(END, START);
+    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.
@@ -2562,6 +2560,11 @@
      */
     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;
         }
@@ -2576,10 +2579,12 @@
      */
     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;
         }
@@ -2627,12 +2632,17 @@
      */
     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;
         }
     };
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 909f383..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
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index ecf19b3..2cacbdca 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -282,6 +282,13 @@
         throw new RuntimeException("setOnItemClickListener cannot be used with a spinner.");
     }
 
+    /**
+     * @hide internal use only
+     */
+    public void setOnItemClickListenerInt(OnItemClickListener l) {
+        super.setOnItemClickListener(l);
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -707,6 +714,9 @@
         
         public void onClick(DialogInterface dialog, int which) {
             setSelection(which);
+            if (mOnItemClickListener != null) {
+                performItemClick(null, which, mListAdapter.getItemId(which));
+            }
             dismiss();
         }
     }
@@ -724,6 +734,9 @@
             setOnItemClickListener(new OnItemClickListener() {
                 public void onItemClick(AdapterView parent, View v, int position, long id) {
                     Spinner.this.setSelection(position);
+                    if (mOnItemClickListener != null) {
+                        Spinner.this.performItemClick(null, position, mAdapter.getItemId(position));
+                    }
                     dismiss();
                 }
             });
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 13798ef..d6dd15e 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,7 +1210,7 @@
             if (imm != null) imm.restartInput(this);
         }
 
-        mTextDisplayListIsValid = false;
+        if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
         prepareCursorControllers();
 
         // start or stop the cursor blinking as appropriate
@@ -1310,7 +1315,7 @@
      * This will frequently be null for non-EditText TextViews.
      */
     public final KeyListener getKeyListener() {
-        return mInput;
+        return mEditor == null ? null : getEditor().mKeyListener;
     }
 
     /**
@@ -1340,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();
@@ -1357,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);
+        }
     }
 
     /**
@@ -1384,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);
@@ -1439,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;
@@ -2311,7 +2326,7 @@
     public void setHighlightColor(int color) {
         if (mHighlightColor != color) {
             mHighlightColor = color;
-            mTextDisplayListIsValid = false;
+            if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
             invalidate();
         }
     }
@@ -2332,7 +2347,7 @@
         mShadowDx = dx;
         mShadowDy = dy;
 
-        mTextDisplayListIsValid = false;
+        if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
         invalidate();
     }
 
@@ -2824,7 +2839,7 @@
             }
         }
         if (inval) {
-            mTextDisplayListIsValid = false;
+            if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
             invalidate();
         }
     }
@@ -2862,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();
@@ -2968,8 +2916,10 @@
                     sp.removeSpan(cw);
                 }
 
-                removeMisspelledSpans(sp);
-                sp.removeSpan(mSuggestionRangeSpan);
+                if (mEditor != null) {
+                    removeMisspelledSpans(sp);
+                    sp.removeSpan(getEditor().mSuggestionRangeSpan);
+                }
 
                 ss.text = sp;
             } else {
@@ -2980,7 +2930,7 @@
                 ss.frozenWithFocus = true;
             }
 
-            ss.error = mError;
+            ss.error = getError();
 
             return ss;
         }
@@ -3030,11 +2980,11 @@
                           "/" + ss.selEnd + " out of range for " + restored +
                           "text " + mText);
                 } else {
-                    Selection.setSelection((Spannable) mText, ss.selStart,
-                                           ss.selEnd);
+                    Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);
 
                     if (ss.frozenWithFocus) {
-                        mFrozenWithFocus = true;
+                        createEditorIfNeeded("restore instance with focus");
+                        getEditor().mFrozenWithFocus = true;
                     }
                 }
             }
@@ -3192,7 +3142,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);
@@ -3257,10 +3208,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) {
@@ -3275,7 +3226,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;
             }
         }
 
@@ -3329,100 +3280,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.
@@ -3474,7 +3331,9 @@
         }
 
         // Invalidate display list if hint will be used
-        if (mText.length() == 0 && mHint != null) mTextDisplayListIsValid = false;
+        if (mEditor != null && mText.length() == 0 && mHint != null) {
+            getEditor().mTextDisplayListIsValid = false;
+        }
     }
 
     /**
@@ -3520,8 +3379,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);
@@ -3605,7 +3464,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) {
@@ -3646,20 +3507,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;
     }
 
     /**
@@ -3671,10 +3534,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;
     }
 
     /**
@@ -3684,8 +3548,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;
     }
 
     /**
@@ -3699,11 +3563,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;
     }
 
     /**
@@ -3713,8 +3578,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;
     }
 
     /**
@@ -3724,8 +3589,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;
     }
 
     /**
@@ -3737,12 +3602,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)
@@ -3764,7 +3630,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,
@@ -3807,21 +3673,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));
         }
     }
 
@@ -3835,8 +3701,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;
     }
 
     /**
@@ -3846,8 +3714,8 @@
      * @see EditorInfo#privateImeOptions
      */
     public String getPrivateImeOptions() {
-        return mInputContentType != null
-                ? mInputContentType.privateImeOptions : null;
+        return mEditor != null && getEditor().mInputContentType != null
+                ? getEditor().mInputContentType.privateImeOptions : null;
     }
 
     /**
@@ -3861,12 +3729,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);
     }
 
     /**
@@ -3880,15 +3749,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;
     }
 
     /**
@@ -3897,7 +3768,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;
     }
 
     /**
@@ -3931,10 +3802,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()) {
@@ -3953,12 +3825,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()) {
@@ -3969,83 +3841,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());
     }
 
     /**
@@ -4060,7 +3878,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);
     }
 
@@ -4090,13 +3908,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) {
@@ -4125,12 +3943,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();
 
@@ -4146,7 +3959,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
      */
@@ -4167,11 +3980,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 {
@@ -4251,14 +4064,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
@@ -4275,16 +4088,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);
                 }
@@ -4338,8 +4151,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);
                     }
@@ -4389,8 +4202,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();
             }
 
@@ -4399,8 +4212,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();
             }
 
@@ -4414,9 +4226,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).
@@ -4438,25 +4250,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
@@ -4468,40 +4270,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
@@ -4648,13 +4419,13 @@
                     if (dr.mDrawableStart != null) dr.mDrawableStart.mutate().setAlpha(alpha);
                     if (dr.mDrawableEnd != null) dr.mDrawableEnd.mutate().setAlpha(alpha);
                 }
-                mTextDisplayListIsValid = false;
+                if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
             }
             return true;
         }
 
         if (mCurrentAlpha != 255) {
-            mTextDisplayListIsValid = false;
+            if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
         }
         mCurrentAlpha = 255;
         return false;
@@ -4678,12 +4449,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
@@ -4694,16 +4465,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);
@@ -4723,7 +4496,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
@@ -4822,7 +4595,6 @@
         }
 
         Layout layout = mLayout;
-        int cursorcolor = color;
 
         if (mHint != null && mText.length() == 0) {
             if (mHintTextColor != null) {
@@ -4870,14 +4642,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);
@@ -4894,150 +4664,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();
@@ -5045,7 +4682,7 @@
 
     private void updateCursorsPositions() {
         if (mCursorDrawableRes == 0) {
-            mCursorCount = 0;
+            getEditor().mCursorCount = 0;
             return; 
         }
 
@@ -5054,40 +4691,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);
     }
@@ -5121,18 +4757,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();
                 }
             }
         }
@@ -5144,6 +4785,8 @@
             paddingTop += getVerticalOffset(false);
         }
         r.offset(paddingLeft, paddingTop);
+        int paddingBottom = getExtendedPaddingBottom();
+        r.bottom += paddingBottom;
     }
 
     /**
@@ -5228,7 +4871,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) {
@@ -5286,14 +4929,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);
@@ -5311,7 +4956,7 @@
      * lines but where it doesn't make sense to insert newlines.
      */
     private boolean shouldAdvanceFocusOnEnter() {
-        if (mInput == null) {
+        if (getKeyListener() == null) {
             return false;
         }
 
@@ -5319,8 +4964,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;
@@ -5335,9 +4980,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;
@@ -5359,13 +5004,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;
                         }
@@ -5402,21 +5047,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) {
@@ -5432,7 +5077,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;
@@ -5478,14 +5123,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);
         }
     }
@@ -5523,11 +5168,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;
                         }
@@ -5578,8 +5223,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)
@@ -5591,22 +5236,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;
             }
@@ -5640,7 +5286,7 @@
                 InputConnection ic = new EditableInputConnection(this);
                 outAttrs.initialSelStart = getSelectionStart();
                 outAttrs.initialSelEnd = getSelectionEnd();
-                outAttrs.initialCapsMode = ic.getCursorCapsMode(mInputType);
+                outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
                 return ic;
             }
         }
@@ -5732,13 +5378,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) {
@@ -5754,8 +5400,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;
@@ -5832,8 +5477,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
@@ -5864,109 +5509,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) {
@@ -5988,8 +5544,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) {
@@ -5999,7 +5556,7 @@
     }
     
     void ensureEndedBatchEdit() {
-        final InputMethodState ims = mInputMethodState;
+        final InputMethodState ims = getEditor().mInputMethodState;
         if (ims != null && ims.mBatchEditNesting != 0) {
             ims.mBatchEditNesting = 0;
             finishBatchEdit(ims);
@@ -6027,7 +5584,7 @@
         }
 
         if (curs >= 0) {
-            mHighlightPathBogus = true;
+            getEditor().mHighlightPathBogus = true;
             makeBlink();
             bringPointIntoView(curs);
         }
@@ -6042,7 +5599,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}.
@@ -6107,8 +5664,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;
         }
     }
@@ -6116,7 +5673,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
@@ -6184,7 +5741,7 @@
         mOldMaximum = mMaximum;
         mOldMaxMode = mMaxMode;
 
-        mHighlightPathBogus = true;
+        if (mEditor != null) getEditor().mHighlightPathBogus = true;
 
         if (wantWidth < 0) {
             wantWidth = 0;
@@ -6194,7 +5751,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;
@@ -6311,7 +5868,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) {
@@ -6772,7 +6329,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;
     }
 
     /**
@@ -6998,11 +6555,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);
@@ -7222,11 +6779,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;
             }
         }
     }
@@ -7305,7 +6862,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);
@@ -7319,8 +6877,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();
@@ -7331,7 +6891,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() {
@@ -7343,7 +6904,7 @@
 
     private void startMarquee() {
         // Do not ellipsize EditText
-        if (mInput != null) return;
+        if (getKeyListener() != null) return;
 
         if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
             return;
@@ -7393,142 +6954,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.
@@ -7557,7 +6982,10 @@
      */
     protected void onSelectionChanged(int selStart, int selEnd) {
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
-        mTextDisplayListIsValid = false;
+        // mEditor may be null if selection is created programatically.
+        createEditorIfNeeded("onSelectionChanged");
+        // Invalidate even when selection range is empty, to remove previous highlight
+        getEditor().mTextDisplayListIsValid = false;
     }
 
     /**
@@ -7636,13 +7064,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);
     }
 
     /**
@@ -7664,7 +7086,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();
         }
@@ -7683,7 +7105,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.
@@ -7694,18 +7116,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();
@@ -7714,14 +7131,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);
@@ -7729,6 +7141,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);
@@ -7744,16 +7161,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;
             }
@@ -7797,8 +7214,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);
         }
     }
 
@@ -7807,289 +7224,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
@@ -8109,7 +7253,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
@@ -8127,95 +7271,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);
@@ -8227,48 +7290,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);
     }
@@ -8276,7 +7302,7 @@
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
-        if (visibility != VISIBLE) {
+        if (mEditor != null && visibility != VISIBLE) {
             hideControllers();
         }
     }
@@ -8311,23 +7337,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);
 
@@ -8336,13 +7346,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) {
@@ -8352,7 +7362,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.
@@ -8365,34 +7376,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();
@@ -8475,6 +7485,8 @@
     }
 
     private void prepareCursorControllers() {
+        if (mEditor == null) return;
+
         boolean windowSupportsHandles = false;
 
         ViewGroup.LayoutParams params = getRootView().getLayoutParams();
@@ -8484,23 +7496,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;
             }
         }
     }
@@ -8520,19 +7532,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;
             }
@@ -8545,49 +7556,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;
@@ -8600,12 +7573,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);
         }
     }
 
@@ -8804,7 +7777,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() {
@@ -8812,7 +7785,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;
         }
 
@@ -8833,7 +7806,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)).
@@ -8874,8 +7847,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 ||
@@ -8945,17 +7919,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) {
@@ -9209,17 +8183,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;
@@ -9230,9 +8193,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();
@@ -9240,7 +8203,7 @@
             vibrate = false;
         }
 
-        if (!handled && mSelectionActionMode != null) {
+        if (!handled && mEditor != null && getEditor().mSelectionActionMode != null) {
             if (touchPositionIsInSelection()) {
                 // Start a drag
                 final int start = getSelectionStart();
@@ -9267,8 +8230,8 @@
             performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         }
 
-        if (handled) {
-            mDiscardNextActionUp = true;
+        if (handled && mEditor != null) {
+            getEditor().mDiscardNextActionUp = true;
         }
 
         return handled;
@@ -9297,10 +8260,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 {
@@ -9308,6 +8271,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)
@@ -9320,6 +9697,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) {
@@ -9398,62 +9776,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;
@@ -9581,7 +9903,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()) {
@@ -9591,7 +9913,7 @@
         }
 
         public SuggestionsPopupWindow() {
-            mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+            mCursorWasVisibleBeforeSuggestions = getEditor().mCursorVisible;
             mSuggestionSpanComparator = new SuggestionSpanComparator();
             mSpansLengths = new HashMap<SuggestionSpan, Integer>();
         }
@@ -9729,7 +10051,7 @@
             if (!(mText instanceof Editable)) return;
 
             if (updateSuggestions()) {
-                mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+                mCursorWasVisibleBeforeSuggestions = getEditor().mCursorVisible;
                 setCursorVisible(false);
                 mIsShowingUp = true;
                 super.show();
@@ -9884,17 +10206,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();
@@ -9927,8 +10249,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() &&
@@ -10028,209 +10350,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
@@ -10246,9 +10365,9 @@
             boolean allowText = getContext().getResources().getBoolean(
                     com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);
 
-            mode.setTitle(allowText ?
-                    mContext.getString(com.android.internal.R.string.textSelectionCABTitle) : null);
+            mode.setTitle(mContext.getString(com.android.internal.R.string.textSelectionCABTitle));
             mode.setSubtitle(null);
+            mode.setTitleOptionalHint(true);
 
             int selectAllIconId = 0; // No icon by default
             if (!allowText) {
@@ -10292,8 +10411,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;
                 }
@@ -10309,16 +10428,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());
@@ -10326,16 +10445,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;
         }
     }
 
@@ -10349,7 +10468,7 @@
         protected void createPopupWindow() {
             mPopupWindow = new PopupWindow(TextView.this.mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
-            mPopupWindow.setClippingEnabled(true);
+            mPopupWindow.setClippingEnabled(true);   
         }
 
         @Override
@@ -10749,7 +10868,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);
             }
@@ -10763,13 +10882,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);
         }
@@ -10990,12 +11110,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;
         }
@@ -11036,12 +11156,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);
             }
         }
@@ -11049,10 +11169,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();
@@ -11097,7 +11217,7 @@
 
                             if (stayedInArea && isPositionOnText(x, y)) {
                                 startSelectionActionMode();
-                                mDiscardNextActionUp = true;
+                                getEditor().mDiscardNextActionUp = true;
                             }
                         }
                     }
@@ -11186,465 +11306,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 f3d891d..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/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index afb5bf1..f3486bd 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -748,6 +748,16 @@
         }
         
         @Override
+        public void setTitleOptionalHint(boolean titleOptional) {
+            mContextView.setTitleOptional(titleOptional);
+        }
+
+        @Override
+        public boolean isTitleOptional() {
+            return mContextView.isTitleOptional();
+        }
+
+        @Override
         public View getCustomView() {
             return mCustomView != null ? mCustomView.get() : null;
         }
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 5d80b79..82b2654 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -43,16 +43,17 @@
     void removeClient(in IInputMethodClient client);
             
     InputBindResult startInput(in IInputMethodClient client,
-            IInputContext inputContext, in EditorInfo attribute,
-            boolean initial, boolean needResult);
+            IInputContext inputContext, in EditorInfo attribute, int controlFlags);
     void finishInput(in IInputMethodClient client);
     boolean showSoftInput(in IInputMethodClient client, int flags,
             in ResultReceiver resultReceiver);
     boolean hideSoftInput(in IInputMethodClient client, int flags,
             in ResultReceiver resultReceiver);
-    void windowGainedFocus(in IInputMethodClient client, in IBinder windowToken,
-            boolean viewHasFocus, boolean isTextEditor,
-            int softInputMode, boolean first, int windowFlags);
+    // Report that a window has gained focus.  If 'attribute' is non-null,
+    // this will also do a startInput.
+    InputBindResult windowGainedFocus(in IInputMethodClient client, in IBinder windowToken,
+            int controlFlags, int softInputMode, int windowFlags,
+            in EditorInfo attribute, IInputContext inputContext);
             
     void showInputMethodPickerFromClient(in IInputMethodClient client);
     void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index edf4443..4b681ec 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -72,6 +72,16 @@
     }
 
     @Override
+    public void setTitleOptionalHint(boolean titleOptional) {
+        mContextView.setTitleOptional(titleOptional);
+    }
+
+    @Override
+    public boolean isTitleOptional() {
+        return mContextView.isTitleOptional();
+    }
+
+    @Override
     public void setCustomView(View view) {
         mContextView.setCustomView(view);
         mCustomView = view != null ? new WeakReference<View>(view) : null;
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index ed02636..16f08f5 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -56,6 +56,7 @@
     private int mTitleStyleRes;
     private int mSubtitleStyleRes;
     private Drawable mSplitBackground;
+    private boolean mTitleOptional;
 
     private Animator mCurrentAnimation;
     private boolean mAnimateInOnLayout;
@@ -354,7 +355,18 @@
         }
 
         if (mTitleLayout != null && mCustomView == null) {
-            availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
+            if (mTitleOptional) {
+                final int titleWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+                mTitleLayout.measure(titleWidthSpec, childSpecHeight);
+                final int titleWidth = mTitleLayout.getMeasuredWidth();
+                final boolean titleFits = titleWidth <= availableWidth;
+                if (titleFits) {
+                    availableWidth -= titleWidth;
+                }
+                mTitleLayout.setVisibility(titleFits ? VISIBLE : GONE);
+            } else {
+                availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
+            }
         }
 
         if (mCustomView != null) {
@@ -405,8 +417,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 +443,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);
                 }
             }
@@ -462,7 +472,7 @@
             }
         }
 
-        if (mTitleLayout != null && mCustomView == null) {
+        if (mTitleLayout != null && mCustomView == null && mTitleLayout.getVisibility() != GONE) {
             x += positionChild(mTitleLayout, x, y, contentHeight);
         }
         
@@ -514,4 +524,15 @@
             super.onInitializeAccessibilityEvent(event);
         }
     }
+
+    public void setTitleOptional(boolean titleOptional) {
+        if (titleOptional != mTitleOptional) {
+            requestLayout();
+        }
+        mTitleOptional = titleOptional;
+    }
+
+    public boolean isTitleOptional() {
+        return mTitleOptional;
+    }
 }
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/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/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 25b0065..1767d68 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -45,7 +45,7 @@
  * across different configurations or circumstances.
  */
 public class ScrollingTabContainerView extends HorizontalScrollView
-        implements AdapterView.OnItemSelectedListener {
+        implements AdapterView.OnItemClickListener {
     private static final String TAG = "ScrollingTabContainerView";
     Runnable mTabSelector;
     private TabClickListener mTabClickListener;
@@ -197,7 +197,7 @@
                 com.android.internal.R.attr.actionDropDownStyle);
         spinner.setLayoutParams(new LinearLayout.LayoutParams(
                 LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
-        spinner.setOnItemSelectedListener(this);
+        spinner.setOnItemClickListenerInt(this);
         return spinner;
     }
 
@@ -347,15 +347,11 @@
     }
 
     @Override
-    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         TabView tabView = (TabView) view;
         tabView.getTab().select();
     }
 
-    @Override
-    public void onNothingSelected(AdapterView<?> parent) {
-    }
-
     private class TabView extends LinearLayout {
         private ActionBar.Tab mTab;
         private TextView mTextView;
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 78e8df3..c6d3cee 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -185,6 +185,7 @@
 	libcore/include
 
 LOCAL_SHARED_LIBRARIES := \
+	libandroidfw \
 	libexpat \
 	libnativehelper \
 	libcutils \
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/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 2241f60..2beedad 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -101,11 +101,6 @@
     SkScalar h_ = SkFloatToScalar(hOffset);
     SkScalar v_ = SkFloatToScalar(vOffset);
 
-    if (!needsLayout(text, count, bidiFlags)) {
-        canvas->drawTextOnPathHV(text, count << 1, *path, h_, v_, *paint);
-        return;
-    }
-
     sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
             text, 0, count, count, bidiFlags);
     if (value == NULL) {
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 536681b..7c88dfc 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -26,8 +26,8 @@
 #include <android_runtime/android_app_NativeActivity.h>
 #include <android_runtime/android_util_AssetManager.h>
 #include <surfaceflinger/Surface.h>
-#include <ui/egl/android_natives.h>
-#include <ui/InputTransport.h>
+#include <system/window.h>
+#include <androidfw/InputTransport.h>
 #include <utils/Looper.h>
 
 #include "JNIHelp.h"
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 abe0104..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
 {
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_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_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index e19bb38..f0560c1 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>
 
@@ -521,6 +521,20 @@
     renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y, paint);
 }
 
+static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count,
+        SkPath* path, jfloat hOffset, jfloat vOffset, int flags, SkPaint* paint) {
+    sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
+            text, 0, count, count, flags);
+    if (value == NULL) {
+        return;
+    }
+    const jchar* glyphs = value->getGlyphs();
+    size_t glyphsCount = value->getGlyphsCount();
+    int bytesCount = glyphsCount * sizeof(jchar);
+    renderer->drawTextOnPath((const char*) glyphs, bytesCount, glyphsCount, path,
+            hOffset, vOffset, paint);
+}
+
 static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
         jint start, jint count, jint contextCount, jfloat x, jfloat y,
         int flags, SkPaint* paint) {
@@ -551,6 +565,24 @@
     env->ReleaseStringChars(text, textArray);
 }
 
+static void android_view_GLES20Canvas_drawTextArrayOnPath(JNIEnv* env, jobject clazz,
+        OpenGLRenderer* renderer, jcharArray text, jint index, jint count,
+        SkPath* path, jfloat hOffset, jfloat vOffset, jint flags, SkPaint* paint) {
+    jchar* textArray = env->GetCharArrayElements(text, NULL);
+    renderTextOnPath(renderer, textArray + index, count, path,
+            hOffset, vOffset, flags, paint);
+    env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+}
+
+static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz,
+        OpenGLRenderer* renderer, jstring text, jint start, jint end,
+        SkPath* path, jfloat hOffset, jfloat vOffset, jint flags, SkPaint* paint) {
+    const jchar* textArray = env->GetStringChars(text, NULL);
+    renderTextOnPath(renderer, textArray + start, end - start, path,
+            hOffset, vOffset, flags, paint);
+    env->ReleaseStringChars(text, textArray);
+}
+
 static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject clazz,
         OpenGLRenderer* renderer, jcharArray text, jint index, jint count,
         jint contextIndex, jint contextCount, jfloat x, jfloat y, jint dirFlags,
@@ -652,9 +684,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));
@@ -885,6 +917,10 @@
     { "nDrawText",          "(ILjava/lang/String;IIFFII)V",
             (void*) android_view_GLES20Canvas_drawText },
 
+    { "nDrawTextOnPath",    "(I[CIIIFFII)V",   (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
+    { "nDrawTextOnPath",    "(ILjava/lang/String;IIIFFII)V",
+            (void*) android_view_GLES20Canvas_drawTextOnPath },
+
     { "nDrawTextRun",       "(I[CIIIIFFII)V",  (void*) android_view_GLES20Canvas_drawTextRunArray },
     { "nDrawTextRun",       "(ILjava/lang/String;IIIIFFII)V",
             (void*) android_view_GLES20Canvas_drawTextRun },
@@ -901,7 +937,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 6d783de..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"
 
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 47b3f66..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"
@@ -264,7 +264,7 @@
     sp<NativeInputEventReceiver> receiver =
             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
     status_t status = receiver->finishInputEvent(seq, handled);
-    if (status) {
+    if (status && status != DEAD_OBJECT) {
         String8 message;
         message.appendFormat("Failed to finish input event.  status=%d", status);
         jniThrowRuntimeException(env, message.string());
@@ -275,7 +275,7 @@
     sp<NativeInputEventReceiver> receiver =
             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
     status_t status = receiver->consumeEvents(true /*consumeBatches*/);
-    if (status) {
+    if (status && status != DEAD_OBJECT) {
         String8 message;
         message.appendFormat("Failed to consume batched input event.  status=%d", status);
         jniThrowRuntimeException(env, message.string());
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_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/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 4fe7600..0f334c3 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -30,6 +30,8 @@
 #include <SkBitmap.h>
 #include <SkPixelRef.h>
 
+#include <ui/ANativeObjectBase.h>
+
 #include <gui/SurfaceTexture.h>
 #include <gui/SurfaceTextureClient.h>
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 68c919e..d4d29ae 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -147,7 +147,7 @@
          @hide -->
     <permission android:name="android.permission.SEND_SMS_NO_CONFIRMATION"
         android:permissionGroup="android.permission-group.COST_MONEY"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_sendSmsNoConfirmation"
         android:description="@string/permdesc_sendSmsNoConfirmation" />
 
@@ -194,7 +194,7 @@
          @hide Pending API council approval -->
     <permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
         android:permissionGroup="android.permission-group.MESSAGES"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_receiveEmergencyBroadcast"
         android:description="@string/permdesc_receiveEmergencyBroadcast" />
 
@@ -383,7 +383,7 @@
 
     <!-- Allows an application to install a location provider into the Location Manager -->
     <permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_installLocationProvider"
         android:description="@string/permdesc_installLocationProvider" />
 
@@ -461,7 +461,7 @@
         @hide -->
     <permission android:name="android.permission.CONNECTIVITY_INTERNAL"
         android:permissionGroup="android.permission-group.NETWORK"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- ================================== -->
     <!-- Permissions for accessing accounts -->
@@ -559,7 +559,7 @@
          @hide -->
     <permission android:name="android.permission.MANAGE_USB"
         android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_manageUsb"
         android:description="@string/permdesc_manageUsb" />
 
@@ -568,7 +568,7 @@
          @hide -->
     <permission android:name="android.permission.ACCESS_MTP"
         android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_accessMtp"
         android:description="@string/permdesc_accessMtp" />
 
@@ -611,7 +611,7 @@
          Does not include placing calls. -->
     <permission android:name="android.permission.MODIFY_PHONE_STATE"
         android:permissionGroup="android.permission-group.PHONE_CALLS"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_modifyPhoneState"
         android:description="@string/permdesc_modifyPhoneState" />
 
@@ -626,7 +626,7 @@
          @hide Used internally. -->
     <permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
         android:permissionGroup="android.permission-group.PHONE_CALLS"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- ================================== -->
     <!-- Permissions for sdcard interaction -->
@@ -651,7 +651,7 @@
         android:permissionGroup="android.permission-group.STORAGE"
         android:label="@string/permlab_mediaStorageWrite"
         android:description="@string/permdesc_mediaStorageWrite"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- ============================================ -->
     <!-- Permissions for low-level system interaction -->
@@ -675,15 +675,9 @@
         android:label="@string/permlab_writeSettings"
         android:description="@string/permdesc_writeSettings" />
 
-    <!-- Allows an application to read or write the secure system settings. -->
-    <permission android:name="android.permission.WRITE_SECURE_SETTINGS"
-        android:protectionLevel="signatureOrSystem"
-        android:label="@string/permlab_writeSecureSettings"
-        android:description="@string/permdesc_writeSecureSettings" />
-
     <!-- Allows an application to modify the Google service map. -->
     <permission android:name="android.permission.WRITE_GSERVICES"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_writeGservices"
         android:description="@string/permdesc_writeGservices" />
 
@@ -757,19 +751,11 @@
         android:label="@string/permlab_forceStopPackages"
         android:description="@string/permdesc_forceStopPackages" />
 
-    <!-- Allows an application to retrieve state dump information from system
-         services. -->
-    <permission android:name="android.permission.DUMP"
-        android:permissionGroup="android.permission-group.PERSONAL_INFO"
-        android:protectionLevel="signatureOrSystem"
-        android:label="@string/permlab_dump"
-        android:description="@string/permdesc_dump" />
-
     <!-- @hide Allows an application to retrieve the content of the active window
          An active window is the window that has fired an accessibility event. -->
     <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_retrieve_window_content"
         android:description="@string/permdesc_retrieve_window_content" />
 
@@ -868,7 +854,7 @@
 
     <!-- Allows applications to set the system time -->
     <permission android:name="android.permission.SET_TIME"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_setTime"
         android:description="@string/permdesc_setTime" />
 
@@ -964,7 +950,7 @@
     <!-- Allows applications to write the apn settings -->
     <permission android:name="android.permission.WRITE_APN_SETTINGS"
                 android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-                android:protectionLevel="signatureOrSystem"
+                android:protectionLevel="signature|system"
                 android:description="@string/permdesc_writeApnSettings"
                 android:label="@string/permlab_writeApnSettings" />
 
@@ -1035,7 +1021,7 @@
     <!-- Allows an application to use any media decoder when decoding for playback
          @hide -->
     <permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_anyCodecForPlayback"
         android:description="@string/permdesc_anyCodecForPlayback" />
 
@@ -1052,6 +1038,21 @@
         android:label="@string/permgrouplab_developmentTools"
         android:description="@string/permgroupdesc_developmentTools" />
 
+    <!-- Allows an application to read or write the secure system settings. -->
+    <permission android:name="android.permission.WRITE_SECURE_SETTINGS"
+        android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+        android:protectionLevel="signature|system|development"
+        android:label="@string/permlab_writeSecureSettings"
+        android:description="@string/permdesc_writeSecureSettings" />
+
+    <!-- Allows an application to retrieve state dump information from system
+         services. -->
+    <permission android:name="android.permission.DUMP"
+        android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+        android:protectionLevel="signature|system|development"
+        android:label="@string/permlab_dump"
+        android:description="@string/permdesc_dump" />
+
     <!-- Configure an application for debugging. -->
     <permission android:name="android.permission.SET_DEBUG_APP"
         android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
@@ -1099,7 +1100,7 @@
     <permission android:name="android.permission.STATUS_BAR"
         android:label="@string/permlab_statusBar"
         android:description="@string/permdesc_statusBar"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to be the status bar.  Currently used only by SystemUI.apk
     @hide -->
@@ -1120,7 +1121,7 @@
     <permission android:name="android.permission.UPDATE_DEVICE_STATS"
         android:label="@string/permlab_batteryStats"
         android:description="@string/permdesc_batteryStats"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to open windows that are for use by parts
          of the system user interface.  Not for use by third party apps. -->
@@ -1160,7 +1161,7 @@
     <permission android:name="android.permission.SHUTDOWN"
         android:label="@string/permlab_shutdown"
         android:description="@string/permdesc_shutdown"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to tell the activity manager to temporarily
          stop application switches, putting it into a special mode that
@@ -1170,7 +1171,7 @@
     <permission android:name="android.permission.STOP_APP_SWITCHES"
         android:label="@string/permlab_stopAppSwitches"
         android:description="@string/permdesc_stopAppSwitches"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to retrieve the current state of keys and
          switches.  This is only for use by the system.-->
@@ -1205,7 +1206,7 @@
     <permission android:name="android.permission.BIND_WALLPAPER"
         android:label="@string/permlab_bindWallpaper"
         android:description="@string/permdesc_bindWallpaper"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Must be required by device administration receiver, to ensure that only the
          system can interact with it. -->
@@ -1232,7 +1233,7 @@
     <permission android:name="android.permission.INSTALL_PACKAGES"
         android:label="@string/permlab_installPackages"
         android:description="@string/permdesc_installPackages"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to clear user data -->
     <permission android:name="android.permission.CLEAR_APP_USER_DATA"
@@ -1244,27 +1245,33 @@
     <permission android:name="android.permission.DELETE_CACHE_FILES"
         android:label="@string/permlab_deleteCacheFiles"
         android:description="@string/permdesc_deleteCacheFiles"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to delete packages. -->
     <permission android:name="android.permission.DELETE_PACKAGES"
         android:label="@string/permlab_deletePackages"
         android:description="@string/permdesc_deletePackages"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to move location of installed package.
          @hide -->
     <permission android:name="android.permission.MOVE_PACKAGE"
         android:label="@string/permlab_movePackage"
         android:description="@string/permdesc_movePackage"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to change whether an application component (other than its own) is
          enabled or not. -->
     <permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
         android:label="@string/permlab_changeComponentState"
         android:description="@string/permdesc_changeComponentState"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
+
+    <!-- @hide Allows an application to grant or revoke specific permissions. -->
+    <permission android:name="android.permission.GRANT_REVOKE_PERMISSIONS"
+        android:label="@string/permlab_grantRevokePermissions"
+        android:description="@string/permdesc_grantRevokePermissions"
+        android:protectionLevel="signature" />
 
     <!-- Allows an application to use SurfaceFlinger's low level features -->
     <permission android:name="android.permission.ACCESS_SURFACE_FLINGER"
@@ -1277,7 +1284,7 @@
     <permission android:name="android.permission.READ_FRAME_BUFFER"
         android:label="@string/permlab_readFrameBuffer"
         android:description="@string/permdesc_readFrameBuffer"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Required to be able to disable the device (very dangerous!). -->
     <permission android:name="android.permission.BRICK"
@@ -1289,7 +1296,7 @@
     <permission android:name="android.permission.REBOOT"
         android:label="@string/permlab_reboot"
         android:description="@string/permdesc_reboot"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
    <!-- Allows low-level access to power management -->
     <permission android:name="android.permission.DEVICE_POWER"
@@ -1329,7 +1336,7 @@
     <permission android:name="android.permission.MASTER_CLEAR"
         android:label="@string/permlab_masterClear"
         android:description="@string/permdesc_masterClear"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to call any phone number, including emergency
          numbers, without going through the Dialer user interface for the user
@@ -1337,34 +1344,34 @@
     <permission android:name="android.permission.CALL_PRIVILEGED"
         android:label="@string/permlab_callPrivileged"
         android:description="@string/permdesc_callPrivileged"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to perform CDMA OTA provisioning @hide -->
     <permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
         android:label="@string/permlab_performCdmaProvisioning"
         android:description="@string/permdesc_performCdmaProvisioning"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows enabling/disabling location update notifications from
          the radio. Not for use by normal applications. -->
     <permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
         android:label="@string/permlab_locationUpdates"
         android:description="@string/permdesc_locationUpdates"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows read/write access to the "properties" table in the checkin
          database, to change values that get uploaded. -->
     <permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
         android:label="@string/permlab_checkinProperties"
         android:description="@string/permdesc_checkinProperties"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to collect component usage
          statistics @hide -->
     <permission android:name="android.permission.PACKAGE_USAGE_STATS"
         android:label="@string/permlab_pkgUsageStats"
         android:description="@string/permdesc_pkgUsageStats"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to collect battery statistics -->
     <permission android:name="android.permission.BATTERY_STATS"
@@ -1377,7 +1384,7 @@
     <permission android:name="android.permission.BACKUP"
         android:label="@string/permlab_backup"
         android:description="@string/permdesc_backup"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows a package to launch the secure full-backup confirmation UI.
          ONLY the system process may hold this permission.
@@ -1392,7 +1399,7 @@
     <permission android:name="android.permission.BIND_REMOTEVIEWS"
         android:label="@string/permlab_bindRemoteViews"
         android:description="@string/permdesc_bindRemoteViews"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to tell the AppWidget service which application
          can access AppWidget's data.  The normal user flow is that a user
@@ -1404,7 +1411,7 @@
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
         android:label="@string/permlab_bindGadget"
         android:description="@string/permdesc_bindGadget"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows applications to change the background data setting
          @hide pending API council -->
@@ -1424,7 +1431,7 @@
          besides global search. -->
     <permission android:name="android.permission.GLOBAL_SEARCH"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Internal permission protecting access to the global search
          system: ensures that only the system can access the provider
@@ -1442,14 +1449,14 @@
          own apk as Ghod Intended. -->
     <permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allow an application to read and write the cache partition.
          @hide -->
     <permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
         android:label="@string/permlab_cache_filesystem"
         android:description="@string/permdesc_cache_filesystem"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Must be required by default container service so that only
          the system can bind to it and use it to copy
@@ -1465,14 +1472,14 @@
         @hide
     -->
     <permission android:name="android.permission.CRYPT_KEEPER"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to read historical network usage for
          specific networks and applications. @hide -->
     <permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
         android:label="@string/permlab_readNetworkUsageHistory"
         android:description="@string/permdesc_readNetworkUsageHistory"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to manage network policies (such as warning and disable
          limits) and to define application-specific rules. @hide -->
@@ -1487,7 +1494,7 @@
     <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
         android:label="@string/permlab_modifyNetworkAccounting"
         android:description="@string/permdesc_modifyNetworkAccounting"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- C2DM permission.
          @hide Used internally.
@@ -1503,7 +1510,7 @@
     <permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
         android:label="@string/permlab_packageVerificationAgent"
         android:description="@string/permdesc_packageVerificationAgent"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Must be required by package verifier receiver, to ensure that only the
          system can interact with it.
@@ -1521,6 +1528,17 @@
         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/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 f58a19c..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 744b1fa..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/layout/app_perms_summary.xml b/core/res/res/layout/app_perms_summary.xml
index 3f99dde..77dbc2e 100755
--- a/core/res/res/layout/app_perms_summary.xml
+++ b/core/res/res/layout/app_perms_summary.xml
@@ -32,6 +32,15 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 
+    <!-- List view containing list of new permissions categorized by groups. -->
+    <LinearLayout
+        android:id="@+id/new_perms_list"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:paddingLeft="16dip"
+        android:paddingRight="12dip"
+        android:layout_height="wrap_content" />
+
     <!-- List view containing list of dangerous permissions categorized by groups. -->
     <LinearLayout
         android:id="@+id/dangerous_perms_list"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 19398cf..abf6676 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -766,6 +766,8 @@
     <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>
@@ -978,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>
@@ -986,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 8f7b70f..bb175e7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 c9de7de..a98a7d4 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 adb1338..233d5a9 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 0a22133..66516b9 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 460fcb7..b848c2c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 545d957..544395f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 65d7cdc..ceefe1d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -757,13 +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>
@@ -976,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>
@@ -984,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 dbe8137..3b3d08d 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 154ca55..e2bc244 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -215,10 +215,8 @@
     <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Επιτρέπει στην εφαρμογή τη μετακίνηση εργασιών στο προσκήνιο και στο φόντο. Τυχόν κακόβουλες εφαρμογές μπορούν να προωθηθούν στο προσκήνιο χωρίς να μπορείτε να τις ελέγξετε."</string>
     <string name="permlab_removeTasks" msgid="6821513401870377403">"διακοπή εκτέλεσης εφαρμογών"</string>
     <string name="permdesc_removeTasks" msgid="1394714352062635493">"Επιτρέπει στην εφαρμογή την κατάργηση ενεργειών και την απομάκρυνση των εφαρμογών τους. Τυχόν κακόβουλες εφαρμογές ενδέχεται να διαταράξουν τη λειτουργία άλλων εφαρμογών."</string>
-    <!-- no translation found for permlab_setScreenCompatibility (6975387118861842061) -->
-    <skip />
-    <!-- no translation found for permdesc_setScreenCompatibility (692043618693917374) -->
-    <skip />
+    <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>
@@ -766,6 +764,8 @@
     <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>
@@ -978,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>
@@ -986,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 0429c8b..bed4de2 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 ad3e949..58df147 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -215,8 +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">"Configurar el modo de la compatibilidad de otras pantalla"</string>
-    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Permite a la aplicación controlar el modo de la compatibilidad de las pantallas de otras aplicaciones. Las aplicaciones malintencionadas pueden interrumpir el funcionamiento 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>
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 1bd13bd..9499214 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 2f42a70..da06538 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 fd7a771..68988f2 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 9cf18ec..2fe9b4e 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 33b0f45..d6e8f30 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 91ac5a3..f5a3cb7 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 a60dcfa..01378666 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -215,8 +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 zaslona kompatibilnost"</string>
-    <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Aplikaciji omogućuje upravljanje načinom kompatibilnosti zaslona drugih aplikacija. Zlonamjerne aplikacije mogu prekinuti takvo ponašanje ostalih 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>
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 6b789ea..54e8b06 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 6ed5c0e..ac5d463 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 8af1f5e..77733b3 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 5d5b9da..a232c69 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 cadf935..66e847a 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -216,7 +216,7 @@
     <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="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>
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 031e9f5..754f791 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 22830ac..43aa1d3 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 f716cb9..5f84a48 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 6aa9e4c..3f240ab 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 b1b3cbe..63df5f4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 9409445..8e5e8bc 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 8564dc7..c17da8f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -216,7 +216,7 @@
     <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 aplikacji."</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>
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 a162aa0..74870ff 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 5c16708..efe6833 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 57daee7..5b8efa6 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1155,6 +1155,10 @@
     <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>
@@ -1424,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) -->
@@ -1440,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 fe3687f..8ffa16e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 55468a0..a6644e5 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 7d1aa78..df25ac7 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -764,6 +764,10 @@
     <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>
@@ -976,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>
@@ -984,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 dfedb9a..159d504 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 2a2104f..199d318 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 0067a12..be397b0 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 369853c..4cb3a3a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -337,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>
@@ -639,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>
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 debe568..41c56f4 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 e74e309..9548fe9 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 8d52c8c..6da7fcd 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 9d049cf..3fd6264 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 8166606..8503dc5 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 a9f431c..bc74518 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -216,7 +216,7 @@
     <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="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>
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 5aee830..5c89004 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 36da63d..282fb91 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -215,7 +215,7 @@
     <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">"misa ukuhambelana kwesikrini"</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>
@@ -764,6 +764,8 @@
     <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>
@@ -976,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>
@@ -984,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 92c59ab..4aa7dde 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -157,7 +157,7 @@
              of permission to a requesting application at installation, without
              asking for the user's explicit approval (though the user always
              has the option to review these permissions before installing). -->
-        <enum name="normal" value="0" />
+        <flag name="normal" value="0" />
         <!-- A higher-risk permission that would give a requesting application
              access to private user data or control over the device that can
              negatively impact the user.  Because this type of permission
@@ -167,13 +167,13 @@
              user and require confirmation before proceeding, or some other
              approach may be taken to avoid the user automatically allowing
              the use of such facilities.  -->
-        <enum name="dangerous" value="1" />
+        <flag name="dangerous" value="1" />
         <!-- A permission that the system is to grant only if the requesting
              application is signed with the same certificate as the application
              that declared the permission. If the certificates match, the system
              automatically grants the permission without notifying the user or
              asking for the user's explicit approval. -->
-        <enum name="signature" value="2" />
+        <flag name="signature" value="2" />
         <!-- A permission that the system is to grant only to packages in the
              Android system image <em>or</em> that are signed with the same
              certificates. Please avoid using this option, as the
@@ -183,7 +183,20 @@
              vendors have applications built in to a system image which need
              to share specific features explicitly because they are being built
              together. -->
-        <enum name="signatureOrSystem" value="3" />
+        <flag name="signatureOrSystem" value="3" />
+        <!-- Additional flag from base permission type: this permission can also
+             be granted to any applications installed on the system image.
+             Please avoid using this option, as the
+             signature protection level should be sufficient for most needs and
+             works regardless of exactly where applications are installed.  This
+             permission flag is used for certain special situations where multiple
+             vendors have applications built in to a system image which need
+             to share specific features explicitly because they are being built
+             together. -->
+        <flag name="system" value="0x10" />
+        <!-- Additional flag from base permission type: this permission can also
+             (optionally) be granted to development applications. -->
+        <flag name="development" value="0x20" />
     </attr>
     
     <!-- Specified the name of a group that this permission is associated
@@ -924,6 +937,13 @@
         tag; often this is one of the {@link android.Manifest.permission standard
         system permissions}. -->
         <attr name="name" />
+        <!--  Specify whether this permission is required for the application.
+              The default is true, meaning the application requires the
+              permission, and it must always be granted when it is installed.
+              If you set this to false, then in some cases the application may
+              be installed with it being granted the permission, and it will
+              need to request the permission later if it needs it. -->
+        <attr name="required" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>uses-configuration</code> tag specifies
@@ -966,7 +986,7 @@
               don't support it.  If you set this to false, then this will
               not impose a restriction on where the application can be
               installed. -->
-        <attr name="required" format="boolean" />
+        <attr name="required" />
     </declare-styleable>
 
     <!-- The <code>uses-sdk</code> tag describes the SDK features that the
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 c281b8b..94a671d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -115,6 +115,7 @@
   <java-symbol type="id" name="new_app_action" />
   <java-symbol type="id" name="new_app_description" />
   <java-symbol type="id" name="new_app_icon" />
+  <java-symbol type="id" name="new_perms_list" />
   <java-symbol type="id" name="no_permissions" />
   <java-symbol type="id" name="non_dangerous_perms_list" />
   <java-symbol type="id" name="numberpicker_input" />
@@ -212,13 +213,16 @@
   <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" />
   <java-symbol type="bool" name="config_allowActionMenuItemTextWithIcon" />
   <java-symbol type="bool" name="config_bluetooth_adapter_quick_switch" />
   <java-symbol type="bool" name="config_bluetooth_sco_off_call" />
-  <java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
   <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
   <java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
   <java-symbol type="bool" name="config_enable_puk_unlock_screen" />
@@ -626,6 +630,7 @@
   <java-symbol type="string" name="orgTypeWork" />
   <java-symbol type="string" name="passwordIncorrect" />
   <java-symbol type="string" name="permissions_format" />
+  <java-symbol type="string" name="perms_new_perm_prefix" />
   <java-symbol type="string" name="perms_hide" />
   <java-symbol type="string" name="perms_show_all" />
   <java-symbol type="string" name="petabyteShort" />
@@ -959,6 +964,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" />
@@ -1085,7 +1091,6 @@
   <java-symbol type="style" name="ActiveWallpaperSettings" />
   <java-symbol type="style" name="Animation.InputMethodFancy" />
   <java-symbol type="style" name="Animation.Wallpaper" />
-  <java-symbol type="style" name="Animation.RecentApplications" />
   <java-symbol type="style" name="Animation.ZoomButtons" />
   <java-symbol type="style" name="PreviewWallpaperSettings" />
   <java-symbol type="style" name="TextAppearance.SlidingTabActive" />
@@ -1441,6 +1446,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" />
@@ -1478,6 +1484,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" />
@@ -1537,6 +1544,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" />
 
@@ -3499,4 +3521,10 @@
 
   <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 c95dddd..f548165 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -861,6 +861,14 @@
         possible to get app components into an unusable, inconsistent, or unstable state.
     </string>
 
+    <!-- Title of an application permission for granting or revoking other permissions [CHAR LIMIT=NONE] -->
+    <string name="permlab_grantRevokePermissions">grant or revoke permissions</string>
+    <!-- Description of an application permission for granting or revoking other permissions [CHAR LIMIT=NONE] -->
+    <string name="permdesc_grantRevokePermissions">Allows an application to grant or revoke
+        specific permissions for it or other applications.  Malicious applications may use this
+        to access features you have not granted them.
+    </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_setPreferredApplications">set preferred apps</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2248,6 +2256,14 @@
     <!-- 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. -->
@@ -2708,6 +2724,12 @@
     <!-- Do not translate. Default access point SSID used for tethering -->
     <string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
 
+    <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>
     <string name="wifi_p2p_invitation_sent_title">Invitation sent</string>
@@ -2761,6 +2783,8 @@
     <string name="default_permission_group">Default</string>
     <!-- Do not translate. -->
     <string name="permissions_format"><xliff:g id="perm_line1">%1$s</xliff:g>, <xliff:g id="perm_line2">%2$s</xliff:g></string>
+    <!-- Text that is placed at the front of a permission name that is being added to an app [CHAR LIMIT=NONE] -->
+    <string name="perms_new_perm_prefix"><font size="12" fgcolor="#ffffa3a3">NEW: </font></string>
     <!-- Shown for an application when it doesn't require any permission grants. -->
     <string name="no_permissions">No permissions required</string>
     <!-- When installing an application, the less-dangerous permissions are hidden.  If the user showed those, this is the text to hide them again.  -->
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/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index 7499f68..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;
@@ -453,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() +
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/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/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/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/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/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/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/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/DrmErrorEvent.java b/drm/java/android/drm/DrmErrorEvent.java
index 2cb82e6..c61819d 100755
--- a/drm/java/android/drm/DrmErrorEvent.java
+++ b/drm/java/android/drm/DrmErrorEvent.java
@@ -24,6 +24,10 @@
  *
  */
 public class DrmErrorEvent extends DrmEvent {
+
+    // Please add newly defined type constants to the end of the list,
+    // and modify checkTypeValidity() accordingly.
+
     /**
      * Something went wrong installing the rights.
      */
@@ -60,28 +64,46 @@
      */
     public static final int TYPE_ACQUIRE_DRM_INFO_FAILED = 2008;
 
+    // Add more type constants here...
+
+    // FIXME:
+    // We may want to add a user-defined type constant, such as
+    // TYPE_VENDOR_SPECIFIC_FAILED, to take care vendor specific use
+    // cases.
+
+
     /**
      * Creates a <code>DrmErrorEvent</code> object with the specified parameters.
      *
      * @param uniqueId Unique session identifier.
-     * @param type Type of the event. Could be any of the event types defined above.
-     * @param message Message description.
+     * @param type Type of the event. Must be any of the event types defined above.
+     * @param message Message description. It can be null.
      */
     public DrmErrorEvent(int uniqueId, int type, String message) {
         super(uniqueId, type, message);
+        checkTypeValidity(type);
     }
 
     /**
      * Creates a <code>DrmErrorEvent</code> object with the specified parameters.
      *
      * @param uniqueId Unique session identifier.
-     * @param type Type of the event. Could be any of the event types defined above.
+     * @param type Type of the event. Must be any of the event types defined above.
      * @param message Message description.
      * @param attributes Attributes for extensible information. Could be any
-     * information provided by the plug-in.
+     * information provided by the plug-in. It can be null.
      */
     public DrmErrorEvent(int uniqueId, int type, String message,
                             HashMap<String, Object> attributes) {
         super(uniqueId, type, message, attributes);
+        checkTypeValidity(type);
+    }
+
+    private void checkTypeValidity(int type) {
+        if (type < TYPE_RIGHTS_NOT_INSTALLED ||
+            type > TYPE_ACQUIRE_DRM_INFO_FAILED) {
+            final String msg = "Unsupported type: " + type;
+            throw new IllegalArgumentException(msg);
+        }
     }
 }
diff --git a/drm/java/android/drm/DrmEvent.java b/drm/java/android/drm/DrmEvent.java
index 4053eb3..1a19f5c 100755
--- a/drm/java/android/drm/DrmEvent.java
+++ b/drm/java/android/drm/DrmEvent.java
@@ -23,6 +23,10 @@
  *
  */
 public class DrmEvent {
+
+    // Please do not add type constants in this class. More event type constants
+    // should go to DrmInfoEvent or DrmErrorEvent classes.
+
     /**
      * All of the rights information associated with all DRM schemes have been successfully removed.
      */
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/DrmInfoEvent.java b/drm/java/android/drm/DrmInfoEvent.java
index 67aa0a9..2826dce 100755
--- a/drm/java/android/drm/DrmInfoEvent.java
+++ b/drm/java/android/drm/DrmInfoEvent.java
@@ -24,6 +24,10 @@
  *
  */
 public class DrmInfoEvent extends DrmEvent {
+
+    // Please add newly defined type constants to the end of the list,
+    // and modify checkTypeValidity() accordingly.
+
     /**
      * The registration has already been done by another account ID.
      */
@@ -50,29 +54,57 @@
      */
     public static final int TYPE_RIGHTS_REMOVED = 6;
 
+    // Add more type constants here...
+
+    // FIXME:
+    // We may want to add a user-defined type constant, such as
+    // TYPE_VENDOR_SPECIFIC, to take care vendor specific use
+    // cases.
+
     /**
      * Creates a <code>DrmInfoEvent</code> object with the specified parameters.
      *
      * @param uniqueId Unique session identifier.
-     * @param type Type of the event. Could be any of the event types defined above.
-     * @param message Message description.
+     * @param type Type of the event. Must be any of the event types defined above,
+     * or the constants defined in {@link DrmEvent}.
+     * @param message Message description. It can be null.
      */
     public DrmInfoEvent(int uniqueId, int type, String message) {
         super(uniqueId, type, message);
+        checkTypeValidity(type);
     }
 
     /**
      * Creates a <code>DrmInfoEvent</code> object with the specified parameters.
      *
      * @param uniqueId Unique session identifier.
-     * @param type Type of the event. Could be any of the event types defined above.
-     * @param message Message description.
+     * @param type Type of the event. Must be any of the event types defined above,
+     * or the constants defined in {@link DrmEvent}
+     * @param message Message description. It can be null.
      * @param attributes Attributes for extensible information. Could be any
      * information provided by the plug-in.
      */
     public DrmInfoEvent(int uniqueId, int type, String message,
                             HashMap<String, Object> attributes) {
         super(uniqueId, type, message, attributes);
+        checkTypeValidity(type);
+    }
+
+    /*
+     * Check the validity of the given type.
+     * To overcome a design flaw, we need also accept the type constants
+     * defined in super class, DrmEvent.
+     */
+    private void checkTypeValidity(int type) {
+        if (type < TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT ||
+            type > TYPE_RIGHTS_REMOVED) {
+
+            if (type != TYPE_ALL_RIGHTS_REMOVED &&
+                type != TYPE_DRM_INFO_PROCESSED) {
+                final String msg = "Unsupported type: " + type;
+                throw new IllegalArgumentException(msg);
+            }
+        }
     }
 }
 
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/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 9a7194c..14d5fae 100755
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -317,6 +317,7 @@
      *
      * @return A {@link android.content.ContentValues} instance that contains
      * key-value pairs representing the constraints. Null in case of failure.
+     * The keys are defined in {@link DrmStore.ConstraintsColumns}.
      */
     public ContentValues getConstraints(String path, int action) {
         if (null == path || path.equals("") || !DrmStore.Action.isValid(action)) {
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/drm/java/android/drm/DrmStore.java b/drm/java/android/drm/DrmStore.java
index ae311de..2f004cf 100755
--- a/drm/java/android/drm/DrmStore.java
+++ b/drm/java/android/drm/DrmStore.java
@@ -23,45 +23,65 @@
 public class DrmStore {
     /**
      * Interface definition for the columns that represent DRM constraints.
+     * {@link android.drm.DrmManagerClient#getConstraints DrmManagerClient.getConstraints()}
+     * can be called by an application to find out the contraints on the
+     * {@link android.drm.DrmStore.Action actions} that can be performed
+     * on right-protected content. The constants defined in this interface
+     * represent three most common types of constraints: count-based,
+     * date-based, and duration-based. Two or more constraints can be used
+     * at the same time to represent more sophisticated constraints.
+     * In addition, user-defined constraint,
+     * {@link #EXTENDED_METADATA extended metadata}, can be
+     * used if these three types of constraints are not sufficient.
      */
     public interface ConstraintsColumns {
         /**
-         * The maximum repeat count.
+         * This is a count-based constraint. It represents the maximum
+         * repeat count that can be performed on an
+         * {@link android.drm.DrmStore.Action action}.
          * <p>
          * Type: INTEGER
          */
         public static final String MAX_REPEAT_COUNT = "max_repeat_count";
 
         /**
-         * The remaining repeat count.
+         * This is a count-based constraint. It represents the remaining
+         * repeat count that can be performed on an
+         * {@link android.drm.DrmStore.Action action}.
          * <p>
          * Type: INTEGER
          */
         public static final String REMAINING_REPEAT_COUNT = "remaining_repeat_count";
 
         /**
-         * The time before which the rights-protected file cannot be played/viewed.
+         * This is a date-based constraint. It represents the time before which
+         * an {@link android.drm.DrmStore.Action action} can be performed on
+         * the rights-protected content.
          * <p>
          * Type: TEXT
          */
         public static final String LICENSE_START_TIME = "license_start_time";
 
         /**
-         * The time after which the rights-protected file cannot be played/viewed.
+         * This is a date-based constaint. It represents the time after which
+         * an {@link android.drm.DrmStore.Action action} can not be performed on
+         * the rights-protected content.
          * <p>
          * Type: TEXT
          */
         public static final String LICENSE_EXPIRY_TIME = "license_expiry_time";
 
         /**
-         * The available time left before the license expires.
+         * This is a duration-based constaint. It represents the available time left
+         * before the license expires.
          * <p>
          * Type: TEXT
          */
         public static final String LICENSE_AVAILABLE_TIME = "license_available_time";
 
         /**
-         * The data stream for extended metadata.
+         * This is a user-defined constraint. It represents the additional constraint
+         * using extended metadata.
          * <p>
          * Type: TEXT
          */
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 182f14d..f7c202f 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -39,11 +39,15 @@
     public static final int RGB_888     = 3;
     public static final int RGB_565     = 4;
 
+    @Deprecated
     public static final int RGBA_5551   = 6;
+    @Deprecated
     public static final int RGBA_4444   = 7;
     public static final int A_8         = 8;
     public static final int L_8         = 9;
+    @Deprecated
     public static final int LA_88       = 0xA;
+    @Deprecated
     public static final int RGB_332     = 0xB;
 
 
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 44401c8..6539ff3 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -102,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.
      *
      */
@@ -124,34 +124,34 @@
     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 The allocation will be
-     * used with a SurfaceTexture object.  This usage will cause the
-     * allocation to be created read only.
+     * 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 with a SurfaceTexture object.  This usage will cause the
-     * allocation to be created read only.
+     * 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 with a SurfaceTexture object.  This usage will cause the
-     * allocation to be created write only.
+     * 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
      */
@@ -323,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();
@@ -503,6 +534,7 @@
      * @param fp
      */
     public void setFromFieldPacker(int xoff, FieldPacker fp) {
+        mRS.validate();
         int eSize = mType.mElement.getSizeBytes();
         final byte[] data = fp.getData();
 
@@ -523,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.");
         }
@@ -887,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);
     }
-    */
 
 
 
@@ -1090,6 +1143,18 @@
 
     }
 
+    /**
+     * @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
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index f6a0281..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.
@@ -832,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.
      *
@@ -845,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.");
+        }
+        }
     }
 
     /**
@@ -933,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/Program.java b/graphics/java/android/renderscript/Program.java
index 3f769ee..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;
 
@@ -111,6 +112,16 @@
     }
 
     /**
+     * @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
      *
@@ -180,6 +191,7 @@
         Type mConstants[];
         Type mTextures[];
         TextureType mTextureTypes[];
+        String mTextureNames[];
         int mInputCount;
         int mOutputCount;
         int mConstantCount;
@@ -197,6 +209,7 @@
             mConstantCount = 0;
             mTextureCount = 0;
             mTextureTypes = new TextureType[MAX_TEXTURE];
+            mTextureNames = new String[MAX_TEXTURE];
         }
 
         /**
@@ -300,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;
         }
 
@@ -317,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 b3c1bd9..a6cd15b 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -116,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++) {
@@ -133,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 d3c801f2..6921f37 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -17,6 +17,7 @@
 package android.renderscript;
 
 import java.lang.reflect.Field;
+import java.io.File;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -82,6 +83,25 @@
     native void nContextInitToClient(int con);
     native void nContextDeinitToClient(int con);
 
+    /**
+     * Name of the file that holds the object cache.
+     */
+    private static final String CACHE_PATH = "com.android.renderscript.cache";
+    static String mCachePath;
+
+     /**
+     * Sets the directory to use as a persistent storage for the
+     * renderscript object file cache.
+     *
+     * @hide
+     * @param cacheDir A directory the current process can write to
+     */
+    public static void setupDiskCache(File cacheDir) {
+        File f = new File(cacheDir, CACHE_PATH);
+        mCachePath = f.getAbsolutePath();
+        f.mkdirs();
+    }
+
 
     // Methods below are wrapped to protect the non-threadsafe
     // lockless fifo.
@@ -274,6 +294,22 @@
         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) {
@@ -553,15 +589,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);
@@ -868,7 +904,9 @@
     }
 
     RenderScript(Context ctx) {
-        mApplicationContext = ctx.getApplicationContext();
+        if (ctx != null) {
+            mApplicationContext = ctx.getApplicationContext();
+        }
     }
 
     /**
@@ -880,21 +918,16 @@
         return mApplicationContext;
     }
 
-    static int getTargetSdkVersion(Context ctx) {
-        return ctx.getApplicationInfo().targetSdkVersion;
-    }
-
     /**
      * Create a basic RenderScript context.
      *
+     * @hide
      * @param ctx The context.
      * @return RenderScript
      */
-    public static RenderScript create(Context ctx) {
+    public static RenderScript create(Context ctx, int sdkVersion) {
         RenderScript rs = new RenderScript(ctx);
 
-        int sdkVersion = getTargetSdkVersion(ctx);
-
         rs.mDev = rs.nDeviceCreate();
         rs.mContext = rs.nContextCreate(rs.mDev, 0, sdkVersion);
         if (rs.mContext == 0) {
@@ -906,6 +939,17 @@
     }
 
     /**
+     * Create a basic RenderScript context.
+     *
+     * @param ctx The context.
+     * @return RenderScript
+     */
+    public static RenderScript create(Context ctx) {
+        int v = ctx.getApplicationInfo().targetSdkVersion;
+        return create(ctx, v);
+    }
+
+    /**
      * Print the currently available debugging information about the state of
      * the RS context to the log.
      *
diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java
index 2cfeb17..1b2ac90 100644
--- a/graphics/java/android/renderscript/RenderScriptGL.java
+++ b/graphics/java/android/renderscript/RenderScriptGL.java
@@ -166,7 +166,7 @@
         super(ctx);
         mSurfaceConfig = new SurfaceConfig(sc);
 
-        int sdkVersion = getTargetSdkVersion(ctx);
+        int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
 
         mWidth = 0;
         mHeight = 0;
diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java
index 90f959f..108b230 100644
--- a/graphics/java/android/renderscript/ScriptC.java
+++ b/graphics/java/android/renderscript/ScriptC.java
@@ -92,13 +92,9 @@
             throw new Resources.NotFoundException();
         }
 
-        // E.g, /system/apps/Fountain.apk
-        //String packageName = rs.getApplicationContext().getPackageResourcePath();
-        // For res/raw/fountain.bc, it wil be /com.android.fountain:raw/fountain
         String resName = resources.getResourceEntryName(resourceID);
-        String cacheDir = rs.getApplicationContext().getCacheDir().toString();
 
         Log.v(TAG, "Create script for resource = " + resName);
-        return rs.nScriptCCreate(resName, cacheDir, pgm, pgmLength);
+        return rs.nScriptCCreate(resName, rs.mCachePath, pgm, pgmLength);
     }
 }
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 94f19b3..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;
 }
 
@@ -451,6 +479,37 @@
 }
 
 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)
 {
     LOG_API("nAllocationGenerateMipmaps, con(%p), a(%p)", con, (RsAllocation)alloc);
@@ -1033,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;
 }
@@ -1050,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);
@@ -1058,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;
 }
@@ -1277,6 +1354,9 @@
 
 {"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 },
@@ -1317,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 },
diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h
index 93fcf69..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>
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/ui/InputTransport.h b/include/androidfw/InputTransport.h
similarity index 98%
rename from include/ui/InputTransport.h
rename to include/androidfw/InputTransport.h
index 0facce3..a846e65 100644
--- a/include/ui/InputTransport.h
+++ b/include/androidfw/InputTransport.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_TRANSPORT_H
-#define _UI_INPUT_TRANSPORT_H
+#ifndef _ANDROIDFW_INPUT_TRANSPORT_H
+#define _ANDROIDFW_INPUT_TRANSPORT_H
 
 /**
  * Native input transport.
@@ -27,11 +27,12 @@
  * The InputConsumer is used by the application to receive events from the input dispatcher.
  */
 
-#include <ui/Input.h>
+#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 {
 
@@ -332,4 +333,4 @@
 
 } // namespace android
 
-#endif // _UI_INPUT_TRANSPORT_H
+#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/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/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index 971a1b8..aa7fe48 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -20,7 +20,7 @@
 #include <gui/ISurfaceTexture.h>
 #include <gui/SurfaceTexture.h>
 
-#include <ui/egl/android_natives.h>
+#include <ui/ANativeObjectBase.h>
 #include <ui/Region.h>
 
 #include <utils/RefBase.h>
@@ -31,7 +31,7 @@
 class Surface;
 
 class SurfaceTextureClient
-    : public EGLNativeBase<ANativeWindow, SurfaceTextureClient, RefBase>
+    : public ANativeObjectBase<ANativeWindow, SurfaceTextureClient, RefBase>
 {
 public:
     SurfaceTextureClient(const sp<ISurfaceTexture>& surfaceTexture);
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index ac7f6cf..9f2bd3a 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -446,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);
@@ -518,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/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 433ce7c..7a2ada0 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -55,6 +55,7 @@
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
                                 audio_io_handle_t output,
+                                bool isTimed,
                                 int *sessionId,
                                 status_t *status) = 0;
 
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index e4772a1..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 {
 
@@ -71,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 77c82b2..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,
 };
 
 
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/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/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h
index 32eed3f..17efd35 100644
--- a/include/media/stagefright/HardwareAPI.h
+++ b/include/media/stagefright/HardwareAPI.h
@@ -19,7 +19,7 @@
 #define HARDWARE_API_H_
 
 #include <media/stagefright/OMXPluginBase.h>
-#include <ui/android_native_buffer.h>
+#include <system/window.h>
 #include <utils/RefBase.h>
 
 #include <OMX_Component.h>
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/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/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/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 0460bbd..06eff8a 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -26,7 +26,6 @@
 
 #include <ui/PixelFormat.h>
 #include <ui/Region.h>
-#include <ui/egl/android_natives.h>
 
 #include <gui/SurfaceTextureClient.h>
 
diff --git a/include/ui/egl/android_natives.h b/include/ui/ANativeObjectBase.h
similarity index 84%
rename from include/ui/egl/android_natives.h
rename to include/ui/ANativeObjectBase.h
index 9ac50a5..76e850f 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/ANativeObjectBase.h
@@ -22,8 +22,7 @@
 
 #include <hardware/gralloc.h>
 #include <system/window.h>
-// FIXME: remove this header, it's for legacy use.  native_window is pulled from frameworks/base/native/include/android/
-#include <android/native_window.h>
+
 // ---------------------------------------------------------------------------
 
 /* FIXME: this is legacy for pixmaps */
@@ -52,11 +51,11 @@
 namespace android {
 
 /*
- * This helper class turns an EGL android_native_xxx type into a C++
+ * This helper class turns a ANativeXXX object type into a C++
  * reference-counted object; with proper type conversions.
  */
 template <typename NATIVE_TYPE, typename TYPE, typename REF>
-class EGLNativeBase : public NATIVE_TYPE, public REF
+class ANativeObjectBase : public NATIVE_TYPE, public REF
 {
 public:
     // Disambiguate between the incStrong in REF and NATIVE_TYPE
@@ -68,8 +67,8 @@
     }
 
 protected:
-    typedef EGLNativeBase<NATIVE_TYPE, TYPE, REF> BASE;
-    EGLNativeBase() : NATIVE_TYPE(), REF() {
+    typedef ANativeObjectBase<NATIVE_TYPE, TYPE, REF> BASE;
+    ANativeObjectBase() : NATIVE_TYPE(), REF() {
         NATIVE_TYPE::common.incRef = incRef;
         NATIVE_TYPE::common.decRef = decRef;
     }
@@ -86,11 +85,11 @@
         return getSelf(reinterpret_cast<NATIVE_TYPE const*>(base));
     }
     static void incRef(android_native_base_t* base) {
-        EGLNativeBase* self = getSelf(base);
+        ANativeObjectBase* self = getSelf(base);
         self->incStrong(self);
     }
     static void decRef(android_native_base_t* base) {
-        EGLNativeBase* self = getSelf(base);
+        ANativeObjectBase* self = getSelf(base);
         self->decStrong(self);
     }
 };
diff --git a/include/ui/EGLNativeSurface.h b/include/ui/EGLNativeSurface.h
deleted file mode 100644
index 7964e7c..0000000
--- a/include/ui/EGLNativeSurface.h
+++ /dev/null
@@ -1,55 +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.
- */
-
-#ifndef ANDROID_EGL_NATIVE_SURFACE_H
-#define ANDROID_EGL_NATIVE_SURFACE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cutils/atomic.h>
-#include <utils/RefBase.h>
-
-#include <EGL/eglnatives.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-template <class TYPE>
-class EGLNativeSurface : public egl_native_window_t, public LightRefBase<TYPE>
-{
-public:
-    EGLNativeSurface() { 
-        memset(egl_native_window_t::reserved, 0, 
-                sizeof(egl_native_window_t::reserved));
-        memset(egl_native_window_t::reserved_proc, 0, 
-                sizeof(egl_native_window_t::reserved_proc));
-        memset(egl_native_window_t::oem, 0, 
-                sizeof(egl_native_window_t::oem));
-    }
-protected:
-    EGLNativeSurface& operator = (const EGLNativeSurface& rhs);
-    EGLNativeSurface(const EGLNativeSurface& rhs);
-    inline ~EGLNativeSurface() { };
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_EGL_SURFACE_H
-
diff --git a/include/ui/EGLUtils.h b/include/ui/EGLUtils.h
deleted file mode 100644
index a5bff81..0000000
--- a/include/ui/EGLUtils.h
+++ /dev/null
@@ -1,53 +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_UI_EGLUTILS_H
-#define ANDROID_UI_EGLUTILS_H
-
-#include <utils/Errors.h>
-#include <ui/PixelFormat.h>
-#include <EGL/egl.h>
-
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class EGLUtils
-{
-public:
-
-    static const char *strerror(EGLint err);
-
-    static status_t selectConfigForPixelFormat(
-            EGLDisplay dpy,
-            EGLint const* attrs,
-            PixelFormat format,
-            EGLConfig* outConfig);
-
-    static status_t selectConfigForNativeWindow(
-            EGLDisplay dpy,
-            EGLint const* attrs,
-            EGLNativeWindowType window,
-            EGLConfig* outConfig);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
-
-#endif /* ANDROID_UI_EGLUTILS_H */
diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h
index 302d012..b202b95 100644
--- a/include/ui/FramebufferNativeWindow.h
+++ b/include/ui/FramebufferNativeWindow.h
@@ -24,12 +24,10 @@
 
 #include <utils/threads.h>
 #include <utils/String8.h>
+
+#include <ui/ANativeObjectBase.h>
 #include <ui/Rect.h>
 
-#include <pixelflinger/pixelflinger.h>
-
-#include <ui/egl/android_natives.h>
-
 #define NUM_FRAME_BUFFERS  2
 
 extern "C" EGLNativeWindowType android_createDisplaySurface(void);
@@ -44,7 +42,7 @@
 // ---------------------------------------------------------------------------
 
 class FramebufferNativeWindow 
-    : public EGLNativeBase<
+    : public ANativeObjectBase<
         ANativeWindow, 
         FramebufferNativeWindow, 
         LightRefBase<FramebufferNativeWindow> >
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 6ab01f4..f318cd8 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -20,11 +20,11 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <ui/android_native_buffer.h>
+#include <ui/ANativeObjectBase.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
-#include <pixelflinger/pixelflinger.h>
+
 
 struct ANativeWindowBuffer;
 
@@ -37,7 +37,7 @@
 // ===========================================================================
 
 class GraphicBuffer
-    : public EGLNativeBase<
+    : public ANativeObjectBase<
         ANativeWindowBuffer,
         GraphicBuffer, 
         LightRefBase<GraphicBuffer> >, public Flattenable
@@ -93,7 +93,6 @@
 
     status_t lock(uint32_t usage, void** vaddr);
     status_t lock(uint32_t usage, const Rect& rect, void** vaddr);
-    status_t lock(GGLSurface* surface, uint32_t usage);
     status_t unlock();
 
     ANativeWindowBuffer* getNativeBuffer() const;
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index 848c5a1..9f3e267 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,19 +63,12 @@
     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
-
-    // New formats can be added if they're also defined in
-    // pixelflinger/format.h
+    PIXEL_FORMAT_A_8         = 8,                           // 8-bit A
 };
 
 typedef int32_t PixelFormat;
 
-struct PixelFormatInfo
-{
+struct PixelFormatInfo {
     enum {
         INDEX_ALPHA   = 0,
         INDEX_RED     = 1,
@@ -86,12 +77,12 @@
     };
 
     enum { // components
-        ALPHA               = 1,
-        RGB                 = 2,
-        RGBA                = 3,
-        LUMINANCE           = 4,
-        LUMINANCE_ALPHA     = 5,
-        OTHER               = 0xFF
+        ALPHA   = 1,
+        RGB     = 2,
+        RGBA    = 3,
+        L       = 4,
+        LA      = 5,
+        OTHER   = 0xFF
     };
 
     struct szinfo {
diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h
index fcc3bcf..85535bd 100644
--- a/include/utils/KeyedVector.h
+++ b/include/utils/KeyedVector.h
@@ -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/ui/InputTransport.cpp b/libs/androidfw/InputTransport.cpp
similarity index 99%
rename from libs/ui/InputTransport.cpp
rename to libs/androidfw/InputTransport.cpp
index ecb3fb5..1ebd75c 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/androidfw/InputTransport.cpp
@@ -20,7 +20,7 @@
 #include <cutils/log.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <ui/InputTransport.h>
+#include <androidfw/InputTransport.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/socket.h>
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/ui/tests/InputChannel_test.cpp b/libs/androidfw/tests/InputChannel_test.cpp
similarity index 98%
rename from libs/ui/tests/InputChannel_test.cpp
rename to libs/androidfw/tests/InputChannel_test.cpp
index ee422fe..0e5d19d 100644
--- a/libs/ui/tests/InputChannel_test.cpp
+++ b/libs/androidfw/tests/InputChannel_test.cpp
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-#include <ui/InputTransport.h>
+#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>
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/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp
similarity index 99%
rename from libs/ui/tests/InputPublisherAndConsumer_test.cpp
rename to libs/androidfw/tests/InputPublisherAndConsumer_test.cpp
index 3303053..bb45247 100644
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <ui/InputTransport.h>
+#include <androidfw/InputTransport.h>
 #include <utils/Timers.h>
 #include <utils/StopWatch.h>
 #include <gtest/gtest.h>
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/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/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..8153823 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -61,6 +61,7 @@
     "DrawLines",
     "DrawPoints",
     "DrawText",
+    "DrawTextOnPath",
     "DrawPosText",
     "ResetShader",
     "SetupShader",
@@ -226,6 +227,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 +322,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;
@@ -477,7 +484,7 @@
             break;
             case DrawText: {
                 getText(&text);
-                int count = getInt();
+                int32_t count = getInt();
                 float x = getFloat();
                 float y = getFloat();
                 SkPaint* paint = getPaint(renderer);
@@ -486,6 +493,17 @@
                     text.text(), text.length(), count, x, y, paint, length);
             }
             break;
+            case DrawTextOnPath: {
+                getText(&text);
+                int32_t count = getInt();
+                SkPath* path = getPath();
+                float hOffset = getFloat();
+                float vOffset = getFloat();
+                SkPaint* paint = getPaint(renderer);
+                ALOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
+                    text.text(), text.length(), count, paint);
+            }
+            break;
             case DrawPosText: {
                 getText(&text);
                 int count = getInt();
@@ -551,7 +569,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 +590,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 +614,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 +625,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 +636,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 +647,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 +698,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 +708,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 +761,7 @@
             }
             break;
             case DrawBitmapMesh: {
-                int verticesCount = 0;
+                int32_t verticesCount = 0;
                 uint32_t colorsCount = 0;
 
                 SkBitmap* bitmap = getBitmap();
@@ -738,7 +769,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 +802,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 +860,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 +875,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 +883,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 +892,7 @@
             break;
             case DrawText: {
                 getText(&text);
-                int count = getInt();
+                int32_t count = getInt();
                 float x = getFloat();
                 float y = getFloat();
                 SkPaint* paint = getPaint(renderer);
@@ -871,10 +902,23 @@
                 renderer.drawText(text.text(), text.length(), count, x, y, paint, length);
             }
             break;
+            case DrawTextOnPath: {
+                getText(&text);
+                int32_t count = getInt();
+                SkPath* path = getPath();
+                float hOffset = getFloat();
+                float vOffset = getFloat();
+                SkPaint* paint = getPaint(renderer);
+                DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
+                    text.text(), text.length(), count, paint);
+                renderer.drawTextOnPath(text.text(), text.length(), count, path,
+                        hOffset, vOffset, paint);
+            }
+            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 +957,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 +969,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 +993,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 +1064,7 @@
 
 void DisplayListRenderer::finish() {
     insertRestoreToCount();
+    insertTranlate();
     OpenGLRenderer::finish();
 }
 
@@ -1043,15 +1089,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 +1123,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 +1169,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 +1188,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 +1241,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 +1260,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 +1303,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 +1328,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 +1338,40 @@
     //       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::drawTextOnPath(const char* text, int bytesCount, int count,
+        SkPath* path, float hOffset, float vOffset, SkPaint* paint) {
+    if (!text || count <= 0) return;
+    addOp(DisplayList::DrawTextOnPath);
+    addText(text, bytesCount);
+    addInt(count);
+    addPath(path);
+    addFloat(hOffset);
+    addFloat(vOffset);
+    paint->setAntiAlias(true);
+    addPaint(paint);
 }
 
 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..5d1b460 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
@@ -98,6 +99,7 @@
         DrawLines,
         DrawPoints,
         DrawText,
+        DrawTextOnPath,
         DrawPosText,
         ResetShader,
         SetupShader,
@@ -110,13 +112,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 +174,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 +216,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 +286,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);
@@ -304,6 +311,8 @@
     virtual void drawPoints(float* points, int count, SkPaint* paint);
     virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
             SkPaint* paint, float length = 1.0f);
+    virtual void drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
+            float hOffset, float vOffset, SkPaint* paint);
     virtual void drawPosText(const char* text, int bytesCount, int count, const float* positions,
             SkPaint* paint);
 
@@ -358,13 +367,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 +432,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 +465,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 +483,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 +526,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 +558,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..ebb6d88 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <SkCanvas.h>
+#include <SkPathMeasure.h>
 #include <SkTypeface.h>
 
 #include <utils/Log.h>
@@ -1321,7 +1322,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 +1330,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 +2190,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;
     }
 
@@ -2293,11 +2293,82 @@
     drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
 }
 
+void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
+        float hOffset, float vOffset, SkPaint* paint) {
+    if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
+            (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
+        return;
+    }
+
+    float x = 0.0f;
+    float y = 0.0f;
+
+    const bool pureTranslate = mSnapshot->transform->isPureTranslate();
+    if (CC_LIKELY(pureTranslate)) {
+        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
+        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
+    }
+
+    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
+    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
+            paint->getTextSize());
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    mCaches.activeTexture(0);
+    setupDraw();
+    setupDrawDirtyRegionsDisabled();
+    setupDrawWithTexture(true);
+    setupDrawAlpha8Color(paint->getColor(), alpha);
+    setupDrawColorFilter();
+    setupDrawShader();
+    setupDrawBlending(true, mode);
+    setupDrawProgram();
+    setupDrawModelView(x, y, x, y, pureTranslate, true);
+    setupDrawTexture(fontRenderer.getTexture(true));
+    setupDrawPureColorUniforms();
+    setupDrawColorFilterUniforms();
+    setupDrawShaderUniforms(pureTranslate);
+
+//    mat4 pathTransform;
+//    pathTransform.loadTranslate(hOffset, vOffset, 0.0f);
+//
+//    float offset = 0.0f;
+//    SkPathMeasure pathMeasure(*path, false);
+//
+//    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
+//        SkScalar pathLength = pathMeasure.getLength();
+//        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
+//            pathLength = SkScalarHalf(pathLength);
+//        }
+//        offset += SkScalarToFloat(pathLength);
+//    }
+
+//        SkScalar x;
+//        SkPath      tmp;
+//        SkMatrix    m(scaledMatrix);
+//
+//        m.postTranslate(xpos + hOffset, 0);
+//        if (matrix) {
+//            m.postConcat(*matrix);
+//        }
+//        morphpath(&tmp, *iterPath, meas, m);
+//        if (fDevice) {
+//            fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true);
+//        } else {
+//            this->drawPath(tmp, iter.getPaint(), NULL, true);
+//        }
+//    }
+}
+
 void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
     if (mSnapshot->isIgnored()) return;
 
     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..4d7a491 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);
@@ -124,8 +124,10 @@
     virtual void drawPoints(float* points, int count, SkPaint* paint);
     virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
             SkPaint* paint, float length = -1.0f);
-    virtual void drawPosText(const char* text, int bytesCount, int count, const float* positions,
-            SkPaint* paint);
+    virtual void drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
+            float hOffset, float vOffset, SkPaint* paint);
+    virtual void drawPosText(const char* text, int bytesCount, int count,
+            const float* positions, SkPaint* paint);
 
     virtual void resetShader();
     virtual void setupShader(SkiaShader* shader);
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index e893f7a9..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/rs/Allocation.cpp b/libs/rs/Allocation.cpp
new file mode 100644
index 0000000..d69c55f
--- /dev/null
+++ b/libs/rs/Allocation.cpp
@@ -0,0 +1,471 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libRS_cpp"
+
+#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 9c5d06b..45ed453 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -127,7 +127,14 @@
 	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 libgui
 
diff --git a/libs/rs/BaseObj.cpp b/libs/rs/BaseObj.cpp
new file mode 100644
index 0000000..82e51e7
--- /dev/null
+++ b/libs/rs/BaseObj.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.
+ */
+
+#define LOG_TAG "libRS_cpp"
+
+#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..f318d40
--- /dev/null
+++ b/libs/rs/Element.cpp
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libRS_cpp"
+
+#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..0b42055
--- /dev/null
+++ b/libs/rs/RenderScript.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libRS_cpp"
+
+#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..25fa673
--- /dev/null
+++ b/libs/rs/Script.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libRS_cpp"
+
+#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..ad82ff4
--- /dev/null
+++ b/libs/rs/ScriptC.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libRS_cpp"
+
+#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..dcbbe10
--- /dev/null
+++ b/libs/rs/ScriptC.h
@@ -0,0 +1,34 @@
+/*
+ * 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,
+            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..1352bd7
--- /dev/null
+++ b/libs/rs/Type.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libRS_cpp"
+
+#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() {
+    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 ea92192..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>
@@ -220,7 +225,8 @@
     }
 
     void * ptr = alloc->mHal.state.usrPtr;
-    if (!ptr) {
+    if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT) {
+    } else {
         ptr = malloc(alloc->mHal.state.type->getSizeBytes());
         if (!ptr) {
             free(drv);
@@ -248,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());
     }
 
@@ -386,9 +392,96 @@
     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();
@@ -407,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 230804b..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);
@@ -69,6 +71,12 @@
                             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 e011955..bf2b62a 100644
--- a/libs/rs/driver/rsdCore.cpp
+++ b/libs/rs/driver/rsdCore.cpp
@@ -74,6 +74,9 @@
         rsdAllocationSyncAll,
         rsdAllocationMarkDirty,
         rsdAllocationInitSurfaceTexture,
+        rsdAllocationSetSurfaceTexture,
+        rsdAllocationIoSend,
+        rsdAllocationIoReceive,
         rsdAllocationData1D,
         rsdAllocationData2D,
         rsdAllocationData3D,
diff --git a/libs/rs/driver/rsdCore.h b/libs/rs/driver/rsdCore.h
index 168bdf3..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 {
diff --git a/libs/rs/driver/rsdGL.cpp b/libs/rs/driver/rsdGL.cpp
index b136cc7..8033b088 100644
--- a/libs/rs/driver/rsdGL.cpp
+++ b/libs/rs/driver/rsdGL.cpp
@@ -16,8 +16,8 @@
 
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/PixelFormat.h>
-#include <ui/EGLUtils.h>
-#include <ui/egl/android_natives.h>
+
+#include <system/window.h>
 
 #include <sys/types.h>
 #include <sys/resource.h>
@@ -47,6 +47,29 @@
 static int32_t gGLContextCount = 0;
 
 static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
+    struct EGLUtils {
+        static const char *strerror(EGLint err) {
+            switch (err){
+                case EGL_SUCCESS:           return "EGL_SUCCESS";
+                case EGL_NOT_INITIALIZED:   return "EGL_NOT_INITIALIZED";
+                case EGL_BAD_ACCESS:        return "EGL_BAD_ACCESS";
+                case EGL_BAD_ALLOC:         return "EGL_BAD_ALLOC";
+                case EGL_BAD_ATTRIBUTE:     return "EGL_BAD_ATTRIBUTE";
+                case EGL_BAD_CONFIG:        return "EGL_BAD_CONFIG";
+                case EGL_BAD_CONTEXT:       return "EGL_BAD_CONTEXT";
+                case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
+                case EGL_BAD_DISPLAY:       return "EGL_BAD_DISPLAY";
+                case EGL_BAD_MATCH:         return "EGL_BAD_MATCH";
+                case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
+                case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
+                case EGL_BAD_PARAMETER:     return "EGL_BAD_PARAMETER";
+                case EGL_BAD_SURFACE:       return "EGL_BAD_SURFACE";
+                case EGL_CONTEXT_LOST:      return "EGL_CONTEXT_LOST";
+                default: return "UNKNOWN";
+            }
+        }
+    };
+
     if (returnVal != EGL_TRUE) {
         fprintf(stderr, "%s() returned %d\n", op, returnVal);
     }
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/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/rsdShader.cpp b/libs/rs/driver/rsdShader.cpp
index 3bca794..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,25 +67,26 @@
     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 {
@@ -135,22 +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) {
             Allocation *a = mRSProgram->mHal.state.textures[ct];
             if (a && a->mHal.state.surfaceTextureID) {
-                snprintf(buf, sizeof(buf), "uniform samplerExternalOES UNI_Tex%i;\n", ct);
+                mShaderTextures.append("uniform samplerExternalOES UNI_");
             } else {
-                snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct);
+                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");
     }
 }
 
@@ -161,7 +167,7 @@
     }
     appendUserConstants();
     appendAttributes();
-    appendTextures();
+    mShader.append(mShaderTextures);
 
     mShader.append(mUserShader);
 
@@ -418,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);
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/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 6759bc7..cf4a391 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -69,168 +69,183 @@
     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
@@ -244,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
@@ -274,125 +289,127 @@
 }
 
 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
@@ -402,4 +419,3 @@
     param float quality
     ret RsPath
     }
-
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 02c6809..83c88fd 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -17,6 +17,7 @@
 #include "rsContext.h"
 #include "rs_hal.h"
 
+#include "system/window.h"
 
 using namespace android;
 using namespace android::renderscript;
@@ -44,6 +45,7 @@
         delete a;
         return NULL;
     }
+
     return a;
 }
 
@@ -420,6 +422,28 @@
     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);
+}
+
+
 /////////////////
 //
 
@@ -670,6 +694,21 @@
     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 58a582b..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 {
@@ -57,6 +59,7 @@
             bool hasReferences;
             void * usrPtr;
             int32_t surfaceTextureID;
+            ANativeWindow *wndSurface;
         };
         State state;
 
@@ -127,6 +130,9 @@
     }
 
     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;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index adaefc6..95ac76e 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -263,6 +263,10 @@
             rsc->timerSet(RS_TIMER_IDLE);
 
 #ifndef ANDROID_RS_SERIALIZE
+            if (!rsc->mRootScript.get() || !rsc->mHasSurface || rsc->mPaused) {
+                targetRate = 0;
+            }
+
             if (vsyncRate != targetRate) {
                 displayEvent.setVsyncRate(targetRate);
                 vsyncRate = targetRate;
diff --git a/libs/rs/RenderScriptDefines.h b/libs/rs/rsDefines.h
similarity index 100%
rename from libs/rs/RenderScriptDefines.h
rename to libs/rs/rsDefines.h
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/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/rsMesh.h b/libs/rs/rsMesh.h
index 166b5d3..8eea427 100644
--- a/libs/rs/rsMesh.h
+++ b/libs/rs/rsMesh.h
@@ -18,7 +18,7 @@
 #define ANDROID_RS_MESH_H
 
 
-#include "RenderScript.h"
+#include "rs.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
diff --git a/libs/rs/rsPath.h b/libs/rs/rsPath.h
index dac795e..7c05503 100644
--- a/libs/rs/rsPath.h
+++ b/libs/rs/rsPath.h
@@ -18,7 +18,7 @@
 #define ANDROID_RS_PATH_H
 
 
-#include "RenderScript.h"
+#include "rs.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
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/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/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
index 4f30573..7182f53 100644
--- a/libs/rs/rsThreadIO.cpp
+++ b/libs/rs/rsThreadIO.cpp
@@ -31,6 +31,8 @@
 
 ThreadIO::ThreadIO() {
     mRunning = true;
+    mPureFifo = false;
+    mMaxInlineSize = 1024;
 }
 
 ThreadIO::~ThreadIO() {
@@ -65,6 +67,16 @@
     mToClient.shutdown();
 }
 
+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) {
     uint32_t buf;
     if (data == NULL) {
@@ -91,6 +103,7 @@
 
 bool ThreadIO::playCoreCommands(Context *con, int waitFd) {
     bool ret = false;
+    const bool isLocal = !isPureFifo();
 
     uint8_t buf[2 * 1024];
     const CoreCmdHeader *cmd = (const CoreCmdHeader *)&buf[0];
@@ -120,14 +133,19 @@
         }
 
         if (p[0].revents) {
-            size_t r = mToCore.read(&buf[0], sizeof(CoreCmdHeader));
-            mToCore.read(&buf[sizeof(CoreCmdHeader)], cmd->bytes);
-
-            if (r != sizeof(CoreCmdHeader)) {
-                // exception or timeout occurred.
-                break;
+            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);
@@ -138,7 +156,12 @@
                 rsAssert(cmd->cmdID < (sizeof(gPlaybackFuncs) / sizeof(void *)));
                 ALOGE("playCoreCommands error con %p, cmd %i", con, cmd->cmdID);
             }
-            gPlaybackFuncs[cmd->cmdID](con, data, cmd->bytes);
+
+            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);
diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h
index 62e3e33..cb7d4ab 100644
--- a/libs/rs/rsThreadIO.h
+++ b/libs/rs/rsThreadIO.h
@@ -34,6 +34,13 @@
     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, int waitFd);
@@ -42,8 +49,16 @@
 
     void * coreHeader(uint32_t, size_t dataLen);
     void coreCommit();
+
     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);
@@ -65,6 +80,8 @@
     ClientCmdHeader mLastClientHeader;
 
     bool mRunning;
+    bool mPureFifo;
+    size_t mMaxInlineSize;
 
     FifoSocket mToClient;
     FifoSocket mToCore;
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
index db6f592..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 1e222e1..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 {
@@ -115,19 +117,23 @@
                        bool zeroNew);
         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,
@@ -151,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;
@@ -172,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;
diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c
index 385c8b5..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,7 +252,7 @@
                 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);
@@ -272,7 +272,7 @@
 
             fprintf(f, "    io->coreCommit();\n");
             if (hasInlineDataPointers(api)) {
-                fprintf(f, "    if (dataSize >= 1024) {\n");
+                fprintf(f, "    if (dataSize >= io->getMaxInlineSize()) {\n");
                 fprintf(f, "        io->coreGetReturn(NULL, 0);\n");
                 fprintf(f, "    }\n");
             } else if (api->ret.typeName[0]) {
@@ -288,81 +288,71 @@
         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");
     }
@@ -418,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,8 +423,6 @@
         }
 
         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)) {
@@ -488,30 +475,40 @@
         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];
-            needFlush += vt->ptrLevel;
+            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");
@@ -535,10 +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");
-        } else if (needFlush) {
-            fprintf(f, "    f->readReturn(NULL, 0);\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");
@@ -608,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/tests/Android.mk b/libs/rs/tests/Android.mk
new file mode 100644
index 0000000..197e862
--- /dev/null
+++ b/libs/rs/tests/Android.mk
@@ -0,0 +1,30 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	compute.cpp \
+	ScriptC_mono.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/ScriptC_mono.cpp b/libs/rs/tests/ScriptC_mono.cpp
new file mode 100644
index 0000000..7f83616
--- /dev/null
+++ b/libs/rs/tests/ScriptC_mono.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "ScriptC_mono.h"
+
+static const char mono[] = \
+    "\xDE\xC0\x17\x0B\x00\x00\x00\x00\x18\x00\x00\x00\xFC\x03\x00\x00\x00\x00\x00\x00" \
+    "\x10\x00\x00\x00\x42\x43\xC0\xDE\x21\x0C\x00\x00\xFC\x00\x00\x00\x01\x10\x00\x00" \
+    "\x12\x00\x00\x00\x07\x81\x23\x91\x41\xC8\x04\x49\x06\x10\x32\x39\x92\x01\x84\x0C" \
+    "\x25\x05\x08\x19\x1E\x04\x8B\x62\x80\x14\x45\x02\x42\x92\x0B\x42\xA4\x10\x32\x14" \
+    "\x38\x08\x18\x49\x0A\x32\x44\x24\x48\x0A\x90\x21\x23\xC4\x52\x80\x0C\x19\x21\x72" \
+    "\x24\x07\xC8\x48\x11\x62\xA8\xA0\xA8\x40\xC6\xF0\x01\x00\x00\x00\x49\x18\x00\x00" \
+    "\x08\x00\x00\x00\x0B\x8C\x00\x04\x41\x10\x04\x09\x01\x04\x41\x10\x04\x89\xFF\xFF" \
+    "\xFF\xFF\x1F\xC0\x60\x81\xF0\xFF\xFF\xFF\xFF\x03\x18\x00\x00\x00\x89\x20\x00\x00" \
+    "\x13\x00\x00\x00\x32\x22\x48\x09\x20\x64\x85\x04\x93\x22\xA4\x84\x04\x93\x22\xE3" \
+    "\x84\xA1\x90\x14\x12\x4C\x8A\x8C\x0B\x84\xA4\x4C\x10\x48\x23\x00\x73\x04\xC8\x30" \
+    "\x02\x11\x90\x28\x03\x18\x83\xC8\x0C\xC0\x30\x02\x61\x14\xE1\x08\x42\xC3\x08\x83" \
+    "\x51\x06\xA3\x14\xAD\x22\x08\x45\x6D\x20\x60\x8E\x00\x0C\x86\x11\x06\x08\x00\x00" \
+    "\x13\xB0\x70\x90\x87\x76\xB0\x87\x3B\x68\x03\x77\x78\x07\x77\x28\x87\x36\x60\x87" \
+    "\x74\x70\x87\x7A\xC0\x87\x36\x38\x07\x77\xA8\x87\x72\x08\x07\x71\x48\x87\x0D\xF2" \
+    "\x50\x0E\x6D\x00\x0F\x7A\x30\x07\x72\xA0\x07\x73\x20\x07\x7A\x30\x07\x72\xD0\x06" \
+    "\xE9\x10\x07\x7A\x80\x07\x7A\x80\x07\x6D\x90\x0E\x78\xA0\x07\x78\xA0\x07\x78\xD0" \
+    "\x06\xE9\x10\x07\x76\xA0\x07\x71\x60\x07\x7A\x10\x07\x76\xD0\x06\xE9\x30\x07\x72" \
+    "\xA0\x07\x73\x20\x07\x7A\x30\x07\x72\xD0\x06\xE9\x60\x07\x74\xA0\x07\x76\x40\x07" \
+    "\x7A\x60\x07\x74\xD0\x06\xE6\x30\x07\x72\xA0\x07\x73\x20\x07\x7A\x30\x07\x72\xD0" \
+    "\x06\xE6\x60\x07\x74\xA0\x07\x76\x40\x07\x7A\x60\x07\x74\xD0\x06\xF6\x60\x07\x74" \
+    "\xA0\x07\x76\x40\x07\x7A\x60\x07\x74\xD0\x06\xF6\x10\x07\x72\x80\x07\x7A\x60\x07" \
+    "\x74\xA0\x07\x71\x20\x07\x78\xD0\x06\xE1\x00\x07\x7A\x00\x07\x7A\x60\x07\x74\xD0" \
+    "\x06\xEE\x30\x07\x72\xD0\x06\xB3\x60\x07\x74\x30\x44\x29\x00\x00\x08\x00\x00\x00" \
+    "\x80\x21\x4A\x02\x04\x00\x00\x00\x00\x00\x0C\x51\x18\x20\x00\x00\x00\x00\x00\x60" \
+    "\x88\xE2\x00\x01\x00\x00\x00\x00\x00\x79\x18\x00\x45\x00\x00\x00\x43\x88\x27\x78" \
+    "\x84\x05\x87\x3D\x94\x83\x3C\xCC\x43\x3A\xBC\x83\x3B\x2C\x08\xE2\x60\x08\xF1\x10" \
+    "\x4F\xB1\x20\x52\x87\x70\xB0\x87\x70\xF8\x05\x78\x08\x87\x71\x58\x87\x70\x38\x87" \
+    "\x72\xF8\x05\x77\x08\x87\x76\x28\x87\x05\x63\x30\x0E\xEF\xD0\x0E\x6E\x50\x0E\xF8" \
+    "\x10\x0E\xED\x00\x0F\xEC\x50\x0E\x6E\x10\x0E\xEE\x40\x0E\xF2\xF0\x0E\xE9\x40\x0E" \
+    "\x6E\x20\x0F\xF3\xE0\x06\xE8\x50\x0E\xEC\xC0\x0E\xEF\x30\x0E\xEF\xD0\x0E\xF0\x50" \
+    "\x0F\xF4\x50\x0E\x43\x84\xE7\x58\x40\xC8\xC3\x3B\xBC\x03\x3D\x0C\x11\x9E\x64\x41" \
+    "\x30\x07\x43\x88\x67\x79\x98\x05\xCF\x3B\xB4\x83\x3B\xA4\x03\x3C\xBC\x03\x3D\x94" \
+    "\x83\x3B\xD0\x03\x18\x8C\x03\x3A\x84\x83\x3C\x0C\x21\x9E\x06\x00\x16\x44\xB3\x90" \
+    "\x0E\xED\x00\x0F\xEC\x50\x0E\x60\x30\x0A\x6F\x30\x0A\x6B\xB0\x06\x60\x40\x0B\xA2" \
+    "\x10\x0A\xA1\x30\xE2\x18\x03\x78\x90\x87\x70\x38\x87\x76\x08\x87\x29\x02\x30\x8C" \
+    "\xB8\xC6\x40\x1E\xE6\xE1\x17\xCA\x01\x1F\xE0\xE1\x1D\xE4\x81\x1E\x7E\xC1\x1C\xDE" \
+    "\x41\x1E\xCA\x21\x1C\xC6\x01\x1D\x7E\xC1\x1D\xC2\xA1\x1D\xCA\x61\x4A\x60\x8C\x90" \
+    "\xC6\x40\x1E\xE6\xE1\x17\xCA\x01\x1F\xE0\xE1\x1D\xE4\x81\x1E\x7E\xC1\x1C\xDE\x41" \
+    "\x1E\xCA\x21\x1C\xC6\x01\x1D\xA6\x04\x08\x00\x00\x61\x20\x00\x00\x24\x00\x00\x00" \
+    "\x13\x04\x41\x2C\x10\x00\x00\x00\x0C\x00\x00\x00\x04\x8B\xA0\x04\x46\x00\xE8\xCC" \
+    "\x00\x90\x9A\x01\x98\x63\x70\x1A\x86\xCC\x18\x41\x6D\xFA\xB2\xEF\x8D\x11\x88\x6D" \
+    "\xCC\xC6\xDF\x18\xC1\x49\x97\x72\xFA\x51\x9C\x63\x40\x0E\x63\x04\x00\x00\x00\x00" \
+    "\x44\x8C\x11\x03\x42\x08\x82\x68\x90\x41\x4A\x9E\x11\x83\x42\x08\x84\x69\x99\x63" \
+    "\x50\x28\x64\x90\xA1\x52\xA0\x11\x03\x42\x08\x06\x6B\x30\xA2\xB8\x06\x00\xC3\x81" \
+    "\x00\x00\x00\x00\x03\x00\x00\x00\x46\x40\x54\x3F\xD2\x58\x41\x51\xFD\x0E\x35\x01" \
+    "\x01\x31\x00\x00\x03\x00\x00\x00\x5B\x06\x20\x50\xB6\x0C\x47\xA0\x00\x00\x00\x00" \
+    "\x00\x00\x00\x00\x79\x18\x00\x00\x0B\x00\x00\x00\x33\x08\x80\x1C\xC4\xE1\x1C\x66" \
+    "\x14\x01\x3D\x88\x43\x38\x84\xC3\x8C\x42\x80\x07\x79\x78\x07\x73\x98\xB1\x0C\xE6" \
+    "\x00\x0F\xE1\x30\x0E\xE3\x50\x0F\xF2\x10\x0E\xE3\x90\x0F\x00\x00\x71\x20\x00\x00" \
+    "\x0E\x00\x00\x00\x06\x40\x44\x8E\x33\x59\x40\x14\x49\x6E\xF3\x00\x82\xC2\x39\x8B" \
+    "\x13\xF1\x3C\xCF\x9B\x40\xF3\xCF\xF7\xE0\x4C\x5D\x75\xFF\x05\xFB\xDB\x80\xF6\xCF" \
+    "\xF5\x1E\x49\x29\x20\x28\x9C\xB3\x38\x51\xEB\xF0\x3C\xCF\x77\xD5\xFD\x17\x00\x00" \
+    "\x00\x00\x00\x00";
+
+
+ScriptC_mono::ScriptC_mono(RenderScript *rs, const char *cacheDir, size_t cacheDirLength) :
+    ScriptC(rs, mono, sizeof(mono) - 1, "mono", 4, cacheDir, cacheDirLength) {
+
+    printf("sizeof text %i", sizeof(mono));
+
+
+
+}
+
+void ScriptC_mono::forEach_root(const Allocation *ain, const Allocation *aout) {
+    forEach(0, ain, aout, NULL, 0);
+}
+
diff --git a/include/ui/android_native_buffer.h b/libs/rs/tests/ScriptC_mono.h
similarity index 66%
rename from include/ui/android_native_buffer.h
rename to libs/rs/tests/ScriptC_mono.h
index b6e1db4..7e4f601 100644
--- a/include/ui/android_native_buffer.h
+++ b/libs/rs/tests/ScriptC_mono.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 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,9 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_ANDROID_NATIVES_PRIV_H
-#define ANDROID_ANDROID_NATIVES_PRIV_H
+#include "ScriptC.h"
 
-#include <ui/egl/android_natives.h>
+class ScriptC_mono : protected ScriptC {
+public:
+    ScriptC_mono(RenderScript *rs, const char *cacheDir, size_t cacheDirLength);
 
-#endif /* ANDROID_ANDROID_NATIVES_PRIV_H */
+    void forEach_root(const Allocation *ain, const Allocation *aout);
+
+};
+
diff --git a/libs/rs/tests/compute.cpp b/libs/rs/tests/compute.cpp
new file mode 100644
index 0000000..42eaa52
--- /dev/null
+++ b/libs/rs/tests/compute.cpp
@@ -0,0 +1,58 @@
+
+#include "RenderScript.h"
+#include "Element.h"
+#include "Type.h"
+#include "Allocation.h"
+
+#include "ScriptC_mono.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);
+
+
+    Allocation *a1 = Allocation::createSized(rs, e, 1000);
+    printf("Allocation %p\n", a1);
+
+    Allocation *ain = Allocation::createTyped(rs, t);
+    Allocation *aout = Allocation::createTyped(rs, t);
+    printf("Allocation %p %p\n", ain, aout);
+
+    ScriptC_mono * sc = new ScriptC_mono(rs, NULL, 0);
+    printf("new script\n");
+
+    uint32_t *buf = new uint32_t[t->getCount()];
+    for (uint32_t ct=0; ct < t->getCount(); ct++) {
+        buf[ct] = ct | (ct << 16);
+    }
+    //ain->copy1DRangeFrom(0, 128*128, (int32_t *)buf, 128*128*4);
+    ain->copy1DRangeFromUnchecked(0, t->getCount(), buf, t->getCount()*4);
+
+
+
+    sc->forEach_root(ain, aout);
+    printf("for each done\n");
+
+
+    printf("Deleting stuff\n");
+    delete sc;
+    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..5aff7a4 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -13,41 +13,13 @@
 # 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
@@ -55,15 +27,11 @@
 LOCAL_SHARED_LIBRARIES := \
 	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..dec99b6 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -27,25 +27,21 @@
 #include <utils/threads.h>
 #include <utils/RefBase.h>
 
-#include <ui/Rect.h>
+#include <ui/ANativeObjectBase.h>
 #include <ui/FramebufferNativeWindow.h>
+#include <ui/Rect.h>
 
 #include <EGL/egl.h>
 
-#include <pixelflinger/format.h>
-#include <pixelflinger/pixelflinger.h>
-
 #include <hardware/hardware.h>
 #include <hardware/gralloc.h>
 
-#include <private/ui/android_natives_priv.h>
-
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
 
 class NativeBuffer 
-    : public EGLNativeBase<
+    : public ANativeObjectBase<
         ANativeWindowBuffer, 
         NativeBuffer, 
         LightRefBase<NativeBuffer> >
@@ -100,6 +96,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/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index f549a37..57063e5 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -28,8 +28,6 @@
 #include <ui/GraphicBufferMapper.h>
 #include <ui/PixelFormat.h>
 
-#include <pixelflinger/pixelflinger.h>
-
 namespace android {
 
 // ===========================================================================
@@ -182,21 +180,6 @@
     return res;
 }
 
-status_t GraphicBuffer::lock(GGLSurface* sur, uint32_t usage) 
-{
-    void* vaddr;
-    status_t res = GraphicBuffer::lock(usage, &vaddr);
-    if (res == NO_ERROR && sur) {
-        sur->version = sizeof(GGLSurface);
-        sur->width = width;
-        sur->height = height;
-        sur->stride = stride;
-        sur->format = format;
-        sur->data = static_cast<GGLubyte*>(vaddr);
-    }
-    return res;
-}
-
 size_t GraphicBuffer::getFlattenedSize() const {
     return (8 + (handle ? handle->numInts : 0))*sizeof(int);
 }
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index ee186c8..fc1d3c2 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -15,13 +15,54 @@
  */
 
 #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},
+        { 1,  8, { 0, 0,   8, 0,   8, 0,   8, 0 }, PixelFormatInfo::L    },
+        { 2, 16, {16, 8,   8, 0,   8, 0,   8, 0 }, PixelFormatInfo::LA   },
+        { 1,  8, { 0, 0,   8, 5,   5, 2,   2, 0 }, PixelFormatInfo::RGB  },
+};
+
+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 +118,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 +135,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/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/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/AudioService.java b/media/java/android/media/AudioService.java
index eae03be..1c7f577 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -591,7 +591,7 @@
                         // Post a persist volume msg
                         sendMsg(mAudioHandler,
                                 MSG_PERSIST_VOLUME,
-                                SENDMSG_REPLACE,
+                                SENDMSG_QUEUE,
                                 PERSIST_LAST_AUDIBLE,
                                 device,
                                 s,
@@ -606,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,
@@ -746,7 +746,7 @@
                 // Post a persist volume msg
                 sendMsg(mAudioHandler,
                         MSG_PERSIST_VOLUME,
-                        SENDMSG_REPLACE,
+                        SENDMSG_QUEUE,
                         PERSIST_LAST_AUDIBLE,
                         device,
                         streamState,
@@ -758,7 +758,7 @@
                 // to persist).
                 sendMsg(mAudioHandler,
                         MSG_SET_DEVICE_VOLUME,
-                        SENDMSG_NOOP,
+                        SENDMSG_QUEUE,
                         device,
                         0,
                         streamState,
@@ -2208,7 +2208,7 @@
                                     }
                                     sendMsg(mAudioHandler,
                                             MSG_SET_ALL_VOLUMES,
-                                            SENDMSG_NOOP,
+                                            SENDMSG_QUEUE,
                                             0,
                                             0,
                                             VolumeStreamState.this, 0);
@@ -2252,7 +2252,7 @@
                                         }
                                         sendMsg(mAudioHandler,
                                                 MSG_SET_ALL_VOLUMES,
-                                                SENDMSG_NOOP,
+                                                SENDMSG_QUEUE,
                                                 0,
                                                 0,
                                                 VolumeStreamState.this, 0);
@@ -2350,7 +2350,7 @@
             // Post a persist volume msg
             sendMsg(mAudioHandler,
                     MSG_PERSIST_VOLUME,
-                    SENDMSG_REPLACE,
+                    SENDMSG_QUEUE,
                     PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
                     device,
                     streamState,
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/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 52d31c7..a08d6c3 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -62,6 +62,9 @@
 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.
  *
@@ -348,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;
         }
 
@@ -373,11 +374,7 @@
 
     private MediaInserter mMediaInserter;
 
-    // hashes file path to FileCacheEntry.
-    // path should be lower case if mCaseInsensitivePaths is true
-    private LinkedHashMap<String, FileCacheEntry> mFileCache;
-
-    private ArrayList<FileCacheEntry> mPlayLists;
+    private ArrayList<FileEntry> mPlayLists;
 
     private DrmManagerClient mDrmManagerClient = null;
 
@@ -432,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;
@@ -465,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;
@@ -477,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);
@@ -525,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)) {
@@ -778,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
@@ -1028,55 +1019,94 @@
         String where = null;
         String[] selectionArgs = null;
 
-        if (mFileCache == null) {
-            mFileCache = new LinkedHashMap<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
@@ -1186,57 +1217,6 @@
     }
 
     private void postscan(String[] directories) throws RemoteException {
-        Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();
-
-        // 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());
-
-        while (iterator.hasNext()) {
-            FileCacheEntry entry = iterator.next();
-            String path = entry.mPath;
-
-            // 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;
-                    }
-                }
-            }
-
-            if (fileMissing) {
-                // 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)) {
-                    deleter.delete(entry.mRowId);
-                    iterator.remove();
-                    if (entry.mPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
-                        deleter.flush();
-                        File f = new File(path);
-                        mMediaProvider.call(MediaStore.UNHIDE_CALL, f.getParent(), null);
-                    }
-                }
-            }
-        }
-        deleter.flush();
 
         // handle playlists last, after we know what media files are on the storage.
         if (mProcessPlaylists) {
@@ -1248,7 +1228,6 @@
 
         // allow GC to clean up
         mPlayLists = null;
-        mFileCache = null;
         mMediaProvider = null;
     }
 
@@ -1422,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);
                 }
@@ -1445,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;
@@ -1495,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) {
@@ -1524,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) {
@@ -1677,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('/');
@@ -1728,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..91d0add 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/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/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index f9f997f..6808aa2 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -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);
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index aead9a1..74c97ed 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -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,
@@ -540,6 +546,10 @@
 {
     int afSamplingRate;
 
+    if (mIsTimed) {
+        return INVALID_OPERATION;
+    }
+
     if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) {
         return NO_INIT;
     }
@@ -553,6 +563,10 @@
 
 uint32_t AudioTrack::getSampleRate() const
 {
+    if (mIsTimed) {
+        return INVALID_OPERATION;
+    }
+
     AutoMutex lock(mLock);
     return mCblk->sampleRate;
 }
@@ -578,6 +592,10 @@
         return NO_ERROR;
     }
 
+    if (mIsTimed) {
+        return INVALID_OPERATION;
+    }
+
     if (loopStart >= loopEnd ||
         loopEnd - loopStart > cblk->frameCount ||
         cblk->server > loopStart) {
@@ -641,6 +659,8 @@
 
 status_t AudioTrack::setPosition(uint32_t position)
 {
+    if (mIsTimed) return INVALID_OPERATION;
+
     AutoMutex lock(mLock);
 
     if (!stopped_l()) return INVALID_OPERATION;
@@ -791,6 +811,7 @@
                                                       ((uint16_t)flags) << 16,
                                                       sharedBuffer,
                                                       output,
+                                                      mIsTimed,
                                                       &mSessionId,
                                                       &status);
 
@@ -957,6 +978,7 @@
 {
 
     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, and it would
@@ -1013,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;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 4507e5d..ebadbfa 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -90,6 +90,7 @@
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
                                 audio_io_handle_t output,
+                                bool isTimed,
                                 int *sessionId,
                                 status_t *status)
     {
@@ -105,6 +106,7 @@
         data.writeInt32(flags);
         data.writeStrongBinder(sharedBuffer->asBinder());
         data.writeInt32((int32_t) output);
+        data.writeInt32(isTimed);
         int lSessionId = 0;
         if (sessionId != NULL) {
             lSessionId = *sessionId;
@@ -689,11 +691,12 @@
             uint32_t flags = data.readInt32();
             sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder());
             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());
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index a7958de..28ebbbfc 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -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>
@@ -114,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");
@@ -159,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/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/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 4df7f3d..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;
 }
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index fe519b0..c5f4f86 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -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,
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/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/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/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/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/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/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 470f750..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 =
@@ -3556,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/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/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/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index e892f92..297f2c9 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -24,7 +24,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MetaData.h>
 #include <surfaceflinger/Surface.h>
-#include <ui/android_native_buffer.h>
+#include <system/window.h>
 #include <ui/GraphicBufferMapper.h>
 #include <gui/ISurfaceTexture.h>
 
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 0a6776e..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/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 8f2ea95..7ab0042 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -20,7 +20,7 @@
 
 #include <media/stagefright/ColorConverter.h>
 #include <utils/RefBase.h>
-#include <ui/android_native_buffer.h>
+#include <system/window.h>
 
 namespace android {
 
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/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 63%
rename from media/libstagefright/timedtext/TimedTextInBandSource.cpp
rename to media/libstagefright/timedtext/TimedText3GPPSource.cpp
index afb73fb..4a3bfd3 100644
--- a/media/libstagefright/timedtext/TimedTextInBandSource.cpp
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
@@ -15,7 +15,7 @@
  */
 
 //#define LOG_NDEBUG 0
-#define LOG_TAG "TimedTextInBandSource"
+#define LOG_TAG "TimedText3GPPSource"
 #include <utils/Log.h>
 
 #include <binder/Parcel.h>
@@ -26,19 +26,19 @@
 #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/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/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/native/android/Android.mk b/native/android/Android.mk
index 9940442..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/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp
index 6a006aa..06d45cc 100644
--- a/opengl/libagl/TextureObjectManager.cpp
+++ b/opengl/libagl/TextureObjectManager.cpp
@@ -19,8 +19,6 @@
 #include "context.h"
 #include "TextureObjectManager.h"
 
-#include <private/ui/android_natives_priv.h>
-
 namespace android {
 // ----------------------------------------------------------------------------
 
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index eb55bee..92d32a2 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -30,6 +30,7 @@
 #include <cutils/atomic.h>
 
 #include <utils/threads.h>
+#include <ui/ANativeObjectBase.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
@@ -39,8 +40,6 @@
 #include <pixelflinger/format.h>
 #include <pixelflinger/pixelflinger.h>
 
-#include <private/ui/android_natives_priv.h>
-
 #include "context.h"
 #include "state.h"
 #include "texture.h"
@@ -49,13 +48,14 @@
 #undef NELEM
 #define NELEM(x) (sizeof(x)/sizeof(*(x)))
 
+// ----------------------------------------------------------------------------
 
 EGLBoolean EGLAPI eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw,
         EGLint left, EGLint top, EGLint width, EGLint height);
 
-
 // ----------------------------------------------------------------------------
 namespace android {
+
 // ----------------------------------------------------------------------------
 
 const unsigned int NUM_DISPLAYS = 1;
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index 88e8651..08536df 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -23,7 +23,6 @@
 #include "texture.h"
 #include "TextureObjectManager.h"
 
-#include <private/ui/android_natives_priv.h>
 #include <ETC1/etc1.h>
 
 namespace android {
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 3e185bc..6c4feb5 100644
--- a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
@@ -379,13 +379,16 @@
     arg_location->add_intvalue(location);
 }
 
-void fixupGLMessage(GLTraceContext *context, nsecs_t start, nsecs_t end, GLMessage *glmsg) {
+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()) {
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/tests/angeles/Android.mk b/opengl/tests/angeles/Android.mk
index d0c3221..84b754b 100644
--- a/opengl/tests/angeles/Android.mk
+++ b/opengl/tests/angeles/Android.mk
@@ -4,14 +4,7 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES:= app-linux.cpp demo.c.arm
 LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM libui
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
 LOCAL_MODULE:= angeles
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= gpustate.c
-LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM
-LOCAL_MODULE:= gpustate
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/angeles/app-linux.cpp b/opengl/tests/angeles/app-linux.cpp
index 4d10ee5..6ac68a2 100644
--- a/opengl/tests/angeles/app-linux.cpp
+++ b/opengl/tests/angeles/app-linux.cpp
@@ -53,7 +53,7 @@
 #include <GLES/gl.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/angeles/gpustate.c b/opengl/tests/angeles/gpustate.c
deleted file mode 100644
index 3c540c9..0000000
--- a/opengl/tests/angeles/gpustate.c
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-static void *map_memory(const char *fn, unsigned base, unsigned size)
-{
-    int fd;
-    void *ptr;
-    
-    fd = open(fn, O_RDWR | O_SYNC);
-    if(fd < 0) {
-        perror("cannot open %s for mapping");
-        return MAP_FAILED;
-    }
-
-    ptr = mmap(0, size, PROT_READ | PROT_WRITE,
-               MAP_SHARED, fd, base);
-    close(fd);
-    
-    if(ptr == MAP_FAILED) {
-        fprintf(stderr,"cannot map %s (@%08x,%08x)\n", fn, base, size);
-    }
-    return ptr;    
-}
-
-
-int main(int argc, char** argv)
-{
-    void *grp_regs = map_memory("/dev/hw3d", 0, 1024 * 1024);
-    printf("GPU base mapped at %p\n", grp_regs);
-    int state_offset = 0x10140;
-    printf("GPU state = %08lx\n",
-            *((long*)((char*)grp_regs + state_offset))  );
-
-    return 0;
-}
diff --git a/opengl/tests/fillrate/Android.mk b/opengl/tests/fillrate/Android.mk
index 191c59b..835f858 100644
--- a/opengl/tests/fillrate/Android.mk
+++ b/opengl/tests/fillrate/Android.mk
@@ -11,6 +11,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-fillrate
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/fillrate/fillrate.cpp b/opengl/tests/fillrate/fillrate.cpp
index 911d354..a708647 100644
--- a/opengl/tests/fillrate/fillrate.cpp
+++ b/opengl/tests/fillrate/fillrate.cpp
@@ -26,7 +26,7 @@
 
 #include <utils/StopWatch.h>
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/filter/Android.mk b/opengl/tests/filter/Android.mk
index a254127..d780362 100644
--- a/opengl/tests/filter/Android.mk
+++ b/opengl/tests/filter/Android.mk
@@ -10,6 +10,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-filter
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/filter/filter.cpp b/opengl/tests/filter/filter.cpp
index 2351909..0067327 100644
--- a/opengl/tests/filter/filter.cpp
+++ b/opengl/tests/filter/filter.cpp
@@ -6,7 +6,7 @@
 #include <GLES/glext.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/finish/Android.mk b/opengl/tests/finish/Android.mk
index aa607c6..8f4f9c3 100644
--- a/opengl/tests/finish/Android.mk
+++ b/opengl/tests/finish/Android.mk
@@ -11,6 +11,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-finish
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/finish/finish.cpp b/opengl/tests/finish/finish.cpp
index 91f5c45..11f0c22 100644
--- a/opengl/tests/finish/finish.cpp
+++ b/opengl/tests/finish/finish.cpp
@@ -27,7 +27,7 @@
 #include <utils/Timers.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/gl2_basic/Android.mk b/opengl/tests/gl2_basic/Android.mk
index a642eaf..07469a0 100644
--- a/opengl/tests/gl2_basic/Android.mk
+++ b/opengl/tests/gl2_basic/Android.mk
@@ -10,6 +10,8 @@
     libGLESv2 \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-gl2_basic
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/gl2_basic/gl2_basic.cpp b/opengl/tests/gl2_basic/gl2_basic.cpp
index f274c7c..7007871 100644
--- a/opengl/tests/gl2_basic/gl2_basic.cpp
+++ b/opengl/tests/gl2_basic/gl2_basic.cpp
@@ -27,7 +27,7 @@
 #include <utils/Timers.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/gl2_copyTexImage/Android.mk b/opengl/tests/gl2_copyTexImage/Android.mk
index bef1f90..b616428 100644
--- a/opengl/tests/gl2_copyTexImage/Android.mk
+++ b/opengl/tests/gl2_copyTexImage/Android.mk
@@ -10,6 +10,8 @@
     libGLESv2 \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-gl2_copyTexImage
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp b/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
index c2bfdec..988d7ac 100644
--- a/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
+++ b/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
@@ -27,7 +27,7 @@
 #include <utils/Timers.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/gl2_yuvtex/Android.mk b/opengl/tests/gl2_yuvtex/Android.mk
index 6304700..e36f319 100644
--- a/opengl/tests/gl2_yuvtex/Android.mk
+++ b/opengl/tests/gl2_yuvtex/Android.mk
@@ -10,6 +10,8 @@
     libGLESv2 \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-gl2_yuvtex
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp b/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp
index f0b8d12..d3e4932 100644
--- a/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp
+++ b/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp
@@ -29,7 +29,7 @@
 
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/GraphicBuffer.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/gl_basic/Android.mk b/opengl/tests/gl_basic/Android.mk
index 6b6341f..2ba327b 100644
--- a/opengl/tests/gl_basic/Android.mk
+++ b/opengl/tests/gl_basic/Android.mk
@@ -10,6 +10,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-gl_basic
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/gl_basic/gl_basic.cpp b/opengl/tests/gl_basic/gl_basic.cpp
index 0cc8398..23ce934 100644
--- a/opengl/tests/gl_basic/gl_basic.cpp
+++ b/opengl/tests/gl_basic/gl_basic.cpp
@@ -6,7 +6,7 @@
 #include <GLES/glext.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 #include <stdio.h>
 
diff --git a/opengl/tests/gl_perf/Android.mk b/opengl/tests/gl_perf/Android.mk
index 37647ca..f32abd3 100644
--- a/opengl/tests/gl_perf/Android.mk
+++ b/opengl/tests/gl_perf/Android.mk
@@ -11,6 +11,8 @@
     libGLESv2 \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-gl2_perf
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/gl_perf/gl2_perf.cpp b/opengl/tests/gl_perf/gl2_perf.cpp
index 9dfcf1c..224acaf 100644
--- a/opengl/tests/gl_perf/gl2_perf.cpp
+++ b/opengl/tests/gl_perf/gl2_perf.cpp
@@ -27,7 +27,7 @@
 #include <utils/Timers.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/gl_yuvtex/Android.mk b/opengl/tests/gl_yuvtex/Android.mk
index a78db25..5b87f2e 100644
--- a/opengl/tests/gl_yuvtex/Android.mk
+++ b/opengl/tests/gl_yuvtex/Android.mk
@@ -10,6 +10,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-gl_yuvtex
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/gl_yuvtex/gl_yuvtex.cpp b/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
index fbe65f1..7a00f76 100644
--- a/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
+++ b/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
@@ -29,7 +29,7 @@
 
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/GraphicBuffer.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/hwc/hwcColorEquiv.cpp b/opengl/tests/hwc/hwcColorEquiv.cpp
index 1d03948..bb305dc 100644
--- a/opengl/tests/hwc/hwcColorEquiv.cpp
+++ b/opengl/tests/hwc/hwcColorEquiv.cpp
@@ -87,7 +87,6 @@
 
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/GraphicBuffer.h>
-#include <ui/EGLUtils.h>
 
 #define LOG_TAG "hwcColorEquivTest"
 #include <utils/Log.h>
diff --git a/opengl/tests/hwc/hwcCommit.cpp b/opengl/tests/hwc/hwcCommit.cpp
index 66ccdae..685dc5d 100644
--- a/opengl/tests/hwc/hwcCommit.cpp
+++ b/opengl/tests/hwc/hwcCommit.cpp
@@ -98,7 +98,6 @@
 
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/GraphicBuffer.h>
-#include <ui/EGLUtils.h>
 
 #define LOG_TAG "hwcCommitTest"
 #include <utils/Log.h>
diff --git a/opengl/tests/hwc/hwcRects.cpp b/opengl/tests/hwc/hwcRects.cpp
index 523e3de..80cde23 100644
--- a/opengl/tests/hwc/hwcRects.cpp
+++ b/opengl/tests/hwc/hwcRects.cpp
@@ -106,7 +106,6 @@
 
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/GraphicBuffer.h>
-#include <ui/EGLUtils.h>
 
 #define LOG_TAG "hwcRectsTest"
 #include <utils/Log.h>
diff --git a/opengl/tests/hwc/hwcStress.cpp b/opengl/tests/hwc/hwcStress.cpp
index 1cefb4b..7d7bc1f 100644
--- a/opengl/tests/hwc/hwcStress.cpp
+++ b/opengl/tests/hwc/hwcStress.cpp
@@ -103,7 +103,6 @@
 
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/GraphicBuffer.h>
-#include <ui/EGLUtils.h>
 
 #define LOG_TAG "hwcStressTest"
 #include <utils/Log.h>
diff --git a/opengl/tests/hwc/hwcTestLib.cpp b/opengl/tests/hwc/hwcTestLib.cpp
index 925405e..63f42ba 100644
--- a/opengl/tests/hwc/hwcTestLib.cpp
+++ b/opengl/tests/hwc/hwcTestLib.cpp
@@ -27,6 +27,8 @@
 
 #include <hwc/hwcTestLib.h>
 
+#include "EGLUtils.h"
+
 // Defines
 #define NUMA(a) (sizeof(a) / sizeof(a [0]))
 
diff --git a/opengl/tests/hwc/hwcTestLib.h b/opengl/tests/hwc/hwcTestLib.h
index 99ee608..b0c3012 100644
--- a/opengl/tests/hwc/hwcTestLib.h
+++ b/opengl/tests/hwc/hwcTestLib.h
@@ -29,7 +29,6 @@
 
 #include <ui/FramebufferNativeWindow.h>
 #include <ui/GraphicBuffer.h>
-#include <ui/EGLUtils.h>
 
 #include <utils/Log.h>
 #include <testUtil.h>
diff --git a/libs/ui/EGLUtils.cpp b/opengl/tests/include/EGLUtils.h
similarity index 82%
rename from libs/ui/EGLUtils.cpp
rename to opengl/tests/include/EGLUtils.h
index f24a71d..014c261 100644
--- a/libs/ui/EGLUtils.cpp
+++ b/opengl/tests/include/EGLUtils.h
@@ -15,21 +15,42 @@
  */
 
 
-#define LOG_TAG "EGLUtils"
+#ifndef ANDROID_UI_EGLUTILS_H
+#define ANDROID_UI_EGLUTILS_H
 
-#include <cutils/log.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <system/window.h>
 #include <utils/Errors.h>
-
-#include <ui/EGLUtils.h>
-
 #include <EGL/egl.h>
 
-#include <private/ui/android_natives_priv.h>
 
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
 
+class EGLUtils
+{
+public:
+
+    static inline const char *strerror(EGLint err);
+
+    static inline status_t selectConfigForPixelFormat(
+            EGLDisplay dpy,
+            EGLint const* attrs,
+            int32_t format,
+            EGLConfig* outConfig);
+
+    static inline status_t selectConfigForNativeWindow(
+            EGLDisplay dpy,
+            EGLint const* attrs,
+            EGLNativeWindowType window,
+            EGLConfig* outConfig);
+};
+
+// ----------------------------------------------------------------------------
+
 const char *EGLUtils::strerror(EGLint err)
 {
     switch (err){
@@ -55,7 +76,7 @@
 status_t EGLUtils::selectConfigForPixelFormat(
         EGLDisplay dpy,
         EGLint const* attrs,
-        PixelFormat format,
+        int32_t format,
         EGLConfig* outConfig)
 {
     EGLint numConfigs = -1, n=0;
@@ -65,7 +86,7 @@
 
     if (outConfig == NULL)
         return BAD_VALUE;
-    
+
     // Get all the "potential match" configs...
     if (eglGetConfigs(dpy, NULL, 0, &numConfigs) == EGL_FALSE)
         return BAD_VALUE;
@@ -75,7 +96,7 @@
         free(configs);
         return BAD_VALUE;
     }
-    
+
     int i;
     EGLConfig config = NULL;
     for (i=0 ; i<n ; i++) {
@@ -88,7 +109,7 @@
     }
 
     free(configs);
-    
+
     if (i<n) {
         *outConfig = config;
         return NO_ERROR;
@@ -105,10 +126,10 @@
 {
     int err;
     int format;
-    
+
     if (!window)
         return BAD_VALUE;
-    
+
     if ((err = window->query(window, NATIVE_WINDOW_FORMAT, &format)) < 0) {
         return err;
     }
@@ -119,3 +140,5 @@
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
+
+#endif /* ANDROID_UI_EGLUTILS_H */
diff --git a/opengl/tests/include/glTestLib.h b/opengl/tests/include/glTestLib.h
index 06fbf5d..c91c594 100644
--- a/opengl/tests/include/glTestLib.h
+++ b/opengl/tests/include/glTestLib.h
@@ -24,9 +24,7 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
-//#include <ui/FramebufferNativeWindow.h>
-//#include <ui/GraphicBuffer.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 void glTestPrintGLString(const char *name, GLenum s);
 void glTestCheckEglError(const char* op, EGLBoolean returnVal = EGL_TRUE);
diff --git a/opengl/tests/lib/glTestLib.cpp b/opengl/tests/lib/glTestLib.cpp
index 052cbd7..b434fc7 100644
--- a/opengl/tests/lib/glTestLib.cpp
+++ b/opengl/tests/lib/glTestLib.cpp
@@ -26,7 +26,7 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 #include <utils/Log.h>
 #include <testUtil.h>
diff --git a/opengl/tests/linetex/Android.mk b/opengl/tests/linetex/Android.mk
index 6ff248d..261940e 100644
--- a/opengl/tests/linetex/Android.mk
+++ b/opengl/tests/linetex/Android.mk
@@ -10,6 +10,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-linetex
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/linetex/linetex.cpp b/opengl/tests/linetex/linetex.cpp
index 6842940..8669492 100644
--- a/opengl/tests/linetex/linetex.cpp
+++ b/opengl/tests/linetex/linetex.cpp
@@ -27,7 +27,7 @@
 
 #include <utils/StopWatch.h>
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/swapinterval/Android.mk b/opengl/tests/swapinterval/Android.mk
index 9a4145e..d014cc9 100644
--- a/opengl/tests/swapinterval/Android.mk
+++ b/opengl/tests/swapinterval/Android.mk
@@ -11,6 +11,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-swapinterval
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/swapinterval/swapinterval.cpp b/opengl/tests/swapinterval/swapinterval.cpp
index 8ca031b..a0f4bc4 100644
--- a/opengl/tests/swapinterval/swapinterval.cpp
+++ b/opengl/tests/swapinterval/swapinterval.cpp
@@ -24,7 +24,7 @@
 
 #include <utils/StopWatch.h>
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/textures/Android.mk b/opengl/tests/textures/Android.mk
index b2fa185..fe9f43c 100644
--- a/opengl/tests/textures/Android.mk
+++ b/opengl/tests/textures/Android.mk
@@ -10,6 +10,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-textures
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/textures/textures.cpp b/opengl/tests/textures/textures.cpp
index cbe8ffd..5d3d94e 100644
--- a/opengl/tests/textures/textures.cpp
+++ b/opengl/tests/textures/textures.cpp
@@ -23,7 +23,7 @@
 #include <GLES/glext.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 using namespace android;
 
diff --git a/opengl/tests/tritex/Android.mk b/opengl/tests/tritex/Android.mk
index 6db3f49..fc544e4 100644
--- a/opengl/tests/tritex/Android.mk
+++ b/opengl/tests/tritex/Android.mk
@@ -10,6 +10,8 @@
     libGLESv1_CM \
     libui
 
+LOCAL_C_INCLUDES += frameworks/base/opengl/tests/include
+
 LOCAL_MODULE:= test-opengl-tritex
 
 LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/tritex/tritex.cpp b/opengl/tests/tritex/tritex.cpp
index 3365ab4..f183483 100644
--- a/opengl/tests/tritex/tritex.cpp
+++ b/opengl/tests/tritex/tritex.cpp
@@ -9,7 +9,7 @@
 #include <GLES/glext.h>
 
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
+#include "EGLUtils.h"
 
 #include <stdio.h>

 #include <stdlib.h>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 3beaac9..3a8e3fc 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -148,4 +148,7 @@
     <!-- 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 882aa66..330a189 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1602,6 +1602,9 @@
 
             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/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index cd8bd4e..14ce266 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -236,10 +236,6 @@
             public void onAnimationEnd(Animator animation) {
                 mCallback.onChildDismissed(view);
                 animView.setLayerType(View.LAYER_TYPE_NONE, null);
-                // Restore the alpha/translation parameters to what they were before swiping
-                // (for when these items are recycled)
-                animView.setAlpha(1f);
-                setTranslation(animView, 0f);
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 4718a17..4dc3e33 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -181,6 +181,11 @@
         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) {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 0605c4c..19fce37 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -187,6 +187,11 @@
         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) {
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 b3cef90..2458495 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -102,6 +102,8 @@
     public static final String ACTION_STATUSBAR_START
             = "com.android.internal.policy.statusbar.START";
 
+    private static final boolean ENABLE_INTRUDERS = false;
+
     static final int EXPANDED_LEAVE_ALONE = -10000;
     static final int EXPANDED_FULL_OPEN = -10001;
 
@@ -232,6 +234,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);
@@ -263,7 +270,7 @@
 
         addNavigationBar();
 
-        //addIntruderView();
+        if (ENABLE_INTRUDERS) addIntruderView();
 
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy = new PhoneStatusBarPolicy(mContext);
@@ -540,6 +547,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;
 
@@ -551,31 +559,31 @@
             }
         } 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 (ENABLE_INTRUDERS && (
+                   (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");
@@ -632,8 +640,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,
@@ -725,69 +733,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);
@@ -850,7 +795,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);
             }
         }
     }
@@ -1807,7 +1752,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 + "\"");
                 }
@@ -2317,6 +2262,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/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 5151cad..6e56cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -859,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);
@@ -1688,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/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index b87b8c3..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;
@@ -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 c12a4b7e..8f35afb 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2285,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;
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 157405a..22fa752 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -7,6 +7,7 @@
     AudioMixer.cpp.arm          \
     AudioResampler.cpp.arm      \
     AudioPolicyService.cpp      \
+    AudioBufferProvider.cpp     \
     ServiceUtilities.cpp
 #   AudioResamplerSinc.cpp.arm
 #   AudioResamplerCubic.cpp.arm
@@ -17,6 +18,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libaudioutils \
+    libcommon_time_client \
     libcutils \
     libutils \
     libbinder \
diff --git a/include/ui/android_native_buffer.h b/services/audioflinger/AudioBufferProvider.cpp
similarity index 65%
copy from include/ui/android_native_buffer.h
copy to services/audioflinger/AudioBufferProvider.cpp
index b6e1db4..678fd58 100644
--- a/include/ui/android_native_buffer.h
+++ b/services/audioflinger/AudioBufferProvider.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,9 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_ANDROID_NATIVES_PRIV_H
-#define ANDROID_ANDROID_NATIVES_PRIV_H
+#undef __STRICT_ANSI__
+#define __STDINT_LIMITS
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
 
-#include <ui/egl/android_natives.h>
+#include "AudioBufferProvider.h"
 
-#endif /* ANDROID_ANDROID_NATIVES_PRIV_H */
+namespace android {
+
+const int64_t AudioBufferProvider::kInvalidPTS = INT64_MAX;
+
+}; // 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 0248687..d522091 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -61,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>
+
 // ----------------------------------------------------------------------------
 
 
@@ -69,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;
 
@@ -99,6 +101,7 @@
 // maximum divider applied to the active sleep time in the mixer thread loop
 static const uint32_t kMaxThreadSleepTimeShift = 2;
 
+nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
 
 // ----------------------------------------------------------------------------
 
@@ -147,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)
 {
 }
 
@@ -162,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;
@@ -193,6 +211,32 @@
 
     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];
 
@@ -203,11 +247,22 @@
             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;
         }
     }
+
+    mMasterVolumeSW = (MVS_NONE == mMasterVolumeSupportLvl)
+                    ? initialVolume
+                    : 1.0;
+    mMasterVolume   = initialVolume;
+    mHardwareStatus = AUDIO_HW_IDLE;
 }
 
 AudioFlinger::~AudioFlinger()
@@ -273,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;
@@ -374,9 +432,11 @@
         audio_format_t format,
         uint32_t channelMask,
         int frameCount,
+        // FIXME dead, remove from IAudioFlinger
         uint32_t flags,
         const sp<IMemory>& sharedBuffer,
         audio_io_handle_t output,
+        bool isTimed,
         int *sessionId,
         status_t *status)
 {
@@ -435,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
@@ -528,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;
+    mMasterVolume   = value;
+    mMasterVolumeSW = swmv;
     for (size_t i = 0; i < mPlaybackThreads.size(); i++)
-       mPlaybackThreads.valueAt(i)->setMasterVolume(value);
+       mPlaybackThreads.valueAt(i)->setMasterVolume(swmv);
 
     return NO_ERROR;
 }
@@ -635,12 +704,36 @@
     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();
 }
 
+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)
 {
@@ -814,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;
@@ -1367,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);
@@ -1485,6 +1578,7 @@
         int frameCount,
         const sp<IMemory>& sharedBuffer,
         int sessionId,
+        bool isTimed,
         status_t *status)
 {
     sp<Track> track;
@@ -1535,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;
         }
@@ -1834,6 +1933,63 @@
     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
+};
+
+void AudioFlinger::PlaybackThread::checkSilentMode_l()
+{
+    if (!mMasterMute) {
+        char value[PROPERTY_VALUE_MAX];
+        if (property_get("ro.audio.silent", value, "0") > 0) {
+            char *endptr;
+            unsigned long ul = strtoul(value, &endptr, 0);
+            if (*endptr == '\0' && ul != 0) {
+                ALOGD("Silence is golden");
+                // The setprop command will not allow a property to be changed after
+                // the first time it is set, so we don't have to worry about un-muting.
+                setMasterMute_l(true);
+            }
+        }
+    }
+}
+
 bool AudioFlinger::MixerThread::threadLoop()
 {
     Vector< sp<Track> > tracksToRemove;
@@ -1852,42 +2008,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;
@@ -1932,16 +2059,9 @@
                     acquireWakeLock_l();
 
                     mPrevMixerStatus = MIXER_IDLE;
-                    if (!mMasterMute) {
-                        char value[PROPERTY_VALUE_MAX];
-                        property_get("ro.audio.silent", value, "0");
-                        if (atoi(value)) {
-                            ALOGD("Silence is golden");
-                            setMasterMute_l(true);
-                        }
-                    }
+                    checkSilentMode_l();
 
-                    standbyTime = systemTime() + kStandbyTimeInNsecs;
+                    standbyTime = systemTime() + mStandbyTimeInNsecs;
                     sleepTime = idleSleepTime;
                     sleepTimeShift = 0;
                     continue;
@@ -1957,8 +2077,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
@@ -1967,7 +2100,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
@@ -2114,7 +2247,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);
@@ -2184,7 +2317,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;
                 }
@@ -2205,25 +2338,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,
@@ -2577,7 +2706,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();
@@ -2633,14 +2761,7 @@
                     ALOGV("DirectOutputThread %p TID %d waking up in active mode", this, gettid());
                     acquireWakeLock_l();
 
-                    if (!mMasterMute) {
-                        char value[PROPERTY_VALUE_MAX];
-                        property_get("ro.audio.silent", value, "0");
-                        if (atoi(value)) {
-                            ALOGD("Silence is golden");
-                            setMasterMute_l(true);
-                        }
-                    }
+                    checkSilentMode_l();
 
                     standbyTime = systemTime() + standbyDelay;
                     sleepTime = idleSleepTime;
@@ -2781,11 +2902,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;
@@ -3028,17 +3150,9 @@
                     ALOGV("DuplicatingThread %p TID %d waking up", this, gettid());
                     acquireWakeLock_l();
 
-                    mPrevMixerStatus = MIXER_IDLE;
-                    if (!mMasterMute) {
-                        char value[PROPERTY_VALUE_MAX];
-                        property_get("ro.audio.silent", value, "0");
-                        if (atoi(value)) {
-                            ALOGD("Silence is golden");
-                            setMasterMute_l(true);
-                        }
-                    }
+                    checkSilentMode_l();
 
-                    standbyTime = systemTime() + kStandbyTimeInNsecs;
+                    standbyTime = systemTime() + mStandbyTimeInNsecs;
                     sleepTime = idleSleepTime;
                     continue;
                 }
@@ -3055,7 +3169,7 @@
         if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
             // mix buffers...
             if (outputsReady(outputTracks)) {
-                mAudioMixer->process();
+                mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
             } else {
                 memset(mMixBuffer, 0, mixBufferSize);
             }
@@ -3092,7 +3206,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);
             }
@@ -3124,7 +3238,7 @@
 {
     // 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,
@@ -3142,7 +3256,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();
@@ -3193,13 +3307,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(),
@@ -3211,7 +3324,7 @@
         mFrameCount(0),
         mState(IDLE),
         mFormat(format),
-        mFlags(flags & ~SYSTEM_FLAGS_MASK),
+        mStepServerFailed(false),
         mSessionId(sessionId)
         // mChannelCount
         // mChannelMask
@@ -3306,7 +3419,7 @@
     result = cblk->stepServer(mFrameCount);
     if (!result) {
         ALOGV("stepServer failed acquiring cblk mutex");
-        mFlags |= STEPSERVER_FAILED;
+        mStepServerFailed = true;
     }
     return result;
 }
@@ -3318,7 +3431,7 @@
     cblk->server = 0;
     cblk->userBase = 0;
     cblk->serverBase = 0;
-    mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK);
+    mStepServerFailed = false;
     ALOGV("TrackBase::reset");
 }
 
@@ -3349,7 +3462,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,
@@ -3358,16 +3471,14 @@
             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 pid %d", mName, IPCThreadState::self()->getCallingPid());
         if (mName < 0) {
@@ -3443,17 +3554,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();
@@ -3484,10 +3596,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);
@@ -3644,20 +3760,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) {
@@ -3680,17 +4186,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();
@@ -3761,17 +4267,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);
@@ -3985,10 +4490,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;
     }
@@ -4002,7 +4506,8 @@
         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
 }
@@ -4018,6 +4523,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,
@@ -4034,9 +4564,7 @@
 void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
 {
     sp<NotificationClient> keep(this);
-    {
-        mAudioFlinger->removeNotificationClient(mPid);
-    }
+    mAudioFlinger->removeNotificationClient(mPid);
 }
 
 // ----------------------------------------------------------------------------
@@ -4084,6 +4612,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)
 {
@@ -4099,6 +4659,7 @@
         audio_format_t format,
         uint32_t channelMask,
         int frameCount,
+        // FIXME dead, remove from IAudioFlinger
         uint32_t flags,
         int *sessionId,
         status_t *status)
@@ -4143,7 +4704,6 @@
                                                 format,
                                                 channelMask,
                                                 frameCount,
-                                                flags,
                                                 lSessionId,
                                                 &lStatus);
     }
@@ -4313,7 +4873,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
@@ -4436,7 +4997,6 @@
         audio_format_t format,
         int channelMask,
         int frameCount,
-        uint32_t flags,
         int sessionId,
         status_t *status)
 {
@@ -4453,7 +5013,7 @@
         Mutex::Autolock _l(mLock);
 
         track = new RecordTrack(this, client, sampleRate,
-                      format, channelMask, frameCount, flags, sessionId);
+                      format, channelMask, frameCount, sessionId);
 
         if (track->getCblk() == 0) {
             lStatus = NO_MEMORY;
@@ -4591,7 +5151,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;
@@ -4979,8 +5539,7 @@
                 }
             }
         }
-        void *param2 = NULL;
-        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
+        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
         mPlaybackThreads.removeItem(output);
     }
     thread->exit();
@@ -5124,8 +5683,7 @@
         }
 
         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();
@@ -5157,8 +5715,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);
@@ -5281,33 +5838,20 @@
 // checkPlaybackThread_l() must be called with AudioFlinger::mLock held
 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(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(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()
@@ -5670,10 +6214,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;
@@ -6046,18 +6587,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;
     }
 
@@ -7027,15 +7567,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) /
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index aa0b8f8..2a5d805 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -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>,
@@ -78,6 +81,7 @@
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
                                 audio_io_handle_t output,
+                                bool isTimed,
                                 int *sessionId,
                                 status_t *status);
 
@@ -102,6 +106,7 @@
     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,
@@ -206,6 +211,8 @@
     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);
@@ -220,12 +227,18 @@
         pid_t               pid() const { return mPid; }
         sp<AudioFlinger>    audioFlinger() const { return mAudioFlinger; }
 
+        bool reserveTimedTrack();
+        void releaseTimedTrack();
+
     private:
                             Client(const Client&);
                             Client& operator = (const Client&);
         const sp<AudioFlinger> mAudioFlinger;
         const sp<MemoryDealer> mMemoryDealer;
         const pid_t         mPid;
+
+        Mutex               mTimedTrackLock;
+        int                 mTimedTrackCount;
     };
 
     // --- Notification Client ---
@@ -299,19 +312,12 @@
                 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);
             virtual             ~TrackBase();
@@ -333,7 +339,9 @@
                                 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 {
@@ -369,7 +377,7 @@
             // we don't really need a lock for these
             track_state         mState;
             const audio_format_t mFormat;
-            uint32_t            mFlags;
+            bool                mStepServerFailed;
             const int           mSessionId;
             uint8_t             mChannelCount;
             uint32_t            mChannelMask;
@@ -576,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,
@@ -609,7 +617,6 @@
                     int16_t     *mainBuffer() const { return mMainBuffer; }
                     int         auxEffectId() const { return mAuxEffectId; }
 
-
         protected:
             friend class ThreadBase;
             friend class TrackHandle;
@@ -620,7 +627,11 @@
                                 Track(const Track&);
                                 Track& operator = (const Track&);
 
-            virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
+            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;
@@ -636,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
@@ -652,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 {
@@ -662,7 +748,7 @@
                 int16_t *mBuffer;
             };
 
-                                OutputTrack(  const wp<ThreadBase>& thread,
+                                OutputTrack(PlaybackThread *thread,
                                         DuplicatingThread *sourceThread,
                                         uint32_t sampleRate,
                                         audio_format_t format,
@@ -673,7 +759,7 @@
             virtual status_t    start(pid_t tid);
             virtual void        stop();
                     bool        write(int16_t* data, uint32_t frames);
-                    bool        bufferQueueEmpty() const { return (mBufferQueue.size() == 0) ? true : false; }
+                    bool        bufferQueueEmpty() const { return mBufferQueue.size() == 0; }
                     bool        isActive() const { return mActive; }
             const wp<ThreadBase>& thread() const { return mThread; }
 
@@ -726,6 +812,7 @@
                                     int frameCount,
                                     const sp<IMemory>& sharedBuffer,
                                     int sessionId,
+                                    bool isTimed,
                                     status_t *status);
 
                     AudioStreamOut* getOutput() const;
@@ -772,6 +859,9 @@
         virtual uint32_t        idleSleepTimeUs() = 0;
         virtual uint32_t        suspendSleepTimeUs() = 0;
 
+        // Code snippets that are temporarily lifted up out of threadLoop() until the merge
+                    void        checkSilentMode_l();
+
     private:
 
         friend class AudioFlinger;
@@ -900,8 +990,8 @@
               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();
@@ -920,6 +1010,12 @@
         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:
@@ -942,13 +1038,12 @@
         // 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);
             virtual             ~RecordTrack();
 
@@ -967,7 +1062,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;
         };
@@ -992,7 +1089,6 @@
                         audio_format_t format,
                         int channelMask,
                         int frameCount,
-                        uint32_t flags,
                         int sessionId,
                         status_t *status);
 
@@ -1004,7 +1100,8 @@
                 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);
@@ -1065,7 +1162,7 @@
     // 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,
@@ -1250,6 +1347,7 @@
     class EffectChain: public RefBase {
     public:
         EffectChain(const wp<ThreadBase>& wThread, int sessionId);
+        EffectChain(ThreadBase *thread, int sessionId);
         virtual ~EffectChain();
 
         // special key used for an entry in mSuspendedEffects keyed vector
@@ -1401,6 +1499,28 @@
     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;   // see ~Client()
@@ -1429,6 +1549,7 @@
         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
@@ -1439,6 +1560,8 @@
 
                 // both are protected by mLock
                 float                               mMasterVolume;
+                float                               mMasterVolumeSW;
+                master_volume_support               mMasterVolumeSupportLvl;
                 bool                                mMasterMute;
 
                 DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> >    mRecordThreads;
@@ -1451,7 +1574,8 @@
                 // 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:
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index cb7678b..020d62a 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -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;
@@ -80,6 +85,7 @@
         t->sampleRate = mSampleRate;
         t->mainBuffer = NULL;
         t->auxBuffer = NULL;
+        t->localTimeFreq = lc.getLocalFreq();
         t++;
     }
 }
@@ -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;
         }
@@ -333,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");
@@ -443,7 +451,7 @@
         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
@@ -549,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)
@@ -558,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);
@@ -757,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;
@@ -787,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);
@@ -797,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)));
 
@@ -809,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
@@ -853,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)) {
@@ -863,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);
@@ -892,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;
@@ -932,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.
@@ -948,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);
                 }
@@ -959,7 +973,8 @@
 }
 
 // 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
@@ -979,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
@@ -1023,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;
@@ -1057,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];
@@ -1071,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];
@@ -1117,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 c956918..b210212 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -79,7 +79,7 @@
     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; }
 
@@ -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,30 +127,46 @@
 
         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 { return resampler != NULL; }
         void        resetResampler() { if (resampler != NULL) resampler->reset(); }
@@ -165,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];
@@ -187,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 041b5a8..987b039 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -116,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();
 
@@ -616,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);
     }
 }
 
@@ -1243,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);
@@ -1294,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)
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index fdaf576..679fd30 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -233,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;
@@ -243,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 9486b9c..398ba0b 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -122,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,
@@ -150,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;
@@ -196,7 +214,8 @@
         // 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;
             }
@@ -290,7 +309,8 @@
         // 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;
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index c23016e..9deb796 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -49,6 +49,10 @@
     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;
@@ -72,6 +76,8 @@
     AudioResampler(const AudioResampler&);
     AudioResampler& operator=(const AudioResampler&);
 
+    int64_t calculateOutputPTS(int outputFrameIndex);
+
     const int32_t mBitDepth;
     const int32_t mChannelCount;
     const int32_t mSampleRate;
@@ -85,6 +91,8 @@
     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 c0e760e..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,7 +95,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
                 in = mBuffer.i16;
@@ -130,7 +131,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=%d", mBuffer.raw, mBuffer.frameCount);
@@ -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 7a27b81..d373c08 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -203,7 +203,8 @@
         // 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 resample_exit;
             }
@@ -354,4 +355,3 @@
 
 // ----------------------------------------------------------------------------
 }; // namespace android
-
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
index 2ac69f7..78e225f 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -22,7 +22,6 @@
 #include <binder/MemoryHeapBase.h>
 #include <utils/RefBase.h>
 #include <surfaceflinger/ISurface.h>
-#include <ui/android_native_buffer.h>
 #include <ui/GraphicBuffer.h>
 #include <camera/Camera.h>
 #include <camera/CameraParameters.h>
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 e6e28df..149c0d3 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -45,7 +45,7 @@
 #include "InputDispatcher.h"
 
 #include <cutils/log.h>
-#include <ui/PowerManager.h>
+#include <androidfw/PowerManager.h>
 
 #include <stddef.h>
 #include <unistd.h>
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 5c517f51..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>
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 df4d299..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 41ede2e..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();
@@ -1066,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) {
@@ -1074,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);
         }
     }
 
@@ -1477,12 +1475,11 @@
     }
 
     AtomicFile savedStateFile() {
-        int userId = UserId.getCallingUserId();
-        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/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 86669f8..c705646 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -786,7 +786,7 @@
         return flags;
     }
 
-    InputBindResult attachNewInputLocked(boolean initial, boolean needResult) {
+    InputBindResult attachNewInputLocked(boolean initial) {
         if (!mBoundToMethod) {
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                     MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
@@ -804,14 +804,11 @@
             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
             showCurrentInputLocked(getAppShowFlags(), null);
         }
-        return needResult
-                ? new InputBindResult(session.session, mCurId, mCurSeq)
-                : null;
+        return new InputBindResult(session.session, mCurId, mCurSeq);
     }
 
     InputBindResult startInputLocked(IInputMethodClient client,
-            IInputContext inputContext, EditorInfo attribute,
-            boolean initial, boolean needResult) {
+            IInputContext inputContext, EditorInfo attribute, int controlFlags) {
         // If no method is currently selected, do nothing.
         if (mCurMethodId == null) {
             return mNoBinding;
@@ -837,6 +834,16 @@
         } catch (RemoteException e) {
         }
 
+        return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags);
+    }
+
+    InputBindResult startInputUncheckedLocked(ClientState cs,
+            IInputContext inputContext, EditorInfo attribute, int controlFlags) {
+        // If no method is currently selected, do nothing.
+        if (mCurMethodId == null) {
+            return mNoBinding;
+        }
+
         if (mCurClient != cs) {
             // If the client is changing, we need to switch over to the new
             // one.
@@ -867,7 +874,8 @@
             if (cs.curSession != null) {
                 // Fast case: if we are already connected to the input method,
                 // then just return it.
-                return attachNewInputLocked(initial, needResult);
+                return attachNewInputLocked(
+                        (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);
             }
             if (mHaveConnection) {
                 if (mCurMethod != null) {
@@ -948,13 +956,11 @@
 
     @Override
     public InputBindResult startInput(IInputMethodClient client,
-            IInputContext inputContext, EditorInfo attribute,
-            boolean initial, boolean needResult) {
+            IInputContext inputContext, EditorInfo attribute, int controlFlags) {
         synchronized (mMethodMap) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return startInputLocked(client, inputContext, attribute,
-                        initial, needResult);
+                return startInputLocked(client, inputContext, attribute, controlFlags);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -997,7 +1003,7 @@
                     mCurClient.curSession = new SessionState(mCurClient,
                             method, session);
                     mCurClient.sessionRequested = false;
-                    InputBindResult res = attachNewInputLocked(true, true);
+                    InputBindResult res = attachNewInputLocked(true);
                     if (res.method != null) {
                         executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
                                 MSG_BIND_METHOD, mCurClient.client, res));
@@ -1482,36 +1488,45 @@
     }
 
     @Override
-    public void windowGainedFocus(IInputMethodClient client, IBinder windowToken,
-            boolean viewHasFocus, boolean isTextEditor, int softInputMode,
-            boolean first, int windowFlags) {
+    public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken,
+            int controlFlags, int softInputMode, int windowFlags,
+            EditorInfo attribute, IInputContext inputContext) {
+        InputBindResult res = null;
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mMethodMap) {
                 if (DEBUG) Slog.v(TAG, "windowGainedFocus: " + client.asBinder()
-                        + " viewHasFocus=" + viewHasFocus
-                        + " isTextEditor=" + isTextEditor
+                        + " controlFlags=#" + Integer.toHexString(controlFlags)
                         + " softInputMode=#" + Integer.toHexString(softInputMode)
-                        + " first=" + first + " flags=#"
-                        + Integer.toHexString(windowFlags));
+                        + " windowFlags=#" + Integer.toHexString(windowFlags));
 
-                if (mCurClient == null || client == null
-                        || mCurClient.client.asBinder() != client.asBinder()) {
-                    try {
-                        // We need to check if this is the current client with
-                        // focus in the window manager, to allow this call to
-                        // be made before input is started in it.
-                        if (!mIWindowManager.inputMethodClientHasFocus(client)) {
-                            Slog.w(TAG, "Client not active, ignoring focus gain of: " + client);
-                            return;
-                        }
-                    } catch (RemoteException e) {
+                ClientState cs = mClients.get(client.asBinder());
+                if (cs == null) {
+                    throw new IllegalArgumentException("unknown client "
+                            + client.asBinder());
+                }
+
+                try {
+                    if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
+                        // Check with the window manager to make sure this client actually
+                        // has a window with focus.  If not, reject.  This is thread safe
+                        // because if the focus changes some time before or after, the
+                        // next client receiving focus that has any interest in input will
+                        // be calling through here after that change happens.
+                        Slog.w(TAG, "Focus gain on non-focused client " + cs.client
+                                + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
+                        return null;
                     }
+                } catch (RemoteException e) {
                 }
 
                 if (mCurFocusedWindow == windowToken) {
                     Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client);
-                    return;
+                    if (attribute != null) {
+                        return startInputUncheckedLocked(cs, inputContext, attribute,
+                                controlFlags);
+                    }
+                    return null;
                 }
                 mCurFocusedWindow = windowToken;
 
@@ -1527,6 +1542,14 @@
                                 == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                         || mRes.getConfiguration().isLayoutSizeAtLeast(
                                 Configuration.SCREENLAYOUT_SIZE_LARGE);
+                final boolean isTextEditor =
+                        (controlFlags&InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0;
+
+                // We want to start input before showing the IME, but after closing
+                // it.  We want to do this after closing it to help the IME disappear
+                // more quickly (not get stuck behind it initializing itself for the
+                // new focused input, even if its window wants to hide the IME).
+                boolean didStart = false;
                         
                 switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
@@ -1542,12 +1565,17 @@
                                 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
                             // There is a focus view, and we are navigating forward
                             // into the window, so show the input window for the user.
-                            // We only do this automatically if the window an resize
-                            // to accomodate the IME (so what the user sees will give
+                            // We only do this automatically if the window can resize
+                            // to accommodate the IME (so what the user sees will give
                             // them good context without input information being obscured
                             // by the IME) or if running on a large screen where there
                             // is more room for the target window + IME.
                             if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
+                            if (attribute != null) {
+                                res = startInputUncheckedLocked(cs, inputContext, attribute,
+                                        controlFlags);
+                                didStart = true;
+                            }
                             showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
                         }
                         break;
@@ -1569,18 +1597,35 @@
                         if ((softInputMode &
                                 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
                             if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
+                            if (attribute != null) {
+                                res = startInputUncheckedLocked(cs, inputContext, attribute,
+                                        controlFlags);
+                                didStart = true;
+                            }
                             showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
                         }
                         break;
                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
                         if (DEBUG) Slog.v(TAG, "Window asks to always show input");
+                        if (attribute != null) {
+                            res = startInputUncheckedLocked(cs, inputContext, attribute,
+                                    controlFlags);
+                            didStart = true;
+                        }
                         showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
                         break;
                 }
+
+                if (!didStart && attribute != null) {
+                    res = startInputUncheckedLocked(cs, inputContext, attribute,
+                            controlFlags);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
+
+        return res;
     }
 
     @Override
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index b3eceb1..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();
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index e953355..7b4372f 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -583,6 +583,7 @@
         }
         
         nativeInit();
+        Power.powerInitNative();
         synchronized (mLocks) {
             updateNativePowerStateLocked();
             // We make sure to start out with the screen on due to user activity.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0dbc7c3..c9b5997 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -132,6 +132,7 @@
         RecognitionManagerService recognition = null;
         ThrottleService throttle = null;
         NetworkTimeUpdateService networkTimeUpdater = null;
+        CommonTimeManagementService commonTimeMgmtService = null;
 
         // Critical services...
         try {
@@ -575,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
@@ -653,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;
 
@@ -752,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/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 586a67e..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;
@@ -241,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();
                     }
                     
@@ -301,9 +287,10 @@
                 @Override
                 public void onChange(boolean selfChange) {
                     super.onChange(selfChange);
-
                     synchronized (mLock) {
                         handleAccessibilityEnabledSettingChangedLocked();
+                        updateInputFilterLocked();
+                        sendStateToClientsLocked();
                     }
                 }
             });
@@ -315,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();
                         }
@@ -333,7 +317,6 @@
                 @Override
                 public void onChange(boolean selfChange) {
                     super.onChange(selfChange);
-
                     synchronized (mLock) {
                         manageServicesLocked();
                     }
@@ -475,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) {
@@ -905,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 {
@@ -1139,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;
@@ -1160,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()");
@@ -1229,13 +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();
                 }
             }
         }
@@ -1256,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 9d5caae..8a5e7fc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -138,7 +138,6 @@
 import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -2752,7 +2751,13 @@
         }
 
         // Just in case...
-        mMainStack.appDiedLocked(app);
+        if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) {
+            if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity);
+            mMainStack.mPausingActivity = null;
+        }
+        if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) {
+            mMainStack.mLastPausedActivity = null;
+        }
 
         // Remove this application's activities from active lists.
         mMainStack.removeHistoryRecordsForAppLocked(app);
@@ -5754,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)
@@ -5826,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,
@@ -5842,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,
@@ -5863,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;
 
@@ -5913,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,
@@ -5947,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
@@ -6005,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.
                     }
@@ -6075,8 +6082,9 @@
                 if (firstClass) {
                     mProviderMap.putProviderByClass(comp, cpr);
                 }
+
                 mProviderMap.putProviderByName(name, cpr);
-                incProviderCount(r, cpr);
+                incProviderCount(r, cpr, token);
             }
         }
 
@@ -6116,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);
     }
 
     /**
@@ -6157,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());
@@ -6178,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();
         }
     }
     
@@ -6286,7 +6312,7 @@
         ContentProviderHolder holder = null;
 
         try {
-            holder = getContentProviderExternal(name);
+            holder = getContentProviderExternalUnchecked(name, null);
             if (holder != null) {
                 return holder.provider.getType(uri);
             }
@@ -6295,7 +6321,7 @@
             return null;
         } finally {
             if (holder != null) {
-                removeContentProviderExternal(name);
+                removeContentProviderExternalUnchecked(name, null);
             }
             Binder.restoreCallingIdentity(ident);
         }
@@ -6400,7 +6426,7 @@
     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
@@ -6422,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 + "'");
         }
@@ -6467,7 +6493,7 @@
                 mMainStack.stopIfSleepingLocked();
                 final long endTime = System.currentTimeMillis() + timeout;
                 while (mMainStack.mResumedActivity != null
-                        || mMainStack.mPausingActivities.size() > 0) {
+                        || mMainStack.mPausingActivity != null) {
                     long delay = endTime - System.currentTimeMillis();
                     if (delay <= 0) {
                         Slog.w(TAG, "Activity manager shutdown timed out");
@@ -8274,13 +8300,8 @@
         }
 
         pw.println(" ");
-        if (mMainStack.mPausingActivities.size() > 0) {
-            pw.println("  mPausingActivities: " + Arrays.toString(
-                    mMainStack.mPausingActivities.toArray()));
-        }
-        if (mMainStack.mInputPausedActivities.size() > 0) {
-            pw.println("  mInputPausedActivities: " + Arrays.toString(
-                    mMainStack.mInputPausedActivities.toArray()));
+        if (mMainStack.mPausingActivity != null) {
+            pw.println("  mPausingActivity: " + mMainStack.mPausingActivity);
         }
         pw.println("  mResumedActivity: " + mMainStack.mResumedActivity);
         pw.println("  mFocusedActivity: " + mFocusedActivity);
@@ -10253,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();
@@ -13455,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;
@@ -13855,13 +13876,7 @@
     private final ActivityRecord resumedAppLocked() {
         ActivityRecord resumedActivity = mMainStack.mResumedActivity;
         if (resumedActivity == null || resumedActivity.app == null) {
-            for (int i=mMainStack.mPausingActivities.size()-1; i>=0; i--) {
-                ActivityRecord r = mMainStack.mPausingActivities.get(i);
-                if (r.app != null) {
-                    resumedActivity = r;
-                    break;
-                }
-            }
+            resumedActivity = mMainStack.mPausingActivity;
             if (resumedActivity == null || resumedActivity.app == null) {
                 resumedActivity = mMainStack.topRunningActivityLocked(null);
             }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 977ee84..cdab6c6 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -602,7 +602,6 @@
     
     public void windowsDrawn() {
         synchronized(service) {
-            stack.reportActivityDrawnLocked(this);
             if (launchTime != 0) {
                 final long curTime = SystemClock.uptimeMillis();
                 final long thisTime = curTime - launchTime;
@@ -691,9 +690,7 @@
             // Hmmm, who might we be waiting for?
             r = stack.mResumedActivity;
             if (r == null) {
-                if (stack.mPausingActivities.size() > 0) {
-                    r = stack.mPausingActivities.get(stack.mPausingActivities.size()-1);
-                }
+                r = stack.mPausingActivity;
             }
             // Both of those null?  Fall back to 'this' again
             if (r == null) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index e8c8275..7b8bc26 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -227,13 +227,7 @@
      * When we are in the process of pausing an activity, before starting the
      * next one, this variable holds the activity that is currently being paused.
      */
-    final ArrayList<ActivityRecord> mPausingActivities = new ArrayList<ActivityRecord>();
-
-    /**
-     * These activities currently have their input paused, as they want for
-     * the next top activity to have its windows visible.
-     */
-    final ArrayList<ActivityRecord> mInputPausedActivities = new ArrayList<ActivityRecord>();
+    ActivityRecord mPausingActivity = null;
 
     /**
      * This is the last activity that we put into the paused state.  This is
@@ -813,9 +807,9 @@
                 startPausingLocked(false, true);
                 return;
             }
-            if (mPausingActivities.size() > 0) {
+            if (mPausingActivity != null) {
                 // Still waiting for something to pause; can't sleep yet.
-                if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivities);
+                if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity);
                 return;
             }
 
@@ -878,6 +872,11 @@
     }
 
     private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+        if (mPausingActivity != null) {
+            RuntimeException e = new RuntimeException();
+            Slog.e(TAG, "Trying to pause when pause is already pending for "
+                  + mPausingActivity, e);
+        }
         ActivityRecord prev = mResumedActivity;
         if (prev == null) {
             RuntimeException e = new RuntimeException();
@@ -885,25 +884,19 @@
             resumeTopActivityLocked(null);
             return;
         }
-        if (mPausingActivities.contains(prev)) {
-            RuntimeException e = new RuntimeException();
-            Slog.e(TAG, "Trying to pause when pause when already pausing " + prev, e);
-        }
         if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
         else if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
         mResumedActivity = null;
-        mPausingActivities.add(prev);
+        mPausingActivity = prev;
         mLastPausedActivity = prev;
         prev.state = ActivityState.PAUSING;
         prev.task.touchActiveTime();
         prev.updateThumbnail(screenshotActivities(prev), null);
 
         mService.updateCpuStats();
-        ActivityRecord pausing;
         
         if (prev.app != null && prev.app.thread != null) {
             if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
-            pausing = prev;
             try {
                 EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                         System.identityHashCode(prev),
@@ -916,14 +909,12 @@
             } catch (Exception e) {
                 // Ignore exception, if process died other code will cleanup.
                 Slog.w(TAG, "Exception thrown during pause", e);
-                mPausingActivities.remove(prev);
+                mPausingActivity = null;
                 mLastPausedActivity = null;
-                pausing = null;
             }
         } else {
-            mPausingActivities.remove(prev);
+            mPausingActivity = null;
             mLastPausedActivity = null;
-            pausing = null;
         }
 
         // If we are not going to sleep, we want to ensure the device is
@@ -937,28 +928,18 @@
             }
         }
 
-        if (pausing != null) {
+
+        if (mPausingActivity != null) {
             // Have the window manager pause its key dispatching until the new
             // activity has started.  If we're pausing the activity just because
             // the screen is being turned off and the UI is sleeping, don't interrupt
             // key dispatch; the same activity will pick it up again on wakeup.
             if (!uiSleeping) {
-                pausing.pauseKeyDispatchingLocked();
-                mInputPausedActivities.add(prev);
+                prev.pauseKeyDispatchingLocked();
             } else {
                 if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
             }
 
-            if (pausing.configDestroy) {
-                // The previous is being paused because the configuration
-                // is changing, which means it is actually stopping...
-                // To juggle the fact that we are also starting a new
-                // instance right now, we need to first completely stop
-                // the current instance before starting the new one.
-                if (DEBUG_PAUSE) Slog.v(TAG, "Destroying at pause: " + prev);
-                destroyActivityLocked(pausing, true, false, "pause-config");
-            }
-
             // Schedule a pause timeout in case the app doesn't respond.
             // We don't give it much time because this directly impacts the
             // responsiveness seen by the user.
@@ -970,10 +951,7 @@
             // This activity failed to schedule the
             // pause, so just treat it as being paused now.
             if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
-        }
-
-        if (!mService.isSleeping()) {
-            resumeTopActivityLocked(pausing);
+            resumeTopActivityLocked(null);
         }
     }
     
@@ -988,17 +966,16 @@
             if (index >= 0) {
                 r = mHistory.get(index);
                 mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-                if (mPausingActivities.contains(r)) {
+                if (mPausingActivity == r) {
                     if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
                             + (timeout ? " (due to timeout)" : " (pause complete)"));
                     r.state = ActivityState.PAUSED;
-                    completePauseLocked(r);
+                    completePauseLocked();
                 } else {
-                    ActivityRecord old = mPausingActivities.size() > 0
-                            ? mPausingActivities.get(0) : null;
                     EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
                             System.identityHashCode(r), r.shortComponentName, 
-                            old != null ? old.shortComponentName : "(none)");
+                            mPausingActivity != null
+                                ? mPausingActivity.shortComponentName : "(none)");
                 }
             }
         }
@@ -1024,14 +1001,8 @@
                 ProcessRecord fgApp = null;
                 if (mResumedActivity != null) {
                     fgApp = mResumedActivity.app;
-                } else {
-                    for (int i=mPausingActivities.size()-1; i>=0; i--) {
-                        ActivityRecord pausing = mPausingActivities.get(i);
-                        if (pausing.app == r.app) {
-                            fgApp = pausing.app;
-                            break;
-                        }
-                    }
+                } else if (mPausingActivity != null) {
+                    fgApp = mPausingActivity.app;
                 }
                 if (r.app != null && fgApp != null && r.app != fgApp
                         && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
@@ -1043,49 +1014,58 @@
         }
     }
 
-    private final void completePauseLocked(ActivityRecord prev) {
+    private final void completePauseLocked() {
+        ActivityRecord prev = mPausingActivity;
         if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
         
-        if (prev.finishing) {
-            if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
-            prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
-        } else if (prev.app != null) {
-            if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
-            if (prev.waitingVisible) {
-                prev.waitingVisible = false;
-                mWaitingVisibleActivities.remove(prev);
-                if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
-                        TAG, "Complete pause, no longer waiting: " + prev);
-            }
-            if (prev.configDestroy) {
-                // The previous is being paused because the configuration
-                // is changing, which means it is actually stopping...
-                // To juggle the fact that we are also starting a new
-                // instance right now, we need to first completely stop
-                // the current instance before starting the new one.
-                if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
-                destroyActivityLocked(prev, true, false, "pause-config");
-            } else {
-                mStoppingActivities.add(prev);
-                if (mStoppingActivities.size() > 3) {
-                    // If we already have a few activities waiting to stop,
-                    // then give up on things going idle and start clearing
-                    // them out.
-                    if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
-                    scheduleIdleLocked();
-                } else {
-                    checkReadyForSleepLocked();
+        if (prev != null) {
+            if (prev.finishing) {
+                if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
+                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
+            } else if (prev.app != null) {
+                if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
+                if (prev.waitingVisible) {
+                    prev.waitingVisible = false;
+                    mWaitingVisibleActivities.remove(prev);
+                    if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
+                            TAG, "Complete pause, no longer waiting: " + prev);
                 }
+                if (prev.configDestroy) {
+                    // The previous is being paused because the configuration
+                    // is changing, which means it is actually stopping...
+                    // To juggle the fact that we are also starting a new
+                    // instance right now, we need to first completely stop
+                    // the current instance before starting the new one.
+                    if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
+                    destroyActivityLocked(prev, true, false, "pause-config");
+                } else {
+                    mStoppingActivities.add(prev);
+                    if (mStoppingActivities.size() > 3) {
+                        // If we already have a few activities waiting to stop,
+                        // then give up on things going idle and start clearing
+                        // them out.
+                        if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
+                        scheduleIdleLocked();
+                    } else {
+                        checkReadyForSleepLocked();
+                    }
+                }
+            } else {
+                if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
+                prev = null;
             }
-        } else {
-            if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
-            prev = null;
+            mPausingActivity = null;
         }
-        mPausingActivities.remove(prev);
 
-        if (mService.isSleeping()) {
+        if (!mService.isSleeping()) {
+            resumeTopActivityLocked(prev);
+        } else {
             checkReadyForSleepLocked();
         }
+        
+        if (prev != null) {
+            prev.resumeKeyDispatchingLocked();
+        }
 
         if (prev.app != null && prev.cpuTimeAtResume > 0
                 && mService.mBatteryStatsService.isOnBattery()) {
@@ -1142,9 +1122,7 @@
         if (mMainStack) {
             mService.setFocusedActivityLocked(next);
         }
-        if (mInputPausedActivities.remove(next)) {
-            next.resumeKeyDispatchingLocked();
-        }
+        next.resumeKeyDispatchingLocked();
         ensureActivitiesVisibleLocked(null, 0);
         mService.mWindowManager.executeAppTransition();
         mNoAnimActivities.clear();
@@ -1374,6 +1352,13 @@
 
         if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
 
+        // If we are currently pausing an activity, then don't do anything
+        // until that is done.
+        if (mPausingActivity != null) {
+            if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
+            return false;
+        }
+
         // Okay we are now going to start a switch, to 'next'.  We may first
         // have to pause the current activity, but this is an important point
         // where we have decided to go to 'next' so keep track of that.
@@ -2455,7 +2440,7 @@
         
         err = startActivityUncheckedLocked(r, sourceRecord,
                 grantedUriPermissions, grantedMode, onlyIfNeeded, true);
-        if (mDismissKeyguardOnNextActivity && mPausingActivities.size() == 0) {
+        if (mDismissKeyguardOnNextActivity && mPausingActivity == null) {
             // Someone asked to have the keyguard dismissed on the next
             // activity start, but we are not actually doing an activity
             // switch...  just dismiss the keyguard now, because we
@@ -3126,18 +3111,7 @@
         }
         mService.notifyAll();
     }
-
-    void reportActivityDrawnLocked(ActivityRecord r) {
-        if (mResumedActivity == r) {
-            // Once the resumed activity has been drawn, we can stop
-            // pausing input on all other activities.
-            for (int i=mInputPausedActivities.size()-1; i>=0; i--) {
-                mInputPausedActivities.get(i).resumeKeyDispatchingLocked();
-            }
-            mInputPausedActivities.clear();
-        }
-    }
-
+    
     void reportActivityVisibleLocked(ActivityRecord r) {
         for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
             WaitResult w = mWaitingActivityVisible.get(i);
@@ -3196,9 +3170,7 @@
                     mService.setFocusedActivityLocked(topRunningActivityLocked(null));
                 }
             }
-            if (mInputPausedActivities.remove(r)) {
-                r.resumeKeyDispatchingLocked();
-            }
+            r.resumeKeyDispatchingLocked();
             try {
                 r.stopped = false;
                 if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r
@@ -3524,7 +3496,7 @@
             // Tell window manager to prepare for this one to be removed.
             mService.mWindowManager.setAppVisibility(r.appToken, false);
                 
-            if (!mPausingActivities.contains(r)) {
+            if (mPausingActivity == null) {
                 if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
                 if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
                 startPausingLocked(false, false);
@@ -3608,20 +3580,6 @@
         return r;
     }
 
-    final void appDiedLocked(ProcessRecord app) {
-        for (int i=mPausingActivities.size()-1; i>=0; i--) {
-            ActivityRecord r = mPausingActivities.get(i);
-            if (r.app == app) {
-                if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + r);
-                mPausingActivities.remove(i);
-                mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-            }
-        }
-        if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
-            mLastPausedActivity = null;
-        }
-    }
-
     /**
      * Perform the common clean-up of an activity record.  This is called both
      * as part of destroyActivityLocked() (when destroying the client-side
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 3835553..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/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 7169015..e471a3f 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1127,7 +1127,10 @@
                     + "; regranting permissions for internal storage");
             mSettings.mInternalSdkPlatform = mSdkVersion;
             
-            updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
+            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+                    | (regrantPermissions
+                            ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
+                            : 0));
 
             // can downgrade to reader
             mSettings.writeLPr();
@@ -1473,7 +1476,7 @@
     PackageInfo generatePackageInfo(PackageParser.Package p, int flags) {
         if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
             // The package has been uninstalled but has retained data and resources.
-            return PackageParser.generatePackageInfo(p, null, flags, 0, 0);
+            return PackageParser.generatePackageInfo(p, null, flags, 0, 0, null);
         }
         final PackageSetting ps = (PackageSetting)p.mExtras;
         if (ps == null) {
@@ -1481,7 +1484,7 @@
         }
         final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
         return PackageParser.generatePackageInfo(p, gp.gids, flags,
-                ps.firstInstallTime, ps.lastUpdateTime);
+                ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions);
     }
 
     public PackageInfo getPackageInfo(String packageName, int flags) {
@@ -1934,6 +1937,7 @@
         BasePermission bp = mSettings.mPermissions.get(info.name);
         boolean added = bp == null;
         boolean changed = true;
+        int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
         if (added) {
             bp = new BasePermission(info.name, tree.sourcePackage,
                     BasePermission.TYPE_DYNAMIC);
@@ -1942,16 +1946,17 @@
                     "Not allowed to modify non-dynamic permission "
                     + info.name);
         } else {
-            if (bp.protectionLevel == info.protectionLevel
+            if (bp.protectionLevel == fixedLevel
                     && bp.perm.owner.equals(tree.perm.owner)
                     && bp.uid == tree.uid
                     && comparePermissionInfos(bp.perm.info, info)) {
                 changed = false;
             }
         }
-        bp.protectionLevel = info.protectionLevel;
-        bp.perm = new PackageParser.Permission(tree.perm.owner,
-                new PermissionInfo(info));
+        bp.protectionLevel = fixedLevel;
+        info = new PermissionInfo(info);
+        info.protectionLevel = fixedLevel;
+        bp.perm = new PackageParser.Permission(tree.perm.owner, info);
         bp.perm.info.packageName = tree.perm.info.packageName;
         bp.uid = tree.uid;
         if (added) {
@@ -1995,6 +2000,77 @@
         }
     }
 
+    public void grantPermission(String packageName, String permissionName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
+        synchronized (mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            final BasePermission bp = mSettings.mPermissions.get(permissionName);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + packageName);
+            }
+            if (!pkg.requestedPermissions.contains(permissionName)) {
+                throw new SecurityException("Package " + packageName
+                        + " has not requested permission " + permissionName);
+            }
+            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) == 0) {
+                throw new SecurityException("Permission " + permissionName
+                        + " is not a development permission");
+            }
+            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            if (ps == null) {
+                return;
+            }
+            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+            if (gp.grantedPermissions.add(permissionName)) {
+                if (ps.haveGids) {
+                    gp.gids = appendInts(gp.gids, bp.gids);
+                }
+                mSettings.writeLPr();
+            }
+        }
+    }
+
+    public void revokePermission(String packageName, String permissionName) {
+        synchronized (mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (pkg.applicationInfo.uid != Binder.getCallingUid()) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
+            }
+            final BasePermission bp = mSettings.mPermissions.get(permissionName);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + packageName);
+            }
+            if (!pkg.requestedPermissions.contains(permissionName)) {
+                throw new SecurityException("Package " + packageName
+                        + " has not requested permission " + permissionName);
+            }
+            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) == 0) {
+                throw new SecurityException("Permission " + permissionName
+                        + " is not a development permission");
+            }
+            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            if (ps == null) {
+                return;
+            }
+            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+            if (gp.grantedPermissions.remove(permissionName)) {
+                gp.grantedPermissions.remove(permissionName);
+                if (ps.haveGids) {
+                    gp.gids = removeInts(gp.gids, bp.gids);
+                }
+                mSettings.writeLPr();
+            }
+        }
+    }
+
     public boolean isProtectedBroadcast(String actionName) {
         synchronized (mPackages) {
             return mProtectedBroadcasts.contains(actionName);
@@ -4117,10 +4193,13 @@
         }
         return false;
     }
-    
+
+    static final int UPDATE_PERMISSIONS_ALL = 1<<0;
+    static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
+    static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+
     private void updatePermissionsLPw(String changingPkg,
-            PackageParser.Package pkgInfo, boolean grantPermissions,
-            boolean replace, boolean replaceAll) {
+            PackageParser.Package pkgInfo, int flags) {
         // Make sure there are no dangling permission trees.
         Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
         while (it.hasNext()) {
@@ -4138,7 +4217,7 @@
                 if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
                     Slog.i(TAG, "Removing old permission tree: " + bp.name
                             + " from package " + bp.sourcePackage);
-                    grantPermissions = true;
+                    flags |= UPDATE_PERMISSIONS_ALL;
                     it.remove();
                 }
             }
@@ -4178,7 +4257,7 @@
                 if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
                     Slog.i(TAG, "Removing old permission: " + bp.name
                             + " from package " + bp.sourcePackage);
-                    grantPermissions = true;
+                    flags |= UPDATE_PERMISSIONS_ALL;
                     it.remove();
                 }
             }
@@ -4186,16 +4265,16 @@
 
         // Now update the permissions for all packages, in particular
         // replace the granted permissions of the system packages.
-        if (grantPermissions) {
+        if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
             for (PackageParser.Package pkg : mPackages.values()) {
                 if (pkg != pkgInfo) {
-                    grantPermissionsLPw(pkg, replaceAll);
+                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0);
                 }
             }
         }
         
         if (pkgInfo != null) {
-            grantPermissionsLPw(pkgInfo, replace);
+            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0);
         }
     }
 
@@ -4205,11 +4284,13 @@
             return;
         }
         final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+        HashSet<String> origPermissions = gp.grantedPermissions;
         boolean changedPermission = false;
 
         if (replace) {
             ps.permissionsFixed = false;
             if (gp == ps) {
+                origPermissions = new HashSet<String>(gp.grantedPermissions);
                 gp.grantedPermissions.clear();
                 gp.gids = mGlobalGids;
             }
@@ -4222,6 +4303,7 @@
         final int N = pkg.requestedPermissions.size();
         for (int i=0; i<N; i++) {
             final String name = pkg.requestedPermissions.get(i);
+            //final boolean required = pkg.requestedPermssionsRequired.get(i);
             final BasePermission bp = mSettings.mPermissions.get(name);
             if (DEBUG_INSTALL) {
                 if (gp != ps) {
@@ -4232,23 +4314,23 @@
                 final String perm = bp.name;
                 boolean allowed;
                 boolean allowedSig = false;
-                if (bp.protectionLevel == PermissionInfo.PROTECTION_NORMAL
-                        || bp.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) {
+                final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+                if (level == PermissionInfo.PROTECTION_NORMAL
+                        || level == PermissionInfo.PROTECTION_DANGEROUS) {
                     allowed = true;
                 } else if (bp.packageSetting == null) {
                     // This permission is invalid; skip it.
                     allowed = false;
-                } else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
-                        || bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+                } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
                     allowed = (compareSignatures(
                             bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
                                     == PackageManager.SIGNATURE_MATCH)
                             || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
                                     == PackageManager.SIGNATURE_MATCH);
-                    if (!allowed && bp.protectionLevel
-                            == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+                    if (!allowed && (bp.protectionLevel
+                            & PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) {
                         if (isSystemApp(pkg)) {
-                            // For updated system applications, the signatureOrSystem permission
+                            // For updated system applications, a system permission
                             // is granted only if it had been defined by the original application.
                             if (isUpdatedSystemApp(pkg)) {
                                 final PackageSetting sysPs = mSettings
@@ -4265,6 +4347,16 @@
                             }
                         }
                     }
+                    if (!allowed && (bp.protectionLevel
+                            & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+                        // For development permissions, a development permission
+                        // is granted only if it was already granted.
+                        if (origPermissions.contains(perm)) {
+                            allowed = true;
+                        } else {
+                            allowed = false;
+                        }
+                    }
                     if (allowed) {
                         allowedSig = true;
                     }
@@ -4883,7 +4975,7 @@
                             // writer
                             synchronized (mPackages) {
                                 updatePermissionsLPw(p.packageName, p,
-                                        p.permissions.size() > 0, false, false);
+                                        p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0);
                             }
                             addedPackage = p.applicationInfo.packageName;
                             addedUid = p.applicationInfo.uid;
@@ -6447,7 +6539,7 @@
                 // writer
                 synchronized (mPackages) {
                     updatePermissionsLPw(deletedPackage.packageName, deletedPackage,
-                            true, false, false);
+                            UPDATE_PERMISSIONS_ALL);
                     // can downgrade to reader
                     mSettings.writeLPr();
                 }
@@ -6591,7 +6683,8 @@
         }
         synchronized (mPackages) {
             updatePermissionsLPw(newPackage.packageName, newPackage,
-                    newPackage.permissions.size() > 0, true, false);
+                    UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
+                            ? UPDATE_PERMISSIONS_ALL : 0));
             res.name = pkgName;
             res.uid = newPackage.applicationInfo.uid;
             res.pkg = newPackage;
@@ -7019,7 +7112,7 @@
                         outInfo.removedUid = mSettings.removePackageLPw(packageName);
                     }
                     if (deletedPs != null) {
-                        updatePermissionsLPw(deletedPs.name, null, false, false, false);
+                        updatePermissionsLPw(deletedPs.name, null, 0);
                         if (deletedPs.sharedUser != null) {
                             // remove permissions associated with package
                             mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
@@ -7102,7 +7195,8 @@
         }
         // writer
         synchronized (mPackages) {
-            updatePermissionsLPw(newPkg.packageName, newPkg, true, true, false);
+            updatePermissionsLPw(newPkg.packageName, newPkg,
+                    UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
             // can downgrade to reader here
             if (writeSettings) {
                 mSettings.writeLPr();
@@ -8320,7 +8414,10 @@
 
             // Make sure group IDs have been assigned, and any permission
             // changes in other apps are accounted for
-            updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
+            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+                    | (regrantPermissions
+                            ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
+                            : 0));
             // can downgrade to reader
             // Persist settings
             mSettings.writeLPr();
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index ebf954b..5da6ac9 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1398,6 +1398,7 @@
                             dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
                     bp.protectionLevel = readInt(parser, null, "protection",
                             PermissionInfo.PROTECTION_NORMAL);
+                    bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
                     if (dynamic) {
                         PermissionInfo pi = new PermissionInfo();
                         pi.packageName = sourcePackage.intern();
@@ -2244,7 +2245,8 @@
             pw.print("    uid="); pw.print(p.uid);
                     pw.print(" gids="); pw.print(PackageManagerService.arrayToString(p.gids));
                     pw.print(" type="); pw.print(p.type);
-                    pw.print(" prot="); pw.println(p.protectionLevel);
+                    pw.print(" prot=");
+                    pw.println(PermissionInfo.protectionToString(p.protectionLevel));
             if (p.packageSetting != null) {
                 pw.print("    packageSetting="); pw.println(p.packageSetting);
             }
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/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index bdbaab4..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;
@@ -269,6 +268,7 @@
 
     final TokenWatcher mKeyguardTokenWatcher = new TokenWatcher(
             new Handler(), "WindowManagerService.mKeyguardTokenWatcher") {
+        @Override
         public void acquired() {
             if (shouldAllowDisableKeyguard()) {
                 mPolicy.enableKeyguard(false);
@@ -277,6 +277,7 @@
                 Log.v(TAG, "Not disabling keyguard since device policy is enforced");
             }
         }
+        @Override
         public void released() {
             mPolicy.enableKeyguard(true);
             synchronized (mKeyguardTokenWatcher) {
@@ -600,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);
@@ -724,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);
@@ -5441,7 +5454,7 @@
                 if (mScreenRotationAnimation.setRotation(rotation, mFxSession,
                         MAX_ANIMATION_DURATION, mTransitionAnimationScale,
                         mCurDisplayWidth, mCurDisplayHeight)) {
-                    mChoreographer.scheduleAnimation();
+                    scheduleAnimationLocked();
                 }
             }
             Surface.setOrientation(0, rotation);
@@ -6367,7 +6380,7 @@
                 INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
-        return reportInjectionResult(result);
+        return reportInjectionResult(result, pid);
     }
 
     /**
@@ -6397,7 +6410,7 @@
                 INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
-        return reportInjectionResult(result);
+        return reportInjectionResult(result, pid);
     }
 
     /**
@@ -6427,7 +6440,7 @@
                 INJECTION_TIMEOUT_MILLIS);
         
         Binder.restoreCallingIdentity(ident);
-        return reportInjectionResult(result);
+        return reportInjectionResult(result, pid);
     }
     
     /**
@@ -6448,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;
         }
     }
@@ -6882,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),
@@ -7611,53 +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;
@@ -7675,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
@@ -7689,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
@@ -7837,6 +7848,8 @@
                 w.performShowLocked();
             }
         } // end forall windows
+
+        return mPolicy.finishAnimationLw();
     }
 
     /**
@@ -8107,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;
@@ -8557,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);
@@ -8643,7 +8661,6 @@
         boolean focusDisplayed = false;
         mInnerFields.mAnimating = false;
         boolean createWatermark = false;
-        boolean updateRotation = false;
 
         if (mFxSession == null) {
             mFxSession = new SurfaceSession();
@@ -8697,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);
@@ -8724,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();
@@ -8750,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) {
@@ -8773,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;
@@ -8980,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.
@@ -9014,16 +9024,17 @@
             mTurnOnScreen = false;
         }
 
-        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();
         }
 
@@ -9100,10 +9111,10 @@
         }
     }
 
-    @Override
-    public void onAnimate() {
-        synchronized(mWindowMap) {
-            performLayoutAndPlaceSurfacesLocked();
+    void scheduleAnimationLocked() {
+        if (!mAnimationScheduled) {
+            mChoreographer.postAnimationCallback(mAnimationRunnable);
+            mAnimationScheduled = true;
         }
     }
 
@@ -9423,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;
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 c63b84d..c02dd36 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -26,6 +26,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
+    libandroidfw \
     libcutils \
     libhardware \
     libhardware_legacy \
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/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 986aec5..cea17f8 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -26,7 +26,6 @@
 
 #include <ui/PixelFormat.h>
 #include <ui/FramebufferNativeWindow.h>
-#include <ui/EGLUtils.h>
 
 #include <GLES/gl.h>
 #include <EGL/egl.h>
@@ -60,6 +59,29 @@
 static __attribute__((noinline))
 void checkEGLErrors(const char* token)
 {
+    struct EGLUtils {
+        static const char *strerror(EGLint err) {
+            switch (err){
+                case EGL_SUCCESS:           return "EGL_SUCCESS";
+                case EGL_NOT_INITIALIZED:   return "EGL_NOT_INITIALIZED";
+                case EGL_BAD_ACCESS:        return "EGL_BAD_ACCESS";
+                case EGL_BAD_ALLOC:         return "EGL_BAD_ALLOC";
+                case EGL_BAD_ATTRIBUTE:     return "EGL_BAD_ATTRIBUTE";
+                case EGL_BAD_CONFIG:        return "EGL_BAD_CONFIG";
+                case EGL_BAD_CONTEXT:       return "EGL_BAD_CONTEXT";
+                case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
+                case EGL_BAD_DISPLAY:       return "EGL_BAD_DISPLAY";
+                case EGL_BAD_MATCH:         return "EGL_BAD_MATCH";
+                case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
+                case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
+                case EGL_BAD_PARAMETER:     return "EGL_BAD_PARAMETER";
+                case EGL_BAD_SURFACE:       return "EGL_BAD_SURFACE";
+                case EGL_CONTEXT_LOST:      return "EGL_CONTEXT_LOST";
+                default: return "UNKNOWN";
+            }
+        }
+    };
+
     EGLint error = eglGetError();
     if (error && error != EGL_SUCCESS) {
         ALOGE("%s: EGL error 0x%04x (%s)",
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3e6b872..efcdd87 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),
@@ -161,7 +160,10 @@
     // this surfaces pixel format
     PixelFormatInfo info;
     status_t err = getPixelFormatInfo(format, &info);
-    if (err) return err;
+    if (err) {
+        ALOGE("unsupported pixelformat %d", format);
+        return err;
+    }
 
     // the display's pixel format
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
@@ -171,6 +173,7 @@
     // never allow a surface larger than what our underlying GL implementation
     // can handle.
     if ((uint32_t(w)>maxSurfaceDims) || (uint32_t(h)>maxSurfaceDims)) {
+        ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h));
         return BAD_VALUE;
     }
 
@@ -408,15 +411,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 +425,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 +540,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..05b5bf5 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
      */
@@ -1279,7 +1285,7 @@
         return surfaceHandle;
     }
 
-    //ALOGD("createSurface for pid %d (%d x %d)", pid, w, h);
+    //ALOGD("createSurface for (%d x %d), name=%s", w, h, name.string());
     sp<Layer> normalLayer;
     switch (flags & eFXSurfaceMask) {
         case eFXSurfaceNormal:
@@ -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 24a4876..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'.
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 32a6975..a9a5e90 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -64,44 +64,47 @@
 
     /**
      * Available radio technologies for GSM, UMTS and CDMA.
+     * Duplicates the constants from hardware/radio/include/ril.h
+     * This should only be used by agents working with the ril.  Others
+     * should use the equivalent TelephonyManager.NETWORK_TYPE_*
      */
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_UNKNOWN = 0;
+    public static final int RIL_RADIO_TECHNOLOGY_UNKNOWN = 0;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_GPRS = 1;
+    public static final int RIL_RADIO_TECHNOLOGY_GPRS = 1;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_EDGE = 2;
+    public static final int RIL_RADIO_TECHNOLOGY_EDGE = 2;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_UMTS = 3;
+    public static final int RIL_RADIO_TECHNOLOGY_UMTS = 3;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_IS95A = 4;
+    public static final int RIL_RADIO_TECHNOLOGY_IS95A = 4;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_IS95B = 5;
+    public static final int RIL_RADIO_TECHNOLOGY_IS95B = 5;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_1xRTT = 6;
+    public static final int RIL_RADIO_TECHNOLOGY_1xRTT = 6;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_EVDO_0 = 7;
+    public static final int RIL_RADIO_TECHNOLOGY_EVDO_0 = 7;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_EVDO_A = 8;
+    public static final int RIL_RADIO_TECHNOLOGY_EVDO_A = 8;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_HSDPA = 9;
+    public static final int RIL_RADIO_TECHNOLOGY_HSDPA = 9;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_HSUPA = 10;
+    public static final int RIL_RADIO_TECHNOLOGY_HSUPA = 10;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_HSPA = 11;
+    public static final int RIL_RADIO_TECHNOLOGY_HSPA = 11;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_EVDO_B = 12;
+    public static final int RIL_RADIO_TECHNOLOGY_EVDO_B = 12;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_EHRPD = 13;
+    public static final int RIL_RADIO_TECHNOLOGY_EHRPD = 13;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_LTE = 14;
+    public static final int RIL_RADIO_TECHNOLOGY_LTE = 14;
     /** @hide */
-    public static final int RADIO_TECHNOLOGY_HSPAP = 15;
+    public static final int RIL_RADIO_TECHNOLOGY_HSPAP = 15;
     /**
      * GSM radio technology only supports voice. It does not support data.
      * @hide
      */
-    public static final int RADIO_TECHNOLOGY_GSM = 16;
+    public static final int RIL_RADIO_TECHNOLOGY_GSM = 16;
 
     /**
      * Available registration states for GSM, UMTS and CDMA.
@@ -400,59 +403,59 @@
      *
      * @hide
      */
-    public static String radioTechnologyToString(int rt) {
+    public static String rilRadioTechnologyToString(int rt) {
         String rtString;
 
         switch(rt) {
-            case 0:
+            case RIL_RADIO_TECHNOLOGY_UNKNOWN:
                 rtString = "Unknown";
                 break;
-            case 1:
+            case RIL_RADIO_TECHNOLOGY_GPRS:
                 rtString = "GPRS";
                 break;
-            case 2:
+            case RIL_RADIO_TECHNOLOGY_EDGE:
                 rtString = "EDGE";
                 break;
-            case 3:
+            case RIL_RADIO_TECHNOLOGY_UMTS:
                 rtString = "UMTS";
                 break;
-            case 4:
+            case RIL_RADIO_TECHNOLOGY_IS95A:
                 rtString = "CDMA-IS95A";
                 break;
-            case 5:
+            case RIL_RADIO_TECHNOLOGY_IS95B:
                 rtString = "CDMA-IS95B";
                 break;
-            case 6:
+            case RIL_RADIO_TECHNOLOGY_1xRTT:
                 rtString = "1xRTT";
                 break;
-            case 7:
+            case RIL_RADIO_TECHNOLOGY_EVDO_0:
                 rtString = "EvDo-rev.0";
                 break;
-            case 8:
+            case RIL_RADIO_TECHNOLOGY_EVDO_A:
                 rtString = "EvDo-rev.A";
                 break;
-            case 9:
+            case RIL_RADIO_TECHNOLOGY_HSDPA:
                 rtString = "HSDPA";
                 break;
-            case 10:
+            case RIL_RADIO_TECHNOLOGY_HSUPA:
                 rtString = "HSUPA";
                 break;
-            case 11:
+            case RIL_RADIO_TECHNOLOGY_HSPA:
                 rtString = "HSPA";
                 break;
-            case 12:
+            case RIL_RADIO_TECHNOLOGY_EVDO_B:
                 rtString = "EvDo-rev.B";
                 break;
-            case 13:
+            case RIL_RADIO_TECHNOLOGY_EHRPD:
                 rtString = "eHRPD";
                 break;
-            case 14:
+            case RIL_RADIO_TECHNOLOGY_LTE:
                 rtString = "LTE";
                 break;
-            case 15:
+            case RIL_RADIO_TECHNOLOGY_HSPAP:
                 rtString = "HSPAP";
                 break;
-            case 16:
+            case RIL_RADIO_TECHNOLOGY_GSM:
                 rtString = "GSM";
                 break;
             default:
@@ -460,12 +463,12 @@
                 Log.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
                 break;
         }
-        return rtString + ":" + rt;
+        return rtString;
     }
 
     @Override
     public String toString() {
-        String radioTechnology = radioTechnologyToString(mRadioTechnology);
+        String radioTechnology = rilRadioTechnologyToString(mRadioTechnology);
 
         return (mState + " " + (mRoaming ? "roaming" : "home")
                 + " " + mOperatorAlphaLong
@@ -644,9 +647,50 @@
     }
 
     /** @hide */
-    public int getRadioTechnology() {
+    public int getRilRadioTechnology() {
         return this.mRadioTechnology;
     }
+    /** @hide */
+    public int getRadioTechnology() {
+        return getRilRadioTechnology();
+    }
+
+    /** @hide */
+    public int getNetworkType() {
+        switch(mRadioTechnology) {
+        case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
+            return TelephonyManager.NETWORK_TYPE_GPRS;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
+            return TelephonyManager.NETWORK_TYPE_EDGE;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
+            return TelephonyManager.NETWORK_TYPE_UMTS;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
+            return TelephonyManager.NETWORK_TYPE_HSDPA;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
+            return TelephonyManager.NETWORK_TYPE_HSUPA;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
+            return TelephonyManager.NETWORK_TYPE_HSPA;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
+        case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
+            return TelephonyManager.NETWORK_TYPE_CDMA;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
+            return TelephonyManager.NETWORK_TYPE_1xRTT;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
+            return TelephonyManager.NETWORK_TYPE_EVDO_0;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
+            return TelephonyManager.NETWORK_TYPE_EVDO_A;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
+            return TelephonyManager.NETWORK_TYPE_EVDO_B;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
+            return TelephonyManager.NETWORK_TYPE_EHRPD;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
+            return TelephonyManager.NETWORK_TYPE_LTE;
+        case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
+            return TelephonyManager.NETWORK_TYPE_HSPAP;
+        default:
+            return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        }
+    }
 
     /** @hide */
     public int getCssIndicator() {
@@ -665,25 +709,25 @@
 
     /** @hide */
     public static boolean isGsm(int radioTechnology) {
-        return radioTechnology == RADIO_TECHNOLOGY_GPRS
-                || radioTechnology == RADIO_TECHNOLOGY_EDGE
-                || radioTechnology == RADIO_TECHNOLOGY_UMTS
-                || radioTechnology == RADIO_TECHNOLOGY_HSDPA
-                || radioTechnology == RADIO_TECHNOLOGY_HSUPA
-                || radioTechnology == RADIO_TECHNOLOGY_HSPA
-                || radioTechnology == RADIO_TECHNOLOGY_LTE
-                || radioTechnology == RADIO_TECHNOLOGY_HSPAP
-                || radioTechnology == RADIO_TECHNOLOGY_GSM;
+        return radioTechnology == RIL_RADIO_TECHNOLOGY_GPRS
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EDGE
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_UMTS
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_HSDPA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_HSUPA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_HSPA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_HSPAP
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_GSM;
     }
 
     /** @hide */
     public static boolean isCdma(int radioTechnology) {
-        return radioTechnology == RADIO_TECHNOLOGY_IS95A
-                || radioTechnology == RADIO_TECHNOLOGY_IS95B
-                || radioTechnology == RADIO_TECHNOLOGY_1xRTT
-                || radioTechnology == RADIO_TECHNOLOGY_EVDO_0
-                || radioTechnology == RADIO_TECHNOLOGY_EVDO_A
-                || radioTechnology == RADIO_TECHNOLOGY_EVDO_B
-                || radioTechnology == RADIO_TECHNOLOGY_EHRPD;
+        return radioTechnology == RIL_RADIO_TECHNOLOGY_IS95A
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_IS95B
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_1xRTT
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_0
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_A
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_B
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EHRPD;
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index d0e304f..238afbe 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -355,14 +355,14 @@
         if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
     }
 
-    protected int getRadioTechnology(int defaultRadioTechnology) {
-        int radioTechnology;
+    protected int getRilRadioTechnology(int defaultRilRadioTechnology) {
+        int rilRadioTechnology;
         if (mRilVersion < 6) {
-            radioTechnology = defaultRadioTechnology;
+            rilRadioTechnology = defaultRilRadioTechnology;
         } else {
-            radioTechnology = phone.getServiceState().getRadioTechnology() + 2;
+            rilRadioTechnology = phone.getServiceState().getRilRadioTechnology() + 2;
         }
-        return radioTechnology;
+        return rilRadioTechnology;
     }
 
     /*
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 2b9fb91..fa90f1c 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -40,6 +40,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -1167,15 +1168,14 @@
     }
 
     protected String getReryConfig(boolean forDefault) {
-        int rt = mPhone.getServiceState().getRadioTechnology();
+        int nt = mPhone.getServiceState().getNetworkType();
 
-        if ((rt == ServiceState.RADIO_TECHNOLOGY_IS95A) ||
-            (rt == ServiceState.RADIO_TECHNOLOGY_IS95B) ||
-            (rt == ServiceState.RADIO_TECHNOLOGY_1xRTT) ||
-            (rt == ServiceState.RADIO_TECHNOLOGY_EVDO_0) ||
-            (rt == ServiceState.RADIO_TECHNOLOGY_EVDO_A) ||
-            (rt == ServiceState.RADIO_TECHNOLOGY_EVDO_B) ||
-            (rt == ServiceState.RADIO_TECHNOLOGY_EHRPD)) {
+        if ((nt == TelephonyManager.NETWORK_TYPE_CDMA) ||
+            (nt == TelephonyManager.NETWORK_TYPE_1xRTT) ||
+            (nt == TelephonyManager.NETWORK_TYPE_EVDO_0) ||
+            (nt == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
+            (nt == TelephonyManager.NETWORK_TYPE_EVDO_B) ||
+            (nt == TelephonyManager.NETWORK_TYPE_EHRPD)) {
             // CDMA variant
             return SystemProperties.get("ro.cdma.data_retry_config");
         } else {
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 2931a02..1a8b3ca 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -149,7 +149,7 @@
                     logd("LTE ON CDMA property is set. Switch to CDMALTEPhone" +
                             " newVoiceRadioTech = " + newVoiceRadioTech +
                             " Active Phone = " + mActivePhone.getPhoneName());
-                    newVoiceRadioTech = ServiceState.RADIO_TECHNOLOGY_1xRTT;
+                    newVoiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT;
                 }
             } else {
                 if ((ServiceState.isCdma(newVoiceRadioTech) &&
@@ -165,7 +165,7 @@
             }
         }
 
-        if (newVoiceRadioTech == ServiceState.RADIO_TECHNOLOGY_UNKNOWN) {
+        if (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
             // We need some voice phone object to be active always, so never
             // delete the phone without anything to replace it with!
             logd("Ignoring voice radio technology changed message. newVoiceRadioTech = Unknown."
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index 9c80de7..fd39e04 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -54,10 +54,10 @@
     protected boolean mDesiredPowerState;
 
     /**
-     *  Values correspond to ServiceState.RADIO_TECHNOLOGY_ definitions.
+     *  Values correspond to ServiceState.RIL_RADIO_TECHNOLOGY_ definitions.
      */
-    protected int mRadioTechnology = 0;
-    protected int mNewRadioTechnology = 0;
+    protected int mRilRadioTechnology = 0;
+    protected int mNewRilRadioTechnology = 0;
 
     /**
      * By default, strength polling is enabled.  However, if we're
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index a93d94f..64d018e 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -86,7 +86,7 @@
         Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
         msg.obj = cp;
         phone.mCM.setupDataCall(
-                Integer.toString(getRadioTechnology(RILConstants.SETUP_DATA_TECH_CDMA)),
+                Integer.toString(getRilRadioTechnology(RILConstants.SETUP_DATA_TECH_CDMA)),
                 Integer.toString(dataProfile),
                 null, null, null,
                 Integer.toString(RILConstants.SETUP_DATA_AUTH_PAP_CHAP),
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 7da23d0..cad2e22 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -185,19 +185,21 @@
 
     @Override
     protected void pollStateDone() {
-        // determine data NetworkType from both LET and CDMA SS
+        // determine data RadioTechnology from both LET and CDMA SS
         if (mLteSS.getState() == ServiceState.STATE_IN_SERVICE) {
             //in LTE service
-            newNetworkType = mLteSS.getRadioTechnology();
+            mNewRilRadioTechnology = mLteSS.getRilRadioTechnology();
             mNewDataConnectionState = mLteSS.getState();
-            newSS.setRadioTechnology(newNetworkType);
-            log("pollStateDone LTE/eHRPD STATE_IN_SERVICE newNetworkType = " + newNetworkType);
+            newSS.setRadioTechnology(mNewRilRadioTechnology);
+            log("pollStateDone LTE/eHRPD STATE_IN_SERVICE mNewRilRadioTechnology = " +
+                    mNewRilRadioTechnology);
         } else {
             // LTE out of service, get CDMA Service State
-            newNetworkType = newSS.getRadioTechnology();
-            mNewDataConnectionState = radioTechnologyToDataServiceState(newNetworkType);
-            log("pollStateDone CDMA STATE_IN_SERVICE newNetworkType = " + newNetworkType +
-                " mNewDataConnectionState = " + mNewDataConnectionState);
+            mNewRilRadioTechnology = newSS.getRilRadioTechnology();
+            mNewDataConnectionState = radioTechnologyToDataServiceState(mNewRilRadioTechnology);
+            log("pollStateDone CDMA STATE_IN_SERVICE mNewRilRadioTechnology = " +
+                    mNewRilRadioTechnology + " mNewDataConnectionState = " +
+                    mNewDataConnectionState);
         }
 
         // TODO: Add proper support for LTE Only, we should be looking at
@@ -239,7 +241,7 @@
         boolean hasCdmaDataConnectionChanged =
             mDataConnectionState != mNewDataConnectionState;
 
-        boolean hasNetworkTypeChanged = networkType != newNetworkType;
+        boolean hasRadioTechnologyChanged = mRilRadioTechnology != mNewRilRadioTechnology;
 
         boolean hasChanged = !newSS.equals(ss);
 
@@ -251,20 +253,20 @@
 
         boolean has4gHandoff =
                 mNewDataConnectionState == ServiceState.STATE_IN_SERVICE &&
-                (((networkType == ServiceState.RADIO_TECHNOLOGY_LTE) &&
-                  (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) ||
-                 ((networkType == ServiceState.RADIO_TECHNOLOGY_EHRPD) &&
-                  (newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE)));
+                (((mRilRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) &&
+                  (mNewRilRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) ||
+                 ((mRilRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD) &&
+                  (mNewRilRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_LTE)));
 
         boolean hasMultiApnSupport =
-                (((newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE) ||
-                  (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) &&
-                 ((networkType != ServiceState.RADIO_TECHNOLOGY_LTE) &&
-                  (networkType != ServiceState.RADIO_TECHNOLOGY_EHRPD)));
+                (((mNewRilRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) ||
+                  (mNewRilRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) &&
+                 ((mRilRadioTechnology != ServiceState.RIL_RADIO_TECHNOLOGY_LTE) &&
+                  (mRilRadioTechnology != ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)));
 
         boolean hasLostMultiApnSupport =
-            ((newNetworkType >= ServiceState.RADIO_TECHNOLOGY_IS95A) &&
-             (newNetworkType <= ServiceState.RADIO_TECHNOLOGY_EVDO_A));
+            ((mNewRilRadioTechnology >= ServiceState.RIL_RADIO_TECHNOLOGY_IS95A) &&
+             (mNewRilRadioTechnology <= ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A));
 
         if (DBG) {
             log("pollStateDone:"
@@ -273,7 +275,7 @@
                 + " hasCdmaDataConnectionAttached=" + hasCdmaDataConnectionAttached
                 + " hasCdmaDataConnectionDetached=" + hasCdmaDataConnectionDetached
                 + " hasCdmaDataConnectionChanged=" + hasCdmaDataConnectionChanged
-                + " hasNetworkTypeChanged = " + hasNetworkTypeChanged
+                + " hasRadioTechnologyChanged = " + hasRadioTechnologyChanged
                 + " hasChanged=" + hasChanged
                 + " hasRoamingOn=" + hasRoamingOn
                 + " hasRoamingOff=" + hasRoamingOff
@@ -316,13 +318,14 @@
         newCellLoc = tcl;
 
         mDataConnectionState = mNewDataConnectionState;
-        networkType = newNetworkType;
+        mRilRadioTechnology = mNewRilRadioTechnology;
+        mNewRilRadioTechnology = 0;
 
         newSS.setStateOutOfService(); // clean slate for next time
 
-        if (hasNetworkTypeChanged) {
+        if (hasRadioTechnologyChanged) {
             phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
-                    ServiceState.radioTechnologyToString(networkType));
+                    ServiceState.rilRadioTechnologyToString(mRilRadioTechnology));
         }
 
         if (hasRegistered) {
@@ -404,7 +407,7 @@
             mDetachedRegistrants.notifyRegistrants();
         }
 
-        if ((hasCdmaDataConnectionChanged || hasNetworkTypeChanged)) {
+        if ((hasCdmaDataConnectionChanged || hasRadioTechnologyChanged)) {
             phone.notifyDataConnection(null);
         }
 
@@ -446,7 +449,7 @@
             int evdoSnr = ((ints[offset + 4] > 0) && (ints[offset + 4] <= 8)) ? ints[offset + 4]
                     : -1;
 
-            if (networkType == ServiceState.RADIO_TECHNOLOGY_LTE) {
+            if (mRilRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) {
                 lteRssi = ints[offset+5];
                 lteRsrp = ints[offset+6];
                 lteRsrq = ints[offset+7];
@@ -454,7 +457,7 @@
                 lteCqi = ints[offset+9];
             }
 
-            if (networkType != ServiceState.RADIO_TECHNOLOGY_LTE) {
+            if (mRilRadioTechnology != ServiceState.RIL_RADIO_TECHNOLOGY_LTE) {
                 mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio,
                         evdoSnr, false);
             } else {
@@ -476,7 +479,7 @@
         // Note: it needs to be confirmed which CDMA network types
         // can support voice and data calls concurrently.
         // For the time-being, the return value will be false.
-        return (networkType == ServiceState.RADIO_TECHNOLOGY_LTE);
+        return (mRilRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 5115d76..75f5d47 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -86,12 +86,6 @@
     private int mNitzUpdateDiff = SystemProperties.getInt("ro.nitz_update_diff",
             NITZ_UPDATE_DIFF_DEFAULT);
 
-    /**
-     *  Values correspond to ServiceState.RADIO_TECHNOLOGY_ definitions.
-     */
-    protected int networkType = 0;
-    protected int newNetworkType = 0;
-
     private boolean mCdmaRoaming = false;
     private int mRoamingIndicator;
     private boolean mIsInPrl;
@@ -556,10 +550,10 @@
     /**
     * Determine data network type based on radio technology.
     */
-    protected void setCdmaTechnology(int radioTechnology){
-        mNewDataConnectionState = radioTechnologyToDataServiceState(radioTechnology);
-        newSS.setRadioTechnology(radioTechnology);
-        newNetworkType = radioTechnology;
+    protected void setCdmaTechnology(int radioTech){
+        mNewDataConnectionState = radioTechnologyToDataServiceState(radioTech);
+        newSS.setRadioTechnology(radioTech);
+        mNewRilRadioTechnology = radioTech;
     }
 
     /**
@@ -925,7 +919,7 @@
         boolean hasCdmaDataConnectionChanged =
                        mDataConnectionState != mNewDataConnectionState;
 
-        boolean hasNetworkTypeChanged = networkType != newNetworkType;
+        boolean hasRadioTechnologyChanged = mRilRadioTechnology != mNewRilRadioTechnology;
 
         boolean hasChanged = !newSS.equals(ss);
 
@@ -955,15 +949,15 @@
         newCellLoc = tcl;
 
         mDataConnectionState = mNewDataConnectionState;
-        networkType = newNetworkType;
+        mRilRadioTechnology = mNewRilRadioTechnology;
         // this new state has been applied - forget it until we get a new new state
-        newNetworkType = 0;
+        mNewRilRadioTechnology = 0;
 
         newSS.setStateOutOfService(); // clean slate for next time
 
-        if (hasNetworkTypeChanged) {
+        if (hasRadioTechnologyChanged) {
             phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
-                    ServiceState.radioTechnologyToString(networkType));
+                    ServiceState.rilRadioTechnologyToString(mRilRadioTechnology));
         }
 
         if (hasRegistered) {
@@ -1030,7 +1024,7 @@
             mDetachedRegistrants.notifyRegistrants();
         }
 
-        if (hasCdmaDataConnectionChanged || hasNetworkTypeChanged) {
+        if (hasCdmaDataConnectionChanged || hasRadioTechnologyChanged) {
             phone.notifyDataConnection(null);
         }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
index 1f28280..4956ef4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
@@ -100,7 +100,7 @@
         }
 
         phone.mCM.setupDataCall(
-                Integer.toString(getRadioTechnology(RILConstants.SETUP_DATA_TECH_GSM)),
+                Integer.toString(getRilRadioTechnology(RILConstants.SETUP_DATA_TECH_GSM)),
                 Integer.toString(mProfileId),
                 mApn.apn, mApn.user, mApn.password,
                 Integer.toString(authType),
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 66e9487..d1873eb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -2318,7 +2318,7 @@
         }
 
         String operator = mPhone.mIccRecords.getOperatorNumeric();
-        int radioTech = mPhone.getServiceState().getRadioTechnology();
+        int networkType = mPhone.getServiceState().getNetworkType();
 
         if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
             if (canSetPreferApn && mPreferredApn != null) {
@@ -2327,7 +2327,7 @@
                         + mPreferredApn.numeric + ":" + mPreferredApn);
                 }
                 if (mPreferredApn.numeric.equals(operator)) {
-                    if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == radioTech) {
+                    if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == networkType) {
                         apnList.add(mPreferredApn);
                         if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
                         return apnList;
@@ -2346,7 +2346,7 @@
         if (mAllApns != null) {
             for (ApnSetting apn : mAllApns) {
                 if (apn.canHandleType(requestedApnType)) {
-                    if (apn.bearer == 0 || apn.bearer == radioTech) {
+                    if (apn.bearer == 0 || apn.bearer == networkType) {
                         if (DBG) log("apn info : " +apn.toString());
                         apnList.add(apn);
                     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 0ebeabe..a4fb1d8 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -629,7 +629,7 @@
                     }
                     newGPRSState = regCodeToServiceState(regState);
                     mDataRoaming = regCodeIsRoaming(regState);
-                    mNewRadioTechnology = type;
+                    mNewRilRadioTechnology = type;
                     newSS.setRadioTechnology(type);
                 break;
 
@@ -746,8 +746,8 @@
                 " mNewMaxDataCalls=" + mNewMaxDataCalls +
                 " oldReasonDataDenied=" + mReasonDataDenied +
                 " mNewReasonDataDenied=" + mNewReasonDataDenied +
-                " oldType=" + ServiceState.radioTechnologyToString(mRadioTechnology) +
-                " newType=" + ServiceState.radioTechnologyToString(mNewRadioTechnology));
+                " oldType=" + ServiceState.rilRadioTechnologyToString(mRilRadioTechnology) +
+                " newType=" + ServiceState.rilRadioTechnologyToString(mNewRilRadioTechnology));
         }
 
         boolean hasRegistered =
@@ -766,7 +766,7 @@
                 gprsState == ServiceState.STATE_IN_SERVICE
                 && newGPRSState != ServiceState.STATE_IN_SERVICE;
 
-        boolean hasRadioTechnologyChanged = mRadioTechnology != mNewRadioTechnology;
+        boolean hasRadioTechnologyChanged = mRilRadioTechnology != mNewRilRadioTechnology;
 
         boolean hasChanged = !newSS.equals(ss);
 
@@ -800,11 +800,11 @@
             int cid = -1;
             GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
             if (loc != null) cid = loc.getCid();
-            EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED, cid, mRadioTechnology,
-                    mNewRadioTechnology);
+            EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED, cid, mRilRadioTechnology,
+                    mNewRilRadioTechnology);
             if (DBG) {
-                log("RAT switched " + ServiceState.radioTechnologyToString(mRadioTechnology) +
-                        " -> " + ServiceState.radioTechnologyToString(mNewRadioTechnology) +
+                log("RAT switched " + ServiceState.rilRadioTechnologyToString(mRilRadioTechnology) +
+                        " -> " + ServiceState.rilRadioTechnologyToString(mNewRilRadioTechnology) +
                         " at cell " + cid);
             }
         }
@@ -812,16 +812,16 @@
         gprsState = newGPRSState;
         mReasonDataDenied = mNewReasonDataDenied;
         mMaxDataCalls = mNewMaxDataCalls;
-        mRadioTechnology = mNewRadioTechnology;
+        mRilRadioTechnology = mNewRilRadioTechnology;
         // this new state has been applied - forget it until we get a new new state
-        mNewRadioTechnology = 0;
+        mNewRilRadioTechnology = 0;
 
 
         newSS.setStateOutOfService(); // clean slate for next time
 
         if (hasRadioTechnologyChanged) {
             phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
-                    ServiceState.radioTechnologyToString(mRadioTechnology));
+                    ServiceState.rilRadioTechnologyToString(mRilRadioTechnology));
         }
 
         if (hasRegistered) {
@@ -1246,7 +1246,7 @@
      * that could support voice and data simultaneously.
      */
     public boolean isConcurrentVoiceAndDataAllowed() {
-        return (mRadioTechnology >= ServiceState.RADIO_TECHNOLOGY_UMTS);
+        return (mRilRadioTechnology >= ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
     }
 
     /**
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 9d9680d..db670f8 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -623,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 674c0b7..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();
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 58680ea..351c771 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -159,6 +159,18 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void grantPermission(String packageName, String permissionName) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public void revokePermission(String packageName, String permissionName) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public int checkSignatures(String pkg1, String pkg2) {
         throw new UnsupportedOperationException();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 3904c21..b310d93 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -568,6 +568,24 @@
         </activity>
 
         <activity
+                android:name="TextOnPathActivity"
+                android:label="_TextOnPath">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </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/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/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
new file mode 100644
index 0000000..b798ee7
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
@@ -0,0 +1,100 @@
+/*
+ * 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.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class TextOnPathActivity extends Activity {
+    private Path mPath;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mPath = makePath();
+
+        final TextOnPathView view = new TextOnPathView(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 TextOnPathView extends View {
+        private static final String TEST_STRING = "Hello OpenGL renderer, text on path! ";
+
+        private final Paint mPaint;
+        private final String mText;
+
+        public TextOnPathView(Context c) {
+            super(c);
+
+            mPaint = new Paint();
+            mPaint.setAntiAlias(true);
+            mPaint.setColor(0xff000000);
+
+            StringBuilder builder = new StringBuilder(TEST_STRING.length() * 2);
+            for (int i = 0; i < 2; i++) {
+                builder.append(TEST_STRING);
+            }
+            mText = builder.toString();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.drawARGB(255, 255, 255, 255);
+
+            canvas.save();
+            canvas.translate(400.0f, 350.0f);
+            mPaint.setTextAlign(Paint.Align.LEFT);
+            canvas.drawTextOnPath(mText + mText, mPath, 0.0f, 0.0f, mPaint);
+            canvas.restore();
+
+            canvas.save();
+            canvas.translate(150.0f, 60.0f);
+            canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint);
+
+            canvas.translate(250.0f, 0.0f);
+            mPaint.setTextAlign(Paint.Align.CENTER);
+            canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint);
+
+            canvas.translate(250.0f, 0.0f);
+            mPaint.setTextAlign(Paint.Align.RIGHT);
+            canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint);
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
index e8d1e8e..67af0fa 100644
--- a/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
+++ b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
@@ -11,6 +11,13 @@
                 <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">
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/raw/blur_h.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
index fa468cc..c34adc9 100644
--- a/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
@@ -3,13 +3,13 @@
 void main() {
    vec2 blurCoord = varTex0;
    blurCoord.x = varTex0.x + UNI_blurOffset0;
-   vec3 col = texture2D(UNI_Tex0, blurCoord).rgb;
+   vec3 col = texture2D(UNI_color, blurCoord).rgb;
    blurCoord.x = varTex0.x + UNI_blurOffset1;
-   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   col += texture2D(UNI_color, blurCoord).rgb;
    blurCoord.x = varTex0.x + UNI_blurOffset2;
-   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   col += texture2D(UNI_color, blurCoord).rgb;
    blurCoord.x = varTex0.x + UNI_blurOffset3;
-   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   col += texture2D(UNI_color, blurCoord).rgb;
 
-   gl_FragColor = vec4(col * 0.25, 0.0); //texture2D(UNI_Tex0, varTex0);
+   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
index a644a3e..ade05a2 100644
--- a/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl
@@ -3,15 +3,15 @@
 void main() {
    vec2 blurCoord = varTex0;
    blurCoord.y = varTex0.y + UNI_blurOffset0;
-   vec3 col = texture2D(UNI_Tex0, blurCoord).rgb;
+   vec3 col = texture2D(UNI_color, blurCoord).rgb;
    blurCoord.y = varTex0.y + UNI_blurOffset1;
-   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   col += texture2D(UNI_color, blurCoord).rgb;
    blurCoord.y = varTex0.y + UNI_blurOffset2;
-   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   col += texture2D(UNI_color, blurCoord).rgb;
    blurCoord.y = varTex0.y + UNI_blurOffset3;
-   col += texture2D(UNI_Tex0, blurCoord).rgb;
+   col += texture2D(UNI_color, blurCoord).rgb;
 
    col = col * 0.25;
 
-   gl_FragColor = vec4(col, 0.0); //texture2D(UNI_Tex0, varTex0);
+   gl_FragColor = vec4(col, 0.0);
 }
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
index 5d8938b..2eb1028 100644
--- a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
@@ -12,8 +12,8 @@
    float light0_Diffuse = dot(worldNorm, light0Vec);
 
    vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
    col.xyz = col.xyz * light0_Diffuse * 1.2;
-   gl_FragColor = col; //vec4(0.0, 1.0, 0.0, 0.0);
+   gl_FragColor = col;
 }
 
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
index 51f0612..b90a7b2 100644
--- a/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
@@ -14,8 +14,8 @@
    float light0_Specular = pow(light0Spec, 15.0) * 0.5;
 
    vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
-   col.xyz = col.xyz * (textureCube(UNI_Tex1, worldNorm).rgb * 0.5 + vec3(light0_Diffuse));
+   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
index 893d553..f3b89ed 100644
--- a/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl
@@ -14,12 +14,12 @@
    float light0_Specular = pow(light0Spec, 150.0) * 0.5;
 
    vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   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_Tex1, -light0R).rgb * 2.4, fresnel);
+   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
index ceb53bd..56f7151f 100644
--- a/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl
@@ -14,7 +14,7 @@
    float light0_Specular = pow(light0Spec, 10.0) * 0.5;
 
    vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   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/select_color.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
index 42b231a..1a927ca 100644
--- a/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
@@ -1,7 +1,7 @@
 varying vec2 varTex0;
 
 void main() {
-   vec3 col = texture2D(UNI_Tex0, varTex0).rgb;
+   vec3 col = texture2D(UNI_color, varTex0).rgb;
 
    vec3 desat = vec3(0.299, 0.587, 0.114);
    float lum = dot(desat, col);
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
index dd709cf..662ecd8 100644
--- a/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
+++ b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
@@ -1,7 +1,7 @@
 varying vec2 varTex0;
 
 void main() {
-   lowp vec4 col = texture2D(UNI_Tex0, varTex0).rgba;
+   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/src/com/android/scenegraph/ColladaParser.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
index d954313..b4b6fb9 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
@@ -248,17 +248,17 @@
                 String description = field.getAttribute("sid");

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

                     Float3 value = getFloat3(field);

-                    current.addComponent(new TranslateComponent(description, value));

+                    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.addComponent(new RotateComponent(description, axis, value.w));

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

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

                     Float3 value = getFloat3(field);

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

-                    current.addComponent(new ScaleComponent(description, value));

+                    current.addScale(description, value);

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

                     getRenderable(field, current);

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

diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
index d995dd0..9274b17 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
@@ -134,6 +134,24 @@
         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");
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
index c8cc3ac..8a468db 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
@@ -48,6 +48,11 @@
             return this;
         }
 
+        public Builder setShader(String code) {
+            mBuilder.setShader(code);
+            return this;
+        }
+
         public Builder setObjectConst(Type type) {
             mShader.mPerObjConstants = type;
             return this;
@@ -78,10 +83,12 @@
                 mBuilder.addConstant(mShader.mPerObjConstants);
             }
             for (int i = 0; i < mShader.mTextureTypes.size(); i ++) {
-                mBuilder.addTexture(mShader.mTextureTypes.get(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));
+                mBuilder.addTexture(mShader.mShaderTextureTypes.get(i),
+                                    mShader.mShaderTextureNames.get(i));
             }
 
             mShader.mProgram = mBuilder.create();
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
index 9f7ab41..9266f30 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
@@ -22,6 +22,7 @@
 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;
@@ -89,6 +90,10 @@
         mMaterialName = name;
     }
 
+    public Transform getTransform() {
+        return mTransform;
+    }
+
     public void setTransform(Transform t) {
         mTransform = t;
         if (mField != null) {
@@ -196,12 +201,17 @@
     }
 
     void updateFieldItem(RenderScriptGL rs) {
+        if (mRenderState == null) {
+            mRenderState = SceneManager.getDefaultState();
+        }
+        if (mTransform == null) {
+            mTransform = SceneManager.getDefaultTransform();
+        }
         updateVertexConstants(rs);
         updateFragmentConstants(rs);
 
-        if (mTransform != null) {
-            mData.transformMatrix = mTransform.getRSData().getAllocation();
-        }
+        mData.transformMatrix = mTransform.getRSData().getAllocation();
+
         mData.name = getNameAlloc(rs);
         mData.render_state = mRenderState.getRSData().getAllocation();
         mData.bVolInitialized = 0;
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
index 8c09860..27336ab 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
@@ -22,6 +22,10 @@
 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;
 
@@ -75,9 +79,24 @@
     }
 
     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);
@@ -88,26 +107,53 @@
     }
 
     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);
     }
 
@@ -120,8 +166,19 @@
     }
 
     public void appendRenderable(RenderableBase d) {
+        if (d == null) {
+            throw new RuntimeException("Adding null object");
+        }
         mRenderables.add(d);
-        mRenderableMap.put(d.getName(), 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() {
@@ -133,6 +190,9 @@
     }
 
     public void appendTextures(Texture2D tex) {
+        if (tex == null) {
+            throw new RuntimeException("Adding null object");
+        }
         mTextures.add(tex);
     }
 
@@ -224,24 +284,29 @@
     }
 
     private void addShaders(RenderScriptGL rs, Resources res, SceneManager sceneManager) {
-        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();
+        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);
         }
-        shaderData.copyFrom(shaderAllocs);
-        sceneManager.mRenderLoop.set_gVertexShaders(shaderData);
 
-        shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs), mFragmentShaders.size());
-        shaderAllocs = new Allocation[mFragmentShaders.size()];
-        for (int i = 0; i < mFragmentShaders.size(); i ++) {
-            FragmentShader sI = mFragmentShaders.get(i);
-            shaderAllocs[i] = sI.getRSData().getAllocation();
+        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);
         }
-        shaderData.copyFrom(shaderAllocs);
-        sceneManager.mRenderLoop.set_gFragmentShaders(shaderData);
     }
 
     public void initRS() {
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
index f77f483..4ff2c8b 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
@@ -29,8 +29,10 @@
 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;
@@ -41,7 +43,6 @@
 import android.renderscript.Allocation.MipmapControl;
 import android.renderscript.Mesh;
 import android.renderscript.RenderScriptGL;
-import android.renderscript.Type.Builder;
 import android.util.Log;
 import android.view.SurfaceHolder;
 
@@ -71,8 +72,15 @@
     Scene mActiveScene;
     private static SceneManager sSceneManager;
 
-    private Allocation sDefault2D;
-    private Allocation sDefaultCube;
+    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;
@@ -101,20 +109,20 @@
         if (sSceneManager == null) {
             return null;
         }
-        if (sSceneManager.sDefault2D == null) {
-            sSceneManager.sDefault2D = getDefault(false);
+        if (sSceneManager.mDefault2D == null) {
+            sSceneManager.mDefault2D = getDefault(false);
         }
-        return sSceneManager.sDefault2D;
+        return sSceneManager.mDefault2D;
     }
 
     static Allocation getDefaultTexCube() {
         if (sSceneManager == null) {
             return null;
         }
-        if (sSceneManager.sDefaultCube == null) {
-            sSceneManager.sDefaultCube = getDefault(true);
+        if (sSceneManager.mDefaultCube == null) {
+            sSceneManager.mDefaultCube = getDefault(true);
         }
-        return sSceneManager.sDefaultCube;
+        return sSceneManager.mDefaultCube;
     }
 
     public static boolean isSDCardPath(String path) {
@@ -154,24 +162,32 @@
         return b;
     }
 
-    public static Allocation loadCubemap(String name, RenderScriptGL rs, Resources res) {
-        Bitmap b = loadBitmap(name, res);
+    static Allocation createFromBitmap(Bitmap b, RenderScriptGL rs, boolean isCube) {
         if (b == null) {
             return null;
         }
-        return Allocation.createCubemapFromBitmap(rs, b,
-                                                  MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                                  Allocation.USAGE_GRAPHICS_TEXTURE);
+        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) {
-        Bitmap b = loadBitmap(name, res);
-        if (b == null) {
-            return null;
-        }
-        return Allocation.createFromBitmap(rs, b,
-                                           Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                           Allocation.USAGE_GRAPHICS_TEXTURE);
+        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) {
@@ -235,6 +251,10 @@
     public void setActiveScene(Scene s) {
         mActiveScene = s;
 
+        if (mActiveScene == null) {
+            return;
+        }
+
         // Do some sanity checking
         if (mActiveScene.getCameras().size() == 0) {
             Matrix4f camPos = new Matrix4f();
@@ -248,6 +268,9 @@
             cam.setTransform(cameraTransform);
             mActiveScene.appendCamera(cam);
         }
+
+        mActiveScene.appendShader(getDefaultVS());
+        mActiveScene.appendTransform(getDefaultTransform());
     }
 
     static RenderScriptGL getRS() {
@@ -264,6 +287,129 @@
         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();
@@ -287,17 +433,10 @@
         Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
                                            3, Mesh.TriangleMeshBuilder.TEXTURE_0);
 
-        tmb.setTexture(0.0f, 1.0f);
-        tmb.addVertex(-1.0f, 1.0f, 1.0f);
-
-        tmb.setTexture(0.0f, 0.0f);
-        tmb.addVertex(-1.0f, -1.0f, 1.0f);
-
-        tmb.setTexture(1.0f, 0.0f);
-        tmb.addVertex(1.0f, -1.0f, 1.0f);
-
-        tmb.setTexture(1.0f, 1.0f);
-        tmb.addVertex(1.0f, 1.0f, 1.0f);
+        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);
@@ -321,6 +460,15 @@
         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);
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
index 8dea535..3dd41ca 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
@@ -75,7 +75,7 @@
             // Make one if it's not there
             if (matchingParam == null) {
                 if (subElem.getDataType() == Element.DataType.FLOAT_32) {
-                    matchingParam = new Float4Param(inputName);
+                    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);
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
index 8fae9d9..b53ab88 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
@@ -30,6 +30,7 @@
 public class Texture2D extends TextureBase {
     String mFileName;
     String mFileDir;
+    int mResourceID;
 
     public Texture2D() {
         super(ScriptC_export.const_TextureType_TEXTURE_2D);
@@ -40,6 +41,17 @@
         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;
     }
@@ -62,8 +74,12 @@
     void load() {
         RenderScriptGL rs = SceneManager.getRS();
         Resources res = SceneManager.getRes();
-        String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1);
-        setTexture(SceneManager.loadTexture2D(mFileDir + shortName, rs, res));
+        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) {
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
index 12c81c2..1269e3c 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
@@ -31,6 +31,7 @@
 public class TextureCube extends TextureBase {
     String mFileName;
     String mFileDir;
+    int mResourceID;
 
     public TextureCube() {
         super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
@@ -47,6 +48,11 @@
         setFileName(file);
     }
 
+    public TextureCube(int resourceID) {
+        super(ScriptC_export.const_TextureType_TEXTURE_2D);
+        mResourceID = resourceID;
+    }
+
     public void setFileDir(String dir) {
         mFileDir = dir;
     }
@@ -69,8 +75,12 @@
     void load() {
         RenderScriptGL rs = SceneManager.getRS();
         Resources res = SceneManager.getRes();
-        String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1);
-        setTexture(SceneManager.loadCubemap(mFileDir + shortName, rs, res));
+        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) {
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
index f7d0e6d..4efaff7 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
@@ -44,6 +44,11 @@
             return this;
         }
 
+        public Builder setShader(String code) {
+            mBuilder.setShader(code);
+            return this;
+        }
+
         public Builder setObjectConst(Type type) {
             mShader.mPerObjConstants = type;
             return this;
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
index d8d48b3..8a73dbd 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
@@ -142,10 +142,14 @@
         return;
     }
 
-    rsForEach(gVertexParamsScript, nullAlloc, gVertexShaders,
-              gActiveCamera, sizeof(gActiveCamera));
-    rsForEach(gFragmentParamsScript, nullAlloc, gFragmentShaders,
-              gActiveCamera, sizeof(gActiveCamera));
+    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));
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/TestAppRS.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
index 7bf7812..f159e85 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
@@ -149,34 +149,27 @@
     }
 
     private void initPaintShaders() {
-        ScriptField_ModelParams objConst = new ScriptField_ModelParams(mRS, 1);
-        ScriptField_ViewProjParams shaderConst = new ScriptField_ViewProjParams(mRS, 1);
+        mGenericV = SceneManager.getDefaultVS();
 
-        VertexShader.Builder vb = new VertexShader.Builder(mRS);
-        vb.addInput(ScriptField_VertexShaderInputs.createElement(mRS));
-        vb.setShader(mRes, R.raw.shader2v);
-        vb.setObjectConst(objConst.getAllocation().getType());
-        vb.setShaderConst(shaderConst.getAllocation().getType());
-        mGenericV = vb.create();
+        ScriptField_CameraParams camParams = new ScriptField_CameraParams(mRS, 1);
+        Type camParamType = camParams.getAllocation().getType();
+        ScriptField_LightParams lightParams = new ScriptField_LightParams(mRS, 1);
 
-        ScriptField_CameraParams fsConst = new ScriptField_CameraParams(mRS, 1);
-        ScriptField_LightParams fsConst2 = new ScriptField_LightParams(mRS, 1);
-
-        mPaintF = createFromResource(R.raw.paintf, true, fsConst.getAllocation().getType());
+        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, fsConst.getAllocation().getType());
+        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, fsConst.getAllocation().getType());
-        mDiffuseF = createFromResource(R.raw.diffuse, false, fsConst.getAllocation().getType());
-        mTextureF = createFromResource(R.raw.texture, false, fsConst.getAllocation().getType());
+        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(fsConst2.getAllocation().getType());
+        fb.setObjectConst(lightParams.getAllocation().getType());
         fb.setShader(mRes, R.raw.plastic_lights);
         mLightsF = fb.create();
 
@@ -214,7 +207,6 @@
         mActiveScene.appendShader(mPlasticF);
         mActiveScene.appendShader(mDiffuseF);
         mActiveScene.appendShader(mTextureF);
-        mActiveScene.appendShader(mGenericV);
     }
 
     public void prepareToRender(Scene s) {
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
index d8e48e8..e272cc5 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
@@ -47,22 +47,17 @@
         mDistValue = new Float3(0, 0, 45);
         mPosValue = new Float3(0, 4, 0);
 
-        mRotateX = new RotateComponent("RotateX", new Float3(1, 0, 0), mRotateXValue);
-        mRotateY = new RotateComponent("RotateY", new Float3(0, 1, 0), mRotateYValue);
-        mDist = new TranslateComponent("Distance", mDistValue);
-        mPosition = new TranslateComponent("Distance", mPosValue);
-
         // Make a camera transform we can manipulate
-        mCameraRig = new CompoundTransform();
+        mCameraRig = scene.appendNewCompoundTransform();
         mCameraRig.setName("CameraRig");
-        mCameraRig.addComponent(mPosition);
-        mCameraRig.addComponent(mRotateY);
-        mCameraRig.addComponent(mRotateX);
-        mCameraRig.addComponent(mDist);
-        scene.appendTransform(mCameraRig);
-        mCamera = new Camera();
+
+        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);
-        scene.appendCamera(mCamera);
     }
 
     public Camera getCamera() {
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 c7bd809..6f56223 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
@@ -64,11 +64,6 @@
 
         unitTests = new ArrayList<UnitTest>();
 
-        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_primitives(this, mRes, mCtx));
         unitTests.add(new UT_constant(this, mRes, mCtx));
         unitTests.add(new UT_vector(this, mRes, mCtx));
@@ -79,10 +74,17 @@
         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_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_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/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/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/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/Type.cpp b/tools/aidl/Type.cpp
index 42e1226..d572af6 100755
--- a/tools/aidl/Type.cpp
+++ b/tools/aidl/Type.cpp
@@ -123,7 +123,7 @@
     RPC_DATA_TYPE = new RpcDataType();
     NAMES.Add(RPC_DATA_TYPE);
 
-    RPC_ERROR_TYPE = new UserDataType("com.android.athome.rpc", "RpcError",
+    RPC_ERROR_TYPE = new UserDataType("android.support.place.rpc", "RpcError",
                                     true, __FILE__, __LINE__);
     NAMES.Add(RPC_ERROR_TYPE);
 
@@ -1234,7 +1234,7 @@
 // ================================================================
 
 RpcDataType::RpcDataType()
-    :UserDataType("com.android.athome.rpc", "RpcData", true, true, true)
+    :UserDataType("android.support.place.rpc", "RpcData", true, true, true)
 {
 }
 
diff --git a/tools/aidl/generate_java_rpc.cpp b/tools/aidl/generate_java_rpc.cpp
index ecff3a1..e5fa076 100644
--- a/tools/aidl/generate_java_rpc.cpp
+++ b/tools/aidl/generate_java_rpc.cpp
@@ -7,26 +7,26 @@
 
 Type* SERVICE_CONTEXT_TYPE = new Type("android.content",
         "Context", Type::BUILT_IN, false, false, false);
-Type* PRESENTER_BASE_TYPE = new Type("com.android.athome.connector",
+Type* PRESENTER_BASE_TYPE = new Type("android.support.place.connector",
         "EventListener", Type::BUILT_IN, false, false, false);
-Type* PRESENTER_LISTENER_BASE_TYPE = new Type("com.android.athome.connector",
+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("com.android.athome.connector", "Broker",
+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("com.android.athome.connector", "Connector",
+Type* RPC_CONNECTOR_TYPE = new Type("android.support.place.connector", "Connector",
         Type::BUILT_IN, false, false, false);
-Type* RPC_ENDPOINT_INFO_TYPE = new UserDataType("com.android.athome.rpc",
+Type* RPC_ENDPOINT_INFO_TYPE = new UserDataType("android.support.place.rpc",
         "EndpointInfo", true, __FILE__, __LINE__);
-Type* RPC_RESULT_HANDLER_TYPE = new UserDataType("com.android.athome.rpc", "RpcResultHandler",
+Type* RPC_RESULT_HANDLER_TYPE = new UserDataType("android.support.place.rpc", "RpcResultHandler",
         true, __FILE__, __LINE__);
-Type* RPC_ERROR_LISTENER_TYPE = new Type("com.android.athome.rpc", "RpcErrorHandler",
+Type* RPC_ERROR_LISTENER_TYPE = new Type("android.support.place.rpc", "RpcErrorHandler",
         Type::BUILT_IN, false, false, false);
-Type* RPC_CONTEXT_TYPE = new UserDataType("com.android.athome.rpc", "RpcContext", true,
+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,
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 6afdfbe..d6abbaa 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
@@ -184,8 +184,8 @@
     }
 
     @Override
-    public InputBindResult startInput(IInputMethodClient arg0, IInputContext arg1, EditorInfo arg2,
-            boolean arg3, boolean arg4) throws RemoteException {
+    public InputBindResult startInput(IInputMethodClient client, IInputContext inputContext,
+            EditorInfo attribute, int controlFlags) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
@@ -209,10 +209,11 @@
     }
 
     @Override
-    public void windowGainedFocus(IInputMethodClient arg0, IBinder arg1, boolean arg2,
-            boolean arg3, int arg4, boolean arg5, int arg6) throws RemoteException {
+    public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken,
+            int controlFlags, int softInputMode, int windowFlags, EditorInfo attribute,
+            IInputContext inputContext) throws RemoteException {
         // TODO Auto-generated method stub
-
+        return null;
     }
 
     @Override
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/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 0134456..1b64f3e 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1754,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,