Merge "Visualizer: replace the FFT implementation with a faster one." into gingerbread
diff --git a/Android.mk b/Android.mk
index b1f43f8..c1324c9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -118,10 +118,6 @@
 	core/java/android/net/IThrottleManager.aidl \
 	core/java/android/os/IHardwareService.aidl \
 	core/java/android/os/IMessenger.aidl \
-	core/java/android/os/storage/IMountService.aidl \
-	core/java/android/os/storage/IMountServiceListener.aidl \
-	core/java/android/os/storage/IMountShutdownObserver.aidl \
-	core/java/android/os/storage/IObbActionListener.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/INetStatService.aidl \
 	core/java/android/os/IPermissionController.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1efe77c..5618eaa 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -67,6 +67,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverb_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverbtest_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/storage/*)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/9.xml b/api/9.xml
index f18623f..46f087f 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -379555,7 +379555,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value=""100-Continue""
+ value=""100-continue""
  static="true"
  final="true"
  deprecated="not deprecated"
diff --git a/api/current.xml b/api/current.xml
index 65a8a8e..e7b0e3b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -96284,6 +96284,17 @@
  visibility="public"
 >
 </field>
+<field name="ERROR_CANNOT_RESUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1008"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ERROR_DEVICE_NOT_FOUND"
  type="int"
  transient="false"
@@ -121330,6 +121341,16 @@
  visibility="public"
 >
 </field>
+<field name="SERIAL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TAGS"
  type="java.lang.String"
  transient="false"
@@ -166320,6 +166341,29 @@
 <parameter name="event" type="android.view.MotionEvent">
 </parameter>
 </method>
+<method name="setCursorController"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursorController" type="android.widget.TextView.CursorController">
+</parameter>
+</method>
+<field name="mCursorController"
+ type="android.widget.TextView.CursorController"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</field>
 </class>
 <class name="BaseKeyListener"
  extends="android.text.method.MetaKeyKeyListener"
@@ -222804,6 +222848,108 @@
 >
 </method>
 </class>
+<interface name="TextView.CursorController"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="draw"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="canvas" type="android.graphics.Canvas">
+</parameter>
+</method>
+<method name="getOffsetX"
+ return="float"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOffsetY"
+ return="float"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hide"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onTouchEvent"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
+<method name="show"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="updatePosition"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<field name="FADE_OUT_DURATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="400"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <interface name="TextView.OnEditorActionListener"
  abstract="true"
  static="true"
@@ -246360,7 +246506,7 @@
 <method name="copySign"
  return="double"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="true"
  final="false"
@@ -246375,7 +246521,7 @@
 <method name="copySign"
  return="float"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="true"
  final="false"
@@ -264811,6 +264957,19 @@
 <parameter name="p" type="java.security.Permission">
 </parameter>
 </method>
+<method name="isValidIP6Address"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ipAddress" type="java.lang.String">
+</parameter>
+</method>
 </class>
 <class name="SocketTimeoutException"
  extends="java.io.InterruptedIOException"
@@ -403590,7 +403749,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;100-Continue&quot;"
+ value="&quot;100-continue&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c88e086..c96d562 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3298,10 +3298,6 @@
             Slog.e(TAG, "Failed to find provider info for " + name);
             return null;
         }
-        if (holder.permissionFailure != null) {
-            throw new SecurityException("Permission " + holder.permissionFailure
-                    + " required for provider " + name);
-        }
 
         IContentProvider prov = installProvider(context, holder.provider,
                 holder.info, true);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e99d4b4..2870c50 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2844,6 +2844,7 @@
                 boolean returnValue;
 
                 boolean hasListeners;
+                boolean changesMade = false;
                 List<String> keysModified = null;
                 Set<OnSharedPreferenceChangeListener> listeners = null;
 
@@ -2857,17 +2858,31 @@
 
                     synchronized (this) {
                         if (mClear) {
-                            mMap.clear();
+                            if (!mMap.isEmpty()) {
+                                changesMade = true;
+                                mMap.clear();
+                            }
                             mClear = false;
                         }
 
                         for (Entry<String, Object> e : mModified.entrySet()) {
                             String k = e.getKey();
                             Object v = e.getValue();
-                            if (v == this) {
-                                mMap.remove(k);
+                            if (v == this) {  // magic value for a removal mutation
+                                if (mMap.containsKey(k)) {
+                                    mMap.remove(k);
+                                    changesMade = true;
+                                }
                             } else {
-                                mMap.put(k, v);
+                                boolean isSame = false;
+                                if (mMap.containsKey(k)) {
+                                    Object existingValue = mMap.get(k);
+                                    isSame = existingValue != null && existingValue.equals(v);
+                                }
+                                if (!isSame) {
+                                    mMap.put(k, v);
+                                    changesMade = true;
+                                }
                             }
 
                             if (hasListeners) {
@@ -2878,7 +2893,7 @@
                         mModified.clear();
                     }
 
-                    returnValue = writeFileLocked();
+                    returnValue = writeFileLocked(changesMade);
                 }
 
                 if (hasListeners) {
@@ -2923,9 +2938,16 @@
             return str;
         }
 
-        private boolean writeFileLocked() {
+        private boolean writeFileLocked(boolean changesMade) {
             // Rename the current file so it may be used as a backup during the next read
             if (mFile.exists()) {
+                if (!changesMade) {
+                    // If the file already exists, but no changes were
+                    // made to the underlying map, it's wasteful to
+                    // re-write the file.  Return as if we wrote it
+                    // out.
+                    return true;
+                }
                 if (!mBackupFile.exists()) {
                     if (!mFile.renameTo(mBackupFile)) {
                         Log.e(TAG, "Couldn't rename file " + mFile
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 78bbb4f4..009671f 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -35,6 +35,9 @@
 
 /**
  * A simple dialog containing an {@link android.widget.DatePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
  */
 public class DatePickerDialog extends AlertDialog implements OnClickListener, 
         OnDateChangedListener {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 81b28b9..416f289 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -326,28 +326,19 @@
     /** Information you can retrieve about a particular application. */
     public static class ContentProviderHolder implements Parcelable {
         public final ProviderInfo info;
-        public final String permissionFailure;
         public IContentProvider provider;
         public boolean noReleaseNeeded;
 
         public ContentProviderHolder(ProviderInfo _info) {
             info = _info;
-            permissionFailure = null;
         }
 
-        public ContentProviderHolder(ProviderInfo _info,
-                String _permissionFailure) {
-            info = _info;
-            permissionFailure = _permissionFailure;
-        }
-        
         public int describeContents() {
             return 0;
         }
 
         public void writeToParcel(Parcel dest, int flags) {
             info.writeToParcel(dest, 0);
-            dest.writeString(permissionFailure);
             if (provider != null) {
                 dest.writeStrongBinder(provider.asBinder());
             } else {
@@ -369,7 +360,6 @@
 
         private ContentProviderHolder(Parcel source) {
             info = ProviderInfo.CREATOR.createFromParcel(source);
-            permissionFailure = source.readString();
             provider = ContentProviderNative.asInterface(
                 source.readStrongBinder());
             noReleaseNeeded = source.readInt() != 0;
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 002b01f..62d3f7d 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -32,6 +32,9 @@
 
 /**
  * A dialog that prompts the user for the time of day using a {@link TimePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.</p>
  */
 public class TimePickerDialog extends AlertDialog implements OnClickListener, 
         OnTimeChangedListener {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 9b9f796..dc4e9c4 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -86,6 +86,7 @@
     private String mReadPermission;
     private String mWritePermission;
     private PathPermission[] mPathPermissions;
+    private boolean mExported;
 
     private Transport mTransport = new Transport();
 
@@ -257,9 +258,9 @@
             final Context context = getContext();
             final String rperm = getReadPermission();
             final int pid = Binder.getCallingPid();
-            if (rperm == null
+            if (mExported && (rperm == null
                     || context.checkPermission(rperm, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
+                    == PackageManager.PERMISSION_GRANTED)) {
                 return;
             }
             
@@ -303,9 +304,9 @@
             final Context context = getContext();
             final String wperm = getWritePermission();
             final int pid = Binder.getCallingPid();
-            if (wperm == null
+            if (mExported && (wperm == null
                     || context.checkPermission(wperm, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
+                    == PackageManager.PERMISSION_GRANTED)) {
                 return true;
             }
             
@@ -786,6 +787,7 @@
                 setReadPermission(info.readPermission);
                 setWritePermission(info.writePermission);
                 setPathPermissions(info.pathPermissions);
+                mExported = info.exported;
             }
             ContentProvider.this.onCreate();
         }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 7901b155..244d126 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -389,15 +389,17 @@
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
-        pw.println(prefix + "uid=" + uid + " taskAffinity=" + taskAffinity);
-        if (theme != 0) {
-            pw.println(prefix + "theme=0x" + Integer.toHexString(theme));
-        }
-        pw.println(prefix + "flags=0x" + Integer.toHexString(flags)
-                + " processName=" + processName);
+        pw.println(prefix + "processName=" + processName);
+        pw.println(prefix + "taskAffinity=" + taskAffinity);
+        pw.println(prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags)
+                + " theme=0x" + Integer.toHexString(theme));
         pw.println(prefix + "sourceDir=" + sourceDir);
-        pw.println(prefix + "publicSourceDir=" + publicSourceDir);
-        pw.println(prefix + "resourceDirs=" + resourceDirs);
+        if (!sourceDir.equals(publicSourceDir)) {
+            pw.println(prefix + "publicSourceDir=" + publicSourceDir);
+        }
+        if (resourceDirs != null) {
+            pw.println(prefix + "resourceDirs=" + resourceDirs);
+        }
         pw.println(prefix + "dataDir=" + dataDir);
         if (sharedLibraryFiles != null) {
             pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
index b18d784..838c5ff 100644
--- a/core/java/android/content/res/ObbInfo.java
+++ b/core/java/android/content/res/ObbInfo.java
@@ -25,6 +25,9 @@
  * @hide
  */
 public class ObbInfo implements Parcelable {
+    /** Flag noting that this OBB is an overlay patch for a base OBB. */
+    public static final int OBB_OVERLAY = 1 << 0;
+
     /**
      * The name of the package to which the OBB file belongs.
      */
@@ -35,13 +38,26 @@
      */
     public int version;
 
+    /**
+     * The flags relating to the OBB.
+     */
+    public int flags;
+
     public ObbInfo() {
     }
 
     public String toString() {
-        return "ObbInfo{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " packageName=" + packageName + ",version=" + version + "}";
+        StringBuilder sb = new StringBuilder();
+        sb.append("ObbInfo{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" packageName=");
+        sb.append(packageName);
+        sb.append(",version=");
+        sb.append(version);
+        sb.append(",flags=");
+        sb.append(flags);
+        sb.append('}');
+        return sb.toString();
     }
 
     public int describeContents() {
@@ -51,6 +67,7 @@
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeString(packageName);
         dest.writeInt(version);
+        dest.writeInt(flags);
     }
 
     public static final Parcelable.Creator<ObbInfo> CREATOR
@@ -67,5 +84,6 @@
     private ObbInfo(Parcel source) {
         packageName = source.readString();
         version = source.readInt();
+        flags = source.readInt();
     }
 }
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index b0c149d..37fdeb6 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -708,9 +708,7 @@
         outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
         outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
         outValue.density = data[index+AssetManager.STYLE_DENSITY];
-        if (type == TypedValue.TYPE_STRING) {
-            outValue.string = loadStringValueAt(index);
-        }
+        outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
         return true;
     }
 
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index f72de67..e432a47 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -960,6 +960,7 @@
         private static final String KEY_PREVIEW_SIZE = "preview-size";
         private static final String KEY_PREVIEW_FORMAT = "preview-format";
         private static final String KEY_PREVIEW_FRAME_RATE = "preview-frame-rate";
+        private static final String KEY_PREVIEW_FPS_RANGE = "preview-fps-range";
         private static final String KEY_PICTURE_SIZE = "picture-size";
         private static final String KEY_PICTURE_FORMAT = "picture-format";
         private static final String KEY_JPEG_THUMBNAIL_SIZE = "jpeg-thumbnail-size";
@@ -1138,9 +1139,9 @@
          */
         public static final String SCENE_MODE_BARCODE = "barcode";
 
-        // Values for focus mode settings.
         /**
-         * Auto-focus mode.
+         * Auto-focus mode. Applications should call {@link
+         * #autoFocus(AutoFocusCallback)} to start the focus in this mode.
          */
         public static final String FOCUS_MODE_AUTO = "auto";
 
@@ -1149,6 +1150,12 @@
          * {@link #autoFocus(AutoFocusCallback)} in this mode.
          */
         public static final String FOCUS_MODE_INFINITY = "infinity";
+
+        /**
+         * Macro (close-up) focus mode. Applications should call
+         * {@link #autoFocus(AutoFocusCallback)} to start the focus in this
+         * mode.
+         */
         public static final String FOCUS_MODE_MACRO = "macro";
 
         /**
@@ -1170,7 +1177,9 @@
          * Continuous auto focus mode. The camera continuously tries to focus.
          * This is ideal for shooting video or shooting photo of moving object.
          * Auto focus starts when the parameter is set. Applications should not
-         * call {@link #autoFocus(AutoFocusCallback)} in this mode.
+         * call {@link #autoFocus(AutoFocusCallback)} in this mode. To stop
+         * continuous focus, applications should change the focus mode to other
+         * modes.
          */
         public static final String FOCUS_MODE_CONTINUOUS = "continuous";
 
@@ -1211,6 +1220,22 @@
          */
         public static final String METERING_MODE_SPOT = "spot";
 
+        /**
+         * The array index of minimum preview fps for use with {@link
+         * #getPreviewFpsRange(int[])} or {@link
+         * #getSupportedPreviewFpsRange()}.
+         * @hide
+         */
+        public static final int PREVIEW_FPS_MIN_INDEX = 0;
+
+        /**
+         * The array index of maximum preview fps for use with {@link
+         * #getPreviewFpsRange(int[])} or {@link
+         * #getSupportedPreviewFpsRange()}.
+         * @hide
+         */
+        public static final int PREVIEW_FPS_MAX_INDEX = 1;
+
         // Formats for setPreviewFormat and setPictureFormat.
         private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp";
         private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp";
@@ -1474,6 +1499,64 @@
         }
 
         /**
+         * Sets the maximum and maximum preview fps. This controls the rate of
+         * preview frames received in {@link #PreviewCallback}. The minimum and
+         * maximum preview fps must be one of the elements from {@link
+         * #getSupportedPreviewFpsRange}.
+         *
+         * @param min the minimum preview fps (scaled by 1000).
+         * @param max the maximum preview fps (scaled by 1000).
+         * @throws RuntimeException if fps range is invalid.
+         * @see #setPreviewCallbackWithBuffer(Camera.PreviewCallback)
+         * @see #getSupportedPreviewFpsRange()
+         * @hide
+         */
+        public void setPreviewFpsRange(int min, int max) {
+            set(KEY_PREVIEW_FPS_RANGE, "" + min + "," + max);
+        }
+
+        /**
+         * Returns the current minimum and maximum preview fps. The values are
+         * one of the elements returned by {@link #getSupportedPreviewFpsRange}.
+         *
+         * @return range the minimum and maximum preview fps (scaled by 1000).
+         * @see #PREVIEW_FPS_MIN_INDEX
+         * @see #PREVIEW_FPS_MAX_INDEX
+         * @see #getSupportedPreviewFpsRange()
+         * @hide
+         */
+        public void getPreviewFpsRange(int[] range) {
+            if (range == null || range.length != 2) {
+                throw new IllegalArgumentException(
+                        "range must be an float array with two elements.");
+            }
+            splitInt(get(KEY_PREVIEW_FPS_RANGE), range);
+        }
+
+        /**
+         * Gets the supported preview fps (frame-per-second) ranges. Each range
+         * contains a minimum fps and maximum fps. If minimum fps equals to
+         * maximum fps, the camera outputs frames in fixed frame rate. If not,
+         * the camera outputs frames in auto frame rate. The actual frame rate
+         * fluctuates between the minimum and the maximum. The values are
+         * multiplied by 1000 and represented in integers. For example, if frame
+         * rate is 26.623 frames per second, the value is 26623.
+         *
+         * @return a list of supported preview fps ranges. This method returns a
+         *         list with at least one element. Every element is an int array
+         *         of two values - minimum fps and maximum fps. The list is
+         *         sorted from small to large (first by maximum fps and then
+         *         minimum fps).
+         * @see #PREVIEW_FPS_MIN_INDEX
+         * @see #PREVIEW_FPS_MAX_INDEX
+         * @hide
+         */
+        public List<int[]> getSupportedPreviewFpsRange() {
+            String str = get(KEY_PREVIEW_FPS_RANGE + SUPPORTED_VALUES_SUFFIX);
+            return splitRange(str);
+        }
+
+        /**
          * Sets the image format for preview pictures.
          * <p>If this is never called, the default format will be
          * {@link android.graphics.ImageFormat#NV21}, which
@@ -1948,6 +2031,7 @@
          * @see #FOCUS_MODE_INFINITY
          * @see #FOCUS_MODE_MACRO
          * @see #FOCUS_MODE_FIXED
+         * @see #FOCUS_MODE_EDOF
          * @see #FOCUS_MODE_CONTINUOUS
          */
         public String getFocusMode() {
@@ -2175,10 +2259,7 @@
                 throw new IllegalArgumentException(
                         "output must be an float array with three elements.");
             }
-            List<Float> distances = splitFloat(get(KEY_FOCUS_DISTANCES));
-            output[0] = distances.get(0);
-            output[1] = distances.get(1);
-            output[2] = distances.get(2);
+            splitFloat(get(KEY_FOCUS_DISTANCES), output);
         }
 
         /**
@@ -2246,19 +2327,27 @@
             return substrings;
         }
 
-        // Splits a comma delimited string to an ArrayList of Float.
-        // Return null if the passing string is null or the size is 0.
-        private ArrayList<Float> splitFloat(String str) {
-            if (str == null) return null;
+        private void splitInt(String str, int[] output) {
+            if (str == null) return;
 
             StringTokenizer tokenizer = new StringTokenizer(str, ",");
-            ArrayList<Float> substrings = new ArrayList<Float>();
+            int index = 0;
             while (tokenizer.hasMoreElements()) {
                 String token = tokenizer.nextToken();
-                substrings.add(Float.parseFloat(token));
+                output[index++] = Integer.parseInt(token);
             }
-            if (substrings.size() == 0) return null;
-            return substrings;
+        }
+
+        // Splits a comma delimited string to an ArrayList of Float.
+        private void splitFloat(String str, float[] output) {
+            if (str == null) return;
+
+            StringTokenizer tokenizer = new StringTokenizer(str, ",");
+            int index = 0;
+            while (tokenizer.hasMoreElements()) {
+                String token = tokenizer.nextToken();
+                output[index++] = Float.parseFloat(token);
+            }
         }
 
         // Returns the value of a float parameter.
@@ -2309,5 +2398,30 @@
             Log.e(TAG, "Invalid size parameter string=" + str);
             return null;
         }
+
+        // Splits a comma delimited string to an ArrayList of int array.
+        // Example string: "(10000,26623),(10000,30000)". Return null if the
+        // passing string is null or the size is 0.
+        private ArrayList<int[]> splitRange(String str) {
+            if (str == null || str.charAt(0) != '('
+                    || str.charAt(str.length() - 1) != ')') {
+                Log.e(TAG, "Invalid range list string=" + str);
+                return null;
+            }
+
+            ArrayList<int[]> rangeList = new ArrayList<int[]>();
+            int endIndex, fromIndex = 1;
+            do {
+                int[] range = new int[2];
+                endIndex = str.indexOf("),(", fromIndex);
+                if (endIndex == -1) endIndex = str.length() - 1;
+                splitInt(str.substring(fromIndex, endIndex), range);
+                rangeList.add(range);
+                fromIndex = endIndex + 3;
+            } while (endIndex != str.length() - 1);
+
+            if (rangeList.size() == 0) return null;
+            return rangeList;
+        }
     };
 }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f6d237a..d2c3eaa 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1525,7 +1525,22 @@
      * Typically the atmospheric pressure is read from a
      * {@link Sensor#TYPE_PRESSURE} sensor. The pressure at sea level must be
      * known, usually it can be retrieved from airport databases in the
-     * vicinity.
+     * vicinity. If unknown, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE}
+     * as an approximation, but absolute altitudes won't be accurate.
+     * </p>
+     * <p>
+     * To calculate altitude differences, you must calculate the difference
+     * between the altitudes at both points. If you don't know the altitude
+     * as sea level, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE} instead,
+     * which will give good results considering the range of pressure typically
+     * involved.
+     * </p>
+     * <p>
+     * <code><ul>
+     *  float altitude_difference =
+     *      getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure_at_point2)
+     *      - getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure_at_point1);
+     * </ul></code>
      * </p>
      *
      * @param p0 pressure at sea level
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index e69c324..cafe0f9 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -51,14 +51,14 @@
     public final static String COLUMN_ID = "id";
 
     /**
-     * The client-supplied title for this download.  This will be displayed in system notifications,
-     * if enabled.
+     * The client-supplied title for this download.  This will be displayed in system notifications.
+     * Defaults to the empty string.
      */
     public final static String COLUMN_TITLE = "title";
 
     /**
      * The client-supplied description of this download.  This will be displayed in system
-     * notifications, if enabled.
+     * notifications.  Defaults to the empty string.
      */
     public final static String COLUMN_DESCRIPTION = "description";
 
@@ -68,22 +68,24 @@
     public final static String COLUMN_URI = "uri";
 
     /**
-     * Internet Media Type of the downloaded file.  This will be filled in based on the server's
-     * response once the download has started.
+     * Internet Media Type of the downloaded file.  If no value is provided upon creation, this will
+     * initially be null and will be filled in based on the server's response once the download has
+     * started.
      *
      * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
      */
     public final static String COLUMN_MEDIA_TYPE = "media_type";
 
     /**
-     * Total size of the download in bytes.  This will be filled in once the download starts.
+     * Total size of the download in bytes.  This will initially be -1 and will be filled in once
+     * the download starts.
      */
     public final static String COLUMN_TOTAL_SIZE_BYTES = "total_size";
 
     /**
      * Uri where downloaded file will be stored.  If a destination is supplied by client, that URI
-     * will be used here.  Otherwise, the value will be filled in with a generated URI once the
-     * download has started.
+     * will be used here.  Otherwise, the value will initially be null and will be filled in with a
+     * generated URI once the download has started.
      */
     public final static String COLUMN_LOCAL_URI = "local_uri";
 
@@ -185,6 +187,12 @@
     public final static int ERROR_DEVICE_NOT_FOUND = 1007;
 
     /**
+     * Value of {@link #COLUMN_ERROR_CODE} when some possibly transient error occurred but we can't
+     * resume the download.
+     */
+    public final static int ERROR_CANNOT_RESUME = 1008;
+
+    /**
      * Broadcast intent action sent by the download manager when a download completes.
      */
     public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
@@ -682,8 +690,13 @@
             if (column.equals(COLUMN_MEDIA_TYPE)) {
                 return getUnderlyingString(Downloads.COLUMN_MIME_TYPE);
             }
+
             assert column.equals(COLUMN_LOCAL_URI);
-            return Uri.fromFile(new File(getUnderlyingString(Downloads._DATA))).toString();
+            String localUri = getUnderlyingString(Downloads._DATA);
+            if (localUri == null) {
+                return null;
+            }
+            return Uri.fromFile(new File(localUri)).toString();
         }
 
         private long translateLong(String column) {
@@ -715,7 +728,8 @@
             if (translateStatus(status) != STATUS_FAILED) {
                 return 0; // arbitrary value when status is not an error
             }
-            if ((400 <= status && status < 490) || (500 <= status && status < 600)) {
+            if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
+                    || (500 <= status && status < 600)) {
                 // HTTP status code
                 return status;
             }
@@ -740,6 +754,9 @@
                 case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
                     return ERROR_DEVICE_NOT_FOUND;
 
+                case Downloads.Impl.STATUS_CANNOT_RESUME:
+                    return ERROR_CANNOT_RESUME;
+
                 default:
                     return ERROR_UNKNOWN;
             }
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 214510d..eb7117b 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -53,6 +53,10 @@
     private boolean mEnabled;
     private BroadcastReceiver mStateReceiver;
 
+    // DEFAULT and HIPRI are the same connection.  If we're one of these we need to check if
+    // the other is also disconnected before we reset sockets
+    private boolean mIsDefaultOrHipri = false;
+
     /**
      * Create a new MobileDataStateTracker
      * @param context the application context of the caller
@@ -71,6 +75,10 @@
         } else {
             mApnTypeToWatchFor = mApnType;
         }
+        if (netType == ConnectivityManager.TYPE_MOBILE ||
+                netType == ConnectivityManager.TYPE_MOBILE_HIPRI) {
+            mIsDefaultOrHipri = true;
+        }
 
         mPhoneService = null;
         if(netType == ConnectivityManager.TYPE_MOBILE) {
@@ -138,6 +146,7 @@
     }
 
     private class MobileDataStateReceiver extends BroadcastReceiver {
+        ConnectivityManager mConnectivityManager;
         public void onReceive(Context context, Intent intent) {
             synchronized(this) {
                 if (intent.getAction().equals(TelephonyIntents.
@@ -190,7 +199,26 @@
                                 }
 
                                 setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
-                                if (mInterfaceName != null) {
+                                boolean doReset = true;
+                                if (mIsDefaultOrHipri == true) {
+                                    // both default and hipri must go down before we reset
+                                    int typeToCheck = (Phone.APN_TYPE_DEFAULT.equals(mApnType) ?
+                                            ConnectivityManager.TYPE_MOBILE_HIPRI :
+                                            ConnectivityManager.TYPE_MOBILE);
+                                    if (mConnectivityManager == null) {
+                                        mConnectivityManager =
+                                                (ConnectivityManager)context.getSystemService(
+                                                Context.CONNECTIVITY_SERVICE);
+                                    }
+                                    if (mConnectivityManager != null) {
+                                        NetworkInfo info = mConnectivityManager.getNetworkInfo(
+                                                    typeToCheck);
+                                        if (info != null && info.isConnected() == true) {
+                                            doReset = false;
+                                        }
+                                    }
+                                }
+                                if (doReset && mInterfaceName != null) {
                                     NetworkUtils.resetConnections(mInterfaceName);
                                 }
                                 // can't do this here - ConnectivityService needs it to clear stuff
@@ -212,6 +240,7 @@
                                 if (mInterfaceName == null) {
                                     Log.d(TAG, "CONNECTED event did not supply interface name.");
                                 }
+                                mDefaultGatewayAddr = intent.getIntExtra(Phone.DATA_GATEWAY_KEY, 0);
                                 setDetailedState(DetailedState.CONNECTED, reason, apnName);
                                 break;
                         }
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index a3ae01b..e4f3d5c 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -128,4 +128,19 @@
                 |  (addrBytes[0] & 0xff);
         return addr;
     }
+
+    public static int v4StringToInt(String str) {
+        int result = 0;
+        String[] array = str.split("\\.");
+        if (array.length != 4) return 0;
+        try {
+            result = Integer.parseInt(array[3]);
+            result = (result << 8) + Integer.parseInt(array[2]);
+            result = (result << 8) + Integer.parseInt(array[1]);
+            result = (result << 8) + Integer.parseInt(array[0]);
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+        return result;
+    }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a0a3bdf..9fe6e01 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -23,6 +23,7 @@
 import android.util.Log;
 import android.util.Printer;
 import android.util.SparseArray;
+import android.util.TimeUtils;
 
 /**
  * A class providing access to battery usage statistics, including information on
@@ -1399,6 +1400,7 @@
                         pw.println(getDischargeStartLevel());
                 pw.print(prefix); pw.print("    Discharge cycle current level: ");
                         pw.println(getDischargeCurrentLevel());
+            } else {
                 pw.print(prefix); pw.println("  Device is currently plugged into power");
                 pw.print(prefix); pw.print("    Last discharge cycle start level: "); 
                         pw.println(getDischargeStartLevel());
@@ -1576,8 +1578,10 @@
                             Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
                             if (ew != null) {
                                 pw.print(prefix); pw.print("      * Killed for wake lock use: ");
-                                        pw.print(ew.usedTime); pw.print("ms over ");
-                                        pw.print(ew.overTime); pw.print("ms (");
+                                        TimeUtils.formatDuration(ew.usedTime, pw);
+                                        pw.print(" over ");
+                                        TimeUtils.formatDuration(ew.overTime, pw);
+                                        pw.print(" (");
                                         pw.print((ew.usedTime*100)/ew.overTime);
                                         pw.println("%)");
                             }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index fdd3573..8925bd0 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -62,6 +62,9 @@
     /** The name of the hardware (from the kernel command line or /proc). */
     public static final String HARDWARE = getString("ro.hardware");
 
+    /** A hardware serial number, if available.  Alphanumeric only, case-insensitive. */ 
+    public static final String SERIAL = getString("ro.serialno");
+  
     /** Various version strings. */
     public static class VERSION {
         /**
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index a9d7342..d360140 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -192,10 +192,11 @@
         pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
         if (mQueue != null) {
             synchronized (mQueue) {
+                long now = SystemClock.uptimeMillis();
                 Message msg = mQueue.mMessages;
                 int n = 0;
                 while (msg != null) {
-                    pw.println(prefix + "  Message " + n + ": " + msg);
+                    pw.println(prefix + "  Message " + n + ": " + msg.toString(now));
                     n++;
                     msg = msg.next;
                 }
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 476da1d..49b72fe 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.TimeUtils;
 
 /**
  * 
@@ -366,13 +367,17 @@
     }
 
     public String toString() {
+        return toString(SystemClock.uptimeMillis());
+    }
+
+    String toString(long now) {
         StringBuilder   b = new StringBuilder();
         
         b.append("{ what=");
         b.append(what);
 
         b.append(" when=");
-        b.append(when);
+        TimeUtils.formatDuration(when-now, b);
 
         if (arg1 != 0) {
             b.append(" arg1=");
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
deleted file mode 100644
index 5c69214..0000000
--- a/core/java/android/os/storage/IMountService.aidl
+++ /dev/null
@@ -1,184 +0,0 @@
-/* //device/java/android/android/os/IUsb.aidl
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-package android.os.storage;
-
-import android.os.storage.IMountServiceListener;
-import android.os.storage.IMountShutdownObserver;
-import android.os.storage.IObbActionListener;
-
-/** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
- * In particular, the ordering of the methods below must match the 
- * _TRANSACTION enum in IMountService.cpp
- * @hide - Applications should use android.os.storage.StorageManager to access
- * storage functions.
- */
-interface IMountService
-{
-    /**
-     * Registers an IMountServiceListener for receiving async
-     * notifications.
-     */
-    void registerListener(IMountServiceListener listener);
-
-    /**
-     * Unregisters an IMountServiceListener
-     */
-    void unregisterListener(IMountServiceListener listener);
-
-    /**
-     * Returns true if a USB mass storage host is connected
-     */
-    boolean isUsbMassStorageConnected();
-
-    /**
-     * Enables / disables USB mass storage.
-     * The caller should check actual status of enabling/disabling
-     * USB mass storage via StorageEventListener.
-     */
-    void setUsbMassStorageEnabled(boolean enable);
-
-    /**
-     * Returns true if a USB mass storage host is enabled (media is shared)
-     */
-    boolean isUsbMassStorageEnabled();
-
-    /**
-     * Mount external storage at given mount point.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int mountVolume(String mountPoint);
-
-    /**
-     * Safely unmount external storage at given mount point.
-     * The unmount is an asynchronous operation. Applications
-     * should register StorageEventListener for storage related
-     * status changes.
-     * 
-     */
-    void unmountVolume(String mountPoint, boolean force);
-
-    /**
-     * Format external storage given a mount point.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int formatVolume(String mountPoint);
-
-    /**
-     * Returns an array of pids with open files on
-     * the specified path.
-     */
-    int[] getStorageUsers(String path);
-
-    /**
-     * Gets the state of a volume via its mountpoint.
-     */
-    String getVolumeState(String mountPoint);
-
-    /*
-     * Creates a secure container with the specified parameters.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid);
-
-    /*
-     * Finalize a container which has just been created and populated.
-     * After finalization, the container is immutable.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int finalizeSecureContainer(String id);
-
-    /*
-     * Destroy a secure container, and free up all resources associated with it.
-     * NOTE: Ensure all references are released prior to deleting.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int destroySecureContainer(String id, boolean force);
-
-    /*
-     * Mount a secure container with the specified key and owner UID.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int mountSecureContainer(String id, String key, int ownerUid);
-
-    /*
-     * Unount a secure container.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int unmountSecureContainer(String id, boolean force);
-
-    /*
-     * Returns true if the specified container is mounted
-     */
-    boolean isSecureContainerMounted(String id);
-
-    /*
-     * Rename an unmounted secure container.
-     * Returns an int consistent with MountServiceResultCode
-     */
-    int renameSecureContainer(String oldId, String newId);
-
-    /*
-     * Returns the filesystem path of a mounted secure container.
-     */
-    String getSecureContainerPath(String id);
-
-    /**
-     * Gets an Array of currently known secure container IDs
-     */
-    String[] getSecureContainerList();
-
-    /**
-     * Shuts down the MountService and gracefully unmounts all external media.
-     * Invokes call back once the shutdown is complete.
-     */
-    void shutdown(IMountShutdownObserver observer);
-
-    /**
-     * Call into MountService by PackageManager to notify that its done
-     * processing the media status update request.
-     */
-    void finishMediaUpdate();
-
-    /**
-     * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only
-     * allows the calling process's UID access to the contents.
-     *
-     * MountService will call back to the supplied IObbActionListener to inform
-     * it of the terminal state of the call.
-     */
-    void mountObb(String filename, String key, IObbActionListener token);
-
-    /**
-     * Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any
-     * program using it will be forcibly killed to unmount the image.
-     *
-     * MountService will call back to the supplied IObbActionListener to inform
-     * it of the terminal state of the call.
-     */
-    void unmountObb(String filename, boolean force, IObbActionListener token);
-
-    /**
-     * Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere.
-     */
-    boolean isObbMounted(String filename);
-
-    /**
-     * Gets the path to the mounted Opaque Binary Blob (OBB).
-     */
-    String getMountedObbPath(String filename);
-}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
new file mode 100644
index 0000000..60ea95c
--- /dev/null
+++ b/core/java/android/os/storage/IMountService.java
@@ -0,0 +1,1046 @@
+/*
+ * 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.os.storage;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * WARNING! Update IMountService.h and IMountService.cpp if you change this
+ * file. In particular, the ordering of the methods below must match the
+ * _TRANSACTION enum in IMountService.cpp
+ * 
+ * @hide - Applications should use android.os.storage.StorageManager to access
+ *       storage functions.
+ */
+public interface IMountService extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IMountService {
+        private static class Proxy implements IMountService {
+            private IBinder mRemote;
+
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+
+            public IBinder asBinder() {
+                return mRemote;
+            }
+
+            public String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+
+            /**
+             * Registers an IMountServiceListener for receiving async
+             * notifications.
+             */
+            public void registerListener(IMountServiceListener listener) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeStrongBinder((listener != null ? listener.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Unregisters an IMountServiceListener
+             */
+            public void unregisterListener(IMountServiceListener listener) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeStrongBinder((listener != null ? listener.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Returns true if a USB mass storage host is connected
+             */
+            public boolean isUsbMassStorageConnected() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_isUsbMassStorageConnected, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Enables / disables USB mass storage. The caller should check
+             * actual status of enabling/disabling USB mass storage via
+             * StorageEventListener.
+             */
+            public void setUsbMassStorageEnabled(boolean enable) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt((enable ? 1 : 0));
+                    mRemote.transact(Stub.TRANSACTION_setUsbMassStorageEnabled, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Returns true if a USB mass storage host is enabled (media is
+             * shared)
+             */
+            public boolean isUsbMassStorageEnabled() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_isUsbMassStorageEnabled, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Mount external storage at given mount point. Returns an int
+             * consistent with MountServiceResultCode
+             */
+            public int mountVolume(String mountPoint) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(mountPoint);
+                    mRemote.transact(Stub.TRANSACTION_mountVolume, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Safely unmount external storage at given mount point. The unmount
+             * is an asynchronous operation. Applications should register
+             * StorageEventListener for storage related status changes.
+             */
+            public void unmountVolume(String mountPoint, boolean force) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(mountPoint);
+                    _data.writeInt((force ? 1 : 0));
+                    mRemote.transact(Stub.TRANSACTION_unmountVolume, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Format external storage given a mount point. Returns an int
+             * consistent with MountServiceResultCode
+             */
+            public int formatVolume(String mountPoint) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(mountPoint);
+                    mRemote.transact(Stub.TRANSACTION_formatVolume, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Returns an array of pids with open files on the specified path.
+             */
+            public int[] getStorageUsers(String path) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int[] _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(path);
+                    mRemote.transact(Stub.TRANSACTION_getStorageUsers, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.createIntArray();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Gets the state of a volume via its mountpoint.
+             */
+            public String getVolumeState(String mountPoint) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(mountPoint);
+                    mRemote.transact(Stub.TRANSACTION_getVolumeState, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Creates a secure container with the specified parameters. Returns
+             * an int consistent with MountServiceResultCode
+             */
+            public int createSecureContainer(String id, int sizeMb, String fstype, String key,
+                    int ownerUid) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeInt(sizeMb);
+                    _data.writeString(fstype);
+                    _data.writeString(key);
+                    _data.writeInt(ownerUid);
+                    mRemote.transact(Stub.TRANSACTION_createSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Destroy a secure container, and free up all resources associated
+             * with it. NOTE: Ensure all references are released prior to
+             * deleting. Returns an int consistent with MountServiceResultCode
+             */
+            public int destroySecureContainer(String id, boolean force) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeInt((force ? 1 : 0));
+                    mRemote.transact(Stub.TRANSACTION_destroySecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Finalize a container which has just been created and populated.
+             * After finalization, the container is immutable. Returns an int
+             * consistent with MountServiceResultCode
+             */
+            public int finalizeSecureContainer(String id) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    mRemote.transact(Stub.TRANSACTION_finalizeSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Mount a secure container with the specified key and owner UID.
+             * Returns an int consistent with MountServiceResultCode
+             */
+            public int mountSecureContainer(String id, String key, int ownerUid)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeString(key);
+                    _data.writeInt(ownerUid);
+                    mRemote.transact(Stub.TRANSACTION_mountSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Unount a secure container. Returns an int consistent with
+             * MountServiceResultCode
+             */
+            public int unmountSecureContainer(String id, boolean force) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    _data.writeInt((force ? 1 : 0));
+                    mRemote.transact(Stub.TRANSACTION_unmountSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Returns true if the specified container is mounted
+             */
+            public boolean isSecureContainerMounted(String id) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    mRemote.transact(Stub.TRANSACTION_isSecureContainerMounted, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Rename an unmounted secure container. Returns an int consistent
+             * with MountServiceResultCode
+             */
+            public int renameSecureContainer(String oldId, String newId) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(oldId);
+                    _data.writeString(newId);
+                    mRemote.transact(Stub.TRANSACTION_renameSecureContainer, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /*
+             * Returns the filesystem path of a mounted secure container.
+             */
+            public String getSecureContainerPath(String id) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    mRemote.transact(Stub.TRANSACTION_getSecureContainerPath, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Gets an Array of currently known secure container IDs
+             */
+            public String[] getSecureContainerList() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String[] _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_getSecureContainerList, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.createStringArray();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Shuts down the MountService and gracefully unmounts all external
+             * media. Invokes call back once the shutdown is complete.
+             */
+            public void shutdown(IMountShutdownObserver observer)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeStrongBinder((observer != null ? observer.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_shutdown, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Call into MountService by PackageManager to notify that its done
+             * processing the media status update request.
+             */
+            public void finishMediaUpdate() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_finishMediaUpdate, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Mounts an Opaque Binary Blob (OBB) with the specified decryption
+             * key and only allows the calling process's UID access to the
+             * contents. MountService will call back to the supplied
+             * IObbActionListener to inform it of the terminal state of the
+             * call.
+             */
+            public void mountObb(String filename, String key, IObbActionListener token)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    _data.writeString(key);
+                    _data.writeStrongBinder((token != null ? token.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_mountObb, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Unmounts an Opaque Binary Blob (OBB). When the force flag is
+             * specified, any program using it will be forcibly killed to
+             * unmount the image. MountService will call back to the supplied
+             * IObbActionListener to inform it of the terminal state of the
+             * call.
+             */
+            public void unmountObb(String filename, boolean force, IObbActionListener token)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    _data.writeInt((force ? 1 : 0));
+                    _data.writeStrongBinder((token != null ? token.asBinder() : null));
+                    mRemote.transact(Stub.TRANSACTION_unmountObb, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Checks whether the specified Opaque Binary Blob (OBB) is mounted
+             * somewhere.
+             */
+            public boolean isObbMounted(String filename) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    mRemote.transact(Stub.TRANSACTION_isObbMounted, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            /**
+             * Gets the path to the mounted Opaque Binary Blob (OBB).
+             */
+            public String getMountedObbPath(String filename) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    mRemote.transact(Stub.TRANSACTION_getMountedObbPath, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+        }
+
+        private static final String DESCRIPTOR = "IMountService";
+
+        static final int TRANSACTION_registerListener = IBinder.FIRST_CALL_TRANSACTION + 0;
+
+        static final int TRANSACTION_unregisterListener = IBinder.FIRST_CALL_TRANSACTION + 1;
+
+        static final int TRANSACTION_isUsbMassStorageConnected = IBinder.FIRST_CALL_TRANSACTION + 2;
+
+        static final int TRANSACTION_setUsbMassStorageEnabled = IBinder.FIRST_CALL_TRANSACTION + 3;
+
+        static final int TRANSACTION_isUsbMassStorageEnabled = IBinder.FIRST_CALL_TRANSACTION + 4;
+
+        static final int TRANSACTION_mountVolume = IBinder.FIRST_CALL_TRANSACTION + 5;
+
+        static final int TRANSACTION_unmountVolume = IBinder.FIRST_CALL_TRANSACTION + 6;
+
+        static final int TRANSACTION_formatVolume = IBinder.FIRST_CALL_TRANSACTION + 7;
+
+        static final int TRANSACTION_getStorageUsers = IBinder.FIRST_CALL_TRANSACTION + 8;
+
+        static final int TRANSACTION_getVolumeState = IBinder.FIRST_CALL_TRANSACTION + 9;
+
+        static final int TRANSACTION_createSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 10;
+
+        static final int TRANSACTION_finalizeSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 11;
+
+        static final int TRANSACTION_destroySecureContainer = IBinder.FIRST_CALL_TRANSACTION + 12;
+
+        static final int TRANSACTION_mountSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 13;
+
+        static final int TRANSACTION_unmountSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 14;
+
+        static final int TRANSACTION_isSecureContainerMounted = IBinder.FIRST_CALL_TRANSACTION + 15;
+
+        static final int TRANSACTION_renameSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 16;
+
+        static final int TRANSACTION_getSecureContainerPath = IBinder.FIRST_CALL_TRANSACTION + 17;
+
+        static final int TRANSACTION_getSecureContainerList = IBinder.FIRST_CALL_TRANSACTION + 18;
+
+        static final int TRANSACTION_shutdown = IBinder.FIRST_CALL_TRANSACTION + 19;
+
+        static final int TRANSACTION_finishMediaUpdate = IBinder.FIRST_CALL_TRANSACTION + 20;
+
+        static final int TRANSACTION_mountObb = IBinder.FIRST_CALL_TRANSACTION + 21;
+
+        static final int TRANSACTION_unmountObb = IBinder.FIRST_CALL_TRANSACTION + 22;
+
+        static final int TRANSACTION_isObbMounted = IBinder.FIRST_CALL_TRANSACTION + 23;
+
+        static final int TRANSACTION_getMountedObbPath = IBinder.FIRST_CALL_TRANSACTION + 24;
+
+        /**
+         * Cast an IBinder object into an IMountService interface, generating a
+         * proxy if needed.
+         */
+        public static IMountService asInterface(IBinder obj) {
+            if (obj == null) {
+                return null;
+            }
+            IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+            if (iin != null && iin instanceof IMountService) {
+                return (IMountService) iin;
+            }
+            return new IMountService.Stub.Proxy(obj);
+        }
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            attachInterface(this, DESCRIPTOR);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply,
+                int flags) throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION: {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+                case TRANSACTION_registerListener: {
+                    data.enforceInterface(DESCRIPTOR);
+                    IMountServiceListener listener;
+                    listener = IMountServiceListener.Stub.asInterface(data.readStrongBinder());
+                    registerListener(listener);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_unregisterListener: {
+                    data.enforceInterface(DESCRIPTOR);
+                    IMountServiceListener listener;
+                    listener = IMountServiceListener.Stub.asInterface(data.readStrongBinder());
+                    unregisterListener(listener);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_isUsbMassStorageConnected: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean result = isUsbMassStorageConnected();
+                    reply.writeNoException();
+                    reply.writeInt((result ? 1 : 0));
+                    return true;
+                }
+                case TRANSACTION_setUsbMassStorageEnabled: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean enable;
+                    enable = 0 != data.readInt();
+                    setUsbMassStorageEnabled(enable);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_isUsbMassStorageEnabled: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean result = isUsbMassStorageEnabled();
+                    reply.writeNoException();
+                    reply.writeInt((result ? 1 : 0));
+                    return true;
+                }
+                case TRANSACTION_mountVolume: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String mountPoint;
+                    mountPoint = data.readString();
+                    int resultCode = mountVolume(mountPoint);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_unmountVolume: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String mountPoint;
+                    mountPoint = data.readString();
+                    boolean force;
+                    force = 0 != data.readInt();
+                    unmountVolume(mountPoint, force);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_formatVolume: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String mountPoint;
+                    mountPoint = data.readString();
+                    int result = formatVolume(mountPoint);
+                    reply.writeNoException();
+                    reply.writeInt(result);
+                    return true;
+                }
+                case TRANSACTION_getStorageUsers: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String path;
+                    path = data.readString();
+                    int[] pids = getStorageUsers(path);
+                    reply.writeNoException();
+                    reply.writeIntArray(pids);
+                    return true;
+                }
+                case TRANSACTION_getVolumeState: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String mountPoint;
+                    mountPoint = data.readString();
+                    String state = getVolumeState(mountPoint);
+                    reply.writeNoException();
+                    reply.writeString(state);
+                    return true;
+                }
+                case TRANSACTION_createSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    int sizeMb;
+                    sizeMb = data.readInt();
+                    String fstype;
+                    fstype = data.readString();
+                    String key;
+                    key = data.readString();
+                    int ownerUid;
+                    ownerUid = data.readInt();
+                    int resultCode = createSecureContainer(id, sizeMb, fstype, key, ownerUid);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_finalizeSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    int resultCode = finalizeSecureContainer(id);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_destroySecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    boolean force;
+                    force = 0 != data.readInt();
+                    int resultCode = destroySecureContainer(id, force);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_mountSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    String key;
+                    key = data.readString();
+                    int ownerUid;
+                    ownerUid = data.readInt();
+                    int resultCode = mountSecureContainer(id, key, ownerUid);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_unmountSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    boolean force;
+                    force = 0 != data.readInt();
+                    int resultCode = unmountSecureContainer(id, force);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_isSecureContainerMounted: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    boolean status = isSecureContainerMounted(id);
+                    reply.writeNoException();
+                    reply.writeInt((status ? 1 : 0));
+                    return true;
+                }
+                case TRANSACTION_renameSecureContainer: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String oldId;
+                    oldId = data.readString();
+                    String newId;
+                    newId = data.readString();
+                    int resultCode = renameSecureContainer(oldId, newId);
+                    reply.writeNoException();
+                    reply.writeInt(resultCode);
+                    return true;
+                }
+                case TRANSACTION_getSecureContainerPath: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    String path = getSecureContainerPath(id);
+                    reply.writeNoException();
+                    reply.writeString(path);
+                    return true;
+                }
+                case TRANSACTION_getSecureContainerList: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String[] ids = getSecureContainerList();
+                    reply.writeNoException();
+                    reply.writeStringArray(ids);
+                    return true;
+                }
+                case TRANSACTION_shutdown: {
+                    data.enforceInterface(DESCRIPTOR);
+                    IMountShutdownObserver observer;
+                    observer = IMountShutdownObserver.Stub.asInterface(data
+                            .readStrongBinder());
+                    shutdown(observer);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_finishMediaUpdate: {
+                    data.enforceInterface(DESCRIPTOR);
+                    finishMediaUpdate();
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_mountObb: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    String key;
+                    key = data.readString();
+                    IObbActionListener observer;
+                    observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
+                    mountObb(filename, key, observer);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_unmountObb: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    boolean force;
+                    force = 0 != data.readInt();
+                    IObbActionListener observer;
+                    observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
+                    unmountObb(filename, force, observer);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_isObbMounted: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    boolean status = isObbMounted(filename);
+                    reply.writeNoException();
+                    reply.writeInt((status ? 1 : 0));
+                    return true;
+                }
+                case TRANSACTION_getMountedObbPath: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    String mountedPath = getMountedObbPath(filename);
+                    reply.writeNoException();
+                    reply.writeString(mountedPath);
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+    }
+
+    /*
+     * Creates a secure container with the specified parameters. Returns an int
+     * consistent with MountServiceResultCode
+     */
+    public int createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid)
+            throws RemoteException;
+
+    /*
+     * Destroy a secure container, and free up all resources associated with it.
+     * NOTE: Ensure all references are released prior to deleting. Returns an
+     * int consistent with MountServiceResultCode
+     */
+    public int destroySecureContainer(String id, boolean force) throws RemoteException;
+
+    /*
+     * Finalize a container which has just been created and populated. After
+     * finalization, the container is immutable. Returns an int consistent with
+     * MountServiceResultCode
+     */
+    public int finalizeSecureContainer(String id) throws RemoteException;
+
+    /**
+     * Call into MountService by PackageManager to notify that its done
+     * processing the media status update request.
+     */
+    public void finishMediaUpdate() throws RemoteException;
+
+    /**
+     * Format external storage given a mount point. Returns an int consistent
+     * with MountServiceResultCode
+     */
+    public int formatVolume(String mountPoint) throws RemoteException;
+
+    /**
+     * Gets the path to the mounted Opaque Binary Blob (OBB).
+     */
+    public String getMountedObbPath(String filename) throws RemoteException;
+
+    /**
+     * Gets an Array of currently known secure container IDs
+     */
+    public String[] getSecureContainerList() throws RemoteException;
+
+    /*
+     * Returns the filesystem path of a mounted secure container.
+     */
+    public String getSecureContainerPath(String id) throws RemoteException;
+
+    /**
+     * Returns an array of pids with open files on the specified path.
+     */
+    public int[] getStorageUsers(String path) throws RemoteException;
+
+    /**
+     * Gets the state of a volume via its mountpoint.
+     */
+    public String getVolumeState(String mountPoint) throws RemoteException;
+
+    /**
+     * Checks whether the specified Opaque Binary Blob (OBB) is mounted
+     * somewhere.
+     */
+    public boolean isObbMounted(String filename) throws RemoteException;
+
+    /*
+     * Returns true if the specified container is mounted
+     */
+    public boolean isSecureContainerMounted(String id) throws RemoteException;
+
+    /**
+     * Returns true if a USB mass storage host is connected
+     */
+    public boolean isUsbMassStorageConnected() throws RemoteException;
+
+    /**
+     * Returns true if a USB mass storage host is enabled (media is shared)
+     */
+    public boolean isUsbMassStorageEnabled() throws RemoteException;
+
+    /**
+     * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and
+     * only allows the calling process's UID access to the contents.
+     * MountService will call back to the supplied IObbActionListener to inform
+     * it of the terminal state of the call.
+     */
+    public void mountObb(String filename, String key, IObbActionListener token)
+            throws RemoteException;
+
+    /*
+     * Mount a secure container with the specified key and owner UID. Returns an
+     * int consistent with MountServiceResultCode
+     */
+    public int mountSecureContainer(String id, String key, int ownerUid) throws RemoteException;
+
+    /**
+     * Mount external storage at given mount point. Returns an int consistent
+     * with MountServiceResultCode
+     */
+    public int mountVolume(String mountPoint) throws RemoteException;
+
+    /**
+     * Registers an IMountServiceListener for receiving async notifications.
+     */
+    public void registerListener(IMountServiceListener listener) throws RemoteException;
+
+    /*
+     * Rename an unmounted secure container. Returns an int consistent with
+     * MountServiceResultCode
+     */
+    public int renameSecureContainer(String oldId, String newId) throws RemoteException;
+
+    /**
+     * Enables / disables USB mass storage. The caller should check actual
+     * status of enabling/disabling USB mass storage via StorageEventListener.
+     */
+    public void setUsbMassStorageEnabled(boolean enable) throws RemoteException;
+
+    /**
+     * Shuts down the MountService and gracefully unmounts all external media.
+     * Invokes call back once the shutdown is complete.
+     */
+    public void shutdown(IMountShutdownObserver observer) throws RemoteException;
+
+    /**
+     * Unmounts an Opaque Binary Blob (OBB). When the force flag is specified,
+     * any program using it will be forcibly killed to unmount the image.
+     * MountService will call back to the supplied IObbActionListener to inform
+     * it of the terminal state of the call.
+     */
+    public void unmountObb(String filename, boolean force, IObbActionListener token)
+            throws RemoteException;
+
+    /*
+     * Unount a secure container. Returns an int consistent with
+     * MountServiceResultCode
+     */
+    public int unmountSecureContainer(String id, boolean force) throws RemoteException;
+
+    /**
+     * Safely unmount external storage at given mount point. The unmount is an
+     * asynchronous operation. Applications should register StorageEventListener
+     * for storage related status changes.
+     */
+    public void unmountVolume(String mountPoint, boolean force) throws RemoteException;
+
+    /**
+     * Unregisters an IMountServiceListener
+     */
+    public void unregisterListener(IMountServiceListener listener) throws RemoteException;
+}
diff --git a/core/java/android/os/storage/IMountServiceListener.aidl b/core/java/android/os/storage/IMountServiceListener.aidl
deleted file mode 100644
index 883413a..0000000
--- a/core/java/android/os/storage/IMountServiceListener.aidl
+++ /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.
- */
-
-package android.os.storage;
-
-/**
- * Callback class for receiving events from MountService.
- *
- * @hide - Applications should use android.os.storage.IStorageEventListener
- * for storage event callbacks.
- */
-interface IMountServiceListener {
-    /**
-     * Detection state of USB Mass Storage has changed
-     *
-     * @param available true if a UMS host is connected.
-     */
-    void onUsbMassStorageConnectionChanged(boolean connected);
-
-    /**
-     * Storage state has changed.
-     *
-     * @param path The volume mount path.
-     * @param oldState The old state of the volume.
-     * @param newState The new state of the volume.
-     *
-     * Note: State is one of the values returned by Environment.getExternalStorageState()
-     */
-    void onStorageStateChanged(String path, String oldState, String newState);
-}
diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java
new file mode 100644
index 0000000..d5c5fa5
--- /dev/null
+++ b/core/java/android/os/storage/IMountServiceListener.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+package android.os.storage;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * Callback class for receiving events from MountService.
+ * 
+ * @hide - Applications should use IStorageEventListener for storage event
+ *       callbacks.
+ */
+public interface IMountServiceListener extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IMountServiceListener {
+        private static final String DESCRIPTOR = "IMountServiceListener";
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            this.attachInterface(this, DESCRIPTOR);
+        }
+
+        /**
+         * Cast an IBinder object into an IMountServiceListener interface,
+         * generating a proxy if needed.
+         */
+        public static IMountServiceListener asInterface(IBinder obj) {
+            if ((obj == null)) {
+                return null;
+            }
+            IInterface iin = (IInterface) obj.queryLocalInterface(DESCRIPTOR);
+            if (((iin != null) && (iin instanceof IMountServiceListener))) {
+                return ((IMountServiceListener) iin);
+            }
+            return new IMountServiceListener.Stub.Proxy(obj);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION: {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+                case TRANSACTION_onUsbMassStorageConnectionChanged: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean connected;
+                    connected = (0 != data.readInt());
+                    this.onUsbMassStorageConnectionChanged(connected);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_onStorageStateChanged: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String path;
+                    path = data.readString();
+                    String oldState;
+                    oldState = data.readString();
+                    String newState;
+                    newState = data.readString();
+                    this.onStorageStateChanged(path, oldState, newState);
+                    reply.writeNoException();
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+
+        private static class Proxy implements IMountServiceListener {
+            private IBinder mRemote;
+
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+
+            public IBinder asBinder() {
+                return mRemote;
+            }
+
+            public String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+
+            /**
+             * Detection state of USB Mass Storage has changed
+             * 
+             * @param available true if a UMS host is connected.
+             */
+            public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(((connected) ? (1) : (0)));
+                    mRemote.transact(Stub.TRANSACTION_onUsbMassStorageConnectionChanged, _data,
+                            _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            /**
+             * Storage state has changed.
+             * 
+             * @param path The volume mount path.
+             * @param oldState The old state of the volume.
+             * @param newState The new state of the volume. Note: State is one
+             *            of the values returned by
+             *            Environment.getExternalStorageState()
+             */
+            public void onStorageStateChanged(String path, String oldState, String newState)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(path);
+                    _data.writeString(oldState);
+                    _data.writeString(newState);
+                    mRemote.transact(Stub.TRANSACTION_onStorageStateChanged, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+        }
+
+        static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0);
+
+        static final int TRANSACTION_onStorageStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 1);
+    }
+
+    /**
+     * Detection state of USB Mass Storage has changed
+     * 
+     * @param available true if a UMS host is connected.
+     */
+    public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException;
+
+    /**
+     * Storage state has changed.
+     * 
+     * @param path The volume mount path.
+     * @param oldState The old state of the volume.
+     * @param newState The new state of the volume. Note: State is one of the
+     *            values returned by Environment.getExternalStorageState()
+     */
+    public void onStorageStateChanged(String path, String oldState, String newState)
+            throws RemoteException;
+}
diff --git a/core/java/android/os/storage/IMountShutdownObserver.aidl b/core/java/android/os/storage/IMountShutdownObserver.aidl
deleted file mode 100644
index 0aa8a45..0000000
--- a/core/java/android/os/storage/IMountShutdownObserver.aidl
+++ /dev/null
@@ -1,33 +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.
- */
-
-package android.os.storage;
-
-/**
- * Callback class for receiving events related
- * to shutdown.
- *
- * @hide - For internal consumption only.
- */
-interface IMountShutdownObserver {
-    /**
-     * This method is called when the shutdown
-     * of MountService completed.
-     * @param statusCode indicates success or failure
-     * of the shutdown.
-     */
-    void onShutDownComplete(int statusCode);
-}
diff --git a/core/java/android/os/storage/IMountShutdownObserver.java b/core/java/android/os/storage/IMountShutdownObserver.java
new file mode 100644
index 0000000..d946e1a
--- /dev/null
+++ b/core/java/android/os/storage/IMountShutdownObserver.java
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+package android.os.storage;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * Callback class for receiving events related to shutdown.
+ * 
+ * @hide - For internal consumption only.
+ */
+public interface IMountShutdownObserver extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IMountShutdownObserver {
+        private static final java.lang.String DESCRIPTOR = "IMountShutdownObserver";
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            this.attachInterface(this, DESCRIPTOR);
+        }
+
+        /**
+         * Cast an IBinder object into an IMountShutdownObserver interface,
+         * generating a proxy if needed.
+         */
+        public static IMountShutdownObserver asInterface(IBinder obj) {
+            if ((obj == null)) {
+                return null;
+            }
+            IInterface iin = (IInterface) obj.queryLocalInterface(DESCRIPTOR);
+            if (((iin != null) && (iin instanceof IMountShutdownObserver))) {
+                return ((IMountShutdownObserver) iin);
+            }
+            return new IMountShutdownObserver.Stub.Proxy(obj);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION: {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+                case TRANSACTION_onShutDownComplete: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int statusCode;
+                    statusCode = data.readInt();
+                    this.onShutDownComplete(statusCode);
+                    reply.writeNoException();
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+
+        private static class Proxy implements IMountShutdownObserver {
+            private IBinder mRemote;
+
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+
+            public IBinder asBinder() {
+                return mRemote;
+            }
+
+            public java.lang.String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+
+            /**
+             * This method is called when the shutdown of MountService
+             * completed.
+             * 
+             * @param statusCode indicates success or failure of the shutdown.
+             */
+            public void onShutDownComplete(int statusCode) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(statusCode);
+                    mRemote.transact(Stub.TRANSACTION_onShutDownComplete, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+        }
+
+        static final int TRANSACTION_onShutDownComplete = (IBinder.FIRST_CALL_TRANSACTION + 0);
+    }
+
+    /**
+     * This method is called when the shutdown of MountService completed.
+     * 
+     * @param statusCode indicates success or failure of the shutdown.
+     */
+    public void onShutDownComplete(int statusCode) throws RemoteException;
+}
diff --git a/core/java/android/os/storage/IObbActionListener.aidl b/core/java/android/os/storage/IObbActionListener.aidl
deleted file mode 100644
index 78d7a9e..0000000
--- a/core/java/android/os/storage/IObbActionListener.aidl
+++ /dev/null
@@ -1,34 +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.os.storage;
-
-/**
- * Callback class for receiving events from MountService about
- * Opaque Binary Blobs (OBBs).
- *
- * @hide - Applications should use android.os.storage.StorageManager
- * to interact with OBBs.
- */
-interface IObbActionListener {
-    /**
-     * Return from an OBB action result.
-     *
-     * @param filename the path to the OBB the operation was performed on
-     * @param returnCode status of the operation
-     */
-    void onObbResult(String filename, String status);
-}
diff --git a/core/java/android/os/storage/IObbActionListener.java b/core/java/android/os/storage/IObbActionListener.java
new file mode 100644
index 0000000..2c098ac
--- /dev/null
+++ b/core/java/android/os/storage/IObbActionListener.java
@@ -0,0 +1,130 @@
+/*
+ * 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.os.storage;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * Callback class for receiving events from MountService about Opaque Binary
+ * Blobs (OBBs).
+ * 
+ * @hide - Applications should use StorageManager to interact with OBBs.
+ */
+public interface IObbActionListener extends IInterface {
+    /** Local-side IPC implementation stub class. */
+    public static abstract class Stub extends Binder implements IObbActionListener {
+        private static final String DESCRIPTOR = "IObbActionListener";
+
+        /** Construct the stub at attach it to the interface. */
+        public Stub() {
+            this.attachInterface(this, DESCRIPTOR);
+        }
+
+        /**
+         * Cast an IBinder object into an IObbActionListener interface,
+         * generating a proxy if needed.
+         */
+        public static IObbActionListener asInterface(IBinder obj) {
+            if ((obj == null)) {
+                return null;
+            }
+            IInterface iin = (IInterface) obj.queryLocalInterface(DESCRIPTOR);
+            if (((iin != null) && (iin instanceof IObbActionListener))) {
+                return ((IObbActionListener) iin);
+            }
+            return new IObbActionListener.Stub.Proxy(obj);
+        }
+
+        public IBinder asBinder() {
+            return this;
+        }
+
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            switch (code) {
+                case INTERFACE_TRANSACTION: {
+                    reply.writeString(DESCRIPTOR);
+                    return true;
+                }
+                case TRANSACTION_onObbResult: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String filename;
+                    filename = data.readString();
+                    String status;
+                    status = data.readString();
+                    this.onObbResult(filename, status);
+                    reply.writeNoException();
+                    return true;
+                }
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+
+        private static class Proxy implements IObbActionListener {
+            private IBinder mRemote;
+
+            Proxy(IBinder remote) {
+                mRemote = remote;
+            }
+
+            public IBinder asBinder() {
+                return mRemote;
+            }
+
+            public String getInterfaceDescriptor() {
+                return DESCRIPTOR;
+            }
+
+            /**
+             * Return from an OBB action result.
+             * 
+             * @param filename the path to the OBB the operation was performed
+             *            on
+             * @param returnCode status of the operation
+             */
+            public void onObbResult(String filename, String status) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(filename);
+                    _data.writeString(status);
+                    mRemote.transact(Stub.TRANSACTION_onObbResult, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+        }
+
+        static final int TRANSACTION_onObbResult = (IBinder.FIRST_CALL_TRANSACTION + 0);
+    }
+
+    /**
+     * Return from an OBB action result.
+     * 
+     * @param filename the path to the OBB the operation was performed on
+     * @param returnCode status of the operation
+     */
+    public void onObbResult(String filename, String status) throws RemoteException;
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index cb1794f..df0b69c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -304,6 +304,8 @@
      * file matches a package ID that is owned by the calling program's UID.
      * That is, shared UID applications can obtain access to any other
      * application's OBB that shares its UID.
+     * <p>
+     * STOPSHIP document more; discuss lack of guarantees of security
      * 
      * @param filename the path to the OBB file
      * @param key decryption key
@@ -328,6 +330,8 @@
      * file matches a package ID that is owned by the calling program's UID.
      * That is, shared UID applications can obtain access to any other
      * application's OBB that shares its UID.
+     * <p>
+     * STOPSHIP document more; discuss lack of guarantees of security
      * 
      * @param filename path to the OBB file
      * @param force whether to kill any programs using this in order to unmount
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
index 1da6d7a..d634672 100644
--- a/core/java/android/pim/vcard/VCardBuilder.java
+++ b/core/java/android/pim/vcard/VCardBuilder.java
@@ -1463,6 +1463,9 @@
                     parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
                 } else if (VCardUtils.isMobilePhoneLabel(label)) {
                     parameterList.add(VCardConstants.PARAM_TYPE_CELL);
+                } else if (mIsV30) {
+                    // This label is appropriately encoded in appendTypeParameters.
+                    parameterList.add(label);
                 } else {
                     final String upperLabel = label.toUpperCase();
                     if (VCardUtils.isValidInV21ButUnknownToContactsPhoteType(upperLabel)) {
@@ -1741,21 +1744,30 @@
         // which would be recommended way in vcard 3.0 though not valid in vCard 2.1.
         boolean first = true;
         for (final String typeValue : types) {
-            // Note: vCard 3.0 specifies the different type of acceptable type Strings, but
-            //       we don't emit that kind of vCard 3.0 specific type since there should be
-            //       high probabilyty in which external importers cannot understand them.
-            //
-            // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they
-            //      are quoted.)
-            if (!VCardUtils.isV21Word(typeValue)) {
-                continue;
+            if (VCardConfig.isV30(mVCardType)) {
+                // Note: vCard 3.0 specifies the different type of acceptable type Strings, but
+                //       we don't emit that kind of vCard 3.0 specific type since there should be
+                //       high probabilyty in which external importers cannot understand them.
+                //
+                // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they
+                //      are quoted.)
+                if (first) {
+                    first = false;
+                } else {
+                    mBuilder.append(VCARD_PARAM_SEPARATOR);
+                }
+                appendTypeParameter(VCardUtils.toStringAvailableAsV30ParameValue(typeValue));
+            } else {  // vCard 2.1
+                if (!VCardUtils.isV21Word(typeValue)) {
+                    continue;
+                }
+                if (first) {
+                    first = false;
+                } else {
+                    mBuilder.append(VCARD_PARAM_SEPARATOR);
+                }
+                appendTypeParameter(typeValue);
             }
-            if (first) {
-                first = false;
-            } else {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-            }
-            appendTypeParameter(typeValue);
         }
     }
 
diff --git a/core/java/android/pim/vcard/VCardEntryConstructor.java b/core/java/android/pim/vcard/VCardEntryConstructor.java
index 290ca2b..ae4ec29 100644
--- a/core/java/android/pim/vcard/VCardEntryConstructor.java
+++ b/core/java/android/pim/vcard/VCardEntryConstructor.java
@@ -157,11 +157,15 @@
         mParamType = type;
     }
 
+    @Override
     public void propertyParamValue(String value) {
         if (mParamType == null) {
             // From vCard 2.1 specification. vCard 3.0 formally does not allow this case.
             mParamType = "TYPE";
         }
+        if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
+            value = encodeString(value, mCharsetForDecodedBytes);
+        }
         mCurrentProperty.addParameter(mParamType, value);
         mParamType = null;
     }
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
index 11b112b..f972799 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -16,10 +16,10 @@
 package android.pim.vcard;
 
 import android.content.ContentProviderOperation;
-import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.CommonDataKinds.Im;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Data;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 
@@ -477,6 +477,43 @@
         return true;
     }
 
+    /**
+     * <P>
+     * Returns String available as parameter value in vCard 3.0.
+     * </P>
+     * <P>
+     * RFC 2426 requires vCard composer to quote parameter values when it contains
+     * semi-colon, for example (See RFC 2426 for more information).
+     * This method checks whether the given String can be used without quotes.
+     * </P>
+     * <P>
+     * Note: We remove DQUOTE silently for now.
+     * </P>
+     */
+    public static String toStringAvailableAsV30ParameValue(String value) {
+        if (TextUtils.isEmpty(value)) {
+            value = "";
+        }
+        final int asciiFirst = 0x20;
+        final int asciiLast = 0x7E;  // included
+        final StringBuilder builder = new StringBuilder();
+        final int length = value.length();
+        boolean needQuote = false;
+        for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
+            final int codePoint = value.codePointAt(i);
+            if (codePoint < asciiFirst || codePoint == '"') {
+                // CTL characters and DQUOTE are never accepted. Remove them.
+                continue;
+            }
+            builder.appendCodePoint(codePoint);
+            if (codePoint == ':' || codePoint == ',' || codePoint == ' ') {
+                needQuote = true;
+            }
+        }
+        final String result = builder.toString();
+        return ((needQuote || result.isEmpty()) ? ('"' + result + '"') : result);
+    }
+
     public static String toHalfWidthString(final String orgString) {
         if (TextUtils.isEmpty(orgString)) {
             return null;
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index c9b5512..6bf0d5b 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -1060,6 +1060,16 @@
         public static final int STATUS_PRECONDITION_FAILED = 412;
 
         /**
+         * The lowest-valued error status that is not an actual HTTP status code.
+         */
+        public static final int MIN_ARTIFICIAL_ERROR_STATUS = 489;
+
+        /**
+         * Some possibly transient error occurred, but we can't resume the download.
+         */
+        public static final int STATUS_CANNOT_RESUME = 489;
+
+        /**
          * This download was canceled
          */
         public static final int STATUS_CANCELED = 490;
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index bb98bce..13cb5e6 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -417,8 +417,8 @@
         }
     }
 
-    private static final class START implements NoCopySpan { };
-    private static final class END implements NoCopySpan { };
+    private static final class START implements NoCopySpan { }
+    private static final class END implements NoCopySpan { }
     
     /*
      * Public constants
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 9af42cc..79a0c37 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -16,30 +16,38 @@
 
 package android.text.method;
 
-import android.util.Log;
+import android.text.Layout;
+import android.text.Selection;
+import android.text.Spannable;
 import android.view.KeyEvent;
-import android.graphics.Rect;
-import android.text.*;
-import android.widget.TextView;
-import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.MotionEvent;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.TextView.CursorController;
 
 // XXX this doesn't extend MetaKeyKeyListener because the signatures
 // don't match.  Need to figure that out.  Meanwhile the meta keys
 // won't work in fields that don't take input.
 
-public class
-ArrowKeyMovementMethod
-implements MovementMethod
-{
+public class ArrowKeyMovementMethod implements MovementMethod {
+    /**
+     * An optional controller for the cursor.
+     * Use {@link #setCursorController(CursorController)} to set this field.
+     */
+    protected CursorController mCursorController;
+
+    private boolean isCap(Spannable buffer) {
+        return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
+                (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
+    }
+
+    private boolean isAlt(Spannable buffer) {
+        return MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_ALT_ON) == 1;
+    }
+
     private boolean up(TextView widget, Spannable buffer) {
-        boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                        KeyEvent.META_SHIFT_ON) == 1) ||
-                      (MetaKeyKeyListener.getMetaState(buffer,
-                        MetaKeyKeyListener.META_SELECTING) != 0);
-        boolean alt = MetaKeyKeyListener.getMetaState(buffer,
-                        KeyEvent.META_ALT_ON) == 1;
+        boolean cap = isCap(buffer);
+        boolean alt = isAlt(buffer);
         Layout layout = widget.getLayout();
 
         if (cap) {
@@ -60,12 +68,8 @@
     }
 
     private boolean down(TextView widget, Spannable buffer) {
-        boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                        KeyEvent.META_SHIFT_ON) == 1) ||
-                      (MetaKeyKeyListener.getMetaState(buffer,
-                        MetaKeyKeyListener.META_SELECTING) != 0);
-        boolean alt = MetaKeyKeyListener.getMetaState(buffer,
-                        KeyEvent.META_ALT_ON) == 1;
+        boolean cap = isCap(buffer);
+        boolean alt = isAlt(buffer);
         Layout layout = widget.getLayout();
 
         if (cap) {
@@ -86,12 +90,8 @@
     }
 
     private boolean left(TextView widget, Spannable buffer) {
-        boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                        KeyEvent.META_SHIFT_ON) == 1) ||
-                      (MetaKeyKeyListener.getMetaState(buffer,
-                        MetaKeyKeyListener.META_SELECTING) != 0);
-        boolean alt = MetaKeyKeyListener.getMetaState(buffer,
-                        KeyEvent.META_ALT_ON) == 1;
+        boolean cap = isCap(buffer);
+        boolean alt = isAlt(buffer);
         Layout layout = widget.getLayout();
 
         if (cap) {
@@ -110,12 +110,8 @@
     }
 
     private boolean right(TextView widget, Spannable buffer) {
-        boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                        KeyEvent.META_SHIFT_ON) == 1) ||
-                      (MetaKeyKeyListener.getMetaState(buffer,
-                        MetaKeyKeyListener.META_SELECTING) != 0);
-        boolean alt = MetaKeyKeyListener.getMetaState(buffer,
-                        KeyEvent.META_ALT_ON) == 1;
+        boolean cap = isCap(buffer);
+        boolean alt = isAlt(buffer);
         Layout layout = widget.getLayout();
 
         if (cap) {
@@ -133,35 +129,6 @@
         }
     }
 
-    private int getOffset(int x, int y, TextView widget){
-      // Converts the absolute X,Y coordinates to the character offset for the
-      // character whose position is closest to the specified
-      // horizontal position.
-      x -= widget.getTotalPaddingLeft();
-      y -= widget.getTotalPaddingTop();
-
-      // Clamp the position to inside of the view.
-      if (x < 0) {
-          x = 0;
-      } else if (x >= (widget.getWidth()-widget.getTotalPaddingRight())) {
-          x = widget.getWidth()-widget.getTotalPaddingRight() - 1;
-      }
-      if (y < 0) {
-          y = 0;
-      } else if (y >= (widget.getHeight()-widget.getTotalPaddingBottom())) {
-          y = widget.getHeight()-widget.getTotalPaddingBottom() - 1;
-      }
-
-      x += widget.getScrollX();
-      y += widget.getScrollY();
-
-      Layout layout = widget.getLayout();
-      int line = layout.getLineForVertical(y);
-
-      int offset = layout.getOffsetForHorizontal(line, x);
-      return offset;
-    }
-
     public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) {
         if (executeDown(widget, buffer, keyCode)) {
             MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
@@ -193,10 +160,9 @@
             break;
 
         case KeyEvent.KEYCODE_DPAD_CENTER:
-            if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) {
-                if (widget.showContextMenu()) {
+            if ((MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) &&
+                (widget.showContextMenu())) {
                     handled = true;
-                }
             }
         }
 
@@ -214,8 +180,7 @@
 
     public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) {
         int code = event.getKeyCode();
-        if (code != KeyEvent.KEYCODE_UNKNOWN
-                && event.getAction() == KeyEvent.ACTION_MULTIPLE) {
+        if (code != KeyEvent.KEYCODE_UNKNOWN && event.getAction() == KeyEvent.ACTION_MULTIPLE) {
             int repeat = event.getRepeatCount();
             boolean handled = false;
             while ((--repeat) > 0) {
@@ -226,13 +191,22 @@
         return false;
     }
 
-    public boolean onTrackballEvent(TextView widget, Spannable text,
-            MotionEvent event) {
+    public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) {
+        if (mCursorController != null) {
+            mCursorController.hide();
+        }
         return false;
     }
 
-    public boolean onTouchEvent(TextView widget, Spannable buffer,
-                                MotionEvent event) {
+    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
+        if (mCursorController != null) {
+            return onTouchEventCursor(widget, buffer, event);
+        } else {
+            return onTouchEventStandard(widget, buffer, event);
+        }
+    }
+
+    private boolean onTouchEventStandard(TextView widget, Spannable buffer, MotionEvent event) {
         int initialScrollX = -1, initialScrollY = -1;
         if (event.getAction() == MotionEvent.ACTION_UP) {
             initialScrollX = Touch.getInitialScrollX(widget, buffer);
@@ -243,53 +217,20 @@
 
         if (widget.isFocused() && !widget.didTouchFocusSelect()) {
             if (event.getAction() == MotionEvent.ACTION_DOWN) {
-              boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                              KeyEvent.META_SHIFT_ON) == 1) ||
-                            (MetaKeyKeyListener.getMetaState(buffer,
-                              MetaKeyKeyListener.META_SELECTING) != 0);
-              int x = (int) event.getX();
-              int y = (int) event.getY();
-              int offset = getOffset(x, y, widget);
-
+              boolean cap = isCap(buffer);
               if (cap) {
-                  buffer.setSpan(LAST_TAP_DOWN, offset, offset,
-                                 Spannable.SPAN_POINT_POINT);
+                  int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+                  
+                  buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
 
                   // Disallow intercepting of the touch events, so that
                   // users can scroll and select at the same time.
                   // without this, users would get booted out of select
                   // mode once the view detected it needed to scroll.
                   widget.getParent().requestDisallowInterceptTouchEvent(true);
-              } else {
-                  OnePointFiveTapState[] tap = buffer.getSpans(0, buffer.length(),
-                      OnePointFiveTapState.class);
-
-                  if (tap.length > 0) {
-                      if (event.getEventTime() - tap[0].mWhen <=
-                          ViewConfiguration.getDoubleTapTimeout() &&
-                          sameWord(buffer, offset, Selection.getSelectionEnd(buffer))) {
-
-                          tap[0].active = true;
-                          MetaKeyKeyListener.startSelecting(widget, buffer);
-                          widget.getParent().requestDisallowInterceptTouchEvent(true);
-                          buffer.setSpan(LAST_TAP_DOWN, offset, offset,
-                              Spannable.SPAN_POINT_POINT);
-                      }
-
-                      tap[0].mWhen = event.getEventTime();
-                  } else {
-                      OnePointFiveTapState newtap = new OnePointFiveTapState();
-                      newtap.mWhen = event.getEventTime();
-                      newtap.active = false;
-                      buffer.setSpan(newtap, 0, buffer.length(),
-                          Spannable.SPAN_INCLUSIVE_INCLUSIVE);
-                  }
               }
             } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
-                boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                                KeyEvent.META_SHIFT_ON) == 1) ||
-                              (MetaKeyKeyListener.getMetaState(buffer,
-                                MetaKeyKeyListener.META_SELECTING) != 0);
+                boolean cap = isCap(buffer);
 
                 if (cap && handled) {
                     // Before selecting, make sure we've moved out of the "slop".
@@ -297,45 +238,15 @@
                     // OUT of the slop
 
                     // Turn long press off while we're selecting. User needs to
-                    // re-tap on the selection to enable longpress
+                    // re-tap on the selection to enable long press
                     widget.cancelLongPress();
 
                     // Update selection as we're moving the selection area.
 
                     // Get the current touch position
-                    int x = (int) event.getX();
-                    int y = (int) event.getY();
-                    int offset = getOffset(x, y, widget);
+                    int offset = widget.getOffset((int) event.getX(), (int) event.getY());
 
-                    final OnePointFiveTapState[] tap = buffer.getSpans(0, buffer.length(),
-                            OnePointFiveTapState.class);
-
-                    if (tap.length > 0 && tap[0].active) {
-                        // Get the last down touch position (the position at which the
-                        // user started the selection)
-                        int lastDownOffset = buffer.getSpanStart(LAST_TAP_DOWN);
-
-                        // Compute the selection boundaries
-                        int spanstart;
-                        int spanend;
-                        if (offset >= lastDownOffset) {
-                            // Expand from word start of the original tap to new word
-                            // end, since we are selecting "forwards"
-                            spanstart = findWordStart(buffer, lastDownOffset);
-                            spanend = findWordEnd(buffer, offset);
-                        } else {
-                            // Expand to from new word start to word end of the original
-                            // tap since we are selecting "backwards".
-                            // The spanend will always need to be associated with the touch
-                            // up position, so that refining the selection with the
-                            // trackball will work as expected.
-                            spanstart = findWordEnd(buffer, lastDownOffset);
-                            spanend = findWordStart(buffer, offset);
-                        }
-                        Selection.setSelection(buffer, spanstart, spanend);
-                    } else {
-                        Selection.extendSelection(buffer, offset);
-                    }
+                    Selection.extendSelection(buffer, offset);
                     return true;
                 }
             } else if (event.getAction() == MotionEvent.ACTION_UP) {
@@ -344,70 +255,17 @@
                 // the current scroll offset to avoid the scroll jumping later
                 // to show it.
                 if ((initialScrollY >= 0 && initialScrollY != widget.getScrollY()) ||
-                        (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) {
+                    (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) {
                     widget.moveCursorToVisibleOffset();
                     return true;
                 }
 
-                int x = (int) event.getX();
-                int y = (int) event.getY();
-                int off = getOffset(x, y, widget);
-
-                // XXX should do the same adjust for x as we do for the line.
-
-                OnePointFiveTapState[] onepointfivetap = buffer.getSpans(0, buffer.length(),
-                    OnePointFiveTapState.class);
-                if (onepointfivetap.length > 0 && onepointfivetap[0].active &&
-                    Selection.getSelectionStart(buffer) == Selection.getSelectionEnd(buffer)) {
-                    // If we've set select mode, because there was a onepointfivetap,
-                    // but there was no ensuing swipe gesture, undo the select mode
-                    // and remove reference to the last onepointfivetap.
-                    MetaKeyKeyListener.stopSelecting(widget, buffer);
-                    for (int i=0; i < onepointfivetap.length; i++) {
-                        buffer.removeSpan(onepointfivetap[i]);
-                    }
+                int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+                if (isCap(buffer)) {
                     buffer.removeSpan(LAST_TAP_DOWN);
-                }
-                boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                                KeyEvent.META_SHIFT_ON) == 1) ||
-                              (MetaKeyKeyListener.getMetaState(buffer,
-                                MetaKeyKeyListener.META_SELECTING) != 0);
-
-                DoubleTapState[] tap = buffer.getSpans(0, buffer.length(),
-                                                       DoubleTapState.class);
-                boolean doubletap = false;
-
-                if (tap.length > 0) {
-                    if (event.getEventTime() - tap[0].mWhen <=
-                        ViewConfiguration.getDoubleTapTimeout() &&
-                        sameWord(buffer, off, Selection.getSelectionEnd(buffer))) {
-
-                        doubletap = true;
-                    }
-
-                    tap[0].mWhen = event.getEventTime();
+                    Selection.extendSelection(buffer, offset);
                 } else {
-                    DoubleTapState newtap = new DoubleTapState();
-                    newtap.mWhen = event.getEventTime();
-                    buffer.setSpan(newtap, 0, buffer.length(),
-                                   Spannable.SPAN_INCLUSIVE_INCLUSIVE);
-                }
-
-                if (cap) {
-                    buffer.removeSpan(LAST_TAP_DOWN);
-                    if (onepointfivetap.length > 0 && onepointfivetap[0].active) {
-                        // If we selecting something with the onepointfivetap-and
-                        // swipe gesture, stop it on finger up.
-                        MetaKeyKeyListener.stopSelecting(widget, buffer);
-                    } else {
-                        Selection.extendSelection(buffer, off);
-                    }
-                } else if (doubletap) {
-                    Selection.setSelection(buffer,
-                                           findWordStart(buffer, off),
-                                           findWordEnd(buffer, off));
-                } else {
-                    Selection.setSelection(buffer, off);
+                    Selection.setSelection(buffer, offset);
                 }
 
                 MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
@@ -420,73 +278,36 @@
         return handled;
     }
 
-    private static class DoubleTapState implements NoCopySpan {
-        long mWhen;
-    }
+    private boolean onTouchEventCursor(TextView widget, Spannable buffer, MotionEvent event) {
+        if (widget.isFocused() && !widget.didTouchFocusSelect()) {
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_MOVE:
+                    widget.cancelLongPress();
 
-    /* We check for a onepointfive tap. This is similar to
-    *  doubletap gesture (where a finger goes down, up, down, up, in a short
-    *  time period), except in the onepointfive tap, a users finger only needs
-    *  to go down, up, down in a short time period. We detect this type of tap
-    *  to implement the onepointfivetap-and-swipe selection gesture.
-    *  This gesture allows users to select a segment of text without going
-    *  through the "select text" option in the context menu.
-    */
-    private static class OnePointFiveTapState implements NoCopySpan {
-        long mWhen;
-        boolean active;
-    }
+                    // Offset the current touch position (from controller to cursor)
+                    final float x = event.getX() + mCursorController.getOffsetX();
+                    final float y = event.getY() + mCursorController.getOffsetY();
+                    int offset = widget.getOffset((int) x, (int) y);
+                    mCursorController.updatePosition(offset);
+                    return true;
 
-    private static boolean sameWord(CharSequence text, int one, int two) {
-        int start = findWordStart(text, one);
-        int end = findWordEnd(text, one);
-
-        if (end == start) {
-            return false;
-        }
-
-        return start == findWordStart(text, two) &&
-               end == findWordEnd(text, two);
-    }
-
-    // TODO: Unify with TextView.getWordForDictionary()
-    private static int findWordStart(CharSequence text, int start) {
-        for (; start > 0; start--) {
-            char c = text.charAt(start - 1);
-            int type = Character.getType(c);
-
-            if (c != '\'' &&
-                type != Character.UPPERCASE_LETTER &&
-                type != Character.LOWERCASE_LETTER &&
-                type != Character.TITLECASE_LETTER &&
-                type != Character.MODIFIER_LETTER &&
-                type != Character.DECIMAL_DIGIT_NUMBER) {
-                break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    mCursorController = null;
+                    return true;
             }
         }
-
-        return start;
+        return false;
     }
 
-    // TODO: Unify with TextView.getWordForDictionary()
-    private static int findWordEnd(CharSequence text, int end) {
-        int len = text.length();
-
-        for (; end < len; end++) {
-            char c = text.charAt(end);
-            int type = Character.getType(c);
-
-            if (c != '\'' &&
-                type != Character.UPPERCASE_LETTER &&
-                type != Character.LOWERCASE_LETTER &&
-                type != Character.TITLECASE_LETTER &&
-                type != Character.MODIFIER_LETTER &&
-                type != Character.DECIMAL_DIGIT_NUMBER) {
-                break;
-            }
-        }
-
-        return end;
+    /**
+     * Defines the cursor controller.
+     *
+     * When set, this object can be used to handle events, that can be translated in cursor updates.
+     * @param cursorController A cursor controller implementation
+     */
+    public void setCursorController(CursorController cursorController) {
+        mCursorController = cursorController;
     }
 
     public boolean canSelectArbitrarily() {
@@ -525,8 +346,9 @@
     }
 
     public static MovementMethod getInstance() {
-        if (sInstance == null)
+        if (sInstance == null) {
             sInstance = new ArrowKeyMovementMethod();
+        }
 
         return sInstance;
     }
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index 42ad10e..a19a78e 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -17,14 +17,13 @@
 package android.text.method;
 
 import android.text.Layout;
-import android.text.NoCopySpan;
 import android.text.Layout.Alignment;
+import android.text.NoCopySpan;
 import android.text.Spannable;
-import android.util.Log;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 import android.widget.TextView;
-import android.view.KeyEvent;
 
 public class Touch {
     private Touch() { }
@@ -99,7 +98,7 @@
                                        MotionEvent event) {
         DragState[] ds;
 
-        switch (event.getAction()) {
+        switch (event.getActionMasked()) {
         case MotionEvent.ACTION_DOWN:
             ds = buffer.getSpans(0, buffer.length(), DragState.class);
 
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 0fc70d5..b01a71d 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -24,6 +24,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.TimeZone;
 import java.util.Date;
 
@@ -130,4 +131,128 @@
     public static String getTimeZoneDatabaseVersion() {
         return ZoneInfoDB.getVersion();
     }
+
+    private static final int SECONDS_PER_MINUTE = 60;
+    private static final int SECONDS_PER_HOUR = 60 * 60;
+    private static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+    /** @hide Just for debugging; not internationalized. */
+    public static void formatDuration(long duration, StringBuilder builder) {
+        if (duration == 0) {
+            builder.append("0");
+            return;
+        }
+        if (duration > 0) {
+            builder.append("+");
+        } else {
+            builder.append("-");
+            duration = -duration;
+        }
+
+        int millis = (int)(duration%1000);
+        int seconds = (int) Math.floor(duration / 1000);
+        int days = 0, hours = 0, minutes = 0;
+
+        if (seconds > SECONDS_PER_DAY) {
+            days = seconds / SECONDS_PER_DAY;
+            seconds -= days * SECONDS_PER_DAY;
+        }
+        if (seconds > SECONDS_PER_HOUR) {
+            hours = seconds / SECONDS_PER_HOUR;
+            seconds -= hours * SECONDS_PER_HOUR;
+        }
+        if (seconds > SECONDS_PER_MINUTE) {
+            minutes = seconds / SECONDS_PER_MINUTE;
+            seconds -= minutes * SECONDS_PER_MINUTE;
+        }
+
+        boolean doall = false;
+        if (days > 0) {
+            builder.append(days);
+            builder.append('d');
+            doall = true;
+        }
+        if (doall || hours > 0) {
+            builder.append(hours);
+            builder.append('h');
+            doall = true;
+        }
+        if (doall || minutes > 0) {
+            builder.append(minutes);
+            builder.append('m');
+            doall = true;
+        }
+        if (doall || seconds > 0) {
+            builder.append(seconds);
+            builder.append('s');
+            doall = true;
+        }
+        builder.append(millis);
+        builder.append("ms");
+    }
+
+    /** @hide Just for debugging; not internationalized. */
+    public static void formatDuration(long duration, PrintWriter pw) {
+        if (duration == 0) {
+            pw.print("0");
+            return;
+        }
+        if (duration > 0) {
+            pw.print("+");
+        } else {
+            pw.print("-");
+            duration = -duration;
+        }
+
+        int millis = (int)(duration%1000);
+        int seconds = (int) Math.floor(duration / 1000);
+        int days = 0, hours = 0, minutes = 0;
+
+        if (seconds > SECONDS_PER_DAY) {
+            days = seconds / SECONDS_PER_DAY;
+            seconds -= days * SECONDS_PER_DAY;
+        }
+        if (seconds > SECONDS_PER_HOUR) {
+            hours = seconds / SECONDS_PER_HOUR;
+            seconds -= hours * SECONDS_PER_HOUR;
+        }
+        if (seconds > SECONDS_PER_MINUTE) {
+            minutes = seconds / SECONDS_PER_MINUTE;
+            seconds -= minutes * SECONDS_PER_MINUTE;
+        }
+
+        boolean doall = false;
+        if (days > 0) {
+            pw.print(days);
+            pw.print('d');
+            doall = true;
+        }
+        if (doall || hours > 0) {
+            pw.print(hours);
+            pw.print('h');
+            doall = true;
+        }
+        if (doall || minutes > 0) {
+            pw.print(minutes);
+            pw.print('m');
+            doall = true;
+        }
+        if (doall || seconds > 0) {
+            pw.print(seconds);
+            pw.print('s');
+            doall = true;
+        }
+        pw.print(millis);
+        pw.print("ms");
+    }
+
+
+    /** @hide Just for debugging; not internationalized. */
+    public static void formatDuration(long time, long now, PrintWriter pw) {
+        if (time == 0) {
+            pw.print("--");
+            return;
+        }
+        formatDuration(time-now, pw);
+    }
 }
diff --git a/core/java/android/view/AbsSavedState.java b/core/java/android/view/AbsSavedState.java
index 840d7c1..6ad33dd 100644
--- a/core/java/android/view/AbsSavedState.java
+++ b/core/java/android/view/AbsSavedState.java
@@ -54,7 +54,7 @@
      */
     protected AbsSavedState(Parcel source) {
         // FIXME need class loader
-        Parcelable superState = (Parcelable) source.readParcelable(null);
+        Parcelable superState = source.readParcelable(null);
          
         mSuperState = superState != null ? superState : EMPTY_STATE;
     }
@@ -75,7 +75,7 @@
         = new Parcelable.Creator<AbsSavedState>() {
         
         public AbsSavedState createFromParcel(Parcel in) {
-            Parcelable superState = (Parcelable) in.readParcelable(null);
+            Parcelable superState = in.readParcelable(null);
             if (superState != null) {
                 throw new IllegalStateException("superState must be null");
             }
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index fabe5c8..34d7935 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -27,7 +27,8 @@
 
     
     /**
-     * Use the WindowManager interface to create a Display object.
+     * Use {@link android.view.WindowManager#getDefaultDisplay()
+     * WindowManager.getDefaultDisplay()} to create a Display object.
      * Display gives you access to some information about a particular display
      * connected to the device.
      */
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 47061c1..74318ba 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -772,7 +772,7 @@
      * 
      * @param pointerId The identifier of the pointer to be found.
      * @return Returns either the index of the pointer (for use with
-     * {@link #getX(int) et al.), or -1 if there is no data available for
+     * {@link #getX(int)} et al.), or -1 if there is no data available for
      * that pointer identifier.
      */
     public final int findPointerIndex(int pointerId) {
@@ -1319,21 +1319,24 @@
      * @param y New absolute Y location.
      */
     public final void setLocation(float x, float y) {
-        mXOffset = x - mDataSamples[mLastDataSampleIndex + SAMPLE_X];
-        mYOffset = y - mDataSamples[mLastDataSampleIndex + SAMPLE_Y];
+        final float[] dataSamples = mDataSamples;
+        final int lastDataSampleIndex = mLastDataSampleIndex;
+        mXOffset = x - dataSamples[lastDataSampleIndex + SAMPLE_X];
+        mYOffset = y - dataSamples[lastDataSampleIndex + SAMPLE_Y];
     }
     
     private final void getPointerCoordsAtSampleIndex(int sampleIndex,
             PointerCoords outPointerCoords) {
-        outPointerCoords.x = mDataSamples[sampleIndex + SAMPLE_X] + mXOffset;
-        outPointerCoords.y = mDataSamples[sampleIndex + SAMPLE_Y] + mYOffset;
-        outPointerCoords.pressure = mDataSamples[sampleIndex + SAMPLE_PRESSURE];
-        outPointerCoords.size = mDataSamples[sampleIndex + SAMPLE_SIZE];
-        outPointerCoords.touchMajor = mDataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR];
-        outPointerCoords.touchMinor = mDataSamples[sampleIndex + SAMPLE_TOUCH_MINOR];
-        outPointerCoords.toolMajor = mDataSamples[sampleIndex + SAMPLE_TOOL_MAJOR];
-        outPointerCoords.toolMinor = mDataSamples[sampleIndex + SAMPLE_TOOL_MINOR];
-        outPointerCoords.orientation = mDataSamples[sampleIndex + SAMPLE_ORIENTATION];
+        final float[] dataSamples = mDataSamples;
+        outPointerCoords.x = dataSamples[sampleIndex + SAMPLE_X] + mXOffset;
+        outPointerCoords.y = dataSamples[sampleIndex + SAMPLE_Y] + mYOffset;
+        outPointerCoords.pressure = dataSamples[sampleIndex + SAMPLE_PRESSURE];
+        outPointerCoords.size = dataSamples[sampleIndex + SAMPLE_SIZE];
+        outPointerCoords.touchMajor = dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR];
+        outPointerCoords.touchMinor = dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR];
+        outPointerCoords.toolMajor = dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR];
+        outPointerCoords.toolMinor = dataSamples[sampleIndex + SAMPLE_TOOL_MINOR];
+        outPointerCoords.orientation = dataSamples[sampleIndex + SAMPLE_ORIENTATION];
     }
     
     private final void setPointerCoordsAtSampleIndex(int sampleIndex,
@@ -1347,28 +1350,30 @@
     
     private final void setPointerCoordsAtSampleIndex(int sampleIndex,
             PointerCoords pointerCoords) {
-        mDataSamples[sampleIndex + SAMPLE_X] = pointerCoords.x - mXOffset;
-        mDataSamples[sampleIndex + SAMPLE_Y] = pointerCoords.y - mYOffset;
-        mDataSamples[sampleIndex + SAMPLE_PRESSURE] = pointerCoords.pressure;
-        mDataSamples[sampleIndex + SAMPLE_SIZE] = pointerCoords.size;
-        mDataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pointerCoords.touchMajor;
-        mDataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pointerCoords.touchMinor;
-        mDataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = pointerCoords.toolMajor;
-        mDataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = pointerCoords.toolMinor;
-        mDataSamples[sampleIndex + SAMPLE_ORIENTATION] = pointerCoords.orientation;
+        final float[] dataSamples = mDataSamples;
+        dataSamples[sampleIndex + SAMPLE_X] = pointerCoords.x - mXOffset;
+        dataSamples[sampleIndex + SAMPLE_Y] = pointerCoords.y - mYOffset;
+        dataSamples[sampleIndex + SAMPLE_PRESSURE] = pointerCoords.pressure;
+        dataSamples[sampleIndex + SAMPLE_SIZE] = pointerCoords.size;
+        dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pointerCoords.touchMajor;
+        dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pointerCoords.touchMinor;
+        dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = pointerCoords.toolMajor;
+        dataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = pointerCoords.toolMinor;
+        dataSamples[sampleIndex + SAMPLE_ORIENTATION] = pointerCoords.orientation;
     }
     
     private final void setPointerCoordsAtSampleIndex(int sampleIndex,
             float x, float y, float pressure, float size) {
-        mDataSamples[sampleIndex + SAMPLE_X] = x - mXOffset;
-        mDataSamples[sampleIndex + SAMPLE_Y] = y - mYOffset;
-        mDataSamples[sampleIndex + SAMPLE_PRESSURE] = pressure;
-        mDataSamples[sampleIndex + SAMPLE_SIZE] = size;
-        mDataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pressure;
-        mDataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pressure;
-        mDataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = size;
-        mDataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = size;
-        mDataSamples[sampleIndex + SAMPLE_ORIENTATION] = 0;
+        final float[] dataSamples = mDataSamples;
+        dataSamples[sampleIndex + SAMPLE_X] = x - mXOffset;
+        dataSamples[sampleIndex + SAMPLE_Y] = y - mYOffset;
+        dataSamples[sampleIndex + SAMPLE_PRESSURE] = pressure;
+        dataSamples[sampleIndex + SAMPLE_SIZE] = size;
+        dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pressure;
+        dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pressure;
+        dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = size;
+        dataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = size;
+        dataSamples[sampleIndex + SAMPLE_ORIENTATION] = 0;
     }
     
     private final void incrementNumSamplesAndReserveStorage(int dataSampleStride) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b8623e7..c9662ff 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2413,11 +2413,10 @@
     }
 
     /**
-     * Call this view's OnLongClickListener, if it is defined. Invokes the context menu
-     * if the OnLongClickListener did not consume the event.
+     * Call this view's OnLongClickListener, if it is defined. Invokes the context menu if the
+     * OnLongClickListener did not consume the event.
      *
-     * @return True there was an assigned OnLongClickListener that was called, false
-     *         otherwise is returned.
+     * @return True if one of the above receivers consumed the event, false otherwise.
      */
     public boolean performLongClick() {
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
@@ -4218,6 +4217,10 @@
      * Show the context menu for this view. It is not safe to hold on to the
      * menu after returning from this method.
      *
+     * You should normally not overload this method. Overload
+     * {@link #onCreateContextMenu(ContextMenu)} or define an
+     * {@link OnCreateContextMenuListener} to add items to the context menu.
+     *
      * @param menu The context menu to populate
      */
     public void createContextMenu(ContextMenu menu) {
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 2ca08ea..402443c 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -990,7 +990,8 @@
                             }
                         }) : 0;
         long durationDraw =
-                (root || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(view,
+                (root || !view.willNotDraw() || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(
+                        view,
                         new ViewOperation<Object>() {
                             public Object[] pre() {
                                 final DisplayMetrics metrics =
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index fed55dc..55d11bb 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -103,11 +103,11 @@
         }
     }
 
-    public int getCurrentRotation() {
+    public int getCurrentRotation(int lastRotation) {
         if (mEnabled) {
-            return mSensorEventListener.getCurrentRotation();
+            return mSensorEventListener.getCurrentRotation(lastRotation);
         }
-        return -1;
+        return lastRotation;
     }
 
     /**
@@ -153,9 +153,15 @@
         private static final int ROTATION_270 = 2;
 
         // Mapping our internal aliases into actual Surface rotation values
-        private static final int[] SURFACE_ROTATIONS = new int[] {
+        private static final int[] INTERNAL_TO_SURFACE_ROTATION = new int[] {
             Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
 
+        // Mapping Surface rotation values to internal aliases.
+        // We have no constant for Surface.ROTATION_180.  That should never happen, but if it
+        // does, we'll arbitrarily choose a mapping.
+        private static final int[] SURFACE_TO_INTERNAL_ROTATION = new int[] {
+            ROTATION_0, ROTATION_90, ROTATION_90, ROTATION_270};
+
         // Threshold ranges of orientation angle to transition into other orientation states.
         // The first list is for transitions from ROTATION_0, the next for ROTATION_90, etc.
         // ROTATE_TO defines the orientation each threshold range transitions to, and must be kept
@@ -243,8 +249,12 @@
             return (float) SAMPLING_PERIOD_MS / (timeConstantMs + SAMPLING_PERIOD_MS);
         }
 
-        int getCurrentRotation() {
-            return SURFACE_ROTATIONS[mRotation];
+        int getCurrentRotation(int lastRotation) {
+            if (mTiltDistrust > 0) {
+                // we really don't know the current orientation, so trust what's currently displayed
+                mRotation = SURFACE_TO_INTERNAL_ROTATION[lastRotation];
+            }
+            return INTERNAL_TO_SURFACE_ROTATION[mRotation];
         }
 
         private void calculateNewRotation(float orientation, float tiltAngle) {
@@ -267,7 +277,7 @@
 
             if (localLOGV) Log.i(TAG, " new rotation = " + rotation);
             mRotation = rotation;
-            mOrientationListener.onOrientationChanged(getCurrentRotation());
+            mOrientationListener.onOrientationChanged(INTERNAL_TO_SURFACE_ROTATION[mRotation]);
         }
 
         private float lowpassFilter(float newValue, float oldValue, float alpha) {
@@ -306,7 +316,8 @@
             mTiltAngle = lowpassFilter(newTiltAngle, mTiltAngle, alpha);
 
             float absoluteTilt = Math.abs(mTiltAngle);
-            if (checkFullyTilted(absoluteTilt)) {
+            checkFullyTilted(absoluteTilt);
+            if (mTiltDistrust > 0) {
                 return; // when fully tilted, ignore orientation entirely
             }
 
@@ -347,11 +358,9 @@
          * get un-tilted.
          *
          * @param absoluteTilt the absolute value of the current tilt angle
-         * @return true if the phone is fully tilted
          */
-        private boolean checkFullyTilted(float absoluteTilt) {
-            boolean fullyTilted = absoluteTilt > MAX_TILT;
-            if (fullyTilted) {
+        private void checkFullyTilted(float absoluteTilt) {
+            if (absoluteTilt > MAX_TILT) {
                 if (mRotation == ROTATION_0) {
                     mOrientationAngle = 0;
                 } else if (mRotation == ROTATION_90) {
@@ -366,7 +375,6 @@
             } else if (mTiltDistrust > 0) {
                 mTiltDistrust--;
             }
-            return fullyTilted;
         }
 
         /**
@@ -389,8 +397,8 @@
          */
         private void filterOrientation(float absoluteTilt, float orientationAngle) {
             float alpha = DEFAULT_LOWPASS_ALPHA;
-            if (mTiltDistrust > 0 || mAccelerationDistrust > 1) {
-                // when fully tilted, or under more than a transient acceleration, distrust heavily
+            if (mAccelerationDistrust > 1) {
+                // when under more than a transient acceleration, distrust heavily
                 alpha = ACCELERATING_LOWPASS_ALPHA;
             } else if (absoluteTilt > PARTIAL_TILT || mAccelerationDistrust == 1) {
                 // when tilted partway, or under transient acceleration, distrust lightly
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index c22f991..fc61700 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -622,6 +622,7 @@
         mPackageName = null;
         mContentDescription = null;
         mBeforeText = null;
+        mParcelableData = null;
         mText.clear();
     }
 
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 1d5aac7..9f642c0 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -320,4 +320,22 @@
     public void openFileChooser(ValueCallback<Uri> uploadFile) {
         uploadFile.onReceiveValue(null);
     }
+
+    /**
+     * Tell the client that the selection has been initiated.
+     * @hide
+     */
+    public void onSelectionStart(WebView view) {
+        // By default we cancel the selection again, thus disabling
+        // text selection unless the chrome client supports it.
+        view.notifySelectDialogDismissed();
+    }
+
+    /**
+     * Tell the client that the selection has been copied or canceled.
+     * @hide
+     */
+    public void onSelectionDone(WebView view) {
+    }
+
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index bf751f5..3e0187d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -117,6 +117,9 @@
  * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code &lt;manifest&gt;}</a>
  * element.</p>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-webview.html">Web View
+ * tutorial</a>.</p>
+ *
  * <h3>Basic usage</h3>
  *
  * <p>By default, a WebView provides no browser-like widgets, does not
@@ -456,8 +459,7 @@
     private static final int TOUCH_SHORTPRESS_MODE = 5;
     private static final int TOUCH_DOUBLE_TAP_MODE = 6;
     private static final int TOUCH_DONE_MODE = 7;
-    private static final int TOUCH_SELECT_MODE = 8;
-    private static final int TOUCH_PINCH_DRAG = 9;
+    private static final int TOUCH_PINCH_DRAG = 8;
 
     // Whether to forward the touch events to WebCore
     private boolean mForwardTouchEvents = false;
@@ -2690,6 +2692,14 @@
         nativeSetFindIsUp(isUp);
     }
 
+    /**
+     * @hide
+     */
+    public 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;
@@ -3306,12 +3316,33 @@
             // Send the click so that the textfield is in focus
             centerKeyPressOnTextField();
             rebuildWebTextView();
+        } else {
+            clearTextEntry(true);
         }
         if (inEditingMode()) {
             return mWebTextView.performLongClick();
-        } else {
-            return super.performLongClick();
         }
+        /* if long click brings up a context menu, the super function
+         * returns true and we're done. Otherwise, nothing happened when
+         * the user clicked. */
+        if (super.performLongClick()) {
+            return true;
+        }
+        /* In the case where the application hasn't already handled the long
+         * click action, look for a word under the  click. If one is found,
+         * animate the text selection into view.
+         * FIXME: no animation code yet */
+        if (mSelectingText) return false; // long click does nothing on selection
+        int x = viewToContentX((int) mLastTouchX + mScrollX);
+        int y = viewToContentY((int) mLastTouchY + mScrollY);
+        setUpSelect();
+        if (mNativeClass != 0 && nativeWordSelection(x, y)) {
+            nativeSetExtendSelection();
+            getWebChromeClient().onSelectionStart(this);
+            return true;
+        }
+        notifySelectDialogDismissed();
+        return false;
     }
 
     boolean inAnimateZoom() {
@@ -3462,19 +3493,12 @@
         // decide which adornments to draw
         int extras = DRAW_EXTRAS_NONE;
         if (mFindIsUp) {
-            // When the FindDialog is up, only draw the matches if we are not in
-            // the process of scrolling them into view.
-            if (!animateScroll) {
                 extras = DRAW_EXTRAS_FIND;
-            }
-        } else if (mShiftIsPressed && !nativeFocusIsPlugin()) {
-            if (!animateZoom && !mPreviewZoomOnly) {
-                extras = DRAW_EXTRAS_SELECTION;
-                nativeSetSelectionRegion(mTouchSelection || mExtendSelection);
-                nativeSetSelectionPointer(!mTouchSelection, mInvActualScale,
-                        mSelectX, mSelectY - getTitleHeight(),
-                        mExtendSelection);
-            }
+        } else if (mSelectingText) {
+            extras = DRAW_EXTRAS_SELECTION;
+            nativeSetSelectionPointer(mDrawSelectionPointer,
+                    mInvActualScale,
+                    mSelectX, mSelectY - getTitleHeight());
         } else if (drawCursorRing) {
             extras = DRAW_EXTRAS_CURSOR_RING;
         }
@@ -3483,11 +3507,6 @@
         if (extras == DRAW_EXTRAS_CURSOR_RING) {
             if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                 mTouchMode = TOUCH_SHORTPRESS_MODE;
-                HitTestResult hitTest = getHitTestResult();
-                if (hitTest == null
-                        || hitTest.mType == HitTestResult.UNKNOWN_TYPE) {
-                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                }
             }
         }
         if (mFocusSizeChanged) {
@@ -3826,8 +3845,8 @@
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
             if (nativeFocusIsPlugin()) {
                 mShiftIsPressed = true;
-            } else if (!nativeCursorWantsKeyEvents() && !mShiftIsPressed) {
-                setUpSelectXY();
+            } else if (!nativeCursorWantsKeyEvents() && !mSelectingText) {
+                setUpSelect();
             }
         }
 
@@ -3838,7 +3857,7 @@
                 letPluginHandleNavKey(keyCode, event.getEventTime(), true);
                 return true;
             }
-            if (mShiftIsPressed) {
+            if (mSelectingText) {
                 int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
                     ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
                 int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
@@ -3858,7 +3877,7 @@
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             switchOutDrawHistory();
             if (event.getRepeatCount() == 0) {
-                if (mShiftIsPressed && !nativeFocusIsPlugin()) {
+                if (mSelectingText) {
                     return true; // discard press if copy in progress
                 }
                 mGotCenterDown = true;
@@ -3876,10 +3895,8 @@
         if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
                 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
             // turn off copy select if a shift-key combo is pressed
-            mExtendSelection = mShiftIsPressed = false;
-            if (mTouchMode == TOUCH_SELECT_MODE) {
-                mTouchMode = TOUCH_INIT_MODE;
-            }
+            selectionDone();
+            mShiftIsPressed = false;
         }
 
         if (getSettings().getNavDump()) {
@@ -3969,7 +3986,8 @@
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
             if (nativeFocusIsPlugin()) {
                 mShiftIsPressed = false;
-            } else if (commitCopy()) {
+            } else if (copySelection()) {
+                selectionDone();
                 return true;
             }
         }
@@ -3990,11 +4008,13 @@
             mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
             mGotCenterDown = false;
 
-            if (mShiftIsPressed && !nativeFocusIsPlugin()) {
+            if (mSelectingText) {
                 if (mExtendSelection) {
-                    commitCopy();
+                    copySelection();
+                    selectionDone();
                 } else {
                     mExtendSelection = true;
+                    nativeSetExtendSelection();
                     invalidate(); // draw the i-beam instead of the arrow
                 }
                 return true; // discard press if copy in progress
@@ -4039,9 +4059,18 @@
         return false;
     }
 
-    private void setUpSelectXY() {
+    /**
+     * @hide pending API council approval.
+     */
+    public void setUpSelect() {
+        if (0 == mNativeClass) return; // client isn't initialized
+        if (inFullScreenMode()) return;
+        if (mSelectingText) return;
         mExtendSelection = false;
-        mShiftIsPressed = true;
+        mSelectingText = mDrawSelectionPointer = true;
+        // don't let the picture change during text selection
+        WebViewCore.pauseUpdatePicture(mWebViewCore);
+        nativeResetSelection();
         if (nativeHasCursorNode()) {
             Rect rect = nativeCursorNodeBounds();
             mSelectX = contentToViewX(rect.left);
@@ -4061,40 +4090,82 @@
      * Do not rely on this functionality; it will be deprecated in the future.
      */
     public void emulateShiftHeld() {
-        if (0 == mNativeClass) return; // client isn't initialized
-        setUpSelectXY();
+        setUpSelect();
     }
 
-    private boolean commitCopy() {
+    /**
+     * @hide pending API council approval.
+     */
+    public void selectAll() {
+        if (0 == mNativeClass) return; // client isn't initialized
+        if (inFullScreenMode()) return;
+        if (!mSelectingText) setUpSelect();
+        nativeSelectAll();
+        mDrawSelectionPointer = false;
+        mExtendSelection = true;
+        invalidate();
+    }
+
+    /**
+     * @hide pending API council approval.
+     */
+    public boolean selectDialogIsUp() {
+        return mSelectingText;
+    }
+
+    /**
+     * @hide pending API council approval.
+     */
+    public void notifySelectDialogDismissed() {
+        mSelectingText = false;
+        WebViewCore.resumeUpdatePicture(mWebViewCore);
+    }
+
+    /**
+     * @hide pending API council approval.
+     */
+    public void selectionDone() {
+        if (mSelectingText) {
+            getWebChromeClient().onSelectionDone(this);
+            invalidate(); // redraw without selection
+            notifySelectDialogDismissed();
+        }
+    }
+
+    /**
+     * @hide pending API council approval.
+     */
+    public boolean copySelection() {
         boolean copiedSomething = false;
-        if (mExtendSelection) {
-            String selection = nativeGetSelection();
-            if (selection != "") {
-                if (DebugFlags.WEB_VIEW) {
-                    Log.v(LOGTAG, "commitCopy \"" + selection + "\"");
-                }
-                Toast.makeText(mContext
-                        , com.android.internal.R.string.text_copied
-                        , Toast.LENGTH_SHORT).show();
-                copiedSomething = true;
-                try {
-                    IClipboard clip = IClipboard.Stub.asInterface(
-                            ServiceManager.getService("clipboard"));
-                            clip.setClipboardText(selection);
-                } catch (android.os.RemoteException e) {
-                    Log.e(LOGTAG, "Clipboard failed", e);
-                }
+        String selection = getSelection();
+        if (selection != "") {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "copySelection \"" + selection + "\"");
             }
-            mExtendSelection = false;
+            Toast.makeText(mContext
+                    , com.android.internal.R.string.text_copied
+                    , Toast.LENGTH_SHORT).show();
+            copiedSomething = true;
+            try {
+                IClipboard clip = IClipboard.Stub.asInterface(
+                        ServiceManager.getService("clipboard"));
+                clip.setClipboardText(selection);
+            } catch (android.os.RemoteException e) {
+                Log.e(LOGTAG, "Clipboard failed", e);
+            }
         }
-        mShiftIsPressed = false;
         invalidate(); // remove selection region and pointer
-        if (mTouchMode == TOUCH_SELECT_MODE) {
-            mTouchMode = TOUCH_INIT_MODE;
-        }
         return copiedSomething;
     }
 
+    /**
+     * @hide pending API council approval.
+     */
+    public String getSelection() {
+        if (mNativeClass == 0) return "";
+        return nativeGetSelection();
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -4669,7 +4740,7 @@
 
     private boolean shouldForwardTouchEvent() {
         return mFullScreenHolder != null || (mForwardTouchEvents
-                && mTouchMode != TOUCH_SELECT_MODE
+                && !mSelectingText
                 && mPreventDefault != PREVENT_DEFAULT_IGNORE);
     }
 
@@ -4757,16 +4828,6 @@
                     mTouchMode = TOUCH_DRAG_START_MODE;
                     mConfirmMove = true;
                     mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
-                } else if (!inFullScreenMode() && mShiftIsPressed) {
-                    mSelectX = mScrollX + (int) x;
-                    mSelectY = mScrollY + (int) y;
-                    mTouchMode = TOUCH_SELECT_MODE;
-                    if (DebugFlags.WEB_VIEW) {
-                        Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
-                    }
-                    nativeMoveSelection(contentX, contentY, false);
-                    mTouchSelection = mExtendSelection = true;
-                    invalidate(); // draw the i-beam instead of the arrow
                 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
                     mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
                     if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
@@ -4791,6 +4852,14 @@
                         EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
                                 (eventTime - mLastTouchUpTime), eventTime);
                     }
+                    if (mSelectingText) {
+                        mDrawSelectionPointer = false;
+                        mSelectionStarted = nativeStartSelection(contentX, contentY);
+                        if (DebugFlags.WEB_VIEW) {
+                            Log.v(LOGTAG, "select=" + contentX + "," + contentY);
+                        }
+                        invalidate();
+                    }
                 }
                 // Trigger the link
                 if (mTouchMode == TOUCH_INIT_MODE
@@ -4882,17 +4951,17 @@
                             + " mTouchMode = " + mTouchMode);
                 }
                 mVelocityTracker.addMovement(ev);
-                if (mTouchMode != TOUCH_DRAG_MODE) {
-                    if (mTouchMode == TOUCH_SELECT_MODE) {
-                        mSelectX = mScrollX + (int) x;
-                        mSelectY = mScrollY + (int) y;
-                        if (DebugFlags.WEB_VIEW) {
-                            Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
-                        }
-                        nativeMoveSelection(contentX, contentY, true);
-                        invalidate();
-                        break;
+                if (mSelectingText && mSelectionStarted) {
+                    if (DebugFlags.WEB_VIEW) {
+                        Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
                     }
+                    nativeExtendSelection(contentX, contentY);
+                    invalidate();
+                    break;
+                }
+
+                if (mTouchMode != TOUCH_DRAG_MODE) {
+
                     if (!mConfirmMove) {
                         break;
                     }
@@ -5059,10 +5128,6 @@
                             mTouchMode = TOUCH_DONE_MODE;
                         }
                         break;
-                    case TOUCH_SELECT_MODE:
-                        commitCopy();
-                        mTouchSelection = false;
-                        break;
                     case TOUCH_INIT_MODE: // tap
                     case TOUCH_SHORTPRESS_START_MODE:
                     case TOUCH_SHORTPRESS_MODE:
@@ -5093,6 +5158,13 @@
                                 break;
                             }
                         } else {
+                            if (mSelectingText) {
+                                // tapping on selection or controls does nothing
+                                if (!nativeHitSelection(contentX, contentY)) {
+                                    selectionDone();
+                                }
+                                break;
+                            }
                             if (mTouchMode == TOUCH_INIT_MODE) {
                                 mPrivateHandler.sendEmptyMessageDelayed(
                                         RELEASE_SINGLE_TAP, ViewConfiguration
@@ -5263,8 +5335,10 @@
     private float mTrackballRemainsY = 0.0f;
     private int mTrackballXMove = 0;
     private int mTrackballYMove = 0;
+    private boolean mSelectingText = false;
+    private boolean mSelectionStarted = false;
     private boolean mExtendSelection = false;
-    private boolean mTouchSelection = false;
+    private boolean mDrawSelectionPointer = false;
     private static final int TRACKBALL_KEY_TIMEOUT = 1000;
     private static final int TRACKBALL_TIMEOUT = 200;
     private static final int TRACKBALL_WAIT = 100;
@@ -5303,10 +5377,8 @@
             if (ev.getY() < 0) pageUp(true);
             return true;
         }
-        boolean shiftPressed = mShiftIsPressed && (mNativeClass == 0
-                || !nativeFocusIsPlugin());
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (shiftPressed) {
+            if (mSelectingText) {
                 return true; // discard press if copy in progress
             }
             mTrackballDown = true;
@@ -5331,11 +5403,13 @@
             mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
             mTrackballDown = false;
             mTrackballUpTime = time;
-            if (shiftPressed) {
+            if (mSelectingText) {
                 if (mExtendSelection) {
-                    commitCopy();
+                    copySelection();
+                    selectionDone();
                 } else {
                     mExtendSelection = true;
+                    nativeSetExtendSelection();
                     invalidate(); // draw the i-beam instead of the arrow
                 }
                 return true; // discard press if copy in progress
@@ -5402,8 +5476,7 @@
                     + " yRate=" + yRate
                     );
         }
-        nativeMoveSelection(viewToContentX(mSelectX),
-                viewToContentY(mSelectY), mExtendSelection);
+        nativeMoveSelection(viewToContentX(mSelectX), viewToContentY(mSelectY));
         int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
                 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
                 : 0;
@@ -5469,7 +5542,16 @@
         float yRate = mTrackballRemainsY * 1000 / elapsed;
         int viewWidth = getViewWidth();
         int viewHeight = getViewHeight();
-        if (mShiftIsPressed && (mNativeClass == 0 || !nativeFocusIsPlugin())) {
+        if (mSelectingText) {
+            if (!mDrawSelectionPointer) {
+                // The last selection was made by touch, disabling drawing the
+                // selection pointer. Allow the trackball to adjust the
+                // position of the touch control.
+                mSelectX = contentToViewX(nativeSelectionX());
+                mSelectY = contentToViewY(nativeSelectionY());
+                mDrawSelectionPointer = mExtendSelection = true;
+                nativeSetExtendSelection();
+            }
             moveSelection(scaleTrackballX(xRate, viewWidth),
                     scaleTrackballY(yRate, viewHeight));
             mTrackballRemainsX = mTrackballRemainsY = 0;
@@ -7389,6 +7471,7 @@
     private native void     nativeDebugDump();
     private native void     nativeDestroy();
     private native boolean  nativeEvaluateLayersAnimations();
+    private native void     nativeExtendSelection(int x, int y);
     private native void     nativeDrawExtras(Canvas canvas, int extra);
     private native void     nativeDumpDisplayTree(String urlOrNull);
     private native int      nativeFindAll(String findLower, String findUpper);
@@ -7417,6 +7500,7 @@
     private native boolean  nativeHasCursorNode();
     private native boolean  nativeHasFocusNode();
     private native void     nativeHideCursor();
+    private native boolean  nativeHitSelection(int x, int y);
     private native String   nativeImageURI(int x, int y);
     private native void     nativeInstrumentReport();
     /* package */ native boolean nativeMoveCursorToNextTextInput();
@@ -7426,21 +7510,27 @@
     private native boolean  nativeMoveCursor(int keyCode, int count,
             boolean noScroll);
     private native int      nativeMoveGeneration();
-    private native void     nativeMoveSelection(int x, int y,
-            boolean extendSelection);
+    private native void     nativeMoveSelection(int x, int y);
     private native boolean  nativePointInNavCache(int x, int y, int slop);
     // Like many other of our native methods, you must make sure that
     // mNativeClass is not null before calling this method.
     private native void     nativeRecordButtons(boolean focused,
             boolean pressed, boolean invalidate);
+    private native void     nativeResetSelection();
+    private native void     nativeSelectAll();
     private native void     nativeSelectBestAt(Rect rect);
+    private native int      nativeSelectionX();
+    private native int      nativeSelectionY();
+    private native int      nativeFindIndex();
+    private native void     nativeSetExtendSelection();
     private native void     nativeSetFindIsEmpty();
     private native void     nativeSetFindIsUp(boolean isUp);
     private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
     private native void     nativeSetRootLayer(int layer);
     private native void     nativeSetSelectionPointer(boolean set,
-            float scale, int x, int y, boolean extendSelection);
+            float scale, int x, int y);
+    private native boolean  nativeStartSelection(int x, int y);
     private native void     nativeSetSelectionRegion(boolean set);
     private native Rect     nativeSubtractLayers(Rect content);
     private native int      nativeTextGeneration();
@@ -7448,6 +7538,7 @@
     // we always want to pass in our generation number.
     private native void     nativeUpdateCachedTextfield(String updatedText,
             int generation);
+    private native boolean  nativeWordSelection(int x, int y);
     // return NO_LEFTEDGE means failure.
     private static final int NO_LEFTEDGE = -1;
     private native int      nativeGetBlockLeftEdge(int x, int y, float scale);
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index e15a520..3971487 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -76,6 +76,9 @@
  * }
  * </pre>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-autocomplete.html">Auto Complete
+ * tutorial</a>.</p>
+ *
  * @attr ref android.R.styleable#AutoCompleteTextView_completionHint
  * @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold
  * @attr ref android.R.styleable#AutoCompleteTextView_completionHintView
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 5e692d4..176233e 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -48,6 +48,9 @@
  * }
  * </pre>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
  * <p><strong>XML attributes</strong></p>
  * <p> 
  * See {@link android.R.styleable#Button Button Attributes}, 
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index ff63a24..b89c2a9 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -41,6 +41,9 @@
  *     }
  * }
  * </pre>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
  *  
  * <p><strong>XML attributes</strong></p> 
  * <p>
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 1fc23ab..8aed454 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -37,6 +37,9 @@
 /**
  * A view for selecting a month / year / day based on a calendar like layout.
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
+ *
  * For a dialog using this view, see {@link android.app.DatePickerDialog}.
  */
 @Widget
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 1532db1..0da68a4 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -35,6 +35,9 @@
 /**
  * EditText is a thin veneer over TextView that configures itself
  * to be editable.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
  * <p>
  * <b>XML attributes</b>
  * <p>
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 1ed6b16..ccd37d3 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -46,6 +46,9 @@
  * <p>
  * Views given to the Gallery should use {@link Gallery.LayoutParams} as their
  * layout parameters type.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gallery.html">Gallery
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#Gallery_animationDuration
  * @attr ref android.R.styleable#Gallery_spacing
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index d2829db..2f86d75 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -31,6 +31,9 @@
 /**
  * A view that shows items in two-dimensional scrolling grid. The items in the
  * grid come from the {@link ListAdapter} associated with this view.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gridview.html">Grid
+ * View tutorial</a>.</p>
  */
 public class GridView extends AbsListView {
     public static final int NO_STRETCH = 0;
diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java
index 5c05170..12a68db 100644
--- a/core/java/android/widget/ImageButton.java
+++ b/core/java/android/widget/ImageButton.java
@@ -62,6 +62,9 @@
  * it will only be applied after {@code android:state_pressed} and {@code
  * android:state_focused} have both evaluated false.</p>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
  * <p><strong>XML attributes</strong></p>
  * <p>
  * See {@link android.R.styleable#ImageView Button Attributes},
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 0525891..faf082d 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -37,6 +37,9 @@
  * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}.
  * The default orientation is horizontal.
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-linearlayout.html">Linear Layout
+ * tutorial</a>.</p>
+ *
  * <p>
  * Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
  * for layout attributes </p>
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index ec6dbb7..a3f86249 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -58,6 +58,9 @@
  * A view that shows items in a vertically scrolling list. The items
  * come from the {@link ListAdapter} associated with this view.
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-listview.html">List View
+ * tutorial</a>.</p>
+ *
  * @attr ref android.R.styleable#ListView_entries
  * @attr ref android.R.styleable#ListView_divider
  * @attr ref android.R.styleable#ListView_dividerHeight
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index c0a546d..ec7d927 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -21,8 +21,9 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
-import android.graphics.Shader;
 import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.Animatable;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ClipDrawable;
@@ -30,11 +31,14 @@
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.StateListDrawable;
-import android.graphics.drawable.Animatable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.RemotableViewMethod;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.animation.AlphaAnimation;
@@ -44,9 +48,6 @@
 import android.view.animation.LinearInterpolator;
 import android.view.animation.Transformation;
 import android.widget.RemoteViews.RemoteView;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
 
 import com.android.internal.R;
 
@@ -762,6 +763,7 @@
     }
 
     @Override
+    @RemotableViewMethod
     public void setVisibility(int v) {
         if (getVisibility() != v) {
             super.setVisibility(v);
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index 14ec8c6..ebbe1cd 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -34,6 +34,9 @@
  * a radio group, checking one radio button unchecks all the others.</p>
  * </p>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
  * <p><strong>XML attributes</strong></p>
  * <p> 
  * See {@link android.R.styleable#CompoundButton CompoundButton Attributes}, 
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 1800c5a..28499d0 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -41,6 +41,9 @@
  * <p>
  * The secondary progress should not be modified by the client as it is used
  * internally as the background for a fractionally filled star.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#RatingBar_numStars
  * @attr ref android.R.styleable#RatingBar_rating
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 64cda49..a47359f 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -54,6 +54,9 @@
  * {@link #ALIGN_PARENT_BOTTOM}.
  * </p>
  *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-relativelayout.html">Relative
+ * Layout tutorial</a>.</p>
+ *
  * <p>
  * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
  * layout attributes
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 2f6dd1e..8ddb06c 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -32,6 +32,9 @@
  * A view that displays one child at a time and lets the user pick among them.
  * The items in the Spinner come from the {@link Adapter} associated with
  * this view.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#Spinner_prompt
  */
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 02cd6a8..f720cee 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -40,6 +40,9 @@
  * user clicks to select a specific tab, and a FrameLayout object that displays the contents of that
  * page. The individual elements are typically controlled using this container object, rather than
  * setting values on the child elements themselves.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
+ * tutorial</a>.</p>
  */
 public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
 
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 4e1b585..afae7ef 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -40,6 +40,9 @@
  * handler, and manage callbacks. You might call this object to iterate the list
  * of tabs, or to tweak the layout of the tab list, but most methods should be
  * called on the containing TabHost object.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#TabWidget_divider
  * @attr ref android.R.styleable#TabWidget_tabStripEnabled
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 73760ac..7f26e28 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -67,6 +67,9 @@
  * <p>Although the typical child of a TableLayout is a TableRow, you can
  * actually use any View subclass as a direct child of TableLayout. The View
  * will be displayed as a single row that spans all the table columns.</p>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tablelayout.html">Table
+ * Layout tutorial</a>.</p>
  */
 public class TableLayout extends LinearLayout {
     private int[] mMaxWidths;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 27e0e94..73b1d6d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -23,6 +23,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -61,6 +62,7 @@
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.text.method.ArrowKeyMovementMethod;
 import android.text.method.DateKeyListener;
 import android.text.method.DateTimeKeyListener;
 import android.text.method.DialerKeyListener;
@@ -89,10 +91,11 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewDebug;
+import android.view.ViewGroup.LayoutParams;
 import android.view.ViewRoot;
 import android.view.ViewTreeObserver;
-import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AnimationUtils;
@@ -185,7 +188,7 @@
  */
 @RemoteView
 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
-    static final String TAG = "TextView";
+    static final String LOG_TAG = "TextView";
     static final boolean DEBUG_EXTRACT = false;
     
     private static int PRIORITY = 100;
@@ -321,6 +324,7 @@
         this(context, attrs, com.android.internal.R.attr.textViewStyle);
     }
 
+    @SuppressWarnings("deprecation")
     public TextView(Context context,
                     AttributeSet attrs,
                     int defStyle) {
@@ -695,9 +699,9 @@
                 try {
                     setInputExtras(a.getResourceId(attr, 0));
                 } catch (XmlPullParserException e) {
-                    Log.w("TextView", "Failure reading input extras", e);
+                    Log.w(LOG_TAG, "Failure reading input extras", e);
                 } catch (IOException e) {
-                    Log.w("TextView", "Failure reading input extras", e);
+                    Log.w(LOG_TAG, "Failure reading input extras", e);
                 }
                 break;
             }
@@ -714,7 +718,7 @@
         }
 
         if (inputMethod != null) {
-            Class c;
+            Class<?> c;
 
             try {
                 c = Class.forName(inputMethod.toString());
@@ -923,6 +927,8 @@
         setFocusable(focusable);
         setClickable(clickable);
         setLongClickable(longClickable);
+
+        prepareCursorControllers();
     }
 
     private void setTypefaceByIndex(int typefaceIndex, int styleIndex) {
@@ -1128,6 +1134,9 @@
             setText(mText);
 
         fixFocusableAndClickableSettings();
+
+        // SelectionModifierCursorController depends on canSelectText, which depends on mMovement
+        prepareCursorControllers();
     }
 
     private void fixFocusableAndClickableSettings() {
@@ -2335,6 +2344,7 @@
             return str + "}";
         }
 
+        @SuppressWarnings("hiding")
         public static final Parcelable.Creator<SavedState> CREATOR
                 = new Parcelable.Creator<SavedState>() {
             public SavedState createFromParcel(Parcel in) {
@@ -2369,8 +2379,8 @@
         int end = 0;
 
         if (mText != null) {
-            start = Selection.getSelectionStart(mText);
-            end = Selection.getSelectionEnd(mText);
+            start = getSelectionStart();
+            end = getSelectionEnd();
             if (start >= 0 || end >= 0) {
                 // Or save state if there is a selection
                 save = true;
@@ -2442,7 +2452,7 @@
                         restored = "(restored) ";
                     }
 
-                    Log.e("TextView", "Saved cursor position " + ss.selStart +
+                    Log.e(LOG_TAG, "Saved cursor position " + ss.selStart +
                           "/" + ss.selEnd + " out of range for " + restored +
                           "text " + mText);
                 } else {
@@ -2694,6 +2704,9 @@
         if (needEditableForNotification) {
             sendAfterTextChanged((Editable) text);
         }
+
+        // SelectionModifierCursorController depends on canSelectText, which depends on text
+        prepareCursorControllers();
     }
 
     /**
@@ -2756,6 +2769,7 @@
             return mChars[off + mStart];
         }
 
+        @Override
         public String toString() {
             return new String(mChars, mStart, mLength);
         }
@@ -2981,7 +2995,7 @@
         } else {
             input = TextKeyListener.getInstance();
         }
-        mInputType = type;
+        setRawInputType(type);
         if (direct) mInput = input;
         else {
             setKeyListenerOnly(input);
@@ -3198,7 +3212,7 @@
      *
      * @param create If true, the extras will be created if they don't already
      * exist.  Otherwise, null will be returned if none have been created.
-     * @see #setInputExtras(int)View
+     * @see #setInputExtras(int)
      * @see EditorInfo#extras
      * @attr ref android.R.styleable#TextView_editorExtras
      */
@@ -3312,7 +3326,7 @@
 
     private static class ErrorPopup extends PopupWindow {
         private boolean mAbove = false;
-        private TextView mView;
+        private final TextView mView;
 
         ErrorPopup(TextView v, int width, int height) {
             super(v, width, height);
@@ -3585,7 +3599,7 @@
     }
 
     private void invalidateCursor() {
-        int where = Selection.getSelectionEnd(mText);
+        int where = getSelectionEnd();
 
         invalidateCursor(where, where, where);
     }
@@ -3661,7 +3675,18 @@
         boolean changed = false;
 
         if (mMovement != null) {
-            int curs = Selection.getSelectionEnd(mText);
+            /* This code also provides auto-scrolling when a cursor is moved using a
+             * CursorController (insertion point or selection limits).
+             * For selection, ensure start or end is visible depending on controller's state.
+             */
+            int curs = getSelectionEnd();
+            if (mSelectionModifierCursorController != null) {
+                SelectionModifierCursorController selectionController =
+                    (SelectionModifierCursorController) mSelectionModifierCursorController;
+                if (selectionController.isSelectionStartDragged()) {
+                    curs = getSelectionStart();
+                }
+            }
 
             /*
              * TODO: This should really only keep the end in view if
@@ -3680,6 +3705,10 @@
             changed = bringTextIntoView();
         }
 
+        if (mShouldStartTextSelectionMode) {
+            startTextSelectionMode();
+            mShouldStartTextSelectionMode = false;
+        }
         mPreDrawState = PREDRAW_DONE;
         return !changed;
     }
@@ -3954,8 +3983,8 @@
         //  XXX This is not strictly true -- a program could set the
         //  selection manually if it really wanted to.
         if (mMovement != null && (isFocused() || isPressed())) {
-            selStart = Selection.getSelectionStart(mText);
-            selEnd = Selection.getSelectionEnd(mText);
+            selStart = getSelectionStart();
+            selEnd = getSelectionEnd();
 
             if (mCursorVisible && selStart >= 0 && isEnabled()) {
                 if (mHighlightPath == null)
@@ -4061,6 +4090,13 @@
         */
 
         canvas.restore();
+
+        if (mInsertionPointCursorController != null) {
+            mInsertionPointCursorController.draw(canvas);
+        }
+        if (mSelectionModifierCursorController != null) {
+            mSelectionModifierCursorController.draw(canvas);
+        }
     }
 
     @Override
@@ -4345,6 +4381,8 @@
             return super.onKeyUp(keyCode, event);
         }
 
+        hideControllers();
+
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_CENTER:
                 /*
@@ -4475,8 +4513,8 @@
             outAttrs.hintText = mHint;
             if (mText instanceof Editable) {
                 InputConnection ic = new EditableInputConnection(this);
-                outAttrs.initialSelStart = Selection.getSelectionStart(mText);
-                outAttrs.initialSelEnd = Selection.getSelectionEnd(mText);
+                outAttrs.initialSelStart = getSelectionStart();
+                outAttrs.initialSelEnd = getSelectionEnd();
                 outAttrs.initialCapsMode = ic.getCursorCapsMode(mInputType);
                 return ic;
             }
@@ -4560,8 +4598,8 @@
                 outText.flags |= ExtractedText.FLAG_SINGLE_LINE;
             }
             outText.startOffset = 0;
-            outText.selectionStart = Selection.getSelectionStart(content);
-            outText.selectionEnd = Selection.getSelectionEnd(content);
+            outText.selectionStart = getSelectionStart();
+            outText.selectionEnd = getSelectionEnd();
             return true;
         }
         return false;
@@ -4578,7 +4616,7 @@
                 if (req != null) {
                     InputMethodManager imm = InputMethodManager.peekInstance();
                     if (imm != null) {
-                        if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start="
+                        if (DEBUG_EXTRACT) Log.v(LOG_TAG, "Retrieving extracted start="
                                 + ims.mChangedStart + " end=" + ims.mChangedEnd
                                 + " delta=" + ims.mChangedDelta);
                         if (ims.mChangedStart < 0 && !contentChanged) {
@@ -4586,7 +4624,7 @@
                         }
                         if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
                                 ims.mChangedDelta, ims.mTmpExtracted)) {
-                            if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start="
+                            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "Reporting extracted start="
                                     + ims.mTmpExtracted.partialStartOffset
                                     + " end=" + ims.mTmpExtracted.partialEndOffset
                                     + ": " + ims.mTmpExtracted.text);
@@ -4736,7 +4774,7 @@
     
     void updateAfterEdit() {
         invalidate();
-        int curs = Selection.getSelectionStart(mText);
+        int curs = getSelectionStart();
 
         if (curs >= 0 || (mGravity & Gravity.VERTICAL_GRAVITY_MASK) ==
                              Gravity.BOTTOM) {
@@ -4878,7 +4916,6 @@
                                 w, alignment, mSpacingMult, mSpacingAdd,
                                 boring, mIncludePad);
                     }
-                    // Log.e("aaa", "Boring: " + mTransformed);
 
                     mSavedLayout = (BoringLayout) mLayout;
                 } else if (shouldEllipsize && boring.width <= w) {
@@ -4904,7 +4941,6 @@
                     mLayout = new StaticLayout(mTransformed, mTextPaint,
                             w, alignment, mSpacingMult, mSpacingAdd,
                             mIncludePad);
-                    // Log.e("aaa", "Boring but wide: " + mTransformed);
                 }
             } else if (shouldEllipsize) {
                 mLayout = new StaticLayout(mTransformed,
@@ -5002,6 +5038,9 @@
                 }
             }
         }
+
+        // CursorControllers need a non-null mLayout
+        prepareCursorControllers();
     }
 
     private boolean compressText(float width) {
@@ -5473,7 +5512,7 @@
         // FIXME: Is it okay to truncate this, or should we round?
         final int x = (int)mLayout.getPrimaryHorizontal(offset);
         final int top = mLayout.getLineTop(line);
-        final int bottom = mLayout.getLineTop(line+1);
+        final int bottom = mLayout.getLineTop(line + 1);
 
         int left = (int) FloatMath.floor(mLayout.getLineLeft(line));
         int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
@@ -5610,8 +5649,8 @@
             // viewport coordinates, but requestRectangleOnScreen()
             // is in terms of content coordinates.
 
-            Rect r = new Rect();
-            getInterestingRect(r, x, top, bottom, line);
+            Rect r = new Rect(x, top, x + 1, bottom);
+            getInterestingRect(r, line);
             r.offset(mScrollX, mScrollY);
 
             if (requestRectangleOnScreen(r)) {
@@ -5627,13 +5666,15 @@
      * to the user.  This will not move the cursor if it represents more than
      * one character (a selection range).  This will only work if the
      * TextView contains spannable text; otherwise it will do nothing.
+     *
+     * @return True if the cursor was actually moved, false otherwise.
      */
     public boolean moveCursorToVisibleOffset() {
         if (!(mText instanceof Spannable)) {
             return false;
         }
-        int start = Selection.getSelectionStart(mText);
-        int end = Selection.getSelectionEnd(mText);
+        int start = getSelectionStart();
+        int end = getSelectionEnd();
         if (start != end) {
             return false;
         }
@@ -5643,7 +5684,7 @@
         int line = mLayout.getLineForOffset(start);
 
         final int top = mLayout.getLineTop(line);
-        final int bottom = mLayout.getLineTop(line+1);
+        final int bottom = mLayout.getLineTop(line + 1);
         final int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
         int vslack = (bottom - top) / 2;
         if (vslack > vspace / 4)
@@ -5689,22 +5730,28 @@
         }
     }
 
-    private void getInterestingRect(Rect r, int h, int top, int bottom,
-                                    int line) {
+    private void getInterestingRect(Rect r, int line) {
+        convertFromViewportToContentCoordinates(r);
+
+        // Rectangle can can be expanded on first and last line to take
+        // padding into account.
+        // TODO Take left/right padding into account too?
+        if (line == 0) r.top -= getExtendedPaddingTop();
+        if (line == mLayout.getLineCount() - 1) r.bottom += getExtendedPaddingBottom();
+    }
+
+    private void convertFromViewportToContentCoordinates(Rect r) {
         int paddingTop = getExtendedPaddingTop();
         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
             paddingTop += getVerticalOffset(false);
         }
-        top += paddingTop;
-        bottom += paddingTop;
-        h += getCompoundPaddingLeft();
+        r.top += paddingTop;
+        r.bottom += paddingTop;
 
-        if (line == 0)
-            top -= getExtendedPaddingTop();
-        if (line == mLayout.getLineCount() - 1)
-            bottom += getExtendedPaddingBottom();
+        int paddingLeft = getCompoundPaddingLeft();
+        r.left += paddingLeft;
+        r.right += paddingLeft;
 
-        r.set(h, top, h+1, bottom);
         r.offset(-mScrollX, -mScrollY);
     }
 
@@ -5872,6 +5919,9 @@
         } else if (mBlink != null) {
             mBlink.removeCallbacks(mBlink);
         }
+
+        // InsertionPointCursorController depends on mCursorVisible
+        prepareCursorControllers();
     }
 
     private boolean canMarquee() {
@@ -5930,7 +5980,7 @@
         private final WeakReference<TextView> mView;
 
         private byte mStatus = MARQUEE_STOPPED;
-        private float mScrollUnit;
+        private final float mScrollUnit;
         private float mMaxScroll;
         float mMaxFadeScroll;
         private float mGhostStart;
@@ -5942,7 +5992,7 @@
 
         Marquee(TextView v) {
             final float density = v.getContext().getResources().getDisplayMetrics().density;
-            mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / (float) MARQUEE_RESOLUTION;
+            mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / MARQUEE_RESOLUTION;
             mView = new WeakReference<TextView>(v);
         }
 
@@ -6175,6 +6225,7 @@
         
         sendOnTextChanged(buffer, start, before, after);
         onTextChanged(buffer, start, before, after);
+        hideControllers();
     }
     
     /**
@@ -6286,7 +6337,7 @@
                         }
                     }
                 } else {
-                    if (DEBUG_EXTRACT) Log.v(TAG, "Span change outside of batch: "
+                    if (DEBUG_EXTRACT) Log.v(LOG_TAG, "Span change outside of batch: "
                             + oldStart + "-" + oldEnd + ","
                             + newStart + "-" + newEnd + what);
                     ims.mContentChanged = true;
@@ -6302,7 +6353,7 @@
 
         public void beforeTextChanged(CharSequence buffer, int start,
                                       int before, int after) {
-            if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "beforeTextChanged start=" + start
                     + " before=" + before + " after=" + after + ": " + buffer);
 
             if (AccessibilityManager.getInstance(mContext).isEnabled()
@@ -6315,7 +6366,7 @@
 
         public void onTextChanged(CharSequence buffer, int start,
                                   int before, int after) {
-            if (DEBUG_EXTRACT) Log.v(TAG, "onTextChanged start=" + start
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onTextChanged start=" + start
                     + " before=" + before + " after=" + after + ": " + buffer);
             TextView.this.handleTextChanged(buffer, start, before, after);
 
@@ -6328,7 +6379,7 @@
         }
 
         public void afterTextChanged(Editable buffer) {
-            if (DEBUG_EXTRACT) Log.v(TAG, "afterTextChanged: " + buffer);
+            if (DEBUG_EXTRACT) Log.v(LOG_TAG, "afterTextChanged: " + buffer);
             TextView.this.sendAfterTextChanged(buffer);
 
             if (MetaKeyKeyListener.getMetaState(buffer,
@@ -6339,19 +6390,19 @@
 
         public void onSpanChanged(Spannable buf,
                                   Object what, int s, int e, int st, int en) {
-            if (DEBUG_EXTRACT) Log.v(TAG, "onSpanChanged s=" + s + " e=" + e
+            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(TAG, "onSpanAdded s=" + s + " e=" + 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(TAG, "onSpanRemoved s=" + s + " e=" + 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);
         }
@@ -6455,12 +6506,22 @@
             if (mError != null) {
                 showError();
             }
+
+            // We cannot start the selection mode immediately. The layout may be null here and is
+            // needed by the cursor controller. Layout creation is deferred up to drawing. The
+            // selection action mode will be started in onPreDraw().
+            if (selStart != selEnd) {
+                mShouldStartTextSelectionMode = true;
+            }
         } else {
             if (mError != null) {
                 hideError();
             }
             // Don't leave us in the middle of a batch edit.
             onEndBatchEdit();
+
+            hideInsertionPointCursorController();
+            terminateTextSelectionMode();
         }
 
         startStopMarquee(focused);
@@ -6527,24 +6588,52 @@
     }
 
     class CommitSelectionReceiver extends ResultReceiver {
-        int mNewStart;
-        int mNewEnd;
+        private final int mPrevStart, mPrevEnd;
+        private final int mNewStart, mNewEnd;
         
-        CommitSelectionReceiver() {
+        public CommitSelectionReceiver(int mPrevStart, int mPrevEnd, int mNewStart, int mNewEnd) {
             super(getHandler());
+            this.mPrevStart = mPrevStart;
+            this.mPrevEnd = mPrevEnd;
+            this.mNewStart = mNewStart;
+            this.mNewEnd = mNewEnd;
         }
         
+        @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
-            if (resultCode != InputMethodManager.RESULT_SHOWN) {
-                final int len = mText.length();
-                if (mNewStart > len) {
-                    mNewStart = len;
+            int start = mNewStart;
+            int end = mNewEnd;
+
+            // Move the cursor to the new position, unless this tap was actually
+            // use to show the IMM. Leave cursor unchanged in that case.
+            if (resultCode == InputMethodManager.RESULT_SHOWN) {
+                start = mPrevStart;
+                end = mPrevEnd;
+            } else {
+                if ((mPrevStart != mPrevEnd) && (start == end)) {
+                    if ((start >= mPrevStart) && (start <= mPrevEnd)) {
+                        // Tapping inside the selection does nothing
+                        Selection.setSelection((Spannable) mText, mPrevStart, mPrevEnd);
+                        return;
+                    } else {
+                        // Tapping outside stops selection mode, if any
+                        stopTextSelectionMode();
+                    }
                 }
-                if (mNewEnd > len) {
-                    mNewEnd = len;
+
+                if (mInsertionPointCursorController != null) {
+                    mInsertionPointCursorController.show();
                 }
-                Selection.setSelection((Spannable)mText, mNewStart, mNewEnd);
             }
+
+            final int len = mText.length();
+            if (start > len) {
+                start = len;
+            }
+            if (end > len) {
+                end = len;
+            }
+            Selection.setSelection((Spannable)mText, start, end);
         }
     }
     
@@ -6557,7 +6646,7 @@
             mTouchFocusSelected = false;
             mScrolled = false;
         }
-        
+
         final boolean superResult = super.onTouchEvent(event);
 
         /*
@@ -6572,42 +6661,37 @@
 
         if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) {
             
-            boolean handled = false;
+            int oldSelStart = getSelectionStart();
+            int oldSelEnd = getSelectionEnd();
             
-            int oldSelStart = Selection.getSelectionStart(mText);
-            int oldSelEnd = Selection.getSelectionEnd(mText);
+            if (mInsertionPointCursorController != null) {
+                mInsertionPointCursorController.onTouchEvent(event);
+            }
+            if (mSelectionModifierCursorController != null) {
+                mSelectionModifierCursorController.onTouchEvent(event);
+            }
+
+            boolean handled = false;
             
             if (mMovement != null) {
                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
             }
 
-            if (mText instanceof Editable && onCheckIsTextEditor()) {
+            if (isTextEditable()) {
                 if (action == MotionEvent.ACTION_UP && isFocused() && !mScrolled) {
                     InputMethodManager imm = (InputMethodManager)
                             getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                     
-                    // This is going to be gross...  if tapping on the text view
-                    // causes the IME to be displayed, we don't want the selection
-                    // to change.  But the selection has already changed, and
-                    // we won't know right away whether the IME is getting
-                    // displayed, so...
+                    final int newSelStart = getSelectionStart();
+                    final int newSelEnd = getSelectionEnd();
                     
-                    int newSelStart = Selection.getSelectionStart(mText);
-                    int newSelEnd = Selection.getSelectionEnd(mText);
                     CommitSelectionReceiver csr = null;
                     if (newSelStart != oldSelStart || newSelEnd != oldSelEnd) {
-                        csr = new CommitSelectionReceiver();
-                        csr.mNewStart = newSelStart;
-                        csr.mNewEnd = newSelEnd;
+                        csr = new CommitSelectionReceiver(oldSelStart, oldSelEnd,
+                                newSelStart, newSelEnd);
                     }
                     
-                    if (imm.showSoftInput(this, 0, csr) && csr != null) {
-                        // The IME might get shown -- revert to the old
-                        // selection, and change to the new when we finally
-                        // find out of it is okay.
-                        Selection.setSelection((Spannable)mText, oldSelStart, oldSelEnd);
-                        handled = true;
-                    }
+                    handled |= imm.showSoftInput(this, 0, csr) && (csr != null);
                 }
             }
 
@@ -6619,6 +6703,34 @@
         return superResult;
     }
 
+    private void prepareCursorControllers() {
+        // TODO Add an extra android:cursorController flag to disable the controller?
+        if (mCursorVisible && mLayout != null) {
+            if (mInsertionPointCursorController == null) {
+                mInsertionPointCursorController = new InsertionPointCursorController();
+            }
+        } else {
+            mInsertionPointCursorController = null;
+        }
+
+        if (canSelectText() && mLayout != null) {
+            if (mSelectionModifierCursorController == null) {
+                mSelectionModifierCursorController = new SelectionModifierCursorController();
+            }
+        } else {
+            // Stop selection mode if the controller becomes unavailable.
+            stopTextSelectionMode();
+            mSelectionModifierCursorController = null;
+        }
+    }
+
+    /**
+     * @return True iff this TextView contains a text that can be edited.
+     */
+    private boolean isTextEditable() {
+        return mText instanceof Editable && onCheckIsTextEditor();
+    }
+
     /**
      * Returns true, only while processing a touch gesture, if the initial
      * touch down event caused focus to move to the text view and as a result
@@ -6652,7 +6764,7 @@
     }
 
     private static class Blink extends Handler implements Runnable {
-        private WeakReference<TextView> mView;
+        private final WeakReference<TextView> mView;
         private boolean mCancelled;
 
         public Blink(TextView v) {
@@ -6669,8 +6781,8 @@
             TextView tv = mView.get();
 
             if (tv != null && tv.isFocused()) {
-                int st = Selection.getSelectionStart(tv.mText);
-                int en = Selection.getSelectionEnd(tv.mText);
+                int st = tv.getSelectionStart();
+                int en = tv.getSelectionEnd();
 
                 if (st == en && st >= 0 && en >= 0) {
                     if (tv.mLayout != null) {
@@ -6851,21 +6963,16 @@
     }
 
     private boolean canSelectAll() {
-        if (mText instanceof Spannable && mText.length() != 0 &&
-            mMovement != null && mMovement.canSelectArbitrarily()) {
-            return true;
-        }
-
-        return false;
+        return canSelectText() && mText.length() != 0;
     }
 
     private boolean canSelectText() {
-        if (mText instanceof Spannable && mText.length() != 0 &&
-            mMovement != null && mMovement.canSelectArbitrarily()) {
-            return true;
-        }
-
-        return false;
+        // prepareCursorController() relies on this method.
+        // If you change this condition, make sure prepareCursorController is called anywhere
+        // the value of this condition might be changed.
+        return (mText instanceof Spannable &&
+                mMovement != null &&
+                mMovement.canSelectArbitrarily());
     }
 
     private boolean canCut() {
@@ -6895,23 +7002,23 @@
     }
 
     private boolean canPaste() {
-        if (mText instanceof Editable && mInput != null &&
-            getSelectionStart() >= 0 && getSelectionEnd() >= 0) {
-            ClipboardManager clip = (ClipboardManager)getContext()
-                    .getSystemService(Context.CLIPBOARD_SERVICE);
-            if (clip.hasText()) {
-                return true;
-            }
-        }
-
-        return false;
+        return (mText instanceof Editable &&
+                mInput != null &&
+                getSelectionStart() >= 0 &&
+                getSelectionEnd() >= 0 &&
+                ((ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE)).
+                hasText());
     }
 
     /**
-     * Returns a word to add to the dictionary from the context menu,
-     * or null if there is no cursor or no word at the cursor.
+     * Returns the offsets delimiting the 'word' located at position offset.
+     *
+     * @param offset An offset in the text.
+     * @return The offsets for the start and end of the word located at <code>offset</code>.
+     * The two ints offsets are packed in a long, with the starting offset shifted by 32 bits.
+     * Returns a negative value if no valid word was found.
      */
-    private String getWordForDictionary() {
+    private long getWordLimitsAt(int offset) {
         /*
          * Quick return if the input type is one where adding words
          * to the dictionary doesn't make any sense.
@@ -6920,7 +7027,7 @@
         if (klass == InputType.TYPE_CLASS_NUMBER ||
             klass == InputType.TYPE_CLASS_PHONE ||
             klass == InputType.TYPE_CLASS_DATETIME) {
-            return null;
+            return -1;
         }
 
         int variation = mInputType & InputType.TYPE_MASK_VARIATION;
@@ -6929,13 +7036,13 @@
             variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ||
             variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
             variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
-            return null;
+            return -1;
         }
 
-        int end = getSelectionEnd();
+        int end = offset;
 
         if (end < 0) {
-            return null;
+            return -1;
         }
 
         int start = end;
@@ -6969,6 +7076,14 @@
             }
         }
 
+        if (start == end) {
+            return -1;
+        }
+
+        if (end - start > 48) {
+            return -1;
+        }
+
         boolean hasLetter = false;
         for (int i = start; i < end; i++) {
             if (Character.isLetter(mTransformed.charAt(i))) {
@@ -6976,21 +7091,61 @@
                 break;
             }
         }
+
         if (!hasLetter) {
-            return null;
+            return -1;
         }
 
-        if (start == end) {
-            return null;
-        }
-
-        if (end - start > 48) {
-            return null;
-        }
-
-        return TextUtils.substring(mTransformed, start, end);
+        // Two ints packed in a long
+        return (((long) start) << 32) | end;
     }
+    
+    private void selectCurrentWord() {
+        // In case selection mode is started after an orientation change or after a select all,
+        // use the current selection instead of creating one
+        if (hasSelection()) {
+            return;
+        }
 
+        int selectionStart, selectionEnd;
+
+        SelectionModifierCursorController selectionModifierCursorController =
+            ((SelectionModifierCursorController) mSelectionModifierCursorController);
+        int minOffset = selectionModifierCursorController.getMinTouchOffset();
+        int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
+
+        long wordLimits = getWordLimitsAt(minOffset);
+        if (wordLimits >= 0) {
+            selectionStart = (int) (wordLimits >>> 32);
+        } else {
+            selectionStart = Math.max(minOffset - 5, 0);
+        }
+
+        wordLimits = getWordLimitsAt(maxOffset);
+        if (wordLimits >= 0) {
+            selectionEnd = (int) (wordLimits & 0x00000000FFFFFFFFL);
+        } else {
+            selectionEnd = Math.min(maxOffset + 5, mText.length());
+        }
+
+        Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+    }
+    
+    private String getWordForDictionary() {
+        int offset = ((SelectionModifierCursorController) mSelectionModifierCursorController).
+                     getMinTouchOffset();
+
+        long wordLimits = getWordLimitsAt(offset);
+        if (wordLimits >= 0) {
+            int start = (int) (wordLimits >>> 32);
+            int end = (int) (wordLimits & 0x00000000FFFFFFFFL);
+            return mTransformed.subSequence(start, end).toString();
+        } else {
+            return null;
+        }
+        
+    }
+    
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         if (!isShown()) {
@@ -7032,114 +7187,98 @@
         super.onCreateContextMenu(menu);
         boolean added = false;
 
-        if (!isFocused()) {
-            if (isFocusable() && mInput != null) {
-                if (canCopy()) {
-                    MenuHandler handler = new MenuHandler();
-                    int name = com.android.internal.R.string.copyAll;
+        if (mIsInTextSelectionMode) {
+            MenuHandler handler = new MenuHandler();
+            
+            if (canCut()) {
+                menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
+                     setOnMenuItemClickListener(handler).
+                     setAlphabeticShortcut('x');
+            }
 
-                    menu.add(0, ID_COPY, 0, name).
-                        setOnMenuItemClickListener(handler).
-                        setAlphabeticShortcut('c');
-                    menu.setHeaderTitle(com.android.internal.R.string.
-                        editTextMenuTitle);
+            if (canCopy()) {
+                menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
+                     setOnMenuItemClickListener(handler).
+                     setAlphabeticShortcut('c');
+            }
+
+            if (canPaste()) {
+                menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
+                     setOnMenuItemClickListener(handler).
+                     setAlphabeticShortcut('v');
+            }
+
+            menu.add(0, ID_STOP_SELECTING_TEXT, 0, com.android.internal.R.string.stopSelectingText).
+                 setOnMenuItemClickListener(handler);
+            
+            added = true;
+        } else {
+            /*
+            if (!isFocused()) {
+                if (isFocusable() && mInput != null) {
+                    if (canCopy()) {
+                        MenuHandler handler = new MenuHandler();
+                        menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
+                             setOnMenuItemClickListener(handler).
+                             setAlphabeticShortcut('c');
+                        menu.setHeaderTitle(com.android.internal.R.string.editTextMenuTitle);
+                    }
+                }
+
+                //return;
+            }
+             */
+            MenuHandler handler = new MenuHandler();
+
+            if (canSelectText()) {
+                menu.add(0, ID_START_SELECTING_TEXT, 0, com.android.internal.R.string.selectText).
+                setOnMenuItemClickListener(handler);
+                added = true;
+            }
+            
+            if (canSelectAll()) {
+                menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+                     setOnMenuItemClickListener(handler).
+                     setAlphabeticShortcut('a');
+                added = true;
+            }
+
+            if (mText instanceof Spanned) {
+                int selStart = getSelectionStart();
+                int selEnd = getSelectionEnd();
+
+                int min = Math.min(selStart, selEnd);
+                int max = Math.max(selStart, selEnd);
+
+                URLSpan[] urls = ((Spanned) mText).getSpans(min, max,
+                        URLSpan.class);
+                if (urls.length == 1) {
+                    menu.add(0, ID_COPY_URL, 0, com.android.internal.R.string.copyUrl).
+                         setOnMenuItemClickListener(handler);
+                    added = true;
                 }
             }
+            
+            if (canPaste()) {
+                menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
+                     setOnMenuItemClickListener(handler).
+                     setAlphabeticShortcut('v');
+            }
 
-            return;
-        }
-
-        MenuHandler handler = new MenuHandler();
-
-        if (canSelectAll()) {
-            menu.add(0, ID_SELECT_ALL, 0,
-                    com.android.internal.R.string.selectAll).
-                setOnMenuItemClickListener(handler).
-                setAlphabeticShortcut('a');
-            added = true;
-        }
-
-        boolean selection = getSelectionStart() != getSelectionEnd();
-
-        if (canSelectText()) {
-            if (MetaKeyKeyListener.getMetaState(mText, MetaKeyKeyListener.META_SELECTING) != 0) {
-                menu.add(0, ID_STOP_SELECTING_TEXT, 0,
-                        com.android.internal.R.string.stopSelectingText).
-                    setOnMenuItemClickListener(handler);
-                added = true;
-            } else {
-                menu.add(0, ID_START_SELECTING_TEXT, 0,
-                        com.android.internal.R.string.selectText).
-                    setOnMenuItemClickListener(handler);
+            if (isInputMethodTarget()) {
+                menu.add(1, ID_SWITCH_INPUT_METHOD, 0, com.android.internal.R.string.inputMethod).
+                     setOnMenuItemClickListener(handler);
                 added = true;
             }
-        }
 
-        if (canCut()) {
-            int name;
-            if (selection) {
-                name = com.android.internal.R.string.cut;
-            } else {
-                name = com.android.internal.R.string.cutAll;
-            }
-
-            menu.add(0, ID_CUT, 0, name).
-                setOnMenuItemClickListener(handler).
-                setAlphabeticShortcut('x');
-            added = true;
-        }
-
-        if (canCopy()) {
-            int name;
-            if (selection) {
-                name = com.android.internal.R.string.copy;
-            } else {
-                name = com.android.internal.R.string.copyAll;
-            }
-
-            menu.add(0, ID_COPY, 0, name).
-                setOnMenuItemClickListener(handler).
-                setAlphabeticShortcut('c');
-            added = true;
-        }
-
-        if (canPaste()) {
-            menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
-                    setOnMenuItemClickListener(handler).
-                    setAlphabeticShortcut('v');
-            added = true;
-        }
-
-        if (mText instanceof Spanned) {
-            int selStart = getSelectionStart();
-            int selEnd = getSelectionEnd();
-
-            int min = Math.min(selStart, selEnd);
-            int max = Math.max(selStart, selEnd);
-
-            URLSpan[] urls = ((Spanned) mText).getSpans(min, max,
-                                                        URLSpan.class);
-            if (urls.length == 1) {
-                menu.add(0, ID_COPY_URL, 0,
-                         com.android.internal.R.string.copyUrl).
-                            setOnMenuItemClickListener(handler);
-                added = true;
-            }
-        }
-
-        if (isInputMethodTarget()) {
-            menu.add(1, ID_SWITCH_INPUT_METHOD, 0, com.android.internal.R.string.inputMethod).
-                    setOnMenuItemClickListener(handler);
-            added = true;
-        }
-
-        String word = getWordForDictionary();
-        if (word != null) {
-            menu.add(1, ID_ADD_TO_DICTIONARY, 0,
+            String word = getWordForDictionary();
+            if (word != null) {
+                menu.add(1, ID_ADD_TO_DICTIONARY, 0,
                      getContext().getString(com.android.internal.R.string.addToDictionary, word)).
-                    setOnMenuItemClickListener(handler);
-            added = true;
+                     setOnMenuItemClickListener(handler);
+                added = true;
 
+            }
         }
 
         if (added) {
@@ -7156,6 +7295,7 @@
         return imm != null && imm.isActive(this);
     }
     
+    // Context menu entries
     private static final int ID_SELECT_ALL = android.R.id.selectAll;
     private static final int ID_START_SELECTING_TEXT = android.R.id.startSelectingText;
     private static final int ID_STOP_SELECTING_TEXT = android.R.id.stopSelectingText;
@@ -7181,22 +7321,15 @@
      * or {@link android.R.id#switchInputMethod}.
      */
     public boolean onTextContextMenuItem(int id) {
-        int selStart = getSelectionStart();
-        int selEnd = getSelectionEnd();
+        int min = 0;
+        int max = mText.length();
 
-        if (!isFocused()) {
-            selStart = 0;
-            selEnd = mText.length();
-        }
+        if (isFocused()) {
+            final int selStart = getSelectionStart();
+            final int selEnd = getSelectionEnd();
 
-        int min = Math.min(selStart, selEnd);
-        int max = Math.max(selStart, selEnd);
-
-        if (min < 0) {
-            min = 0;
-        }
-        if (max < 0) {
-            max = 0;
+            min = Math.max(0, Math.min(selStart, selEnd));
+            max = Math.max(0, Math.max(selStart, selEnd));
         }
 
         ClipboardManager clip = (ClipboardManager)getContext()
@@ -7204,63 +7337,44 @@
 
         switch (id) {
             case ID_SELECT_ALL:
-                Selection.setSelection((Spannable) mText, 0,
-                        mText.length());
+                Selection.setSelection((Spannable) mText, 0, mText.length());
+                startTextSelectionMode();
                 return true;
 
             case ID_START_SELECTING_TEXT:
-                MetaKeyKeyListener.startSelecting(this, (Spannable) mText);
+                startTextSelectionMode();
                 return true;
 
             case ID_STOP_SELECTING_TEXT:
-                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
-                Selection.setSelection((Spannable) mText, getSelectionEnd());
+                stopTextSelectionMode();
                 return true;
 
-            case ID_CUT:
-                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
-
-                if (min == max) {
-                    min = 0;
-                    max = mText.length();
-                }
-
+            case ID_CUT:                
                 clip.setText(mTransformed.subSequence(min, max));
                 ((Editable) mText).delete(min, max);
+                stopTextSelectionMode();
                 return true;
 
             case ID_COPY:
-                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
-
-                if (min == max) {
-                    min = 0;
-                    max = mText.length();
-                }
-
                 clip.setText(mTransformed.subSequence(min, max));
+                stopTextSelectionMode();
                 return true;
 
             case ID_PASTE:
-                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
-
                 CharSequence paste = clip.getText();
 
                 if (paste != null) {
                     Selection.setSelection((Spannable) mText, max);
                     ((Editable) mText).replace(min, max, paste);
+                    stopTextSelectionMode();
                 }
-
                 return true;
 
             case ID_COPY_URL:
-                MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
-
-                URLSpan[] urls = ((Spanned) mText).getSpans(min, max,
-                                                       URLSpan.class);
+                URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
                 if (urls.length == 1) {
                     clip.setText(urls[0].getURL());
                 }
-
                 return true;
 
             case ID_SWITCH_INPUT_METHOD:
@@ -7279,13 +7393,13 @@
                     i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                     getContext().startActivity(i);
                 }
-
                 return true;
             }
 
         return false;
     }
 
+    @Override
     public boolean performLongClick() {
         if (super.performLongClick()) {
             mEatTouchRelease = true;
@@ -7295,7 +7409,572 @@
         return false;
     }
 
-    @ViewDebug.ExportedProperty(category = "text")
+    private void startTextSelectionMode() {
+        if (mSelectionModifierCursorController == null) {
+            Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled.");
+            return;
+        }
+
+        if (!requestFocus()) {
+            return;
+        }
+
+        selectCurrentWord();
+        mSelectionModifierCursorController.show();
+        mIsInTextSelectionMode = true;
+    }
+    
+    /**
+     * Same as {@link #stopTextSelectionMode()}, except that there is no cursor controller
+     * fade out animation. Needed since the drawable and their alpha values are shared by all
+     * TextViews. Switching from one TextView to another would fade the cursor controllers in the
+     * new one otherwise.
+     */
+    private void terminateTextSelectionMode() {
+        stopTextSelectionMode();
+        if (mSelectionModifierCursorController != null) {
+            SelectionModifierCursorController selectionModifierCursorController =
+                (SelectionModifierCursorController) mSelectionModifierCursorController;
+            selectionModifierCursorController.cancelFadeOutAnimation();
+        }
+    }
+
+    private void stopTextSelectionMode() {
+        if (mIsInTextSelectionMode) {
+            Selection.setSelection((Spannable) mText, getSelectionEnd());
+            if (mSelectionModifierCursorController != null) {
+                mSelectionModifierCursorController.hide();
+            }
+
+            mIsInTextSelectionMode = false;
+        }
+    }
+
+    /**
+     * A CursorController instance can be used to control a cursor in the text.
+     *
+     * It can be passed to an {@link ArrowKeyMovementMethod} which can intercepts events
+     * and send them to this object instead of the cursor.
+     */
+    public interface CursorController {
+        /* Cursor fade-out animation duration, in milliseconds. */
+        static final int FADE_OUT_DURATION = 400;
+
+        /**
+         * Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
+         * See also {@link #hide()}.
+         */
+        public void show();
+
+        /**
+         * Hide the cursor controller from screen.
+         * See also {@link #show()}.
+         */
+        public void hide();
+
+        /**
+         * Update the controller's position.
+         */
+        public void updatePosition(int offset);
+
+        /**
+         * The controller and the cursor's positions can be link by a fixed offset,
+         * computed when the controller is touched, and then maintained as it moves
+         * @return Horizontal offset between the controller and the cursor.
+         */
+        public float getOffsetX();
+
+        /**
+         * @return Vertical offset between the controller and the cursor.
+         */
+        public float getOffsetY();
+
+        /**
+         * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
+         * a chance to become active and/or visible.
+         * @param event The touch event
+         */
+        public void onTouchEvent(MotionEvent event);
+
+        /**
+         * Draws a visual representation of the controller on the canvas.
+         *
+         * Called at the end of {@link #draw(Canvas)}, in the content coordinates system.
+         * @param canvas The Canvas used by this TextView.
+         */
+        public void draw(Canvas canvas);
+    }
+
+    private class Handle {
+        Drawable mDrawable;
+        // Vertical extension of the touch region
+        int mTopExtension, mBottomExtension;
+        // Position of the virtual finger position on screen
+        int mHopSpotVertcalPosition;
+
+        Handle(Drawable drawable) {
+            mDrawable = drawable;
+        }
+
+        void positionAtCursor(final int offset, boolean bottom) {
+            final int drawableWidth = mDrawable.getIntrinsicWidth();
+            final int drawableHeight = mDrawable.getIntrinsicHeight();
+            final int line = mLayout.getLineForOffset(offset);
+            final int lineTop = mLayout.getLineTop(line);
+            final int lineBottom = mLayout.getLineBottom(line);
+
+            mHopSpotVertcalPosition = lineTop + (bottom ? (3 * (lineBottom - lineTop)) / 4 :
+                (lineBottom - lineTop) / 4);
+
+            final Rect bounds = sCursorControllerTempRect;
+            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0);
+            bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2;
+
+            mTopExtension = bottom ? 0 : drawableHeight / 2;
+            mBottomExtension = drawableHeight;
+
+            // Extend touch region up when editing the last line of text (or a single line) so that
+            // it is easier to grab.
+            if (line == mLayout.getLineCount() - 1) {
+                mTopExtension = (lineBottom - lineTop) - drawableHeight / 2;
+            }
+
+            bounds.right = bounds.left + drawableWidth;
+            bounds.bottom = bounds.top + drawableHeight;
+
+            int boundTopBefore = bounds.top;
+            convertFromViewportToContentCoordinates(bounds);
+            mHopSpotVertcalPosition += bounds.top - boundTopBefore;
+            mDrawable.setBounds(bounds);
+            postInvalidate();
+        }
+
+        boolean hasFingerOn(float x, float y) {
+            // Simulate a 'fat finger' to ease grabbing of the controller.
+            // Expands according to controller image size instead of using dip distance.
+            // Assumes controller imager has a sensible size, proportionnal to screen density.
+            final int drawableWidth = mDrawable.getIntrinsicWidth();
+            final Rect fingerRect = sCursorControllerTempRect;
+            fingerRect.set((int) (x - drawableWidth / 2.0),
+                           (int) (y - mBottomExtension),
+                           (int) (x + drawableWidth / 2.0),
+                           (int) (y + mTopExtension));
+            return Rect.intersects(mDrawable.getBounds(), fingerRect);
+        }
+
+        void postInvalidate() {
+            final Rect bounds = mDrawable.getBounds();
+            TextView.this.postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        }
+
+        void postInvalidateDelayed(long delay) {
+            final Rect bounds = mDrawable.getBounds();
+            TextView.this.postInvalidateDelayed(delay, bounds.left, bounds.top,
+                    bounds.right, bounds.bottom);
+        }
+    }
+
+    class InsertionPointCursorController implements CursorController {
+        private static final int DELAY_BEFORE_FADE_OUT = 2100;
+
+        // Whether or not the cursor control is currently visible
+        private boolean mIsVisible = false;
+        // Starting time of the fade timer
+        private long mFadeOutTimerStart;
+        // The cursor controller image
+        private final Handle mHandle;
+        // Used to detect a tap (vs drag) on the controller
+        private long mOnDownTimerStart;
+        // Offset between finger hot point on cursor controller and actual cursor
+        private float mOffsetX, mOffsetY;
+
+        InsertionPointCursorController() {
+            Resources res = mContext.getResources();
+            mHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
+        }
+
+        public void show() {
+            updateDrawablePosition();
+            // Has to be done after updatePosition, so that previous position invalidate
+            // in only done if necessary.
+            mIsVisible = true;
+        }
+
+        public void hide() {
+            if (mIsVisible) {
+                long time = System.currentTimeMillis();
+                // Start fading out, only if not already in progress
+                if (time - mFadeOutTimerStart < DELAY_BEFORE_FADE_OUT) {
+                    mFadeOutTimerStart = time - DELAY_BEFORE_FADE_OUT;
+                    mHandle.postInvalidate();
+                }
+            }
+        }
+
+        public void draw(Canvas canvas) {
+            if (mIsVisible) {
+                int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
+                if (time <= DELAY_BEFORE_FADE_OUT) {
+                    mHandle.postInvalidateDelayed(DELAY_BEFORE_FADE_OUT - time);
+                } else {
+                    time -= DELAY_BEFORE_FADE_OUT;
+                    if (time <= FADE_OUT_DURATION) {
+                        final int alpha = (int)
+                        ((255.0 * (FADE_OUT_DURATION - time)) / FADE_OUT_DURATION);
+                        mHandle.mDrawable.setAlpha(alpha);
+                        mHandle.postInvalidateDelayed(30);
+                    } else {
+                        mHandle.mDrawable.setAlpha(0);
+                        mIsVisible = false;
+                    }
+                }
+                mHandle.mDrawable.draw(canvas);
+            }
+        }
+
+        public void updatePosition(int offset) {
+            if (offset == getSelectionStart()) {
+                return; // No change, no need to redraw
+            }
+            Selection.setSelection((Spannable) mText, offset);
+            updateDrawablePosition();
+        }
+
+        private void updateDrawablePosition() {
+            if (mIsVisible) {
+                // Clear previous cursor controller before bounds are updated
+                mHandle.postInvalidate();
+            }
+
+            final int offset = getSelectionStart();
+
+            if (offset < 0) {
+                // Should never happen, safety check.
+                Log.w(LOG_TAG, "Update cursor controller position called with no cursor");
+                mIsVisible = false;
+                return;
+            }
+
+            mHandle.positionAtCursor(offset, true);
+
+            mFadeOutTimerStart = System.currentTimeMillis();
+            mHandle.mDrawable.setAlpha(255);
+        }
+
+        public void onTouchEvent(MotionEvent event) {
+            if (isFocused() && isTextEditable() && mIsVisible) {
+                switch (event.getActionMasked()) {
+                    case MotionEvent.ACTION_DOWN : {
+                        final float x = event.getX();
+                        final float y = event.getY();
+
+                        if (mHandle.hasFingerOn(x, y)) {
+                            show();
+
+                            if (mMovement instanceof ArrowKeyMovementMethod) {
+                                ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
+                            }
+
+                            if (mParent != null) {
+                                // Prevent possible scrollView parent from scrolling, so that
+                                // we can use auto-scrolling.
+                                mParent.requestDisallowInterceptTouchEvent(true);
+                            }
+
+                            final Rect bounds = mHandle.mDrawable.getBounds();
+                            mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
+                            mOffsetY = mHandle.mHopSpotVertcalPosition - y;
+
+                            mOnDownTimerStart = event.getEventTime();
+                        }
+                        break;
+                    }
+
+                    case MotionEvent.ACTION_UP : {
+                        int time = (int) (event.getEventTime() - mOnDownTimerStart);
+
+                        if (time <= ViewConfiguration.getTapTimeout()) {
+                            // A tap on the controller (not a drag) will move the cursor
+                            int offset = getOffset((int) event.getX(), (int) event.getY());
+                            Selection.setSelection((Spannable) mText, offset);
+
+                            // Modified by cancelLongPress and prevents the cursor from changing
+                            mScrolled = false;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+        public float getOffsetX() {
+            return mOffsetX;
+        }
+
+        public float getOffsetY() {
+            return mOffsetY;
+        }
+    }
+
+    class SelectionModifierCursorController implements CursorController {
+        // Whether or not the selection controls are currently visible
+        private boolean mIsVisible = false;
+        // Whether that start or the end of selection controller is dragged
+        private boolean mStartIsDragged = false;
+        // Starting time of the fade timer
+        private long mFadeOutTimerStart;
+        // The cursor controller images
+        private final Handle mStartHandle, mEndHandle;
+        // Offset between finger hot point on active cursor controller and actual cursor
+        private float mOffsetX, mOffsetY;
+        // The offsets of that last touch down event. Remembered to start selection there.
+        private int mMinTouchOffset, mMaxTouchOffset;
+
+        SelectionModifierCursorController() {
+            Resources res = mContext.getResources();
+            mStartHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
+            mEndHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
+        }
+
+        public void show() {
+            updateDrawablesPositions();
+            // Has to be done after updatePosition, so that previous position invalidate
+            // in only done if necessary.
+            mIsVisible = true;
+            mFadeOutTimerStart = -1;
+            hideInsertionPointCursorController();
+        }
+
+        public void hide() {
+            if (mIsVisible && (mFadeOutTimerStart < 0)) {
+                mFadeOutTimerStart = System.currentTimeMillis();
+                mStartHandle.postInvalidate();
+                mEndHandle.postInvalidate();
+            }
+        }
+
+        public void cancelFadeOutAnimation() {
+            mIsVisible = false;
+            mStartHandle.postInvalidate();
+            mEndHandle.postInvalidate();
+        }
+
+        public void draw(Canvas canvas) {
+            if (mIsVisible) {
+                if (mFadeOutTimerStart >= 0) {
+                    int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
+                    if (time <= FADE_OUT_DURATION) {
+                        final int alpha = 255 * (FADE_OUT_DURATION - time) / FADE_OUT_DURATION;
+                        mStartHandle.mDrawable.setAlpha(alpha);
+                        mEndHandle.mDrawable.setAlpha(alpha);
+                        mStartHandle.postInvalidateDelayed(30);
+                        mEndHandle.postInvalidateDelayed(30);
+                    } else {
+                        mStartHandle.mDrawable.setAlpha(0);
+                        mEndHandle.mDrawable.setAlpha(0);
+                        mIsVisible = false;
+                    }
+                }
+                mStartHandle.mDrawable.draw(canvas);
+                mEndHandle.mDrawable.draw(canvas);
+            }
+        }
+
+        public void updatePosition(int offset) {
+            int selectionStart = getSelectionStart();
+            int selectionEnd = getSelectionEnd();
+
+            // Handle the case where start and end are swapped, making sure start <= end
+            if (mStartIsDragged) {
+                if (offset <= selectionEnd) {
+                    if (selectionStart == offset) {
+                        return; // no change, no need to redraw;
+                    }
+                    selectionStart = offset;
+                } else {
+                    selectionStart = selectionEnd;
+                    selectionEnd = offset;
+                    mStartIsDragged = false;
+                }
+            } else {
+                if (offset >= selectionStart) {
+                    if (selectionEnd == offset) {
+                        return; // no change, no need to redraw;
+                    }
+                    selectionEnd = offset;
+                } else {
+                    selectionEnd = selectionStart;
+                    selectionStart = offset;
+                    mStartIsDragged = true;
+                }
+            }
+
+            Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+            updateDrawablesPositions();
+        }
+
+        private void updateDrawablesPositions() {
+            if (mIsVisible) {
+                // Clear previous cursor controller before bounds are updated
+                mStartHandle.postInvalidate();
+                mEndHandle.postInvalidate();
+            }
+
+            final int selectionStart = getSelectionStart();
+            final int selectionEnd = getSelectionEnd();
+
+            if ((selectionStart < 0) || (selectionEnd < 0)) {
+                // Should never happen, safety check.
+                Log.w(LOG_TAG, "Update selection controller position called with no cursor");
+                mIsVisible = false;
+                return;
+            }
+
+            boolean oneLineSelection = mLayout.getLineForOffset(selectionStart) == mLayout.getLineForOffset(selectionEnd); 
+            mStartHandle.positionAtCursor(selectionStart, oneLineSelection);
+            mEndHandle.positionAtCursor(selectionEnd, true);
+
+            mStartHandle.mDrawable.setAlpha(255);
+            mEndHandle.mDrawable.setAlpha(255);
+        }
+
+        public void onTouchEvent(MotionEvent event) {
+            if (isTextEditable()) {
+                switch (event.getActionMasked()) {
+                    case MotionEvent.ACTION_DOWN:
+                        final int x = (int) event.getX();
+                        final int y = (int) event.getY();
+
+                        // Remember finger down position, to be able to start selection from there
+                        mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
+
+                        if (mIsVisible) {
+                            if (mMovement instanceof ArrowKeyMovementMethod) {
+                                boolean isOnStart = mStartHandle.hasFingerOn(x, y);
+                                boolean isOnEnd = mEndHandle.hasFingerOn(x, y);
+                                if (isOnStart || isOnEnd) {
+                                    if (mParent != null) {
+                                        // Prevent possible scrollView parent from scrolling, so
+                                        // that we can use auto-scrolling.
+                                        mParent.requestDisallowInterceptTouchEvent(true);
+                                    }
+
+                                    // In case both controllers are under finger (very small
+                                    // selection region), arbitrarily pick end controller.
+                                    mStartIsDragged = !isOnEnd;
+                                    final Handle draggedHandle = mStartIsDragged ? mStartHandle : mEndHandle;
+                                    final Rect bounds = draggedHandle.mDrawable.getBounds();
+                                    mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
+                                    mOffsetY = draggedHandle.mHopSpotVertcalPosition - y;
+
+                                    ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
+                                }
+                            }
+                        }
+                        break;
+
+                    case MotionEvent.ACTION_POINTER_DOWN:
+                    case MotionEvent.ACTION_POINTER_UP:
+                        // Handle multi-point gestures. Keep min and max offset positions.
+                        // Only activated for devices that correctly handle multi-touch.
+                        if (mContext.getPackageManager().hasSystemFeature(
+                                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)) {
+                            updateMinAndMaxOffsets(event);
+                        }
+                        break;
+                }
+            }
+        }
+
+        /**
+         * @param event
+         */
+        private void updateMinAndMaxOffsets(MotionEvent event) {
+            int pointerCount = event.getPointerCount();
+            for (int index = 0; index < pointerCount; index++) {
+                final int x = (int) event.getX(index);
+                final int y = (int) event.getY(index);
+                int offset = getOffset(x, y);
+                if (offset < mMinTouchOffset) mMinTouchOffset = offset;
+                if (offset > mMaxTouchOffset) mMaxTouchOffset = offset;
+            }
+        }
+
+        public int getMinTouchOffset() {
+            return mMinTouchOffset;
+        }
+
+        public int getMaxTouchOffset() {
+            return mMaxTouchOffset;
+        }
+
+        public float getOffsetX() {
+            return mOffsetX;
+        }
+
+        public float getOffsetY() {
+            return mOffsetY;
+        }
+
+        /**
+         * @return true iff this controller is currently used to move the selection start.
+         */
+        public boolean isSelectionStartDragged() {
+            return mIsVisible && mStartIsDragged;
+        }
+    }
+
+    private void hideInsertionPointCursorController() {
+        if (mInsertionPointCursorController != null) {
+            mInsertionPointCursorController.hide();
+        }
+    }
+
+    private void hideControllers() {
+        hideInsertionPointCursorController();
+        stopTextSelectionMode();
+    }
+
+    /**
+     * Get the offset character closest to the specified absolute position.
+     *
+     * @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.
+     *
+     * @hide
+     */
+    public int getOffset(int x, int y) {
+        x -= getTotalPaddingLeft();
+        y -= getTotalPaddingTop();
+
+        // Clamp the position to inside of the view.
+        if (x < 0) {
+            x = 0;
+        } else if (x >= (getWidth() - getTotalPaddingRight())) {
+            x = getWidth()-getTotalPaddingRight() - 1;
+        }
+        if (y < 0) {
+            y = 0;
+        } else if (y >= (getHeight() - getTotalPaddingBottom())) {
+            y = getHeight()-getTotalPaddingBottom() - 1;
+        }
+
+        x += getScrollX();
+        y += getScrollY();
+
+        Layout layout = getLayout();
+        if (layout != null) {
+            final int line = layout.getLineForVertical(y);
+            final int offset = layout.getOffsetForHorizontal(line, x);
+            return offset;
+        } else {
+            return -1;
+        }
+    }
+
+    @ViewDebug.ExportedProperty
     private CharSequence            mText;
     private CharSequence            mTransformed;
     private BufferType              mBufferType = BufferType.NORMAL;
@@ -7313,16 +7992,25 @@
     private ArrayList<TextWatcher>  mListeners = null;
 
     // display attributes
-    private TextPaint               mTextPaint;
+    private final TextPaint         mTextPaint;
     private boolean                 mUserSetTextScaleX;
-    private Paint                   mHighlightPaint;
-    private int                     mHighlightColor = 0xFFBBDDFF;
+    private final Paint             mHighlightPaint;
+    private int                     mHighlightColor = 0xCC475925;
     private Layout                  mLayout;
 
     private long                    mShowCursor;
     private Blink                   mBlink;
     private boolean                 mCursorVisible = true;
 
+    // Cursor Controllers. Null when disabled.
+    private CursorController        mInsertionPointCursorController;
+    private CursorController        mSelectionModifierCursorController;
+    private boolean                 mShouldStartTextSelectionMode = false;
+    private boolean                 mIsInTextSelectionMode = false;
+    // Created once and shared by different CursorController helper methods.
+    // Only one cursor controller is active at any time which prevent race conditions.
+    private static Rect             sCursorControllerTempRect = new Rect();
+
     private boolean                 mSelectAllOnFocus = false;
 
     private int                     mGravity = Gravity.TOP | Gravity.LEFT;
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index caed308..e61fac3 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -45,6 +45,9 @@
  * Under AM/PM mode, the user can hit 'a', 'A", 'p' or 'P' to pick.
  *
  * For a dialog using this view, see {@link android.app.TimePickerDialog}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.</p>
  */
 @Widget
 public class TimePicker extends FrameLayout {
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index dc791e3..3b680e8 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -26,6 +26,9 @@
 /**
  * Displays checked/unchecked states as a button
  * with a "light" indicator and by default accompanied with the text "ON" or "OFF".
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
  * 
  * @attr ref android.R.styleable#ToggleButton_textOn
  * @attr ref android.R.styleable#ToggleButton_textOff
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index ce5959d..e1c5564 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,17 +18,31 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+import android.view.MotionEvent;
 import android.widget.ImageView;
+import android.widget.Toast;
 
 public class PlatLogoActivity extends Activity {
+    Toast mToast;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         
+        mToast = Toast.makeText(this, "Zombie art by Jack Larson", Toast.LENGTH_SHORT);
+
         ImageView content = new ImageView(this);
         content.setImageResource(com.android.internal.R.drawable.platlogo);
         content.setScaleType(ImageView.ScaleType.FIT_CENTER);
         
         setContentView(content);
     }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_UP) {
+            mToast.show();
+        }
+        return super.dispatchTouchEvent(ev);
+    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2f26135..f4447ab 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -27,7 +27,6 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.BatteryStats.Uid.Proc.ExcessiveWake;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
@@ -1241,25 +1240,31 @@
     int mWakeLockNesting;
 
     public void noteStartWakeLocked(int uid, int pid, String name, int type) {
-        if (mWakeLockNesting == 0) {
-            mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
-            if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
-                    + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+        if (type == WAKE_TYPE_PARTIAL) {
+            // Only care about partial wake locks, since full wake locks
+            // will be canceled when the user puts the screen to sleep.
+            if (mWakeLockNesting == 0) {
+                mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
+                if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+                        + Integer.toHexString(mHistoryCur.states));
+                addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            }
+            mWakeLockNesting++;
         }
-        mWakeLockNesting++;
         if (uid >= 0) {
             getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
         }
     }
 
     public void noteStopWakeLocked(int uid, int pid, String name, int type) {
-        mWakeLockNesting--;
-        if (mWakeLockNesting == 0) {
-            mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
-            if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
-                    + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+        if (type == WAKE_TYPE_PARTIAL) {
+            mWakeLockNesting--;
+            if (mWakeLockNesting == 0) {
+                mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
+                if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+                        + Integer.toHexString(mHistoryCur.states));
+                addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            }
         }
         if (uid >= 0) {
             getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
@@ -1353,7 +1358,7 @@
 
             // Fake a wake lock, so we consider the device waked as long
             // as the screen is on.
-            noteStartWakeLocked(-1, -1, "dummy", 0);
+            noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
         }
     }
     
@@ -1369,7 +1374,7 @@
                 mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
             }
 
-            noteStopWakeLocked(-1, -1, "dummy", 0);
+            noteStopWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
         }
     }
     
@@ -3463,7 +3468,7 @@
             if (t != null) {
                 t.startRunningLocked(BatteryStatsImpl.this);
             }
-            if (pid >= 0) {
+            if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
                 Pid p = getPidStatsLocked(pid);
                 p.mWakeStart = SystemClock.elapsedRealtime();
             }
@@ -3474,7 +3479,7 @@
             if (t != null) {
                 t.stopRunningLocked(BatteryStatsImpl.this);
             }
-            if (pid >= 0) {
+            if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
                 Pid p = mPids.get(pid);
                 if (p != null) {
                     p.mWakeSum += SystemClock.elapsedRealtime() - p.mWakeStart;
@@ -3686,8 +3691,8 @@
                 mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
                 mDischargeCurrentLevel = level;
                 if (level < mDischargeUnplugLevel) {
-                    mLowDischargeAmountSinceCharge = mDischargeUnplugLevel-level-1;
-                    mHighDischargeAmountSinceCharge = mDischargeUnplugLevel-level;
+                    mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
+                    mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
                 }
                 doPlugLocked(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
             }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index efdc399..42135c5 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -78,6 +78,7 @@
 	android_util_Process.cpp \
 	android_util_StringBlock.cpp \
 	android_util_XmlBlock.cpp \
+	android/graphics/AutoDecodeCancel.cpp \
 	android/graphics/Bitmap.cpp \
 	android/graphics/BitmapFactory.cpp \
 	android/graphics/Camera.cpp \
@@ -101,6 +102,7 @@
 	android_graphics_PixelFormat.cpp \
 	android/graphics/Picture.cpp \
 	android/graphics/PorterDuff.cpp \
+	android/graphics/LargeBitmap.cpp \
 	android/graphics/Rasterizer.cpp \
 	android/graphics/Region.cpp \
 	android/graphics/Shader.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 62ca2ef..407d2e7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -53,6 +53,7 @@
 extern int register_android_os_Process(JNIEnv* env);
 extern int register_android_graphics_Bitmap(JNIEnv*);
 extern int register_android_graphics_BitmapFactory(JNIEnv*);
+extern int register_android_graphics_LargeBitmap(JNIEnv*);
 extern int register_android_graphics_Camera(JNIEnv* env);
 extern int register_android_graphics_Graphics(JNIEnv* env);
 extern int register_android_graphics_Interpolator(JNIEnv* env);
@@ -1264,6 +1265,7 @@
 
     REG_JNI(register_android_graphics_Bitmap),
     REG_JNI(register_android_graphics_BitmapFactory),
+    REG_JNI(register_android_graphics_LargeBitmap),
     REG_JNI(register_android_graphics_Camera),
     REG_JNI(register_android_graphics_Canvas),
     REG_JNI(register_android_graphics_ColorFilter),
diff --git a/core/jni/android/graphics/AutoDecodeCancel.cpp b/core/jni/android/graphics/AutoDecodeCancel.cpp
new file mode 100644
index 0000000..f0739ea
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.cpp
@@ -0,0 +1,100 @@
+#include "AutoDecodeCancel.h"
+
+static SkMutex  gAutoDecoderCancelMutex;
+static AutoDecoderCancel* gAutoDecoderCancel;
+#ifdef SK_DEBUG
+static int gAutoDecoderCancelCount;
+#endif
+
+AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
+                                       SkImageDecoder* decoder) {
+    fJOptions = joptions;
+    fDecoder = decoder;
+
+    if (NULL != joptions) {
+        SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+        // Add us as the head of the list
+        fPrev = NULL;
+        fNext = gAutoDecoderCancel;
+        if (gAutoDecoderCancel) {
+            gAutoDecoderCancel->fPrev = this;
+        }
+        gAutoDecoderCancel = this;
+
+        SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
+        Validate();
+    }
+}
+
+AutoDecoderCancel::~AutoDecoderCancel() {
+    if (NULL != fJOptions) {
+        SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+        // take us out of the dllist
+        AutoDecoderCancel* prev = fPrev;
+        AutoDecoderCancel* next = fNext;
+
+        if (prev) {
+            SkASSERT(prev->fNext == this);
+            prev->fNext = next;
+        } else {
+            SkASSERT(gAutoDecoderCancel == this);
+            gAutoDecoderCancel = next;
+        }
+        if (next) {
+            SkASSERT(next->fPrev == this);
+            next->fPrev = prev;
+        }
+
+        SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
+        Validate();
+    }
+}
+
+bool AutoDecoderCancel::RequestCancel(jobject joptions) {
+    SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+    Validate();
+
+    AutoDecoderCancel* pair = gAutoDecoderCancel;
+    while (pair != NULL) {
+        if (pair->fJOptions == joptions) {
+            pair->fDecoder->cancelDecode();
+            return true;
+        }
+        pair = pair->fNext;
+    }
+    return false;
+}
+
+#ifdef SK_DEBUG
+// can only call this inside a lock on gAutoDecoderCancelMutex
+void AutoDecoderCancel::Validate() {
+    const int gCount = gAutoDecoderCancelCount;
+
+    if (gCount == 0) {
+        SkASSERT(gAutoDecoderCancel == NULL);
+    } else {
+        SkASSERT(gCount > 0);
+
+        AutoDecoderCancel* curr = gAutoDecoderCancel;
+        SkASSERT(curr);
+        SkASSERT(curr->fPrev == NULL);
+
+        int count = 0;
+        while (curr) {
+            count += 1;
+            SkASSERT(count <= gCount);
+            if (curr->fPrev) {
+                SkASSERT(curr->fPrev->fNext == curr);
+            }
+            if (curr->fNext) {
+                SkASSERT(curr->fNext->fPrev == curr);
+            }
+            curr = curr->fNext;
+        }
+        SkASSERT(count == gCount);
+    }
+}
+#endif
diff --git a/core/jni/android/graphics/AutoDecodeCancel.h b/core/jni/android/graphics/AutoDecodeCancel.h
new file mode 100644
index 0000000..37b86f9
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.h
@@ -0,0 +1,27 @@
+#ifndef AutoDecodeCancel_DEFINED
+#define AutoDecodeCancel_DEFINED
+
+#include <jni.h>
+#include "SkImageDecoder.h"
+
+class AutoDecoderCancel {
+public:
+    AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
+    ~AutoDecoderCancel();
+
+    static bool RequestCancel(jobject options);
+
+private:
+    AutoDecoderCancel*  fNext;
+    AutoDecoderCancel*  fPrev;
+    jobject             fJOptions;  // java options object
+    SkImageDecoder*     fDecoder;
+
+#ifdef SK_DEBUG
+    static void Validate();
+#else
+    static void Validate() {}
+#endif
+};
+
+#endif
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index b41bad0..254d9a4 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -1,14 +1,15 @@
 #define LOG_TAG "BitmapFactory"
 
+#include "BitmapFactory.h"
 #include "SkImageDecoder.h"
 #include "SkImageRef_ashmem.h"
 #include "SkImageRef_GlobalPool.h"
 #include "SkPixelRef.h"
 #include "SkStream.h"
-#include "GraphicsJNI.h"
 #include "SkTemplates.h"
 #include "SkUtils.h"
 #include "CreateJavaOutputStreamAdaptor.h"
+#include "AutoDecodeCancel.h"
 
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Asset.h>
@@ -16,18 +17,18 @@
 #include <netinet/in.h>
 #include <sys/mman.h>
 
-static jclass gOptions_class;
-static jfieldID gOptions_justBoundsFieldID;
-static jfieldID gOptions_sampleSizeFieldID;
-static jfieldID gOptions_configFieldID;
-static jfieldID gOptions_ditherFieldID;
-static jfieldID gOptions_purgeableFieldID;
-static jfieldID gOptions_shareableFieldID;
-static jfieldID gOptions_nativeAllocFieldID;
-static jfieldID gOptions_widthFieldID;
-static jfieldID gOptions_heightFieldID;
-static jfieldID gOptions_mimeFieldID;
-static jfieldID gOptions_mCancelID;
+jclass gOptions_class;
+jfieldID gOptions_justBoundsFieldID;
+jfieldID gOptions_sampleSizeFieldID;
+jfieldID gOptions_configFieldID;
+jfieldID gOptions_ditherFieldID;
+jfieldID gOptions_purgeableFieldID;
+jfieldID gOptions_shareableFieldID;
+jfieldID gOptions_nativeAllocFieldID;
+jfieldID gOptions_widthFieldID;
+jfieldID gOptions_heightFieldID;
+jfieldID gOptions_mimeFieldID;
+jfieldID gOptions_mCancelID;
 
 static jclass gFileDescriptor_class;
 static jfieldID gFileDescriptor_descriptor;
@@ -38,129 +39,6 @@
     #define TRACE_BITMAP(code)
 #endif
 
-///////////////////////////////////////////////////////////////////////////////
-
-class AutoDecoderCancel {
-public:
-    AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
-    ~AutoDecoderCancel();
-
-    static bool RequestCancel(jobject options);
-    
-private:
-    AutoDecoderCancel*  fNext;
-    AutoDecoderCancel*  fPrev;
-    jobject             fJOptions;  // java options object
-    SkImageDecoder*     fDecoder;
-    
-#ifdef SK_DEBUG
-    static void Validate();
-#else
-    static void Validate() {}
-#endif
-};
-
-static SkMutex  gAutoDecoderCancelMutex;
-static AutoDecoderCancel* gAutoDecoderCancel;
-#ifdef SK_DEBUG
-    static int gAutoDecoderCancelCount;
-#endif
-
-AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
-                                       SkImageDecoder* decoder) {
-    fJOptions = joptions;
-    fDecoder = decoder;
-
-    if (NULL != joptions) {
-        SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
-        // Add us as the head of the list
-        fPrev = NULL;
-        fNext = gAutoDecoderCancel;
-        if (gAutoDecoderCancel) {
-            gAutoDecoderCancel->fPrev = this;
-        }
-        gAutoDecoderCancel = this;
-        
-        SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
-        Validate();
-    }
-}
-
-AutoDecoderCancel::~AutoDecoderCancel() {
-    if (NULL != fJOptions) {
-        SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-        
-        // take us out of the dllist
-        AutoDecoderCancel* prev = fPrev;
-        AutoDecoderCancel* next = fNext;
-        
-        if (prev) {
-            SkASSERT(prev->fNext == this);
-            prev->fNext = next;
-        } else {
-            SkASSERT(gAutoDecoderCancel == this);
-            gAutoDecoderCancel = next;
-        }
-        if (next) {
-            SkASSERT(next->fPrev == this);
-            next->fPrev = prev;
-        }
-
-        SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
-        Validate();
-    }
-}
-
-bool AutoDecoderCancel::RequestCancel(jobject joptions) {
-    SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
-    Validate();
-
-    AutoDecoderCancel* pair = gAutoDecoderCancel;
-    while (pair != NULL) {
-        if (pair->fJOptions == joptions) {
-            pair->fDecoder->cancelDecode();
-            return true;
-        }
-        pair = pair->fNext;
-    }
-    return false;
-}
-
-#ifdef SK_DEBUG
-// can only call this inside a lock on gAutoDecoderCancelMutex 
-void AutoDecoderCancel::Validate() {
-    const int gCount = gAutoDecoderCancelCount;
-
-    if (gCount == 0) {
-        SkASSERT(gAutoDecoderCancel == NULL);
-    } else {
-        SkASSERT(gCount > 0);
-        
-        AutoDecoderCancel* curr = gAutoDecoderCancel;
-        SkASSERT(curr);
-        SkASSERT(curr->fPrev == NULL);
-
-        int count = 0;
-        while (curr) {
-            count += 1;
-            SkASSERT(count <= gCount);
-            if (curr->fPrev) {
-                SkASSERT(curr->fPrev->fNext == curr);
-            }
-            if (curr->fNext) {
-                SkASSERT(curr->fNext->fPrev == curr);
-            }
-            curr = curr->fNext;
-        }
-        SkASSERT(count == gCount);
-    }
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
 using namespace android;
 
 class NinePatchPeeker : public SkImageDecoder::Peeker {
@@ -279,7 +157,7 @@
     return ((int32_t)isValid - 1) | value;
 }
 
-static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
     static const struct {
         SkImageDecoder::Format fFormat;
         const char*            fMimeType;
@@ -340,6 +218,7 @@
     }
     pr->setDitherImage(ditherImage);
     bitmap->setPixelRef(pr)->unref();
+    pr->isOpaque(bitmap);
     return pr;
 }
 
@@ -477,7 +356,7 @@
                                   jobject padding,
                                   jobject options) {  // BitmapFactory$Options
     jobject bitmap = NULL;
-    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
+    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
 
     if (stream) {
         // for now we don't allow purgeable with java inputstreams
@@ -586,10 +465,8 @@
                                  jobject options) { // BitmapFactory$Options
     SkStream* stream;
     Asset* asset = reinterpret_cast<Asset*>(native_asset);
-    // assets can always be rebuilt, so force this
-    bool forcePurgeable = true;
-
-    if (forcePurgeable || optionsPurgeable(env, options)) {
+    bool forcePurgeable = optionsPurgeable(env, options);
+    if (forcePurgeable) {
         // if we could "ref/reopen" the asset, we may not need to copy it here
         // and we could assume optionsShareable, since assets are always RO
         stream = copyAssetToStream(asset);
@@ -682,6 +559,107 @@
     }
 }
 
+static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
+    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+    int width, height;
+    if (NULL == decoder) {
+        doThrowIOE(env, "Image format not supported");
+        return nullObjectReturn("SkImageDecoder::Factory returned null");
+    }
+
+    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+    decoder->setAllocator(javaAllocator);
+    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
+    decoder->setReporter(javaMemoryReporter);
+    javaAllocator->unref();
+    javaMemoryReporter->unref();
+
+    if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
+        char msg[1024];
+        snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
+        doThrowIOE(env, msg);
+        return nullObjectReturn("decoder->buildTileIndex returned false");
+    }
+
+    SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
+
+    return GraphicsJNI::createLargeBitmap(env, bm);
+}
+
+static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+                                     int offset, int length, jboolean isShareable) {
+    AutoJavaByteArray ar(env, byteArray);
+    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
+    SkAutoUnref aur(stream);
+    if (isShareable) {
+        aur.detach();
+    }
+    return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
+                                          jobject fileDescriptor, jboolean isShareable) {
+    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+    jint descriptor = env->GetIntField(fileDescriptor,
+                                       gFileDescriptor_descriptor);
+    bool weOwnTheFD = false;
+
+    if (isShareable) {
+        int newFD = ::dup(descriptor);
+        if (-1 != newFD) {
+            weOwnTheFD = true;
+            descriptor = newFD;
+        }
+    }
+
+    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
+    SkAutoUnref aur(stream);
+    if (!stream->isValid()) {
+        return NULL;
+    }
+
+    if (isShareable) {
+        aur.detach();
+    }
+
+    /* Restore our offset when we leave, so we can be called more than once
+       with the same descriptor. This is only required if we didn't dup the
+       file descriptor, but it is OK to do it all the time.
+    */
+    AutoFDSeek as(descriptor);
+
+    return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
+                                  jobject is,       // InputStream
+                                  jbyteArray storage, // byte[]
+                                  jboolean isShareable) {
+    jobject largeBitmap = NULL;
+    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
+
+    if (stream) {
+        // for now we don't allow shareable with java inputstreams
+        largeBitmap = doBuildTileIndex(env, stream, false);
+        stream->unref();
+    }
+    return largeBitmap;
+}
+
+static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
+                                 jint native_asset, // Asset
+                                 jboolean isShareable) {
+    SkStream* stream;
+    Asset* asset = reinterpret_cast<Asset*>(native_asset);
+    stream = new AssetStreamAdaptor(asset);
+    SkAutoUnref aur(stream);
+    if (isShareable) {
+        aur.detach();
+    }
+    return doBuildTileIndex(env, stream, isShareable);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static JNINativeMethod gMethods[] = {
@@ -711,6 +689,26 @@
     },
 
     {   "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
+
+    {   "nativeCreateLargeBitmap",
+        "([BIIZ)Landroid/graphics/LargeBitmap;",
+        (void*)nativeCreateLargeBitmapFromByteArray
+    },
+
+    {   "nativeCreateLargeBitmap",
+        "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
+        (void*)nativeCreateLargeBitmapFromStream
+    },
+
+    {   "nativeCreateLargeBitmap",
+        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
+        (void*)nativeCreateLargeBitmapFromFileDescriptor
+    },
+
+    {   "nativeCreateLargeBitmap",
+        "(IZ)Landroid/graphics/LargeBitmap;",
+        (void*)nativeCreateLargeBitmapFromAsset
+    },
 };
 
 static JNINativeMethod gOptionsMethods[] = {
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
new file mode 100644
index 0000000..f868434
--- /dev/null
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -0,0 +1,21 @@
+#ifndef BitmapFactory_DEFINE
+#define BitmapFactory_DEFINE
+
+#include "GraphicsJNI.h"
+
+extern jclass gOptions_class;
+extern jfieldID gOptions_justBoundsFieldID;
+extern jfieldID gOptions_sampleSizeFieldID;
+extern jfieldID gOptions_configFieldID;
+extern jfieldID gOptions_ditherFieldID;
+extern jfieldID gOptions_purgeableFieldID;
+extern jfieldID gOptions_shareableFieldID;
+extern jfieldID gOptions_nativeAllocFieldID;
+extern jfieldID gOptions_widthFieldID;
+extern jfieldID gOptions_heightFieldID;
+extern jfieldID gOptions_mimeFieldID;
+extern jfieldID gOptions_mCancelID;
+
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
+
+#endif
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 007757f..137acc6 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -5,6 +5,7 @@
 
 static jclass       gInputStream_Clazz;
 static jmethodID    gInputStream_resetMethodID;
+static jmethodID    gInputStream_markMethodID;
 static jmethodID    gInputStream_availableMethodID;
 static jmethodID    gInputStream_readMethodID;
 static jmethodID    gInputStream_skipMethodID;
@@ -143,7 +144,7 @@
 };
 
 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
-                                       jbyteArray storage) {
+                                       jbyteArray storage, int markSize) {
     static bool gInited;
 
     if (!gInited) {
@@ -153,6 +154,8 @@
 
         gInputStream_resetMethodID      = env->GetMethodID(gInputStream_Clazz,
                                                            "reset", "()V");
+        gInputStream_markMethodID       = env->GetMethodID(gInputStream_Clazz,
+                                                           "mark", "(I)V");
         gInputStream_availableMethodID  = env->GetMethodID(gInputStream_Clazz,
                                                            "available", "()I");
         gInputStream_readMethodID       = env->GetMethodID(gInputStream_Clazz,
@@ -161,6 +164,7 @@
                                                            "skip", "(J)J");
 
         RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
+        RETURN_NULL_IF_NULL(gInputStream_markMethodID);
         RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
         RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
         RETURN_NULL_IF_NULL(gInputStream_skipMethodID);
@@ -168,6 +172,10 @@
         gInited = true;
     }
 
+    if (markSize) {
+        env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
+    }
+
     return new JavaInputStreamAdaptor(env, stream, storage);
 }
 
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
index cf21dde..c34c96a 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
@@ -6,7 +6,7 @@
 #include "SkStream.h"
 
 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
-                                       jbyteArray storage);
+                                       jbyteArray storage, int markSize = 0);
 SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
                                          jbyteArray storage);
 
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5659ba2..578de6f 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -46,6 +46,10 @@
     doThrow(env, "java/lang/OutOfMemoryError", msg);
 }
 
+void doThrowIOE(JNIEnv* env, const char* msg) {
+    doThrow(env, "java/lang/IOException", msg);
+}
+
 bool GraphicsJNI::hasException(JNIEnv *env) {
     if (env->ExceptionCheck() != 0) {
         LOGE("*** Uncaught exception returned from Java call!\n");
@@ -165,6 +169,9 @@
 static jclass   gBitmapConfig_class;
 static jfieldID gBitmapConfig_nativeInstanceID;
 
+static jclass   gLargeBitmap_class;
+static jmethodID gLargeBitmap_constructorMethodID;
+
 static jclass   gCanvas_class;
 static jfieldID gCanvas_nativeInstanceID;
 
@@ -370,6 +377,23 @@
     }
     return obj;
 }
+jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
+{
+    SkASSERT(bitmap != NULL);
+
+    jobject obj = env->AllocObject(gLargeBitmap_class);
+    if (hasException(env)) {
+        obj = NULL;
+        return obj;
+    }
+    if (obj) {
+        env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
+        if (hasException(env)) {
+            obj = NULL;
+        }
+    }
+    return obj;
+}
 
 jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
 {
@@ -494,10 +518,52 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM)
-    : fEnv(env), fReportSizeToVM(reportSizeToVM) {}
+    : fReportSizeToVM(reportSizeToVM) {
+    if (env->GetJavaVM(&fVM) != JNI_OK) {
+        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+        sk_throw();
+    }
+}
     
 bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
-    return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM);
+    JNIEnv* env = vm2env(fVM);
+    return GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, fReportSizeToVM);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
+    : fTotalSize(0) {
+    if (env->GetJavaVM(&fVM) != JNI_OK) {
+        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+        sk_throw();
+    }
+}
+
+JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
+    JNIEnv* env = vm2env(fVM);
+    jlong jtotalSize = fTotalSize;
+    env->CallVoidMethod(gVMRuntime_singleton,
+            gVMRuntime_trackExternalFreeMethodID,
+            jtotalSize);
+}
+
+bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
+    jlong jsize = memorySize;  // the VM wants longs for the size
+    JNIEnv* env = vm2env(fVM);
+    bool r = env->CallBooleanMethod(gVMRuntime_singleton,
+            gVMRuntime_trackExternalAllocationMethodID,
+            jsize);
+    if (GraphicsJNI::hasException(env)) {
+        return false;
+    }
+    if (!r) {
+        LOGE("VM won't let us allocate %zd bytes\n", memorySize);
+        doThrowOOME(env, "bitmap size exceeds VM budget");
+        return false;
+    }
+    fTotalSize += memorySize;
+    return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -547,6 +613,9 @@
     gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
                                             "(IZ[BI)V");
 
+    gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
+    gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
+
     gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
     gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
                                                      "nativeInt", "I");    
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index fe24b05..1a43a3e 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -4,6 +4,8 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkBitmap.h"
+#include "../images/SkLargeBitmap.h"
+#include "../images/SkImageDecoder.h"
 #include <jni.h>
 
 class SkCanvas;
@@ -54,6 +56,8 @@
     
     static jobject createRegion(JNIEnv* env, SkRegion* region);
 
+    static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
+
     /** Set a pixelref for the bitmap (needs setConfig to already be called)
         Returns true on success. If it returns false, then it failed, and the
         appropriate exception will have been raised.
@@ -76,10 +80,22 @@
     virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
     
 private:
-    JNIEnv* fEnv;
+    JavaVM* fVM;
     bool fReportSizeToVM;
 };
 
+class JavaMemoryUsageReporter : public SkVMMemoryReporter {
+public:
+    JavaMemoryUsageReporter(JNIEnv* env);
+    virtual ~JavaMemoryUsageReporter();
+    // overrides
+    virtual bool reportMemory(size_t memorySize);
+
+private:
+    JavaVM* fVM;
+    size_t fTotalSize;
+};
+
 enum JNIAccess {
     kRO_JNIAccess,
     kRW_JNIAccess
@@ -156,6 +172,7 @@
 void doThrowRE(JNIEnv* env, const char* msg = NULL);   // Runtime
 void doThrowISE(JNIEnv* env, const char* msg = NULL);   // Illegal State
 void doThrowOOME(JNIEnv* env, const char* msg = NULL);   // Out of memory
+void doThrowIOE(JNIEnv* env, const char* msg = NULL);   // IO Exception
 
 #define NPE_CHECK_RETURN_ZERO(env, object)    \
     do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0)
diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp
new file mode 100644
index 0000000..4cf5dfa
--- /dev/null
+++ b/core/jni/android/graphics/LargeBitmap.cpp
@@ -0,0 +1,138 @@
+#define LOG_TAG "LargeBitmap"
+
+#include "SkBitmap.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkPixelRef.h"
+#include "BitmapFactory.h"
+#include "AutoDecodeCancel.h"
+#include "SkLargeBitmap.h"
+
+#include <binder/Parcel.h>
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#if 0
+    #define TRACE_BITMAP(code)  code
+#else
+    #define TRACE_BITMAP(code)
+#endif
+
+static jobject nullObjectReturn(const char msg[]) {
+    if (msg) {
+        SkDebugf("--- %s\n", msg);
+    }
+    return NULL;
+}
+
+/*
+ * nine patch not supported
+ *
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
+        int start_x, int start_y, int width, int height, jobject options) {
+    SkImageDecoder *decoder = bm->getDecoder();
+    int sampleSize = 1;
+    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+    bool doDither = true;
+
+    if (NULL != options) {
+        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+        // initialize these, in case we fail later on
+        env->SetIntField(options, gOptions_widthFieldID, -1);
+        env->SetIntField(options, gOptions_heightFieldID, -1);
+        env->SetObjectField(options, gOptions_mimeFieldID, 0);
+
+        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
+    }
+
+    decoder->setDitherImage(doDither);
+    SkBitmap*           bitmap = new SkBitmap;
+    SkAutoTDelete<SkBitmap>       adb(bitmap);
+    AutoDecoderCancel   adc(options, decoder);
+
+    // To fix the race condition in case "requestCancelDecode"
+    // happens earlier than AutoDecoderCancel object is added
+    // to the gAutoDecoderCancelMutex linked list.
+    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
+        return nullObjectReturn("gOptions_mCancelID");;
+    }
+
+    SkIRect region;
+    region.fLeft = start_x;
+    region.fTop = start_y;
+    region.fRight = start_x + width;
+    region.fBottom = start_y + height;
+
+    if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+        return nullObjectReturn("decoder->decodeRegion returned false");
+    }
+
+    // update options (if any)
+    if (NULL != options) {
+        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
+        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+        // TODO: set the mimeType field with the data from the codec.
+        // but how to reuse a set of strings, rather than allocating new one
+        // each time?
+        env->SetObjectField(options, gOptions_mimeFieldID,
+                            getMimeTypeString(env, decoder->getFormat()));
+    }
+
+    // detach bitmap from its autotdeleter, since we want to own it now
+    adb.detach();
+
+    SkPixelRef* pr;
+    pr = bitmap->pixelRef();
+    // promise we will never change our pixels (great for sharing and pictures)
+    pr->setImmutable();
+    // now create the java bitmap
+    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+}
+
+static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+    return bm->getHeight();
+}
+
+static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+    return bm->getWidth();
+}
+
+static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+    delete bm;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gLargeBitmapMethods[] = {
+    {   "nativeDecodeRegion",
+        "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeRegion},
+    {   "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
+    {   "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
+    {   "nativeClean", "(I)V", (void*)nativeClean},
+};
+
+#define kClassPathName  "android/graphics/LargeBitmap"
+
+int register_android_graphics_LargeBitmap(JNIEnv* env);
+int register_android_graphics_LargeBitmap(JNIEnv* env)
+{
+    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+                                gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
+}
+
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 1239274..62c89fc 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -31,6 +31,7 @@
 
     jfieldID packageName;
     jfieldID version;
+    jfieldID flags;
 } gObbInfoClassInfo;
 
 static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
@@ -85,6 +86,8 @@
             "packageName", "Ljava/lang/String;");
     GET_FIELD_ID(gObbInfoClassInfo.version, gObbInfoClassInfo.clazz,
             "version", "I");
+    GET_FIELD_ID(gObbInfoClassInfo.flags, gObbInfoClassInfo.clazz,
+            "flags", "I");
 
     return AndroidRuntime::registerNativeMethods(env, "android/content/res/ObbScanner", gMethods,
             NELEM(gMethods));
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 17f5daf..f78f83c 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -453,30 +453,23 @@
 // return -1 if there was an error querying the buffer size.
 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
     jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
-    
-    size_t inputBuffSize = 0;
+
     LOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
-    
-    status_t result = AudioSystem::getInputBufferSize(
-                        sampleRateInHertz, 
-                        (audioFormat == javaAudioRecordFields.PCM16 ? 
-                            AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT), 
-                        nbChannels, &inputBuffSize);
-    switch(result) {
-    case(NO_ERROR):
-        if(inputBuffSize == 0) {
-            LOGV("Recording parameters are not supported: %dHz, %d channel(s), (java) format %d",
-                sampleRateInHertz, nbChannels, audioFormat);
-            return 0;
-        } else {
-            // the minimum buffer size is twice the hardware input buffer size
-            return 2*inputBuffSize;
-        }
-        break;
-    case(PERMISSION_DENIED):
-    default:
-        return -1; 
+
+    int frameCount = 0;
+    status_t result = AudioRecord::getMinFrameCount(&frameCount,
+            sampleRateInHertz,
+            (audioFormat == javaAudioRecordFields.PCM16 ?
+                AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT),
+            nbChannels);
+
+    if (result == BAD_VALUE) {
+        return 0;
     }
+    if (result != NO_ERROR) {
+        return -1;
+    }
+    return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
 }
 
 
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index c559670..9d215b7 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -782,29 +782,13 @@
 // returns -1 if there was an error querying the hardware.
 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
     jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
-    int afSamplingRate;
-    int afFrameCount;
-    uint32_t afLatency;
-    
-    if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
+
+    int frameCount = 0;
+    if (AudioTrack::getMinFrameCount(&frameCount, AudioSystem::DEFAULT,
+            sampleRateInHertz) != NO_ERROR) {
         return -1;
     }
-    if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
-        return -1;
-    }
-    
-    if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {
-        return -1;
-    }
-    
-    // Ensure that buffer depth covers at least audio hardware latency
-    uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);
-    if (minBufCount < 2) minBufCount = 2;
-    uint32_t minFrameCount = (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;
-    int minBuffSize = minFrameCount 
-            * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
-            * nbChannels;
-    return minBuffSize;
+    return frameCount * nbChannels * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 7392442..903283e 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -493,6 +493,15 @@
     return doBooleanCommand("BLACKLIST clear", "OK");
 }
 
+static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject clazz, jboolean enabled)
+{
+    char cmdstr[25];
+
+    snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETSUSPEND %d", enabled ? 0 : 1);
+    return doBooleanCommand(cmdstr, "OK");
+}
+
+
 static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info)
 {
     jint ipaddr, gateway, mask, dns1, dns2, server, lease;
@@ -571,6 +580,7 @@
     { "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
     { "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
     { "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
+    { "setSuspendOptimizationsCommand", "(Z)Z", (void*) android_net_wifi_setSuspendOptimizationsCommand},
 
     { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError },
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 556d367..42f35d1 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -76,10 +76,14 @@
             STATUS_ZOMBIE
         };
 
-        Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop);
+        Connection(uint16_t id,
+                const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop);
 
         inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
 
+        // A unique id for this connection.
+        uint16_t id;
+
         Status status;
 
         sp<InputChannel> inputChannel;
@@ -91,29 +95,34 @@
         // The sequence number of the current event being dispatched.
         // This is used as part of the finished token as a way to determine whether the finished
         // token is still valid before sending a finished signal back to the publisher.
-        uint32_t messageSeqNum;
+        uint16_t messageSeqNum;
 
         // True if a message has been received from the publisher but not yet finished.
         bool messageInProgress;
     };
 
     Mutex mLock;
+    uint16_t mNextConnectionId;
     KeyedVector<int32_t, sp<Connection> > mConnectionsByReceiveFd;
 
+    ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+
     static void handleInputChannelDisposed(JNIEnv* env,
             jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data);
 
     static bool handleReceiveCallback(int receiveFd, int events, void* data);
 
-    static jlong generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum);
+    static jlong generateFinishedToken(int32_t receiveFd,
+            uint16_t connectionId, uint16_t messageSeqNum);
 
     static void parseFinishedToken(jlong finishedToken,
-            int32_t* outReceiveFd, uint32_t* outMessageIndex);
+            int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex);
 };
 
 // ----------------------------------------------------------------------------
 
-NativeInputQueue::NativeInputQueue() {
+NativeInputQueue::NativeInputQueue() :
+        mNextConnectionId(0) {
 }
 
 NativeInputQueue::~NativeInputQueue() {
@@ -134,18 +143,17 @@
 
     sp<PollLoop> pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj);
 
-    int receiveFd;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        receiveFd = inputChannel->getReceivePipeFd();
-        if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) {
+        if (getConnectionIndex(inputChannel) >= 0) {
             LOGW("Attempted to register already registered input channel '%s'",
                     inputChannel->getName().string());
             return BAD_VALUE;
         }
 
-        sp<Connection> connection = new Connection(inputChannel, pollLoop);
+        uint16_t connectionId = mNextConnectionId++;
+        sp<Connection> connection = new Connection(connectionId, inputChannel, pollLoop);
         status_t result = connection->inputConsumer.initialize();
         if (result) {
             LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
@@ -155,13 +163,14 @@
 
         connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);
 
+        int32_t receiveFd = inputChannel->getReceivePipeFd();
         mConnectionsByReceiveFd.add(receiveFd, connection);
+
+        pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
     } // release lock
 
     android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
             handleInputChannelDisposed, this);
-
-    pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
     return OK;
 }
 
@@ -177,38 +186,56 @@
     LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
 #endif
 
-    int32_t receiveFd;
-    sp<Connection> connection;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        receiveFd = inputChannel->getReceivePipeFd();
-        ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+        ssize_t connectionIndex = getConnectionIndex(inputChannel);
         if (connectionIndex < 0) {
             LOGW("Attempted to unregister already unregistered input channel '%s'",
                     inputChannel->getName().string());
             return BAD_VALUE;
         }
 
-        connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+        sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
         mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
 
         connection->status = Connection::STATUS_ZOMBIE;
 
+        connection->pollLoop->removeCallback(inputChannel->getReceivePipeFd());
+
         env->DeleteGlobalRef(connection->inputHandlerObjGlobal);
         connection->inputHandlerObjGlobal = NULL;
+
+        if (connection->messageInProgress) {
+            LOGI("Sending finished signal for input channel '%s' since it is being unregistered "
+                    "while an input message is still in progress.",
+                    connection->getInputChannelName());
+            connection->messageInProgress = false;
+            connection->inputConsumer.sendFinishedSignal(); // ignoring result
+        }
     } // release lock
 
     android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
-
-    connection->pollLoop->removeCallback(receiveFd);
     return OK;
 }
 
+ssize_t NativeInputQueue::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+    ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+    if (connectionIndex >= 0) {
+        sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+        if (connection->inputChannel.get() == inputChannel.get()) {
+            return connectionIndex;
+        }
+    }
+
+    return -1;
+}
+
 status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) {
     int32_t receiveFd;
-    uint32_t messageSeqNum;
-    parseFinishedToken(finishedToken, &receiveFd, &messageSeqNum);
+    uint16_t connectionId;
+    uint16_t messageSeqNum;
+    parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);
 
     { // acquire lock
         AutoMutex _l(mLock);
@@ -216,16 +243,25 @@
         ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
         if (connectionIndex < 0) {
             if (! ignoreSpuriousFinish) {
-                LOGW("Attempted to finish input on channel that is no longer registered.");
+                LOGI("Ignoring finish signal on channel that is no longer registered.");
             }
             return DEAD_OBJECT;
         }
 
         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+        if (connectionId != connection->id) {
+            if (! ignoreSpuriousFinish) {
+                LOGI("Ignoring finish signal on channel that is no longer registered.");
+            }
+            return DEAD_OBJECT;
+        }
+
         if (messageSeqNum != connection->messageSeqNum || ! connection->messageInProgress) {
             if (! ignoreSpuriousFinish) {
-                LOGW("Attempted to finish input twice on channel '%s'.",
-                        connection->getInputChannelName());
+                LOGW("Attempted to finish input twice on channel '%s'.  "
+                        "finished messageSeqNum=%d, current messageSeqNum=%d, messageInProgress=%d",
+                        connection->getInputChannelName(),
+                        messageSeqNum, connection->messageSeqNum, connection->messageInProgress);
             }
             return INVALID_OPERATION;
         }
@@ -312,7 +348,7 @@
         connection->messageInProgress = true;
         connection->messageSeqNum += 1;
 
-        finishedToken = generateFinishedToken(receiveFd, connection->messageSeqNum);
+        finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
 
         inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
     } // release lock
@@ -384,20 +420,23 @@
     return true;
 }
 
-jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum) {
-    return (jlong(receiveFd) << 32) | jlong(messageSeqNum);
+jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId,
+        uint16_t messageSeqNum) {
+    return (jlong(receiveFd) << 32) | (jlong(connectionId) << 16) | jlong(messageSeqNum);
 }
 
 void NativeInputQueue::parseFinishedToken(jlong finishedToken,
-        int32_t* outReceiveFd, uint32_t* outMessageIndex) {
+        int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex) {
     *outReceiveFd = int32_t(finishedToken >> 32);
-    *outMessageIndex = uint32_t(finishedToken & 0xffffffff);
+    *outConnectionId = uint16_t(finishedToken >> 16);
+    *outMessageIndex = uint16_t(finishedToken);
 }
 
 // ----------------------------------------------------------------------------
 
-NativeInputQueue::Connection::Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) :
-    status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
+NativeInputQueue::Connection::Connection(uint16_t id,
+        const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) :
+    id(id), status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
     pollLoop(pollLoop), inputHandlerObjGlobal(NULL),
     messageSeqNum(0), messageInProgress(false) {
 }
diff --git a/core/res/res/drawable-hdpi/text_select_handle.png b/core/res/res/drawable-hdpi/text_select_handle.png
new file mode 100644
index 0000000..80d48ab
--- /dev/null
+++ b/core/res/res/drawable-hdpi/text_select_handle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle.png b/core/res/res/drawable-mdpi/text_select_handle.png
new file mode 100644
index 0000000..93a5a15
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_select_handle.png
Binary files differ
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index c442fee..245643b 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Označit text"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Zastavit označování textu"</string>
     <string name="cut" msgid="3092569408438626261">"Vyjmout"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Vyjmout vše"</string>
     <string name="copy" msgid="2681946229533511987">"Kopírovat"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Kopírovat vše"</string>
     <string name="paste" msgid="5629880836805036433">"Vložit"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopírovat adresu URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metoda zadávání dat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 3ea8266..e7e8019 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Marker tekst"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Hold op med at markere tekst"</string>
     <string name="cut" msgid="3092569408438626261">"Klip"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Klip alle"</string>
     <string name="copy" msgid="2681946229533511987">"Kopier"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Kopier alle"</string>
     <string name="paste" msgid="5629880836805036433">"Indsæt"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopier webadresse"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Inputmetode"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index bf3efd6..edbe4c4 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Text auswählen"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Textauswahl beenden"</string>
     <string name="cut" msgid="3092569408438626261">"Ausschneiden"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Alles ausschneiden"</string>
     <string name="copy" msgid="2681946229533511987">"Kopieren"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Alles kopieren"</string>
     <string name="paste" msgid="5629880836805036433">"Einfügen"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL kopieren"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Eingabemethode"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 2c61e1f..cfa36d2 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Επιλογή κειμένου"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Διακοπή επιλογής κειμένου"</string>
     <string name="cut" msgid="3092569408438626261">"Αποκοπή"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Αποκοπή όλων"</string>
     <string name="copy" msgid="2681946229533511987">"Αντιγραφή"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Αντιγραφή όλων"</string>
     <string name="paste" msgid="5629880836805036433">"Επικόλληση"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Αντιγραφή διεύθυνσης URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Μέθοδος εισόδου"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 3de378b..1cbfbba 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1079,11 +1079,9 @@
     <skip />
     <!-- no translation found for cut (5845613239192595662) -->
     <skip />
-    <!-- no translation found for cutAll (4474519683293791451) -->
     <skip />
     <!-- no translation found for copy (8603721575469529820) -->
     <skip />
-    <!-- no translation found for copyAll (4777548804630476932) -->
     <skip />
     <!-- no translation found for paste (6458036735811828538) -->
     <skip />
diff --git a/core/res/res/values-en-rSG/strings.xml b/core/res/res/values-en-rSG/strings.xml
index 2ec6b0b..09a8490 100644
--- a/core/res/res/values-en-rSG/strings.xml
+++ b/core/res/res/values-en-rSG/strings.xml
@@ -1074,11 +1074,9 @@
     <skip />
     <!-- no translation found for cut (5845613239192595662) -->
     <skip />
-    <!-- no translation found for cutAll (4474519683293791451) -->
     <skip />
     <!-- no translation found for copy (8603721575469529820) -->
     <skip />
-    <!-- no translation found for copyAll (4777548804630476932) -->
     <skip />
     <!-- no translation found for paste (6458036735811828538) -->
     <skip />
diff --git a/core/res/res/values-en-rUS/strings.xml b/core/res/res/values-en-rUS/strings.xml
index 05f30fc..fdc0d69 100644
--- a/core/res/res/values-en-rUS/strings.xml
+++ b/core/res/res/values-en-rUS/strings.xml
@@ -1115,11 +1115,9 @@
     <skip />
     <!-- no translation found for cut (5845613239192595662) -->
     <skip />
-    <!-- no translation found for cutAll (4474519683293791451) -->
     <skip />
     <!-- no translation found for copy (8603721575469529820) -->
     <skip />
-    <!-- no translation found for copyAll (4777548804630476932) -->
     <skip />
     <!-- no translation found for paste (6458036735811828538) -->
     <skip />
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index afc2862..dbf32b6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Seleccionar texto"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Detener la selección de texto"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Cortar llamada"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Copiar todo"</string>
     <string name="paste" msgid="5629880836805036433">"Pegar"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 045b6a1..425fca3 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Seleccionar texto"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Detener selección de texto"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Cortar todo"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Copiar todo"</string>
     <string name="paste" msgid="5629880836805036433">"Pegar"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de introducción de texto"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3ab7814..2dacb30 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Sélectionner le texte"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Arrêter sélection de texte"</string>
     <string name="cut" msgid="3092569408438626261">"Couper"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Tout couper"</string>
     <string name="copy" msgid="2681946229533511987">"Copier"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Tout copier"</string>
     <string name="paste" msgid="5629880836805036433">"Coller"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copier l\'URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Mode de saisie"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 6e50d21..c7aac82 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Seleziona testo"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Termina selezione testo"</string>
     <string name="cut" msgid="3092569408438626261">"Taglia"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Taglia tutto"</string>
     <string name="copy" msgid="2681946229533511987">"Copia"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Copia tutto"</string>
     <string name="paste" msgid="5629880836805036433">"Incolla"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copia URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metodo inserimento"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index f1e98da..25e977d 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"テキストを選択"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"テキストの選択を終了"</string>
     <string name="cut" msgid="3092569408438626261">"切り取り"</string>
-    <string name="cutAll" msgid="2436383270024931639">"すべて切り取り"</string>
     <string name="copy" msgid="2681946229533511987">"コピー"</string>
-    <string name="copyAll" msgid="2590829068100113057">"すべてコピー"</string>
     <string name="paste" msgid="5629880836805036433">"貼り付け"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URLをコピー"</string>
     <string name="inputMethod" msgid="1653630062304567879">"入力方法"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 712100e..faf589e 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"텍스트 선택"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"텍스트 선택 중지"</string>
     <string name="cut" msgid="3092569408438626261">"잘라내기"</string>
-    <string name="cutAll" msgid="2436383270024931639">"모두 잘라내기"</string>
     <string name="copy" msgid="2681946229533511987">"복사"</string>
-    <string name="copyAll" msgid="2590829068100113057">"모두 복사"</string>
     <string name="paste" msgid="5629880836805036433">"붙여넣기"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL 복사"</string>
     <string name="inputMethod" msgid="1653630062304567879">"입력 방법"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index f09039d..2c23d53 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Merk tekst"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Slutt å merke tekst"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Klipp ut alt"</string>
     <string name="copy" msgid="2681946229533511987">"Kopier"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Kopier alt"</string>
     <string name="paste" msgid="5629880836805036433">"Lim inn"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopier URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Inndatametode"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 17a2fe4..10a8e74 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Tekst selecteren"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Stoppen met tekst selecteren"</string>
     <string name="cut" msgid="3092569408438626261">"Knippen"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Alles knippen"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiëren"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Alles kopiëren"</string>
     <string name="paste" msgid="5629880836805036433">"Plakken"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL kopiëren"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Invoermethode"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 87e045df3..70c3170 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Zaznacz tekst"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Zatrzymaj wybieranie tekstu"</string>
     <string name="cut" msgid="3092569408438626261">"Wytnij"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Wytnij wszystko"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiuj"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Kopiuj wszystko"</string>
     <string name="paste" msgid="5629880836805036433">"Wklej"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopiuj adres URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metoda wprowadzania"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3fb8d55..9a8c6e9 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Seleccionar texto"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Parar selecção de texto"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Cortar tudo"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Copiar tudo"</string>
     <string name="paste" msgid="5629880836805036433">"Colar"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 81ceaf3..2119539 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Selecionar texto"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Parar seleção de texto"</string>
     <string name="cut" msgid="3092569408438626261">"Recortar"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Recortar tudo"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Copiar tudo"</string>
     <string name="paste" msgid="5629880836805036433">"Colar"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 4176886..a99107e 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Выбрать текст"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Остановить выделение текста"</string>
     <string name="cut" msgid="3092569408438626261">"Вырезать"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Вырезать все"</string>
     <string name="copy" msgid="2681946229533511987">"Копировать"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Копировать все"</string>
     <string name="paste" msgid="5629880836805036433">"Вставить"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Копировать URL"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Способ ввода"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4c78f4f..b3e415d 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Markera text"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Sluta välja text"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Klipp ut alla"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiera"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Kopiera alla"</string>
     <string name="paste" msgid="5629880836805036433">"Klistra in"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopiera webbadress"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Indatametod"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index f2fd734..a6bc41e 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"Metin seç"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"Metin seçmeyi durdur"</string>
     <string name="cut" msgid="3092569408438626261">"Kes"</string>
-    <string name="cutAll" msgid="2436383270024931639">"Tümünü kes"</string>
     <string name="copy" msgid="2681946229533511987">"Kopyala"</string>
-    <string name="copyAll" msgid="2590829068100113057">"Tümünü kopyala"</string>
     <string name="paste" msgid="5629880836805036433">"Yapıştır"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL\'yi kopyala"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Giriş yöntemi"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index a0fb721..aa3f12d 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"选择文字"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"停止选择文字"</string>
     <string name="cut" msgid="3092569408438626261">"剪切"</string>
-    <string name="cutAll" msgid="2436383270024931639">"全部剪切"</string>
     <string name="copy" msgid="2681946229533511987">"复制"</string>
-    <string name="copyAll" msgid="2590829068100113057">"全部复制"</string>
     <string name="paste" msgid="5629880836805036433">"粘贴"</string>
     <string name="copyUrl" msgid="2538211579596067402">"复制网址"</string>
     <string name="inputMethod" msgid="1653630062304567879">"输入法"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index d5dd857..5bf0342 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -706,9 +706,7 @@
     <string name="selectText" msgid="3889149123626888637">"選取文字"</string>
     <string name="stopSelectingText" msgid="4157931463872320996">"停止選取文字"</string>
     <string name="cut" msgid="3092569408438626261">"剪下"</string>
-    <string name="cutAll" msgid="2436383270024931639">"全部剪下"</string>
     <string name="copy" msgid="2681946229533511987">"複製"</string>
-    <string name="copyAll" msgid="2590829068100113057">"全部複製"</string>
     <string name="paste" msgid="5629880836805036433">"貼上"</string>
     <string name="copyUrl" msgid="2538211579596067402">"複製網址"</string>
     <string name="inputMethod" msgid="1653630062304567879">"輸入方式"</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index bd65fee..1df424b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1245,7 +1245,7 @@
   <public type="anim" name="cycle_interpolator" id="0x010a000c" />
 
 <!-- ===============================================================
-     Resources introduced in kraken.
+     Resources introduced in Gingerbread.
      =============================================================== -->
      
   <public type="attr" name="logo" id="0x010102be" />
@@ -1273,5 +1273,4 @@
   <public-padding type="dimen" name="kraken_resource_pad" end="0x01050010" />
   <public-padding type="color" name="kraken_resource_pad" end="0x01060020" />
   <public-padding type="array" name="kraken_resource_pad" end="0x01070010" />
-
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 62fd169..52abe45 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1854,23 +1854,17 @@
     <string name="selectAll">Select all</string>
 
     <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
-    <string name="selectText">Select text</string>
+    <string name="selectText">Select word</string>
 
-    <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
+    <!-- Item on EditText context menu. This action is used to stop selecting text in the edit field. -->
     <string name="stopSelectingText">Stop selecting text</string>
 
     <!-- Item on EditText context menu.  This action is used to cut selected the text into the clipboard.  -->
     <string name="cut">Cut</string>
 
-    <!-- Item on EditText context menu. This action is used to cut all the text into the clipboard. -->
-    <string name="cutAll">Cut all</string>
-
     <!-- Item on EditText context menu. This action is used to cut selected the text into the clipboard. -->
     <string name="copy">Copy</string>
 
-    <!-- Item on EditText context menu. This action is used to copy all the text into the clipboard. -->
-    <string name="copyAll">Copy all</string>
-
     <!-- Item on EditText context menu. This action is used t o paste from the clipboard into the eidt field -->
     <string name="paste">Paste</string>
 
diff --git a/core/tests/coretests/res/raw/v30_multibyte_param.vcf b/core/tests/coretests/res/raw/v30_multibyte_param.vcf
new file mode 100644
index 0000000..cd200e5
--- /dev/null
+++ b/core/tests/coretests/res/raw/v30_multibyte_param.vcf
@@ -0,0 +1,5 @@
+BEGIN:VCARD

+VERSION:3.0

+N:F;G;M;;

+TEL;TYPE="费":1

+END:VCARD

diff --git a/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java b/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java
index f3b7c06..95aad91 100644
--- a/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java
+++ b/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java
@@ -22,8 +22,8 @@
 import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.List;
-import javax.net.ssl.TestSSLContext;
 import junit.framework.TestCase;
+import libcore.javax.net.ssl.TestSSLContext;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
index 21f2254..e0e1f87 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
@@ -1008,4 +1008,26 @@
                 .put(Phone.TYPE, Phone.TYPE_PAGER)
                 .put(Phone.NUMBER, "6101231234@pagersample.com");
     }
+
+    public void testMultiBytePropV30_Parse() {
+        mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "3.0")
+                .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
+                .addExpectedNodeWithOrder("TEL", "1", new TypeSet("\u8D39"));
+    }
+
+    public void testMultiBytePropV30() {
+        mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
+        final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "F")
+                .put(StructuredName.MIDDLE_NAME, "M")
+                .put(StructuredName.GIVEN_NAME, "G")
+                .put(StructuredName.DISPLAY_NAME, "G M F");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "\u8D39")
+                .put(Phone.NUMBER, "1");
+    }
 }
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
index 59299f9..e805bee 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
@@ -82,4 +82,34 @@
             assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
         }
     }
+
+    public void testToStringAvailableAsV30ParamValue() {
+        // Smoke tests.
+        assertEquals("HOME", VCardUtils.toStringAvailableAsV30ParameValue("HOME"));
+        assertEquals("TEL", VCardUtils.toStringAvailableAsV30ParameValue("TEL"));
+        assertEquals("PAGER", VCardUtils.toStringAvailableAsV30ParameValue("PAGER"));
+
+        assertEquals("\"\"", VCardUtils.toStringAvailableAsV30ParameValue(""));
+
+        // non-Ascii must be allowed
+        assertEquals("\u4E8B\u52D9\u6240",
+                VCardUtils.toStringAvailableAsV30ParameValue("\u4E8B\u52D9\u6240"));
+        // Reported as bug report.
+        assertEquals("\u8D39", VCardUtils.toStringAvailableAsV30ParameValue("\u8D39"));
+        assertEquals("\"comma,separated\"",
+                VCardUtils.toStringAvailableAsV30ParameValue("comma,separated"));
+        assertEquals("\"colon:aware\"",
+                VCardUtils.toStringAvailableAsV30ParameValue("colon:aware"));
+        // CTL characters.
+        assertEquals("CTLExample",
+                VCardUtils.toStringAvailableAsV30ParameValue("CTL\u0001Example"));
+        // DQUOTE must be removed.
+        assertEquals("quoted",
+                VCardUtils.toStringAvailableAsV30ParameValue("\"quoted\""));
+        // DQUOTE must be removed basically, but we should detect a space, which
+        // require us to use DQUOTE again.
+        // Right-side has one more illegal dquote to test quote-handle code thoroughly.
+        assertEquals("\"Already quoted\"",
+                VCardUtils.toStringAvailableAsV30ParameValue("\"Already quoted\"\""));
+    }
 }
diff --git a/docs/html/resources/tutorials/views/hello-formstuff.jd b/docs/html/resources/tutorials/views/hello-formstuff.jd
index 3dd5f21..b9f6c16 100644
--- a/docs/html/resources/tutorials/views/hello-formstuff.jd
+++ b/docs/html/resources/tutorials/views/hello-formstuff.jd
@@ -32,9 +32,19 @@
 }
 </pre>
 
+<p>Now select which kind of form widget you'd like to create:</p>
+<ul>
+  <li><a href="#CustomButton">Custom Button</a></li>
+  <li><a href="#EditText">Edit Text</a></li>
+  <li><a href="#Checkbox">Checkbox</a></li>
+  <li><a href="#RadioButtons">Radio Buttons</a></li>
+  <li><a href="#ToggleButton">Toggle Button</a></li>
+  <li><a href="#RatingBar">Rating Bar</a></li>
+</ul>
 
 
-<h2>Custom Button</h2>
+
+<h2 id="CustomButton">Custom Button</h2>
 
 <p>In this section, you will create a button with a custom image instead of text, using the {@link
 android.widget.Button} widget and an XML file that defines three different images to use for the
@@ -111,7 +121,8 @@
 </ol>
 
 
-<h2>EditText</h2>
+
+<h2 id="EditText">Edit Text</h2>
 
 <p>In this section, you will create a text field for user input, using the {@link
 android.widget.EditText} widget. Once text has been entered into the field, the "Enter" key will
@@ -158,7 +169,8 @@
 </ol>
 
 
-<h2>CheckBox</h2>
+
+<h2 id="Checkbox">Checkbox</h2>
 
 <p>In this section, you will create a checkbox for selecting items, using the {@link
 android.widget.CheckBox} widget. When the checkbox is pressed, a toast message will
@@ -209,7 +221,8 @@
 android.widget.CompoundButton#toggle()} method.</p>
 
 
-<h2>RadioButton</h2>
+
+<h2 id="RadioButtons">Radio Buttons</h2>
 
 <p>In this section, you will create two mutually-exclusive radio buttons (enabling one disables
 the other), using the {@link android.widget.RadioGroup} and {@link android.widget.RadioButton}
@@ -274,7 +287,8 @@
 android.widget.CompoundButton#toggle()} method.</p>
 
 
-<h2>ToggleButton</h2>
+
+<h2 id="ToggleButton">Toggle Button</h2>
 
 <p>In this section, you'll create a button used specifically for toggling between two
 states, using the {@link android.widget.ToggleButton} widget. This widget is an excellent
@@ -330,7 +344,7 @@
 
 
 
-<h2>RatingBar</h2>
+<h2 id="RatingBar">Rating Bar</h2>
 
 <p>In this section, you'll create a widget that allows the user to provide a rating,
 with the {@link android.widget.RatingBar} widget.</p>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 2313f4c..02e16cd 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -39,7 +39,7 @@
          * the same result from the decoder as if null were passed.
          */
         public Options() {
-            inDither = true;
+            inDither = false;
             inScaled = true;
         }
 
@@ -69,8 +69,11 @@
          * the decoder will try to pick the best matching config based on the
          * system's screen depth, and characteristics of the original image such
          * as if it has per-pixel alpha (requiring a config that also does).
+         * 
+         * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
+         * default.
          */
-        public Bitmap.Config inPreferredConfig;
+        public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
 
         /**
          * If dither is true, the decoder will attempt to dither the decoded
@@ -452,10 +455,8 @@
             // into is.read(...) This number is not related to the value passed
             // to mark(...) above.
             byte [] tempStorage = null;
-            if (opts != null)
-                tempStorage = opts.inTempStorage;
-            if (tempStorage == null)
-                tempStorage = new byte[16 * 1024];
+            if (opts != null) tempStorage = opts.inTempStorage;
+            if (tempStorage == null) tempStorage = new byte[16 * 1024];
             bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
         }
 
@@ -474,8 +475,7 @@
         
         bm.setDensity(density);
         final int targetDensity = opts.inTargetDensity;
-        if (targetDensity == 0 || density == targetDensity
-                || density == opts.inScreenDensity) {
+        if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
             return bm;
         }
         
@@ -581,6 +581,138 @@
         nativeSetDefaultConfig(config.nativeInt);
     }
 
+    /**
+     * Create a LargeBitmap from the specified byte array.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param data byte array of compressed image data.
+     * @param offset offset into data for where the decoder should begin
+     *               parsing.
+     * @param length the number of bytes, beginning at offset, to parse
+     * @param isShareable If this is true, then the LargeBitmap may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the LargeBitmap will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return LargeBitmap, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     * @hide
+     */
+    public static LargeBitmap createLargeBitmap(byte[] data,
+            int offset, int length, boolean isShareable) throws IOException {
+        if ((offset | length) < 0 || data.length < offset + length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        return nativeCreateLargeBitmap(data, offset, length, isShareable);
+    }
+
+    /**
+     * Create a LargeBitmap from the file descriptor.
+     * The position within the descriptor will not be changed when
+     * this returns, so the descriptor can be used again as is.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param fd The file descriptor containing the data to decode
+     * @param isShareable If this is true, then the LargeBitmap may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the LargeBitmap will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return LargeBitmap, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     * @hide
+     */
+    public static LargeBitmap createLargeBitmap(
+            FileDescriptor fd, boolean isShareable) throws IOException {
+        if (MemoryFile.isMemoryFile(fd)) {
+            int mappedlength = MemoryFile.getSize(fd);
+            MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+            InputStream is = file.getInputStream();
+            return createLargeBitmap(is, isShareable);
+        }
+        return nativeCreateLargeBitmap(fd, isShareable);
+    }
+
+    /**
+     * Create a LargeBitmap from an input stream.
+     * The stream's position will be where ever it was after the encoded data
+     * was read.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param is The input stream that holds the raw data to be decoded into a
+     *           LargeBitmap.
+     * @param isShareable If this is true, then the LargeBitmap may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the LargeBitmap will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return LargeBitmap, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     * @hide
+     */
+    public static LargeBitmap createLargeBitmap(InputStream is,
+            boolean isShareable) throws IOException {
+        // we need mark/reset to work properly in JNI
+
+        if (!is.markSupported()) {
+            is = new BufferedInputStream(is, 16 * 1024);
+        }
+
+        if (is instanceof AssetManager.AssetInputStream) {
+            return nativeCreateLargeBitmap(
+                    ((AssetManager.AssetInputStream) is).getAssetInt(),
+                    isShareable);
+        } else {
+            // pass some temp storage down to the native code. 1024 is made up,
+            // but should be large enough to avoid too many small calls back
+            // into is.read(...).
+            byte [] tempStorage = new byte[16 * 1024];
+            return nativeCreateLargeBitmap(is, tempStorage, isShareable);
+        }
+    }
+
+    /**
+     * Create a LargeBitmap from a file path.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param pathName complete path name for the file to be decoded.
+     * @param isShareable If this is true, then the LargeBitmap may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the LargeBitmap will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return LargeBitmap, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     * @hide
+     */
+    public static LargeBitmap createLargeBitmap(String pathName, boolean isShareable)
+            throws IOException {
+        LargeBitmap bm = null;
+        InputStream stream = null;
+
+        try {
+            stream = new FileInputStream(pathName);
+            bm = createLargeBitmap(stream, isShareable);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // do nothing here
+                }
+            }
+        }
+        return bm;
+    }
+
     private static native void nativeSetDefaultConfig(int nativeConfig);
     private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
             Rect padding, Options opts);
@@ -590,5 +722,14 @@
     private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
             int length, Options opts);
     private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
+
+    private static native LargeBitmap nativeCreateLargeBitmap(
+            byte[] data, int offset, int length, boolean isShareable);
+    private static native LargeBitmap nativeCreateLargeBitmap(
+            FileDescriptor fd, boolean isShareable);
+    private static native LargeBitmap nativeCreateLargeBitmap(
+            InputStream is, byte[] storage, boolean isShareable);
+    private static native LargeBitmap nativeCreateLargeBitmap(
+            int asset, boolean isShareable);
 }
 
diff --git a/graphics/java/android/graphics/LargeBitmap.java b/graphics/java/android/graphics/LargeBitmap.java
new file mode 100644
index 0000000..6656b17
--- /dev/null
+++ b/graphics/java/android/graphics/LargeBitmap.java
@@ -0,0 +1,128 @@
+/*
+ * 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.graphics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
+
+import java.io.OutputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * LargeBitmap can be used to decode a rectangle region from an image.
+ * LargeBimap is particularly useful when an original image is large and
+ * you only need parts of the image.
+ *
+ * To create a LargeBitmap, call BitmapFactory.createLargeBitmap().
+ * Given a LargeBitmap, users can call decodeRegion() repeatedly
+ * to get a decoded Bitmap of the specified region.
+ * @hide
+ */
+public final class LargeBitmap {
+    private int mNativeLargeBitmap;
+    private boolean mRecycled;
+
+    /*  Private constructor that must received an already allocated native
+        large bitmap int (pointer).
+
+        This can be called from JNI code.
+    */
+    private LargeBitmap(int lbm) {
+        mNativeLargeBitmap = lbm;
+        mRecycled = false;
+    }
+
+    /**
+     * Decodes a rectangle region in the image specified by rect.
+     *
+     * @param rect The rectangle that specified the region to be decode.
+     * @param opts null-ok; Options that control downsampling.
+     *             inPurgeable is not supported.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded.
+     */
+    public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+        checkRecycled("decodeRegion called on recycled large bitmap");
+        if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() || rect.bottom > getHeight())
+            throw new IllegalArgumentException("rectangle is not inside the image");
+        return nativeDecodeRegion(mNativeLargeBitmap, rect.left, rect.top,
+                rect.right - rect.left, rect.bottom - rect.top, options);
+    }
+
+    /** Returns the original image's width */
+    public int getWidth() {
+        checkRecycled("getWidth called on recycled large bitmap");
+        return nativeGetWidth(mNativeLargeBitmap);
+    }
+
+    /** Returns the original image's height */
+    public int getHeight() {
+        checkRecycled("getHeight called on recycled large bitmap");
+        return nativeGetHeight(mNativeLargeBitmap);
+    }
+
+    /**
+     * Frees up the memory associated with this large bitmap, and mark the
+     * large bitmap as "dead", meaning it will throw an exception if decodeRegion(),
+     * getWidth() or getHeight() is called.
+     * This operation cannot be reversed, so it should only be called if you are
+     * sure there are no further uses for the large bitmap. This is an advanced call,
+     * and normally need not be called, since the normal GC process will free up this
+     * memory when there are no more references to this bitmap.
+     */
+    public void recycle() {
+        if (!mRecycled) {
+            nativeClean(mNativeLargeBitmap);
+            mRecycled = true;
+        }
+    }
+
+    /**
+     * Returns true if this large bitmap has been recycled.
+     * If so, then it is an error to try use its method.
+     *
+     * @return true if the large bitmap has been recycled
+     */
+    public final boolean isRecycled() {
+        return mRecycled;
+    }
+
+    /**
+     * Called by methods that want to throw an exception if the bitmap
+     * has already been recycled.
+     */
+    private void checkRecycled(String errorMessage) {
+        if (mRecycled) {
+            throw new IllegalStateException(errorMessage);
+        }
+    }
+
+    protected void finalize() {
+        recycle();
+    }
+
+    private static native Bitmap nativeDecodeRegion(int lbm,
+            int start_x, int start_y, int width, int height,
+            BitmapFactory.Options options);
+    private static native int nativeGetWidth(int lbm);
+    private static native int nativeGetHeight(int lbm);
+    private static native void nativeClean(int lbm);
+}
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 98ffb8b..7830224 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -75,6 +75,7 @@
         bottom = r.bottom;
     }
 
+    @Override
     public boolean equals(Object obj) {
         Rect r = (Rect) obj;
         if (r != null) {
@@ -84,6 +85,7 @@
         return false;
     }
 
+    @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(32);
         sb.append("Rect("); sb.append(left); sb.append(", ");
@@ -351,7 +353,7 @@
      * rectangle, return true and set this rectangle to that intersection,
      * otherwise return false and do not change this rectangle. No check is
      * performed to see if either rectangle is empty. Note: To just test for
-     * intersection, use intersects()
+     * intersection, use {@link #intersects(Rect, Rect)}.
      *
      * @param left The left side of the rectangle being intersected with this
      *             rectangle
@@ -445,7 +447,7 @@
     /**
      * Returns true iff the two specified rectangles intersect. In no event are
      * either of the rectangles modified. To record the intersection,
-     * use intersect() or setIntersect().
+     * use {@link #intersect(Rect)} or {@link #setIntersect(Rect, Rect)}.
      *
      * @param a The first rectangle being tested for intersection
      * @param b The second rectangle being tested for intersection
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 17c0778..b27c7f5 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -363,6 +363,17 @@
     static public Allocation createFromBitmapResourceBoxed(RenderScript rs, Resources res, int id, Element dstFmt, boolean genMips)
         throws IllegalArgumentException {
 
+        mBitmapOptions.inPreferredConfig = null;
+        if (dstFmt == rs.mElement_RGBA_8888) {
+            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        } else if (dstFmt == rs.mElement_RGB_888) {
+            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        } else if (dstFmt == rs.mElement_RGBA_4444) {
+            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_4444;
+        } else if (dstFmt == rs.mElement_RGB_565) {
+            mBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+        }
+
         Bitmap b = BitmapFactory.decodeResource(res, id, mBitmapOptions);
         return createFromBitmapBoxed(rs, b, dstFmt, genMips);
     }
diff --git a/include/camera/CameraHardwareInterface.h b/include/camera/CameraHardwareInterface.h
index 1529db7..6a66e3c 100644
--- a/include/camera/CameraHardwareInterface.h
+++ b/include/camera/CameraHardwareInterface.h
@@ -221,6 +221,7 @@
  */
 extern "C" int HAL_getNumberOfCameras();
 extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo);
+/* HAL should return NULL if it fails to open camera hardware. */
 extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId);
 
 };  // namespace android
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 6a5d254..a5c7874 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -22,6 +22,21 @@
 
 namespace android {
 
+struct Size {
+    int width;
+    int height;
+
+    Size() {
+        width = 0;
+        height = 0;
+    }
+
+    Size(int w, int h) {
+        width = w;
+        height = h;
+    }
+};
+
 class CameraParameters
 {
 public:
@@ -43,12 +58,15 @@
 
     void setPreviewSize(int width, int height);
     void getPreviewSize(int *width, int *height) const;
+    void getSupportedPreviewSizes(Vector<Size> &sizes) const;
     void setPreviewFrameRate(int fps);
     int getPreviewFrameRate() const;
+    void getPreviewFpsRange(int *min_fps, int *max_fps) const;
     void setPreviewFormat(const char *format);
     const char *getPreviewFormat() const;
     void setPictureSize(int width, int height);
     void getPictureSize(int *width, int *height) const;
+    void getSupportedPictureSizes(Vector<Size> &sizes) const;
     void setPictureFormat(const char *format);
     const char *getPictureFormat() const;
 
@@ -65,6 +83,20 @@
     // Supported preview frame sizes in pixels.
     // Example value: "800x600,480x320". Read only.
     static const char KEY_SUPPORTED_PREVIEW_SIZES[];
+    // The current minimum and maximum preview fps. This controls the rate of
+    // preview frames received (CAMERA_MSG_PREVIEW_FRAME). The minimum and
+    // maximum fps must be one of the elements from
+    // KEY_SUPPORTED_PREVIEW_FPS_RANGE parameter.
+    // Example value: "10500,26623"
+    static const char KEY_PREVIEW_FPS_RANGE[];
+    // The supported preview fps (frame-per-second) ranges. Each range contains
+    // a minimum fps and maximum fps. If minimum fps equals to maximum fps, the
+    // camera outputs frames in fixed frame rate. If not, the camera outputs
+    // frames in auto frame rate. The actual frame rate fluctuates between the
+    // minimum and the maximum. The list has at least one element. The list is
+    // sorted from small to large (first by maximum fps and then minimum fps).
+    // Example value: "(10500,26623),(15000,26623),(30000,30000)"
+    static const char KEY_SUPPORTED_PREVIEW_FPS_RANGE[];
     // The image format for preview frames. See CAMERA_MSG_PREVIEW_FRAME in
     // frameworks/base/include/camera/Camera.h.
     // Example value: "yuv420sp" or PIXEL_FORMAT_XXX constants. Read/write.
@@ -337,11 +369,14 @@
     static const char PIXEL_FORMAT_JPEG[];
 
     // Values for focus mode settings.
-    // Auto-focus mode.
+    // Auto-focus mode. Applications should call
+    // CameraHardwareInterface.autoFocus to start the focus in this mode.
     static const char FOCUS_MODE_AUTO[];
     // Focus is set at infinity. Applications should not call
     // CameraHardwareInterface.autoFocus in this mode.
     static const char FOCUS_MODE_INFINITY[];
+    // Macro (close-up) focus mode. Applications should call
+    // CameraHardwareInterface.autoFocus to start the focus in this mode.
     static const char FOCUS_MODE_MACRO[];
     // Focus is fixed. The camera is always in this mode if the focus is not
     // adjustable. If the camera has auto-focus, this mode can fix the
@@ -355,7 +390,8 @@
     // Continuous auto focus mode. The camera continuously tries to focus. This
     // is ideal for shooting video or shooting photo of moving object. Auto
     // focus starts when the parameter is set. Applications should not call
-    // CameraHardwareInterface.autoFocus in this mode.
+    // CameraHardwareInterface.autoFocus in this mode.  To stop continuous
+    // focus, applications should change the focus mode to other modes.
     static const char FOCUS_MODE_CONTINUOUS[];
 
     // The camera determines the exposure by giving more weight to the
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 813dd43..aa965e1 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -37,8 +37,8 @@
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual bool reachedEOS();
     virtual status_t start(MetaData *params = NULL);
-    virtual void stop();
-    virtual void pause();
+    virtual status_t stop();
+    virtual status_t pause();
 
 protected:
     virtual ~AMRWriter();
@@ -57,7 +57,7 @@
     int64_t mEstimatedDurationUs;
 
     static void *ThreadWrapper(void *);
-    void threadFunc();
+    status_t threadFunc();
     bool exceedsFileSizeLimit();
     bool exceedsFileDurationLimit();
 
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index be96935..de82b38 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -37,9 +37,9 @@
 
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual status_t start(MetaData *param = NULL);
+    virtual status_t stop();
+    virtual status_t pause();
     virtual bool reachedEOS();
-    virtual void stop();
-    virtual void pause();
 
     void beginBox(const char *fourcc);
     void writeInt8(int8_t x);
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 8d3a9df..151bf16 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -35,8 +35,9 @@
     virtual status_t addSource(const sp<MediaSource> &source) = 0;
     virtual bool reachedEOS() = 0;
     virtual status_t start(MetaData *params = NULL) = 0;
-    virtual void stop() = 0;
-    virtual void pause() = 0;
+    virtual status_t stop() = 0;
+    virtual status_t pause() = 0;
+
     virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
     virtual void setMaxFileDuration(int64_t durationUs) { mMaxFileDurationLimitUs = durationUs; }
     virtual void setListener(const sp<IMediaRecorderClient>& listener) {
diff --git a/media/libstagefright/mpeg2ts/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h
similarity index 100%
rename from media/libstagefright/mpeg2ts/ABitReader.h
rename to include/media/stagefright/foundation/ABitReader.h
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
new file mode 100644
index 0000000..a2735a4
--- /dev/null
+++ b/include/storage/IMountService.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMOUNTSERVICE_H
+#define ANDROID_IMOUNTSERVICE_H
+
+#include <storage/IMountServiceListener.h>
+#include <storage/IMountShutdownObserver.h>
+#include <storage/IObbActionListener.h>
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMountService: public IInterface {
+public:
+    DECLARE_META_INTERFACE(MountService);
+
+    virtual void registerListener(const sp<IMountServiceListener>& listener) = 0;
+    virtual void
+            unregisterListener(const sp<IMountServiceListener>& listener) = 0;
+    virtual bool isUsbMassStorageConnected() = 0;
+    virtual void setUsbMassStorageEnabled(const bool enable) = 0;
+    virtual bool isUsbMassStorageEnabled() = 0;
+    virtual int32_t mountVolume(const String16& mountPoint) = 0;
+    virtual int32_t
+            unmountVolume(const String16& mountPoint, const bool force) = 0;
+    virtual int32_t formatVolume(const String16& mountPoint) = 0;
+    virtual int32_t
+            getStorageUsers(const String16& mountPoint, int32_t** users) = 0;
+    virtual int32_t getVolumeState(const String16& mountPoint) = 0;
+    virtual int32_t createSecureContainer(const String16& id,
+            const int32_t sizeMb, const String16& fstype, const String16& key,
+            const int32_t ownerUid) = 0;
+    virtual int32_t finalizeSecureContainer(const String16& id) = 0;
+    virtual int32_t destroySecureContainer(const String16& id) = 0;
+    virtual int32_t mountSecureContainer(const String16& id,
+            const String16& key, const int32_t ownerUid) = 0;
+    virtual int32_t
+            unmountSecureContainer(const String16& id, const bool force) = 0;
+    virtual bool isSecureContainerMounted(const String16& id) = 0;
+    virtual int32_t renameSecureContainer(const String16& oldId,
+            const String16& newId) = 0;
+    virtual bool getSecureContainerPath(const String16& id, String16& path) = 0;
+    virtual int32_t getSecureContainerList(const String16& id,
+            String16*& containers) = 0;
+    virtual void shutdown(const sp<IMountShutdownObserver>& observer) = 0;
+    virtual void finishMediaUpdate() = 0;
+    virtual void mountObb(const String16& filename, const String16& key,
+            const sp<IObbActionListener>& token) = 0;
+    virtual void unmountObb(const String16& filename, const bool force) = 0;
+    virtual bool isObbMounted(const String16& filename) = 0;
+    virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMountService: public BnInterface<IMountService> {
+public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+            Parcel* reply, uint32_t flags = 0);
+};
+
+}
+; // namespace android
+
+#endif // ANDROID_IMOUNTSERVICE_H
diff --git a/include/storage/IMountServiceListener.h b/include/storage/IMountServiceListener.h
new file mode 100644
index 0000000..5b1f21c
--- /dev/null
+++ b/include/storage/IMountServiceListener.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMOUNTSERVICELISTENER_H
+#define ANDROID_IMOUNTSERVICELISTENER_H
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMountServiceListener: public IInterface {
+public:
+    DECLARE_META_INTERFACE(MountServiceListener);
+
+    virtual void onUsbMassStorageConnectionChanged(const bool connected) = 0;
+    virtual void onStorageStateChanged(const String16& path,
+            const String16& oldState, const String16& newState) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMountServiceListener: public BnInterface<IMountServiceListener> {
+public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+            Parcel* reply, uint32_t flags = 0);
+};
+
+}
+; // namespace android
+
+#endif // ANDROID_IMOUNTSERVICELISTENER_H
diff --git a/include/storage/IMountShutdownObserver.h b/include/storage/IMountShutdownObserver.h
new file mode 100644
index 0000000..d019e01
--- /dev/null
+++ b/include/storage/IMountShutdownObserver.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMOUNTSHUTDOWNOBSERVER_H
+#define ANDROID_IMOUNTSHUTDOWNOBSERVER_H
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMountShutdownObserver: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(MountShutdownObserver);
+
+    virtual void onShutDownComplete(const int32_t statusCode) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMountShutdownObserver: public BnInterface<IMountShutdownObserver>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMOUNTSHUTDOWNOBSERVER_H
diff --git a/include/storage/IObbActionListener.h b/include/storage/IObbActionListener.h
new file mode 100644
index 0000000..1bedcc6
--- /dev/null
+++ b/include/storage/IObbActionListener.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IOBBACTIONLISTENER_H
+#define ANDROID_IOBBACTIONLISTENER_H
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <utils/String16.h>
+
+namespace android {
+
+class IObbActionListener: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(ObbActionListener);
+
+    virtual void onObbResult(const String16& filename, const String16& status) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnObbActionListener: public BnInterface<IObbActionListener>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IOBBACTIONLISTENER_H
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index dab35b3..3d42856 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -224,6 +224,7 @@
         uint8_t*        keyBitmask;
         KeyLayoutMap*   layoutMap;
         String8         keylayoutFilename;
+        int             fd;
         device_t*       next;
         
         device_t(int32_t _id, const char* _path, const char* name);
@@ -266,6 +267,12 @@
 #ifdef EV_SW
     int32_t         mSwitches[SW_MAX + 1];
 #endif
+
+    static const int INPUT_BUFFER_SIZE = 64;
+    struct input_event mInputBufferData[INPUT_BUFFER_SIZE];
+    int32_t mInputBufferIndex;
+    int32_t mInputBufferCount;
+    int32_t mInputDeviceIndex;
 };
 
 }; // namespace android
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index d3495fe..aed4fa1 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -159,6 +159,12 @@
     virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid,
             Vector<InputTarget>& outTargets) = 0;
+
+    /* Gets the maximum suggested event delivery rate per second.
+     * This value is used to throttle motion event movement actions on a per-device
+     * basis.  It is not intended to be a hard limit.
+     */
+    virtual int32_t getMaxEventsPerSecond() = 0;
 };
 
 
@@ -332,6 +338,8 @@
         // Linked list of motion samples associated with this motion event.
         MotionSample firstSample;
         MotionSample* lastSample;
+
+        uint32_t countSamples() const;
     };
 
     // Tracks the progress of dispatching a particular event to a particular connection.
@@ -554,6 +562,8 @@
     // All registered connections mapped by receive pipe file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
 
+    ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+
     // Active connections are connections that have a non-empty outbound queue.
     // We don't use a ref-counted pointer here because we explicitly abort connections
     // during unregistration which causes the connection's outbound queue to be cleared
@@ -585,6 +595,17 @@
     Condition mInjectionSyncFinishedCondition;
     void decrementPendingSyncDispatchesLocked(EventEntry* entry);
 
+    // Throttling state.
+    struct ThrottleState {
+        nsecs_t minTimeBetweenEvents;
+
+        nsecs_t lastEventTime;
+        int32_t lastDeviceId;
+        uint32_t lastSource;
+
+        uint32_t originalSampleCount; // only collected during debugging
+    } mThrottleState;
+
     // Key repeat tracking.
     // XXX Move this up to the input reader instead.
     struct KeyRepeatState {
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 71c6c51..56d2765 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -480,10 +480,6 @@
         inline void clear() {
             fields = 0;
         }
-
-        inline bool isDirty() {
-            return fields != 0;
-        }
     } mAccumulator;
 
     float mXScale;
@@ -702,7 +698,7 @@
         } historyData[AVERAGING_HISTORY_SIZE];
     } mAveragingTouchFilter;
 
-    struct JumpTouchFilterState {
+    struct JumpyTouchFilterState {
         uint32_t jumpyPointsDropped;
     } mJumpyTouchFilter;
 
@@ -765,10 +761,6 @@
         inline void clear() {
             fields = 0;
         }
-
-        inline bool isDirty() {
-            return fields != 0;
-        }
     } mAccumulator;
 
     bool mDown;
@@ -804,7 +796,8 @@
             FIELD_ABS_MT_WIDTH_MAJOR = 16,
             FIELD_ABS_MT_WIDTH_MINOR = 32,
             FIELD_ABS_MT_ORIENTATION = 64,
-            FIELD_ABS_MT_TRACKING_ID = 128
+            FIELD_ABS_MT_TRACKING_ID = 128,
+            FIELD_ABS_MT_PRESSURE = 256,
         };
 
         uint32_t pointerCount;
@@ -819,6 +812,7 @@
             int32_t absMTWidthMinor;
             int32_t absMTOrientation;
             int32_t absMTTrackingId;
+            int32_t absMTPressure;
 
             inline void clear() {
                 fields = 0;
@@ -829,10 +823,6 @@
             pointerCount = 0;
             pointers[0].clear();
         }
-
-        inline bool isDirty() {
-            return pointerCount != 0;
-        }
     } mAccumulator;
 
     void initialize();
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
index d2ca82e..5243f50 100644
--- a/include/utils/ObbFile.h
+++ b/include/utils/ObbFile.h
@@ -18,12 +18,16 @@
 #define OBBFILE_H_
 
 #include <stdint.h>
+#include <strings.h>
 
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 
 namespace android {
 
+// OBB flags (bit 0)
+#define OBB_OVERLAY         (1 << 0)
+
 class ObbFile : public RefBase {
 protected:
     virtual ~ObbFile();
@@ -46,18 +50,38 @@
         return mPackageName;
     }
 
-    int32_t getVersion() const {
-        return mVersion;
-    }
-
     void setPackageName(String8 packageName) {
         mPackageName = packageName;
     }
 
+    int32_t getVersion() const {
+        return mVersion;
+    }
+
     void setVersion(int32_t version) {
         mVersion = version;
     }
 
+    int32_t getFlags() const {
+        return mFlags;
+    }
+
+    void setFlags(int32_t flags) {
+        mFlags = flags;
+    }
+
+    bool isOverlay() {
+        return (mFlags & OBB_OVERLAY) == OBB_OVERLAY;
+    }
+
+    void setOverlay(bool overlay) {
+        if (overlay) {
+            mFlags |= OBB_OVERLAY;
+        } else {
+            mFlags &= ~OBB_OVERLAY;
+        }
+    }
+
     static inline uint32_t get4LE(const unsigned char* buf) {
         return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
     }
@@ -76,6 +100,9 @@
     /* Package version this ObbFile is associated with */
     int32_t mVersion;
 
+    /* Flags for this OBB type. */
+    int32_t mFlags;
+
     const char* mFileName;
 
     size_t mFileSize;
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 1415493..d0ed7df 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -30,6 +30,8 @@
 const char CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS[] = "preview-format-values";
 const char CameraParameters::KEY_PREVIEW_FRAME_RATE[] = "preview-frame-rate";
 const char CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES[] = "preview-frame-rate-values";
+const char CameraParameters::KEY_PREVIEW_FPS_RANGE[] = "preview-fps-range";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE[] = "preview-fps-range-values";
 const char CameraParameters::KEY_PICTURE_SIZE[] = "picture-size";
 const char CameraParameters::KEY_SUPPORTED_PICTURE_SIZES[] = "picture-size-values";
 const char CameraParameters::KEY_PICTURE_FORMAT[] = "picture-format";
@@ -269,24 +271,57 @@
     mMap.removeItem(String8(key));
 }
 
-static int parse_size(const char *str, int &width, int &height)
+// Parse string like "640x480" or "10000,20000"
+static int parse_pair(const char *str, int *first, int *second, char delim,
+                      char **endptr = NULL)
 {
-    // Find the width.
+    // Find the first integer.
     char *end;
     int w = (int)strtol(str, &end, 10);
-    // If an 'x' does not immediately follow, give up.
-    if (*end != 'x')
+    // If a delimeter does not immediately follow, give up.
+    if (*end != delim) {
+        LOGE("Cannot find delimeter (%c) in str=%s", delim, str);
         return -1;
+    }
 
-    // Find the height, immediately after the 'x'.
-    int h = (int)strtol(end+1, 0, 10);
+    // Find the second integer, immediately after the delimeter.
+    int h = (int)strtol(end+1, &end, 10);
 
-    width = w;
-    height = h;
+    *first = w;
+    *second = h;
+
+    if (endptr) {
+        *endptr = end;
+    }
 
     return 0;
 }
 
+static void parseSizesList(const char *sizesStr, Vector<Size> &sizes)
+{
+    if (sizesStr == 0) {
+        return;
+    }
+
+    char *sizeStartPtr = (char *)sizesStr;
+
+    while (true) {
+        int width, height;
+        int success = parse_pair(sizeStartPtr, &width, &height, 'x',
+                                 &sizeStartPtr);
+        if (success == -1 || (*sizeStartPtr != ',' && *sizeStartPtr != '\0')) {
+            LOGE("Picture sizes string \"%s\" contains invalid character.", sizesStr);
+            return;
+        }
+        sizes.push(Size(width, height));
+
+        if (*sizeStartPtr == '\0') {
+            return;
+        }
+        sizeStartPtr++;
+    }
+}
+
 void CameraParameters::setPreviewSize(int width, int height)
 {
     char str[32];
@@ -296,19 +331,17 @@
 
 void CameraParameters::getPreviewSize(int *width, int *height) const
 {
-    *width = -1;
-    *height = -1;
-
+    *width = *height = -1;
     // Get the current string, if it doesn't exist, leave the -1x-1
     const char *p = get(KEY_PREVIEW_SIZE);
-    if (p == 0)
-        return;
+    if (p == 0)  return;
+    parse_pair(p, width, height, 'x');
+}
 
-    int w, h;
-    if (parse_size(p, w, h) == 0) {
-        *width = w;
-        *height = h;
-    }
+void CameraParameters::getSupportedPreviewSizes(Vector<Size> &sizes) const
+{
+    const char *previewSizesStr = get(KEY_SUPPORTED_PREVIEW_SIZES);
+    parseSizesList(previewSizesStr, sizes);
 }
 
 void CameraParameters::setPreviewFrameRate(int fps)
@@ -321,6 +354,14 @@
     return getInt(KEY_PREVIEW_FRAME_RATE);
 }
 
+void CameraParameters::getPreviewFpsRange(int *min_fps, int *max_fps) const
+{
+    *min_fps = *max_fps = -1;
+    const char *p = get(KEY_PREVIEW_FPS_RANGE);
+    if (p == 0) return;
+    parse_pair(p, min_fps, max_fps, ',');
+}
+
 void CameraParameters::setPreviewFormat(const char *format)
 {
     set(KEY_PREVIEW_FORMAT, format);
@@ -340,19 +381,17 @@
 
 void CameraParameters::getPictureSize(int *width, int *height) const
 {
-    *width = -1;
-    *height = -1;
-
+    *width = *height = -1;
     // Get the current string, if it doesn't exist, leave the -1x-1
     const char *p = get(KEY_PICTURE_SIZE);
-    if (p == 0)
-        return;
+    if (p == 0) return;
+    parse_pair(p, width, height, 'x');
+}
 
-    int w, h;
-    if (parse_size(p, w, h) == 0) {
-        *width = w;
-        *height = h;
-    }
+void CameraParameters::getSupportedPictureSizes(Vector<Size> &sizes) const
+{
+    const char *pictureSizesStr = get(KEY_SUPPORTED_PICTURE_SIZES);
+    parseSizesList(pictureSizesStr, sizes);
 }
 
 void CameraParameters::setPictureFormat(const char *format)
diff --git a/libs/storage/Android.mk b/libs/storage/Android.mk
new file mode 100644
index 0000000..1e52fa4
--- /dev/null
+++ b/libs/storage/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	IMountServiceListener.cpp \
+	IMountShutdownObserver.cpp \
+	IObbActionListener.cpp \
+	IMountService.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+	libutils \
+	libbinder
+
+LOCAL_MODULE:= libstorage
+
+ifeq ($(TARGET_SIMULATOR),true)
+    LOCAL_LDLIBS += -lpthread
+endif
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
new file mode 100644
index 0000000..902bb27
--- /dev/null
+++ b/libs/storage/IMountService.cpp
@@ -0,0 +1,508 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "IMountService"
+
+#include <storage/IMountService.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+    TRANSACTION_registerListener = IBinder::FIRST_CALL_TRANSACTION,
+    TRANSACTION_unregisterListener,
+    TRANSACTION_isUsbMassStorageConnected,
+    TRANSACTION_setUsbMassStorageEnabled,
+    TRANSACTION_isUsbMassStorageEnabled,
+    TRANSACTION_mountVolume,
+    TRANSACTION_unmountVolume,
+    TRANSACTION_formatVolume,
+    TRANSACTION_getStorageUsers,
+    TRANSACTION_getVolumeState,
+    TRANSACTION_createSecureContainer,
+    TRANSACTION_finalizeSecureContainer,
+    TRANSACTION_destroySecureContainer,
+    TRANSACTION_mountSecureContainer,
+    TRANSACTION_unmountSecureContainer,
+    TRANSACTION_isSecureContainerMounted,
+    TRANSACTION_renameSecureContainer,
+    TRANSACTION_getSecureContainerPath,
+    TRANSACTION_getSecureContainerList,
+    TRANSACTION_shutdown,
+    TRANSACTION_finishMediaUpdate,
+    TRANSACTION_mountObb,
+    TRANSACTION_unmountObb,
+    TRANSACTION_isObbMounted,
+    TRANSACTION_getMountedObbPath,
+};
+
+class BpMountService: public BpInterface<IMountService>
+{
+public:
+    BpMountService(const sp<IBinder>& impl)
+        : BpInterface<IMountService>(impl)
+    {
+    }
+
+    virtual void registerListener(const sp<IMountServiceListener>& listener)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        if (remote()->transact(TRANSACTION_registerListener, data, &reply) != NO_ERROR) {
+            LOGD("registerListener could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("registerListener caught exception %d\n", err);
+            return;
+        }
+    }
+
+    virtual void unregisterListener(const sp<IMountServiceListener>& listener)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        if (remote()->transact(TRANSACTION_unregisterListener, data, &reply) != NO_ERROR) {
+            LOGD("unregisterListener could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("unregisterListener caught exception %d\n", err);
+            return;
+        }
+    }
+
+    virtual bool isUsbMassStorageConnected()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        if (remote()->transact(TRANSACTION_isUsbMassStorageConnected, data, &reply) != NO_ERROR) {
+            LOGD("isUsbMassStorageConnected could not contact remote\n");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("isUsbMassStorageConnected caught exception %d\n", err);
+            return false;
+        }
+        return reply.readInt32() != 0;
+    }
+
+    virtual void setUsbMassStorageEnabled(const bool enable)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeInt32(enable != 0);
+        if (remote()->transact(TRANSACTION_setUsbMassStorageEnabled, data, &reply) != NO_ERROR) {
+            LOGD("setUsbMassStorageEnabled could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("setUsbMassStorageEnabled caught exception %d\n", err);
+            return;
+        }
+    }
+
+    virtual bool isUsbMassStorageEnabled()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        if (remote()->transact(TRANSACTION_isUsbMassStorageEnabled, data, &reply) != NO_ERROR) {
+            LOGD("isUsbMassStorageEnabled could not contact remote\n");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("isUsbMassStorageEnabled caught exception %d\n", err);
+            return false;
+        }
+        return reply.readInt32() != 0;
+    }
+
+    int32_t mountVolume(const String16& mountPoint)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        if (remote()->transact(TRANSACTION_mountVolume, data, &reply) != NO_ERROR) {
+            LOGD("mountVolume could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("mountVolume caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t unmountVolume(const String16& mountPoint, const bool force)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        data.writeInt32(force ? 1 : 0);
+        if (remote()->transact(TRANSACTION_unmountVolume, data, &reply) != NO_ERROR) {
+            LOGD("unmountVolume could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("unmountVolume caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t formatVolume(const String16& mountPoint)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        if (remote()->transact(TRANSACTION_formatVolume, data, &reply) != NO_ERROR) {
+            LOGD("formatVolume could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("formatVolume caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t getStorageUsers(const String16& mountPoint, int32_t** users)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        if (remote()->transact(TRANSACTION_getStorageUsers, data, &reply) != NO_ERROR) {
+            LOGD("getStorageUsers could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("getStorageUsers caught exception %d\n", err);
+            return err;
+        }
+        const int32_t numUsers = reply.readInt32();
+        *users = (int32_t*)malloc(sizeof(int32_t)*numUsers);
+        for (int i = 0; i < numUsers; i++) {
+            **users++ = reply.readInt32();
+        }
+        return numUsers;
+    }
+
+    int32_t getVolumeState(const String16& mountPoint)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(mountPoint);
+        if (remote()->transact(TRANSACTION_getVolumeState, data, &reply) != NO_ERROR) {
+            LOGD("getVolumeState could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("getVolumeState caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t createSecureContainer(const String16& id, const int32_t sizeMb, const String16& fstype,
+            const String16& key, const int32_t ownerUid)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        data.writeInt32(sizeMb);
+        data.writeString16(fstype);
+        data.writeString16(key);
+        data.writeInt32(ownerUid);
+        if (remote()->transact(TRANSACTION_createSecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("createSecureContainer could not contact remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("createSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t finalizeSecureContainer(const String16& id)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_finalizeSecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("finalizeSecureContainer couldn't call remote\n");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("finalizeSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t destroySecureContainer(const String16& id)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_destroySecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("destroySecureContainer couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("destroySecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t mountSecureContainer(const String16& id, const String16& key, const int32_t ownerUid)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        data.writeString16(key);
+        data.writeInt32(ownerUid);
+        if (remote()->transact(TRANSACTION_mountSecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("mountSecureContainer couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("mountSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    int32_t unmountSecureContainer(const String16& id, const bool force)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        data.writeInt32(force ? 1 : 0);
+        if (remote()->transact(TRANSACTION_getSecureContainerPath, data, &reply) != NO_ERROR) {
+            LOGD("unmountSecureContainer couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("unmountSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    bool isSecureContainerMounted(const String16& id)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_isSecureContainerMounted, data, &reply) != NO_ERROR) {
+            LOGD("isSecureContainerMounted couldn't call remote");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("isSecureContainerMounted caught exception %d\n", err);
+            return false;
+        }
+        return reply.readInt32() != 0;
+    }
+
+    int32_t renameSecureContainer(const String16& oldId, const String16& newId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(oldId);
+        data.writeString16(newId);
+        if (remote()->transact(TRANSACTION_renameSecureContainer, data, &reply) != NO_ERROR) {
+            LOGD("renameSecureContainer couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("renameSecureContainer caught exception %d\n", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
+    bool getSecureContainerPath(const String16& id, String16& path)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_getSecureContainerPath, data, &reply) != NO_ERROR) {
+            LOGD("getSecureContainerPath couldn't call remote");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode(); // What to do...
+        if (err < 0) {
+            LOGD("getSecureContainerPath caught exception %d\n", err);
+            return false;
+        }
+        path = reply.readString16();
+        return true;
+    }
+
+    int32_t getSecureContainerList(const String16& id, String16*& containers)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(id);
+        if (remote()->transact(TRANSACTION_getSecureContainerList, data, &reply) != NO_ERROR) {
+            LOGD("getSecureContainerList couldn't call remote");
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("getSecureContainerList caught exception %d\n", err);
+            return err;
+        }
+        const int32_t numStrings = reply.readInt32();
+        containers = new String16[numStrings];
+        for (int i = 0; i < numStrings; i++) {
+            containers[i] = reply.readString16();
+        }
+        return numStrings;
+    }
+
+    void shutdown(const sp<IMountShutdownObserver>& observer)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeStrongBinder(observer->asBinder());
+        if (remote()->transact(TRANSACTION_shutdown, data, &reply) != NO_ERROR) {
+            LOGD("shutdown could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("shutdown caught exception %d\n", err);
+            return;
+        }
+        reply.readExceptionCode();
+    }
+
+    void finishMediaUpdate()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        if (remote()->transact(TRANSACTION_finishMediaUpdate, data, &reply) != NO_ERROR) {
+            LOGD("finishMediaUpdate could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("finishMediaUpdate caught exception %d\n", err);
+            return;
+        }
+        reply.readExceptionCode();
+    }
+
+    void mountObb(const String16& filename, const String16& key, const sp<
+            IObbActionListener>& token)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(filename);
+        data.writeString16(key);
+        data.writeStrongBinder(token->asBinder());
+        if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
+            LOGD("mountObb could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("mountObb caught exception %d\n", err);
+            return;
+        }
+    }
+
+    void unmountObb(const String16& filename, const bool force)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(filename);
+        data.writeInt32(force ? 1 : 0);
+        if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) {
+            LOGD("unmountObb could not contact remote\n");
+            return;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("unmountObb caught exception %d\n", err);
+            return;
+        }
+    }
+
+    bool isObbMounted(const String16& filename)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(filename);
+        if (remote()->transact(TRANSACTION_isObbMounted, data, &reply) != NO_ERROR) {
+            LOGD("isObbMounted could not contact remote\n");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("isObbMounted caught exception %d\n", err);
+            return false;
+        }
+        return reply.readInt32() != 0;
+    }
+
+    bool getMountedObbPath(const String16& filename, String16& path)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+        data.writeString16(filename);
+        if (remote()->transact(TRANSACTION_getMountedObbPath, data, &reply) != NO_ERROR) {
+            LOGD("getMountedObbPath could not contact remote\n");
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        if (err < 0) {
+            LOGD("getMountedObbPath caught exception %d\n", err);
+            return false;
+        }
+        path = reply.readString16();
+        return true;
+    }
+};
+
+IMPLEMENT_META_INTERFACE(MountService, "IMountService");
+
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IMountServiceListener.cpp b/libs/storage/IMountServiceListener.cpp
new file mode 100644
index 0000000..c98a424
--- /dev/null
+++ b/libs/storage/IMountServiceListener.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <storage/IMountServiceListener.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+    TRANSACTION_onUsbMassStorageConnectionChanged = IBinder::FIRST_CALL_TRANSACTION,
+    TRANSACTION_onStorageStateChanged,
+};
+
+status_t BnMountServiceListener::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case TRANSACTION_onUsbMassStorageConnectionChanged: {
+            CHECK_INTERFACE(IMountServiceListener, data, reply);
+            bool connected = (data.readInt32() != 0);
+            onUsbMassStorageConnectionChanged(connected);
+            reply->writeNoException();
+            return NO_ERROR;
+        } break;
+        case TRANSACTION_onStorageStateChanged: {
+            CHECK_INTERFACE(IMountServiceListener, data, reply);
+            String16 path = data.readString16();
+            String16 oldState = data.readString16();
+            String16 newState = data.readString16();
+            onStorageStateChanged(path, oldState, newState);
+            reply->writeNoException();
+            return NO_ERROR;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IMountShutdownObserver.cpp b/libs/storage/IMountShutdownObserver.cpp
new file mode 100644
index 0000000..1a6fdee
--- /dev/null
+++ b/libs/storage/IMountShutdownObserver.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <storage/IMountShutdownObserver.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+    TRANSACTION_onShutDownComplete = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+status_t BnMountShutdownObserver::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case TRANSACTION_onShutDownComplete: {
+            CHECK_INTERFACE(IMountShutdownObserver, data, reply);
+            int32_t statusCode = data.readInt32();
+            onShutDownComplete(statusCode);
+            reply->writeNoException();
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp
new file mode 100644
index 0000000..5bfece7
--- /dev/null
+++ b/libs/storage/IObbActionListener.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <storage/IObbActionListener.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+    TRANSACTION_onObbResult = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+// This is a stub that real consumers should override.
+class BpObbActionListener: public BpInterface<IObbActionListener> {
+public:
+    BpObbActionListener(const sp<IBinder>& impl)
+        : BpInterface<IObbActionListener>(impl)
+    { }
+
+    virtual void onObbResult(const String16& filename, const String16& status) { }
+};
+
+IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener");
+
+// ----------------------------------------------------------------------
+
+status_t BnObbActionListener::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case TRANSACTION_onObbResult: {
+            CHECK_INTERFACE(IObbActionListener, data, reply);
+            String16 filename = data.readString16();
+            String16 state = data.readString16();
+            onObbResult(filename, state);
+            reply->writeNoException();
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/MODULE_LICENSE_APACHE2 b/libs/storage/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/storage/MODULE_LICENSE_APACHE2
diff --git a/libs/storage/NOTICE b/libs/storage/NOTICE
new file mode 100644
index 0000000..5d14293
--- /dev/null
+++ b/libs/storage/NOTICE
@@ -0,0 +1,190 @@
+
+   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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 124f7b3..891661d 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -60,7 +60,6 @@
 #define ID_MASK  0x0000ffff
 #define SEQ_MASK 0x7fff0000
 #define SEQ_SHIFT 16
-#define id_to_index(id)         ((id&ID_MASK)+1)
 
 #ifndef ABS_MT_TOUCH_MAJOR
 #define ABS_MT_TOUCH_MAJOR      0x30    /* Major axis of touching ellipse */
@@ -87,7 +86,7 @@
 
 EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
     : id(_id), path(_path), name(name), classes(0)
-    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
+    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
 }
 
 EventHub::device_t::~device_t() {
@@ -100,6 +99,7 @@
     , mDevicesById(0), mNumDevicesById(0)
     , mOpeningDevices(0), mClosingDevices(0)
     , mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
+    , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0)
 {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 #ifdef EV_SW
@@ -151,9 +151,9 @@
 
     struct input_absinfo info;
 
-    if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
+    if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
         LOGW("Error reading absolute controller %d for device %s fd %d\n",
-             axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
+             axis, device->name.string(), device->fd);
         return -errno;
     }
 
@@ -182,7 +182,7 @@
 int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
     memset(key_bitmask, 0, sizeof(key_bitmask));
-    if (ioctl(mFDs[id_to_index(device->id)].fd,
+    if (ioctl(device->fd,
                EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
         return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
     }
@@ -205,8 +205,7 @@
 
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
     memset(key_bitmask, 0, sizeof(key_bitmask));
-    if (ioctl(mFDs[id_to_index(device->id)].fd,
-               EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+    if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
         #if 0
         for (size_t i=0; i<=KEY_MAX; i++) {
             LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask));
@@ -242,7 +241,7 @@
 int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
     uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
     memset(sw_bitmask, 0, sizeof(sw_bitmask));
-    if (ioctl(mFDs[id_to_index(device->id)].fd,
+    if (ioctl(device->fd,
                EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
         return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
     }
@@ -343,13 +342,6 @@
     outEvent->value = 0;
     outEvent->when = 0;
 
-    status_t err;
-
-    int i;
-    int res;
-    int pollres;
-    struct input_event iev;
-
     // Note that we only allow one caller to getEvent(), so don't need
     // to do locking here...  only when adding/removing devices.
 
@@ -358,9 +350,8 @@
         mOpened = true;
     }
 
-    while(1) {
-
-        // First, report any devices that had last been added/removed.
+    for (;;) {
+        // Report any devices that had last been added/removed.
         if (mClosingDevices != NULL) {
             device_t* device = mClosingDevices;
             LOGV("Reporting device closed: id=0x%x, name=%s\n",
@@ -390,77 +381,96 @@
             return true;
         }
 
-        release_wake_lock(WAKE_LOCK_ID);
+        // Grab the next input event.
+        for (;;) {
+            // Consume buffered input events, if any.
+            if (mInputBufferIndex < mInputBufferCount) {
+                const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
+                const device_t* device = mDevices[mInputDeviceIndex];
 
-        pollres = poll(mFDs, mFDCount, -1);
-
-        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
-
-        if (pollres <= 0) {
-            if (errno != EINTR) {
-                LOGW("select failed (errno=%d)\n", errno);
-                usleep(100000);
-            }
-            continue;
-        }
-
-        //printf("poll %d, returned %d\n", mFDCount, pollres);
-
-        // mFDs[0] is used for inotify, so process regular events starting at mFDs[1]
-        for(i = 1; i < mFDCount; i++) {
-            if(mFDs[i].revents) {
-                LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
-                if(mFDs[i].revents & POLLIN) {
-                    res = read(mFDs[i].fd, &iev, sizeof(iev));
-                    if (res == sizeof(iev)) {
-                        device_t* device = mDevices[i];
-                        LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
-                             device->path.string(),
-                             (int) iev.time.tv_sec, (int) iev.time.tv_usec,
-                             iev.type, iev.code, iev.value);
-                        if (device->id == mFirstKeyboardId) {
-                            outEvent->deviceId = 0;
-                        } else {
-                            outEvent->deviceId = device->id;
-                        }
-                        outEvent->type = iev.type;
-                        outEvent->scanCode = iev.code;
-                        if (iev.type == EV_KEY) {
-                            err = device->layoutMap->map(iev.code,
-                                    & outEvent->keyCode, & outEvent->flags);
-                            LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
-                                iev.code, outEvent->keyCode, outEvent->flags, err);
-                            if (err != 0) {
-                                outEvent->keyCode = AKEYCODE_UNKNOWN;
-                                outEvent->flags = 0;
-                            }
-                        } else {
-                            outEvent->keyCode = iev.code;
-                        }
-                        outEvent->value = iev.value;
-
-                        // Use an event timestamp in the same timebase as
-                        // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
-                        // as expected by the rest of the system.
-                        outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
-                        return true;
-                    } else {
-                        if (res<0) {
-                            LOGW("could not get event (errno=%d)", errno);
-                        } else {
-                            LOGE("could not get event (wrong size: %d)", res);
-                        }
-                        continue;
+                LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
+                     (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
+                if (device->id == mFirstKeyboardId) {
+                    outEvent->deviceId = 0;
+                } else {
+                    outEvent->deviceId = device->id;
+                }
+                outEvent->type = iev.type;
+                outEvent->scanCode = iev.code;
+                if (iev.type == EV_KEY) {
+                    status_t err = device->layoutMap->map(iev.code,
+                            & outEvent->keyCode, & outEvent->flags);
+                    LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+                        iev.code, outEvent->keyCode, outEvent->flags, err);
+                    if (err != 0) {
+                        outEvent->keyCode = AKEYCODE_UNKNOWN;
+                        outEvent->flags = 0;
                     }
+                } else {
+                    outEvent->keyCode = iev.code;
+                }
+                outEvent->value = iev.value;
+
+                // Use an event timestamp in the same timebase as
+                // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
+                // as expected by the rest of the system.
+                outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+                return true;
+            }
+
+            // Finish reading all events from devices identified in previous poll().
+            // This code assumes that mInputDeviceIndex is initially 0 and that the
+            // revents member of pollfd is initialized to 0 when the device is first added.
+            // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
+            mInputDeviceIndex += 1;
+            if (mInputDeviceIndex >= mFDCount) {
+                mInputDeviceIndex = 0;
+                break;
+            }
+
+            const struct pollfd &pfd = mFDs[mInputDeviceIndex];
+            if (pfd.revents & POLLIN) {
+                int32_t readSize = read(pfd.fd, mInputBufferData,
+                        sizeof(struct input_event) * INPUT_BUFFER_SIZE);
+                if (readSize < 0) {
+                    if (errno != EAGAIN && errno != EINTR) {
+                        LOGW("could not get event (errno=%d)", errno);
+                    }
+                } else if ((readSize % sizeof(struct input_event)) != 0) {
+                    LOGE("could not get event (wrong size: %d)", readSize);
+                } else {
+                    mInputBufferCount = readSize / sizeof(struct input_event);
+                    mInputBufferIndex = 0;
                 }
             }
         }
-        
+
         // read_notify() will modify mFDs and mFDCount, so this must be done after
         // processing all other events.
         if(mFDs[0].revents & POLLIN) {
             read_notify(mFDs[0].fd);
         }
+
+        // Poll for events.  Mind the wake lock dance!
+        // We hold a wake lock at all times except during poll().  This works due to some
+        // subtle choreography.  When a device driver has pending (unread) events, it acquires
+        // a kernel wake lock.  However, once the last pending event has been read, the device
+        // driver will release the kernel wake lock.  To prevent the system from going to sleep
+        // when this happens, the EventHub holds onto its own user wake lock while the client
+        // is processing events.  Thus the system can only sleep if there are no events
+        // pending or currently being processed.
+        release_wake_lock(WAKE_LOCK_ID);
+
+        int pollResult = poll(mFDs, mFDCount, -1);
+
+        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+
+        if (pollResult <= 0) {
+            if (errno != EINTR) {
+                LOGW("select failed (errno=%d)\n", errno);
+                usleep(100000);
+            }
+        }
     }
 }
 
@@ -478,6 +488,7 @@
     mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
     mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
     mFDs[0].events = POLLIN;
+    mFDs[0].revents = 0;
     mDevices[0] = NULL;
 #ifdef HAVE_INOTIFY
     mFDs[0].fd = inotify_init();
@@ -570,7 +581,6 @@
         if (strcmp(name, test) == 0) {
             LOGI("ignoring event id %s driver %s\n", deviceName, test);
             close(fd);
-            fd = -1;
             return -1;
         }
     }
@@ -584,6 +594,12 @@
         idstr[0] = '\0';
     }
 
+    if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
+        LOGE("Error %d making device file descriptor non-blocking.", errno);
+        close(fd);
+        return -1;
+    }
+
     int devid = 0;
     while (devid < mNumDevicesById) {
         if (mDevicesById[devid].device == NULL) {
@@ -638,8 +654,10 @@
         return -1;
     }
 
+    device->fd = fd;
     mFDs[mFDCount].fd = fd;
     mFDs[mFDCount].events = POLLIN;
+    mFDs[mFDCount].revents = 0;
 
     // Figure out the kinds of events the device reports.
     
@@ -794,6 +812,14 @@
                 device->id, name, propName, keylayoutFilename);
     }
 
+    // If the device isn't recognized as something we handle, don't monitor it.
+    if (device->classes == 0) {
+        LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid);
+        close(fd);
+        delete device;
+        return -1;
+    }
+
     LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
          deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
          
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index b53f140..e35050c 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -28,6 +28,9 @@
 // Log debug messages about input event injection.
 #define DEBUG_INJECTION 0
 
+// Log debug messages about input event throttling.
+#define DEBUG_THROTTLING 0
+
 #include <cutils/log.h>
 #include <ui/InputDispatcher.h>
 
@@ -66,6 +69,15 @@
 
     mKeyRepeatState.lastKeyEntry = NULL;
 
+    int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
+    mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
+    mThrottleState.lastDeviceId = -1;
+
+#if DEBUG_THROTTLING
+    mThrottleState.originalSampleCount = 0;
+    LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
+#endif
+
     mCurrentInputTargetsValid = false;
 }
 
@@ -144,12 +156,61 @@
                 }
             } else {
                 // Inbound queue has at least one entry.
-                // Start processing it but leave it on the queue until later so that the
+                EventEntry* entry = mInboundQueue.head.next;
+
+                // Consider throttling the entry if it is a move event and there are no
+                // other events behind it in the queue.  Due to movement batching, additional
+                // samples may be appended to this event by the time the throttling timeout
+                // expires.
+                // TODO Make this smarter and consider throttling per device independently.
+                if (entry->type == EventEntry::TYPE_MOTION) {
+                    MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+                    int32_t deviceId = motionEntry->deviceId;
+                    uint32_t source = motionEntry->source;
+                    if (motionEntry->next == & mInboundQueue.tail
+                            && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+                            && deviceId == mThrottleState.lastDeviceId
+                            && source == mThrottleState.lastSource) {
+                        nsecs_t nextTime = mThrottleState.lastEventTime
+                                + mThrottleState.minTimeBetweenEvents;
+                        if (currentTime < nextTime) {
+                            // Throttle it!
+#if DEBUG_THROTTLING
+                            LOGD("Throttling - Delaying motion event for "
+                                    "device 0x%x, source 0x%08x by up to %0.3fms.",
+                                    deviceId, source, (nextTime - currentTime) * 0.000001);
+#endif
+                            if (nextTime < nextWakeupTime) {
+                                nextWakeupTime = nextTime;
+                            }
+                            if (mThrottleState.originalSampleCount == 0) {
+                                mThrottleState.originalSampleCount =
+                                        motionEntry->countSamples();
+                            }
+                            goto Throttle;
+                        }
+                    }
+
+#if DEBUG_THROTTLING
+                    if (mThrottleState.originalSampleCount != 0) {
+                        uint32_t count = motionEntry->countSamples();
+                        LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+                                count - mThrottleState.originalSampleCount,
+                                mThrottleState.originalSampleCount, count);
+                        mThrottleState.originalSampleCount = 0;
+                    }
+#endif
+
+                    mThrottleState.lastEventTime = entry->eventTime < currentTime
+                            ? entry->eventTime : currentTime;
+                    mThrottleState.lastDeviceId = deviceId;
+                    mThrottleState.lastSource = source;
+                }
+
+                // Start processing the entry but leave it on the queue until later so that the
                 // input reader can keep appending samples onto a motion event between the
                 // time we started processing it and the time we finally enqueue dispatch
                 // entries for it.
-                EventEntry* entry = mInboundQueue.head.next;
-
                 switch (entry->type) {
                 case EventEntry::TYPE_CONFIGURATION_CHANGED: {
                     ConfigurationChangedEntry* typedEntry =
@@ -179,6 +240,8 @@
                 mInboundQueue.dequeue(entry);
                 mAllocator.releaseEventEntry(entry);
                 skipPoll = true;
+
+            Throttle: ;
             }
         }
 
@@ -192,8 +255,8 @@
         return;
     }
 
-    // Wait for callback or timeout or wake.
-    nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime);
+    // Wait for callback or timeout or wake.  (make sure we round up, not down)
+    nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
     int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
     mPollLoop->pollOnce(timeoutMillis);
 }
@@ -433,8 +496,7 @@
     for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
         const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
 
-        ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(
-                inputTarget.inputChannel->getReceivePipeFd());
+        ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
         if (connectionIndex >= 0) {
             sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
             prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
@@ -1367,12 +1429,10 @@
     LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
 #endif
 
-    int receiveFd;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        receiveFd = inputChannel->getReceivePipeFd();
-        if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) {
+        if (getConnectionIndex(inputChannel) >= 0) {
             LOGW("Attempted to register already registered input channel '%s'",
                     inputChannel->getName().string());
             return BAD_VALUE;
@@ -1386,12 +1446,13 @@
             return status;
         }
 
+        int32_t receiveFd = inputChannel->getReceivePipeFd();
         mConnectionsByReceiveFd.add(receiveFd, connection);
 
+        mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
+
         runCommandsLockedInterruptible();
     } // release lock
-
-    mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
     return OK;
 }
 
@@ -1400,12 +1461,10 @@
     LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
 #endif
 
-    int32_t receiveFd;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        receiveFd = inputChannel->getReceivePipeFd();
-        ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+        ssize_t connectionIndex = getConnectionIndex(inputChannel);
         if (connectionIndex < 0) {
             LOGW("Attempted to unregister already unregistered input channel '%s'",
                     inputChannel->getName().string());
@@ -1417,20 +1476,32 @@
 
         connection->status = Connection::STATUS_ZOMBIE;
 
+        mPollLoop->removeCallback(inputChannel->getReceivePipeFd());
+
         nsecs_t currentTime = now();
         abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
 
         runCommandsLockedInterruptible();
     } // release lock
 
-    mPollLoop->removeCallback(receiveFd);
-
     // Wake the poll loop because removing the connection may have changed the current
     // synchronization state.
     mPollLoop->wake();
     return OK;
 }
 
+ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+    ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+    if (connectionIndex >= 0) {
+        sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+        if (connection->inputChannel.get() == inputChannel.get()) {
+            return connectionIndex;
+        }
+    }
+
+    return -1;
+}
+
 void InputDispatcher::activateConnectionLocked(Connection* connection) {
     for (size_t i = 0; i < mActiveConnections.size(); i++) {
         if (mActiveConnections.itemAt(i) == connection) {
@@ -1700,6 +1771,16 @@
     motionEntry->lastSample = sample;
 }
 
+// --- InputDispatcher::MotionEntry ---
+
+uint32_t InputDispatcher::MotionEntry::countSamples() const {
+    uint32_t count = 1;
+    for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
+        count += 1;
+    }
+    return count;
+}
+
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 5f5a4ac..6f042ec 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -945,7 +945,6 @@
         mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
         mAccumulator.btnMouse = false;
         sync(when);
-        mAccumulator.clear();
     }
 
     InputMapper::reset();
@@ -958,9 +957,9 @@
         case BTN_MOUSE:
             mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
             mAccumulator.btnMouse = rawEvent->value != 0;
-
+            // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
+            // we need to ensure that we report the up/down promptly.
             sync(rawEvent->when);
-            mAccumulator.clear();
             break;
         }
         break;
@@ -981,10 +980,7 @@
     case EV_SYN:
         switch (rawEvent->scanCode) {
         case SYN_REPORT:
-            if (mAccumulator.isDirty()) {
-                sync(rawEvent->when);
-                mAccumulator.clear();
-            }
+            sync(rawEvent->when);
             break;
         }
         break;
@@ -992,13 +988,17 @@
 }
 
 void TrackballInputMapper::sync(nsecs_t when) {
+    uint32_t fields = mAccumulator.fields;
+    if (fields == 0) {
+        return; // no new state changes, so nothing to do
+    }
+
     int motionEventAction;
     PointerCoords pointerCoords;
     nsecs_t downTime;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        uint32_t fields = mAccumulator.fields;
         bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
 
         if (downChanged) {
@@ -1061,6 +1061,8 @@
     } // release lock
 
     applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime);
+
+    mAccumulator.clear();
 }
 
 void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction,
@@ -2380,8 +2382,8 @@
     mDown = false;
     mX = 0;
     mY = 0;
-    mPressure = 0;
-    mSize = 0;
+    mPressure = 1; // default to 1 for devices that don't report pressure
+    mSize = 0; // default to 0 for devices that don't report size
 }
 
 void SingleTouchInputMapper::reset() {
@@ -2397,9 +2399,9 @@
         case BTN_TOUCH:
             mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
             mAccumulator.btnTouch = rawEvent->value != 0;
-
-            sync(rawEvent->when);
-            mAccumulator.clear();
+            // Don't sync immediately.  Wait until the next SYN_REPORT since we might
+            // not have received valid position information yet.  This logic assumes that
+            // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
             break;
         }
         break;
@@ -2428,10 +2430,7 @@
     case EV_SYN:
         switch (rawEvent->scanCode) {
         case SYN_REPORT:
-            if (mAccumulator.isDirty()) {
-                sync(rawEvent->when);
-                mAccumulator.clear();
-            }
+            sync(rawEvent->when);
             break;
         }
         break;
@@ -2439,9 +2438,10 @@
 }
 
 void SingleTouchInputMapper::sync(nsecs_t when) {
-    /* Update device state */
-
     uint32_t fields = mAccumulator.fields;
+    if (fields == 0) {
+        return; // no new state changes, so nothing to do
+    }
 
     if (fields & Accumulator::FIELD_BTN_TOUCH) {
         mDown = mAccumulator.btnTouch;
@@ -2472,8 +2472,8 @@
         mCurrentTouch.pointers[0].y = mY;
         mCurrentTouch.pointers[0].pressure = mPressure;
         mCurrentTouch.pointers[0].size = mSize;
-        mCurrentTouch.pointers[0].touchMajor = mPressure;
-        mCurrentTouch.pointers[0].touchMinor = mPressure;
+        mCurrentTouch.pointers[0].touchMajor = mSize;
+        mCurrentTouch.pointers[0].touchMinor = mSize;
         mCurrentTouch.pointers[0].toolMajor = mSize;
         mCurrentTouch.pointers[0].toolMinor = mSize;
         mCurrentTouch.pointers[0].orientation = 0;
@@ -2482,6 +2482,8 @@
     }
 
     syncTouch(when, true);
+
+    mAccumulator.clear();
 }
 
 void SingleTouchInputMapper::configureAxes() {
@@ -2494,8 +2496,8 @@
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure);
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size);
 
-    mAxes.touchMajor = mAxes.pressure;
-    mAxes.touchMinor = mAxes.pressure;
+    mAxes.touchMajor = mAxes.size;
+    mAxes.touchMinor = mAxes.size;
     mAxes.toolMajor = mAxes.size;
     mAxes.toolMinor = mAxes.size;
 }
@@ -2585,10 +2587,7 @@
         }
 
         case SYN_REPORT:
-            if (mAccumulator.isDirty()) {
-                sync(rawEvent->when);
-                mAccumulator.clear();
-            }
+            sync(rawEvent->when);
             break;
         }
         break;
@@ -2598,11 +2597,7 @@
 void MultiTouchInputMapper::sync(nsecs_t when) {
     static const uint32_t REQUIRED_FIELDS =
             Accumulator::FIELD_ABS_MT_POSITION_X
-            | Accumulator::FIELD_ABS_MT_POSITION_Y
-            | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
-            | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
-
-    /* Update device state */
+            | Accumulator::FIELD_ABS_MT_POSITION_Y;
 
     uint32_t inCount = mAccumulator.pointerCount;
     uint32_t outCount = 0;
@@ -2611,53 +2606,76 @@
     mCurrentTouch.clear();
 
     for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
-        uint32_t fields = mAccumulator.pointers[inIndex].fields;
+        const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex];
+        uint32_t fields = inPointer.fields;
 
         if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
-#if DEBUG_POINTERS
-            LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
-                    inIndex, fields);
-            continue;
-#endif
-        }
-
-        if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) {
-            // Pointer is not down.  Drop it.
+            // Some drivers send empty MT sync packets without X / Y to indicate a pointer up.
+            // Drop this finger.
             continue;
         }
 
-        mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX;
-        mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY;
+        PointerData& outPointer = mCurrentTouch.pointers[outCount];
+        outPointer.x = inPointer.absMTPositionX;
+        outPointer.y = inPointer.absMTPositionY;
 
-        mCurrentTouch.pointers[outCount].touchMajor =
-                mAccumulator.pointers[inIndex].absMTTouchMajor;
-        mCurrentTouch.pointers[outCount].touchMinor =
-                (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
-                ? mAccumulator.pointers[inIndex].absMTTouchMinor
-                        : mAccumulator.pointers[inIndex].absMTTouchMajor;
+        if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) {
+            int32_t value = inPointer.absMTTouchMajor;
+            if (value <= 0) {
+                // Some devices send sync packets with X / Y but with a 0 touch major to indicate
+                // a pointer up.  Drop this finger.
+                continue;
+            }
+            outPointer.touchMajor = inPointer.absMTTouchMajor;
+        } else {
+            outPointer.touchMajor = 0;
+        }
 
-        mCurrentTouch.pointers[outCount].toolMajor =
-                mAccumulator.pointers[inIndex].absMTWidthMajor;
-        mCurrentTouch.pointers[outCount].toolMinor =
-                (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
-                ? mAccumulator.pointers[inIndex].absMTWidthMinor
-                        : mAccumulator.pointers[inIndex].absMTWidthMajor;
+        if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) {
+            outPointer.touchMinor = inPointer.absMTTouchMinor;
+        } else {
+            outPointer.touchMinor = outPointer.touchMajor;
+        }
 
-        mCurrentTouch.pointers[outCount].orientation =
-                (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
-                ? mAccumulator.pointers[inIndex].absMTOrientation : 0;
+        if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) {
+            outPointer.toolMajor = inPointer.absMTWidthMajor;
+        } else {
+            outPointer.toolMajor = outPointer.touchMajor;
+        }
 
-        // Derive an approximation of pressure and size.
-        // FIXME assignment of pressure may be incorrect, probably better to let
-        // pressure = touch / width.  Later on we pass width to MotionEvent as a size, which
-        // isn't quite right either.  Should be using touch for that.
-        mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor;
-        mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor;
+        if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) {
+            outPointer.toolMinor = inPointer.absMTWidthMinor;
+        } else {
+            outPointer.toolMinor = outPointer.toolMajor;
+        }
+
+        if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) {
+            outPointer.orientation = inPointer.absMTOrientation;
+        } else {
+            outPointer.orientation = 0;
+        }
+
+        if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
+            outPointer.pressure = inPointer.absMTPressure;
+        } else {
+            // Derive an approximation of pressure.
+            // FIXME Traditionally we have just passed a normalized value based on
+            //       ABS_MT_TOUCH_MAJOR as an estimate of pressure but the result is not
+            //       very meaningful, particularly on large displays.  We should probably let
+            //       pressure = touch_major / tool_major but it is unclear whether that will
+            //       break applications.
+            outPointer.pressure = outPointer.touchMajor;
+        }
+
+        // Size is an alias for a normalized tool width.
+        // FIXME Normalized tool width doesn't actually make much sense since it literally
+        //       means the approaching contact major axis is divided by its full range as
+        //       reported by the driver.  On a large display this could produce very small values.
+        outPointer.size = outPointer.toolMajor;
 
         if (havePointerIds) {
-            if (fields & Accumulator::
-                    FIELD_ABS_MT_TRACKING_ID) {
-                uint32_t id = uint32_t(mAccumulator.pointers[inIndex].absMTTrackingId);
+            if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+                uint32_t id = uint32_t(inPointer.absMTTrackingId);
 
                 if (id > MAX_POINTER_ID) {
 #if DEBUG_POINTERS
@@ -2668,7 +2686,7 @@
                     havePointerIds = false;
                 }
                 else {
-                    mCurrentTouch.pointers[outCount].id = id;
+                    outPointer.id = id;
                     mCurrentTouch.idToIndex[id] = outCount;
                     mCurrentTouch.idBits.markBit(id);
                 }
@@ -2683,6 +2701,8 @@
     mCurrentTouch.pointerCount = outCount;
 
     syncTouch(when, havePointerIds);
+
+    mAccumulator.clear();
 }
 
 void MultiTouchInputMapper::configureAxes() {
@@ -2697,6 +2717,7 @@
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor);
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor);
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mAxes.pressure);
 
     if (! mAxes.touchMinor.valid) {
         mAxes.touchMinor = mAxes.touchMajor;
@@ -2706,7 +2727,10 @@
         mAxes.toolMinor = mAxes.toolMajor;
     }
 
-    mAxes.pressure = mAxes.touchMajor;
+    if (! mAxes.pressure.valid) {
+        mAxes.pressure = mAxes.touchMajor;
+    }
+
     mAxes.size = mAxes.toolMajor;
 }
 
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index edf1aed..ee186c8 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -63,7 +63,6 @@
         info->bitsPerPixel = 16;
         goto done;
     case HAL_PIXEL_FORMAT_YCrCb_420_SP:
-    case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
     case HAL_PIXEL_FORMAT_YV12:
         info->bitsPerPixel = 12;
      done:
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
index adedf0c..e170ab8 100644
--- a/libs/utils/ObbFile.cpp
+++ b/libs/utils/ObbFile.cpp
@@ -29,12 +29,13 @@
 
 #define kFooterTagSize 8  /* last two 32-bit integers */
 
-#define kFooterMinSize 21 /* 32-bit signature version
-                           * 32-bit package version
-                           * 32-bit package name size
-                           * 1-character package name
-                           * 32-bit footer size
-                           * 32-bit footer marker
+#define kFooterMinSize 25 /* 32-bit signature version (4 bytes)
+                           * 32-bit package version (4 bytes)
+                           * 32-bit flags (4 bytes)
+                           * 32-bit package name size (4-bytes)
+                           * >=1-character package name (1 byte)
+                           * 32-bit footer size (4 bytes)
+                           * 32-bit footer marker (4 bytes)
                            */
 
 #define kMaxBufSize    32768 /* Maximum file read buffer */
@@ -45,8 +46,9 @@
 
 /* offsets in version 1 of the header */
 #define kPackageVersionOffset 4
-#define kPackageNameLenOffset 8
-#define kPackageNameOffset    12
+#define kFlagsOffset          8
+#define kPackageNameLenOffset 12
+#define kPackageNameOffset    16
 
 /*
  * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
@@ -78,7 +80,10 @@
 namespace android {
 
 ObbFile::ObbFile() :
-        mVersion(-1) {
+        mPackageName(""),
+        mVersion(-1),
+        mFlags(0)
+{
 }
 
 ObbFile::~ObbFile() {
@@ -199,6 +204,7 @@
     }
 
     mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
+    mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset);
 
     uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
     if (packageNameLen <= 0
@@ -268,6 +274,12 @@
         return false;
     }
 
+    put4LE(intBuf, mFlags);
+    if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+        LOGW("couldn't write package version");
+        return false;
+    }
+
     size_t packageNameLen = mPackageName.size();
     put4LE(intBuf, packageNameLen);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
@@ -280,7 +292,7 @@
         return false;
     }
 
-    put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen);
+    put4LE(intBuf, kPackageNameOffset + packageNameLen);
     if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
         LOGW("couldn't write footer size: %s", strerror(errno));
         return false;
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index a4c3500..604f558 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -636,7 +636,7 @@
         memcpy(buffer, ptr, uncompLen);
     } else {
         if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     if (compLen > kSequentialMin)
@@ -644,6 +644,8 @@
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
@@ -667,7 +669,7 @@
 
     getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
 
-    const FileMap* file = createEntryFileMap(entry);
+    FileMap* file = createEntryFileMap(entry);
     if (file == NULL) {
         goto bail;
     }
@@ -678,21 +680,23 @@
         ssize_t actual = write(fd, ptr, uncompLen);
         if (actual < 0) {
             LOGE("Write failed: %s\n", strerror(errno));
-            goto bail;
+            goto unmap;
         } else if ((size_t) actual != uncompLen) {
             LOGE("Partial write during uncompress (%zd of %zd)\n",
                 (size_t)actual, (size_t)uncompLen);
-            goto bail;
+            goto unmap;
         } else {
             LOGI("+++ successful write\n");
         }
     } else {
         if (!inflateBuffer(fd, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/BassBoost.java
index 73c1751..476b056 100644
--- a/media/java/android/media/BassBoost.java
+++ b/media/java/android/media/BassBoost.java
@@ -99,7 +99,7 @@
            UnsupportedOperationException, RuntimeException {
         super(EFFECT_TYPE_BASS_BOOST, EFFECT_TYPE_NULL, priority, audioSession);
 
-        short[] value = new short[1];
+        int[] value = new int[1];
         checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
         mStrengthSupported = (value[0] != 0);
     }
diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/Virtualizer.java
index d03c2a8..b08f36e 100644
--- a/media/java/android/media/Virtualizer.java
+++ b/media/java/android/media/Virtualizer.java
@@ -100,7 +100,7 @@
            UnsupportedOperationException, RuntimeException {
         super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
 
-        short[] value = new short[1];
+        int[] value = new int[1];
         checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
         mStrengthSupported = (value[0] != 0);
     }
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index a70bdff..bcd646a 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -1626,9 +1626,15 @@
 
     switch (param){
         case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
+            if (*pValueSize != sizeof(uint32_t)){
+                LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize %d", *pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(uint32_t);
+            break;
         case BASSBOOST_PARAM_STRENGTH:
             if (*pValueSize != sizeof(int16_t)){
-                LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize2 %d", *pValueSize);
+                LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize %d", *pValueSize);
                 return -EINVAL;
             }
             *pValueSize = sizeof(int16_t);
@@ -1736,9 +1742,16 @@
 
     switch (param){
         case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
+            if (*pValueSize != sizeof(uint32_t)){
+                LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
+                return -EINVAL;
+            }
+            *pValueSize = sizeof(uint32_t);
+            break;
+
         case VIRTUALIZER_PARAM_STRENGTH:
             if (*pValueSize != sizeof(int16_t)){
-                LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize2 %d",*pValueSize);
+                LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
                 return -EINVAL;
             }
             *pValueSize = sizeof(int16_t);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 8481d493..796731b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -341,10 +341,14 @@
 
 status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) {
     LOGV("setParamMaxFileDurationUs: %lld us", timeUs);
-    if (timeUs <= 100000LL) {  // XXX: 100 milli-seconds
+    if (timeUs <= 0) {
+        LOGW("Max file duration is not positive: %lld us. Disabling duration limit.", timeUs);
+        timeUs = 0; // Disable the duration limit for zero or negative values.
+    } else if (timeUs <= 100000LL) {  // XXX: 100 milli-seconds
         LOGE("Max file duration is too short: %lld us", timeUs);
         return BAD_VALUE;
     }
+
     mMaxFileDurationUs = timeUs;
     return OK;
 }
@@ -675,7 +679,9 @@
     encMeta->setInt32(kKeyChannelCount, mAudioChannels);
     encMeta->setInt32(kKeySampleRate, mSampleRate);
     encMeta->setInt32(kKeyBitRate, mAudioBitRate);
-    encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+    if (mAudioTimeScale > 0) {
+        encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+    }
 
     OMXClient client;
     CHECK_EQ(client.connect(), OK);
@@ -961,7 +967,9 @@
     enc_meta->setInt32(kKeyStride, stride);
     enc_meta->setInt32(kKeySliceHeight, sliceHeight);
     enc_meta->setInt32(kKeyColorFormat, colorFormat);
-    enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+    if (mVideoTimeScale > 0) {
+        enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+    }
     if (mVideoEncoderProfile != -1) {
         enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
     }
@@ -1041,7 +1049,9 @@
     meta->setInt32(kKeyFileType, mOutputFormat);
     meta->setInt32(kKeyBitRate, totalBitRate);
     meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
-    meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+    if (mMovieTimeScale > 0) {
+        meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+    }
     if (mTrackEveryTimeDurationUs > 0) {
         meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
     }
@@ -1061,8 +1071,9 @@
 
 status_t StagefrightRecorder::stop() {
     LOGV("stop");
+    status_t err = OK;
     if (mWriter != NULL) {
-        mWriter->stop();
+        err = mWriter->stop();
         mWriter.clear();
     }
 
@@ -1084,7 +1095,7 @@
         mOutputFd = -1;
     }
 
-    return OK;
+    return err;
 }
 
 status_t StagefrightRecorder::close() {
@@ -1117,9 +1128,9 @@
     mIFramesIntervalSec = 1;
     mAudioSourceNode = 0;
     mUse64BitFileOffset = false;
-    mMovieTimeScale  = 1000;
-    mAudioTimeScale  = 1000;
-    mVideoTimeScale  = 1000;
+    mMovieTimeScale  = -1;
+    mAudioTimeScale  = -1;
+    mVideoTimeScale  = -1;
     mCameraId        = 0;
     mVideoEncoderProfile = -1;
     mVideoEncoderLevel   = -1;
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index c71743e..71d48b3 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -136,16 +136,17 @@
     return OK;
 }
 
-void AMRWriter::pause() {
+status_t AMRWriter::pause() {
     if (!mStarted) {
-        return;
+        return OK;
     }
     mPaused = true;
+    return OK;
 }
 
-void AMRWriter::stop() {
+status_t AMRWriter::stop() {
     if (!mStarted) {
-        return;
+        return OK;
     }
 
     mDone = true;
@@ -153,9 +154,17 @@
     void *dummy;
     pthread_join(mThread, &dummy);
 
-    mSource->stop();
+    status_t err = (status_t) dummy;
+    {
+        status_t status = mSource->stop();
+        if (err == OK &&
+            (status != OK && status != ERROR_END_OF_STREAM)) {
+            err = status;
+        }
+    }
 
     mStarted = false;
+    return err;
 }
 
 bool AMRWriter::exceedsFileSizeLimit() {
@@ -174,21 +183,20 @@
 
 // static
 void *AMRWriter::ThreadWrapper(void *me) {
-    static_cast<AMRWriter *>(me)->threadFunc();
-
-    return NULL;
+    return (void *) static_cast<AMRWriter *>(me)->threadFunc();
 }
 
-void AMRWriter::threadFunc() {
+status_t AMRWriter::threadFunc() {
     mEstimatedDurationUs = 0;
     mEstimatedSizeBytes = 0;
     bool stoppedPrematurely = true;
     int64_t previousPausedDurationUs = 0;
     int64_t maxTimestampUs = 0;
+    status_t err = OK;
 
     while (!mDone) {
         MediaBuffer *buffer;
-        status_t err = mSource->read(&buffer);
+        err = mSource->read(&buffer);
 
         if (err != OK) {
             break;
@@ -260,6 +268,10 @@
     fclose(mFile);
     mFile = NULL;
     mReachedEOS = true;
+    if (err == ERROR_END_OF_STREAM) {
+        return OK;
+    }
+    return err;
 }
 
 bool AMRWriter::reachedEOS() {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index b8b2f3f..86fa668 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -39,6 +39,7 @@
         TimedEventQueue.cpp               \
         Utils.cpp                         \
         WAVExtractor.cpp                  \
+        avc_utils.cpp                     \
         string.cpp
 
 LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6af3a7f..12a1e6e 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1273,6 +1273,14 @@
     hexdump(csd, csd_size);
 #endif
 
+    if (csd_size == 0) {
+        // There's no further information, i.e. no codec specific data
+        // Let's assume that the information provided in the mpeg4 headers
+        // is accurate and hope for the best.
+
+        return OK;
+    }
+
     if (csd_size < 2) {
         return ERROR_MALFORMED;
     }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 9fe3864..568037e 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -48,8 +48,8 @@
     ~Track();
 
     status_t start(MetaData *params);
-    void stop();
-    void pause();
+    status_t stop();
+    status_t pause();
     bool reachedEOS();
 
     int64_t getDurationUs() const;
@@ -144,7 +144,7 @@
     int64_t mTrackEveryTimeDurationUs;
 
     static void *ThreadWrapper(void *me);
-    void threadEntry();
+    status_t threadEntry();
 
     const uint8_t *parseParamSet(
         const uint8_t *data, size_t length, int type, size_t *paramSetLen);
@@ -168,6 +168,15 @@
 
     void getCodecSpecificDataFromInputFormatIfPossible();
 
+    // Determine the track time scale
+    // If it is an audio track, try to use the sampling rate as
+    // the time scale; however, if user chooses the overwrite
+    // value, the user-supplied time scale will be used.
+    void setTimeScale();
+
+    // Simple validation on the codec specific data
+    status_t checkCodecSpecificData() const;
+
     Track(const Track &);
     Track &operator=(const Track &);
 };
@@ -372,15 +381,20 @@
     return OK;
 }
 
-void MPEG4Writer::pause() {
+status_t MPEG4Writer::pause() {
     if (mFile == NULL) {
-        return;
+        return OK;
     }
     mPaused = true;
+    status_t err = OK;
     for (List<Track *>::iterator it = mTracks.begin();
          it != mTracks.end(); ++it) {
-        (*it)->pause();
+        status_t status = (*it)->pause();
+        if (status != OK) {
+            err = status;
+        }
     }
+    return err;
 }
 
 void MPEG4Writer::stopWriterThread() {
@@ -397,15 +411,19 @@
     pthread_join(mThread, &dummy);
 }
 
-void MPEG4Writer::stop() {
+status_t MPEG4Writer::stop() {
     if (mFile == NULL) {
-        return;
+        return OK;
     }
 
+    status_t err = OK;
     int64_t maxDurationUs = 0;
     for (List<Track *>::iterator it = mTracks.begin();
          it != mTracks.end(); ++it) {
-        (*it)->stop();
+        status_t status = (*it)->stop();
+        if (err == OK && status != OK) {
+            err = status;
+        }
 
         int64_t durationUs = (*it)->getDurationUs();
         if (durationUs > maxDurationUs) {
@@ -415,6 +433,15 @@
 
     stopWriterThread();
 
+    // Do not write out movie header on error.
+    if (err != OK) {
+        fflush(mFile);
+        fclose(mFile);
+        mFile = NULL;
+        mStarted = false;
+        return err;
+    }
+
     // Fix up the size of the 'mdat' chunk.
     if (mUse32BitOffset) {
         fseeko(mFile, mMdatOffset, SEEK_SET);
@@ -434,7 +461,7 @@
     mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
     mMoovBoxBufferOffset = 0;
     CHECK(mMoovBoxBuffer != NULL);
-    int32_t duration = (maxDurationUs * mTimeScale) / 1E6;
+    int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6;
 
     beginBox("moov");
 
@@ -502,6 +529,7 @@
     fclose(mFile);
     mFile = NULL;
     mStarted = false;
+    return err;
 }
 
 status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
@@ -749,10 +777,6 @@
       mReachedEOS(false) {
     getCodecSpecificDataFromInputFormatIfPossible();
 
-    if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
-        mTimeScale = 1000;
-    }
-
     const char *mime;
     mMeta->findCString(kKeyMIMEType, &mime);
     mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -760,6 +784,28 @@
     mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
                !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
 
+    setTimeScale();
+}
+
+void MPEG4Writer::Track::setTimeScale() {
+    LOGV("setTimeScale");
+    // Default time scale
+    mTimeScale = 90000;
+
+    if (mIsAudio) {
+        // Use the sampling rate as the default time scale for audio track.
+        int32_t sampleRate;
+        bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
+        CHECK(success);
+        mTimeScale = sampleRate;
+    }
+
+    // If someone would like to overwrite the timescale, use user-supplied value.
+    int32_t timeScale;
+    if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
+        mTimeScale = timeScale;
+    }
+
     CHECK(mTimeScale > 0);
 }
 
@@ -1006,13 +1052,14 @@
     return OK;
 }
 
-void MPEG4Writer::Track::pause() {
+status_t MPEG4Writer::Track::pause() {
     mPaused = true;
+    return OK;
 }
 
-void MPEG4Writer::Track::stop() {
+status_t MPEG4Writer::Track::stop() {
     if (mDone) {
-        return;
+        return OK;
     }
 
     mDone = true;
@@ -1020,7 +1067,16 @@
     void *dummy;
     pthread_join(mThread, &dummy);
 
-    mSource->stop();
+    status_t err = (status_t) dummy;
+
+    {
+        status_t status = mSource->stop();
+        if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
+            err = status;
+        }
+    }
+
+    return err;
 }
 
 bool MPEG4Writer::Track::reachedEOS() {
@@ -1031,9 +1087,8 @@
 void *MPEG4Writer::Track::ThreadWrapper(void *me) {
     Track *track = static_cast<Track *>(me);
 
-    track->threadEntry();
-
-    return NULL;
+    status_t err = track->threadEntry();
+    return (void *) err;
 }
 
 #include <ctype.h>
@@ -1328,7 +1383,7 @@
     return false;
 }
 
-void MPEG4Writer::Track::threadEntry() {
+status_t MPEG4Writer::Track::threadEntry() {
     int32_t count = 0;
     const int64_t interleaveDurationUs = mOwner->interleaveDuration();
     int64_t chunkTimestampUs = 0;
@@ -1336,6 +1391,8 @@
     int32_t nZeroLengthFrames = 0;
     int64_t lastTimestampUs = 0;  // Previous sample time stamp in ms
     int64_t lastDurationUs = 0;   // Between the previous two samples in ms
+    int64_t currDurationTicks = 0;  // Timescale based ticks
+    int64_t lastDurationTicks = 0;  // Timescale based ticks
     int32_t sampleCount = 1;      // Sample count in the current stts table entry
     uint32_t previousSampleSize = 0;  // Size of the previous sample
     int64_t previousPausedDurationUs = 0;
@@ -1467,10 +1524,24 @@
         CHECK(timestampUs >= 0);
         if (mNumSamples > 1) {
             if (timestampUs <= lastTimestampUs) {
-                LOGW("Drop a frame, since it arrives too late!");
+                LOGW("Frame arrives too late!");
+#if 0
+                // Drop the late frame.
                 copy->release();
                 copy = NULL;
                 continue;
+#else
+                // Don't drop the late frame, since dropping a frame may cause
+                // problems later during playback
+
+                // The idea here is to avoid having two or more samples with the
+                // same timestamp in the output file.
+                if (mTimeScale >= 1000000LL) {
+                    timestampUs += 1;
+                } else {
+                    timestampUs += (1000000LL + (mTimeScale >> 1)) / mTimeScale;
+                }
+#endif
             }
         }
 
@@ -1483,7 +1554,16 @@
         mSampleSizes.push_back(sampleSize);
         ++mNumSamples;
         if (mNumSamples > 2) {
-            if (lastDurationUs != timestampUs - lastTimestampUs) {
+            // We need to use the time scale based ticks, rather than the
+            // timestamp itself to determine whether we have to use a new
+            // stts entry, since we may have rounding errors.
+            // The calculation is intended to reduce the accumulated
+            // rounding errors.
+            currDurationTicks =
+                     ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
+                     (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+
+            if (currDurationTicks != lastDurationTicks) {
                 SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
                 mSttsTableEntries.push_back(sttsEntry);
                 sampleCount = 1;
@@ -1498,6 +1578,7 @@
             previousSampleSize = sampleSize;
         }
         lastDurationUs = timestampUs - lastTimestampUs;
+        lastDurationTicks = currDurationTicks;
         lastTimestampUs = timestampUs;
         if (mIsRealTimeRecording && mIsAudio) {
             wallClockTimeUs = systemTime() / 1000;
@@ -1559,7 +1640,9 @@
     }
 
     if (mSampleSizes.empty()) {
-        err = UNKNOWN_ERROR;
+        err = ERROR_MALFORMED;
+    } else if (OK != checkCodecSpecificData()) {
+        err = ERROR_MALFORMED;
     }
     mOwner->trackProgressStatus(this, -1, err);
 
@@ -1590,6 +1673,10 @@
             count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
 
     logStatisticalData(mIsAudio);
+    if (err == ERROR_END_OF_STREAM) {
+        return OK;
+    }
+    return err;
 }
 
 void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
@@ -1765,6 +1852,27 @@
     return mEstimatedTrackSizeBytes;
 }
 
+status_t MPEG4Writer::Track::checkCodecSpecificData() const {
+    const char *mime;
+    CHECK(mMeta->findCString(kKeyMIMEType, &mime));
+    if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
+        !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
+        !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+        if (!mCodecSpecificData ||
+            mCodecSpecificDataSize <= 0) {
+            // Missing codec specific data
+            return ERROR_MALFORMED;
+        }
+    } else {
+        if (mCodecSpecificData ||
+            mCodecSpecificDataSize > 0) {
+            // Unexepected codec specific data found
+            return ERROR_MALFORMED;
+        }
+    }
+    return OK;
+}
+
 void MPEG4Writer::Track::writeTrackHeader(
         int32_t trackID, bool use32BitOffset) {
     const char *mime;
@@ -1774,7 +1882,6 @@
     LOGV("%s track time scale: %d",
         mIsAudio? "Audio": "Video", mTimeScale);
 
-
     time_t now = time(NULL);
     int32_t mvhdTimeScale = mOwner->getTimeScale();
     int64_t trakDurationUs = getDurationUs();
@@ -1938,7 +2045,6 @@
                   int32_t samplerate;
                   bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
                   CHECK(success);
-
                   mOwner->writeInt32(samplerate << 16);
                   if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
                     mOwner->beginBox("esds");
@@ -2089,10 +2195,18 @@
           mOwner->beginBox("stts");
             mOwner->writeInt32(0);  // version=0, flags=0
             mOwner->writeInt32(mSttsTableEntries.size());
+            int64_t prevTimestampUs = 0;
             for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
                  it != mSttsTableEntries.end(); ++it) {
                 mOwner->writeInt32(it->sampleCount);
-                int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6;
+
+                // Make sure that we are calculating the sample duration the exactly
+                // same way as we made decision on how to create stts entries.
+                int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
+                int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
+                               (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+                prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
+
                 mOwner->writeInt32(dur);
             }
           mOwner->endBox();  // stts
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index b699d8f..9630092 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -158,7 +158,7 @@
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
         off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
-        LOGI("seeking to offset %ld", pos);
+        LOGV("seeking to offset %ld", pos);
 
         if (mExtractor->mImpl->seekToOffset(pos) != OK) {
             return ERROR_END_OF_STREAM;
@@ -267,7 +267,7 @@
     uint8_t header[27];
     if (mSource->readAt(offset, header, sizeof(header))
             < (ssize_t)sizeof(header)) {
-        LOGE("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
+        LOGV("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
 
         return ERROR_IO;
     }
@@ -384,7 +384,7 @@
                     packetSize);
 
             if (n < (ssize_t)packetSize) {
-                LOGE("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
+                LOGV("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
                 return ERROR_IO;
             }
 
@@ -418,7 +418,7 @@
                 buffer = NULL;
             }
 
-            LOGE("readPage returned %ld", n);
+            LOGV("readPage returned %ld", n);
 
             return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
         }
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
new file mode 100644
index 0000000..511ae12
--- /dev/null
+++ b/media/libstagefright/avc_utils.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/avc_utils.h"
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+static unsigned parseUE(ABitReader *br) {
+    unsigned numZeroes = 0;
+    while (br->getBits(1) == 0) {
+        ++numZeroes;
+    }
+
+    unsigned x = br->getBits(numZeroes);
+
+    return x + (1u << numZeroes) - 1;
+}
+
+// Determine video dimensions from the sequence parameterset.
+void FindAVCDimensions(
+        const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height) {
+    ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
+
+    unsigned profile_idc = br.getBits(8);
+    br.skipBits(16);
+    parseUE(&br);  // seq_parameter_set_id
+
+    if (profile_idc == 100 || profile_idc == 110
+            || profile_idc == 122 || profile_idc == 244
+            || profile_idc == 44 || profile_idc == 83 || profile_idc == 86) {
+        unsigned chroma_format_idc = parseUE(&br);
+        if (chroma_format_idc == 3) {
+            br.skipBits(1);  // residual_colour_transform_flag
+        }
+        parseUE(&br);  // bit_depth_luma_minus8
+        parseUE(&br);  // bit_depth_chroma_minus8
+        br.skipBits(1);  // qpprime_y_zero_transform_bypass_flag
+        CHECK_EQ(br.getBits(1), 0u);  // seq_scaling_matrix_present_flag
+    }
+
+    parseUE(&br);  // log2_max_frame_num_minus4
+    unsigned pic_order_cnt_type = parseUE(&br);
+
+    if (pic_order_cnt_type == 0) {
+        parseUE(&br);  // log2_max_pic_order_cnt_lsb_minus4
+    } else if (pic_order_cnt_type == 1) {
+        // offset_for_non_ref_pic, offset_for_top_to_bottom_field and
+        // offset_for_ref_frame are technically se(v), but since we are
+        // just skipping over them the midpoint does not matter.
+
+        br.getBits(1);  // delta_pic_order_always_zero_flag
+        parseUE(&br);  // offset_for_non_ref_pic
+        parseUE(&br);  // offset_for_top_to_bottom_field
+
+        unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
+        for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
+            parseUE(&br);  // offset_for_ref_frame
+        }
+    }
+
+    parseUE(&br);  // num_ref_frames
+    br.getBits(1);  // gaps_in_frame_num_value_allowed_flag
+
+    unsigned pic_width_in_mbs_minus1 = parseUE(&br);
+    unsigned pic_height_in_map_units_minus1 = parseUE(&br);
+    unsigned frame_mbs_only_flag = br.getBits(1);
+
+    *width = pic_width_in_mbs_minus1 * 16 + 16;
+
+    *height = (2 - frame_mbs_only_flag)
+        * (pic_height_in_map_units_minus1 * 16 + 16);
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index 389180c..52a391f 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -338,10 +338,15 @@
 
     MediaBuffer *outputBuffer;
     CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
+    uint8_t *outPtr = (uint8_t *) outputBuffer->data();
+    uint32_t dataLength = outputBuffer->size();
 
-    // Add 4 bytes for the start code 0x00000001
-    uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4;
-    uint32_t dataLength = outputBuffer->size() - 4;
+    if (!mSpsPpsHeaderReceived && mNumInputFrames < 0) {
+        // 4 bytes are reserved for holding the start code 0x00000001
+        // of the sequence parameter set at the beginning.
+        outPtr += 4;
+        dataLength -= 4;
+    }
 
     int32_t type;
     AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
@@ -358,7 +363,7 @@
             switch (type) {
                 case AVC_NALTYPE_SPS:
                     ++mNumInputFrames;
-                    memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4);
+                    memcpy((uint8_t *)outputBuffer->data(), "\x00\x00\x00\x01", 4);
                     outputBuffer->set_range(0, dataLength + 4);
                     outPtr += (dataLength + 4);  // 4 bytes for next start code
                     dataLength = outputBuffer->size() -
diff --git a/media/libstagefright/mpeg2ts/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
similarity index 100%
rename from media/libstagefright/mpeg2ts/ABitReader.cpp
rename to media/libstagefright/foundation/ABitReader.cpp
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index 35eea7e..f6a8a52 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -3,6 +3,7 @@
 
 LOCAL_SRC_FILES:=               \
     AAtomizer.cpp               \
+    ABitReader.cpp              \
     ABuffer.cpp                 \
     ADebug.cpp                  \
     AHandler.cpp                \
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
new file mode 100644
index 0000000..cc405b5
--- /dev/null
+++ b/media/libstagefright/include/avc_utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AVC_UTILS_H_
+
+#define AVC_UTILS_H_
+
+#include <media/stagefright/foundation/ABuffer.h>
+
+namespace android {
+
+void FindAVCDimensions(
+        const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height);
+
+}  // namespace android
+
+#endif  // AVC_UTILS_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index d05975d..26a0fb3 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -16,9 +16,10 @@
 
 #include "ATSParser.h"
 
-#include "ABitReader.h"
 #include "AnotherPacketSource.h"
+#include "include/avc_utils.h"
 
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -473,60 +474,6 @@
     }
 }
 
-static unsigned parseUE(ABitReader *br) {
-    unsigned numZeroes = 0;
-    while (br->getBits(1) == 0) {
-        ++numZeroes;
-    }
-
-    unsigned x = br->getBits(numZeroes);
-
-    return x + (1u << numZeroes) - 1;
-}
-
-// Determine video dimensions from the sequence parameterset.
-static void FindDimensions(
-        const sp<ABuffer> seqParamSet, int32_t *width, int32_t *height) {
-    ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
-
-    unsigned profile_idc = br.getBits(8);
-    br.skipBits(16);
-    parseUE(&br);  // seq_parameter_set_id
-
-    if (profile_idc == 100 || profile_idc == 110
-            || profile_idc == 122 || profile_idc == 144) {
-        TRESPASS();
-    }
-
-    parseUE(&br);  // log2_max_frame_num_minus4
-    unsigned pic_order_cnt_type = parseUE(&br);
-
-    if (pic_order_cnt_type == 0) {
-        parseUE(&br);  // log2_max_pic_order_cnt_lsb_minus4
-    } else if (pic_order_cnt_type == 1) {
-        br.getBits(1);  // delta_pic_order_always_zero_flag
-        parseUE(&br);  // offset_for_non_ref_pic
-        parseUE(&br);  // offset_for_top_to_bottom_field
-
-        unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
-        for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
-            parseUE(&br);  // offset_for_ref_frame
-        }
-    }
-
-    parseUE(&br);  // num_ref_frames
-    br.getBits(1);  // gaps_in_frame_num_value_allowed_flag
-
-    unsigned pic_width_in_mbs_minus1 = parseUE(&br);
-    unsigned pic_height_in_map_units_minus1 = parseUE(&br);
-    unsigned frame_mbs_only_flag = br.getBits(1);
-
-    *width = pic_width_in_mbs_minus1 * 16 + 16;
-
-    *height = (2 - frame_mbs_only_flag)
-        * (pic_height_in_map_units_minus1 * 16 + 16);
-}
-
 static sp<ABuffer> MakeAVCCodecSpecificData(
         const sp<ABuffer> &buffer, int32_t *width, int32_t *height) {
     const uint8_t *data = buffer->data();
@@ -537,7 +484,7 @@
         return NULL;
     }
 
-    FindDimensions(seqParamSet, width, height);
+    FindAVCDimensions(seqParamSet, width, height);
 
     size_t stopOffset;
     sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index b6772eb..3544b4c 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -3,7 +3,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=                 \
-        ABitReader.cpp            \
         AnotherPacketSource.cpp   \
         ATSParser.cpp             \
         MPEG2TSExtractor.cpp      \
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
new file mode 100644
index 0000000..7e633d7
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AMPEG4ElementaryAssembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <stdint.h>
+
+#define BE_VERBOSE      0
+
+namespace android {
+
+// static
+AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(const sp<AMessage> &notify)
+    : mNotifyMsg(notify),
+      mAccessUnitRTPTime(0),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0),
+      mAccessUnitDamaged(false) {
+}
+
+AMPEG4ElementaryAssembler::~AMPEG4ElementaryAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+#if BE_VERBOSE
+        LOG(VERBOSE) << "Not the sequence number I expected";
+#endif
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    uint32_t rtpTime;
+    CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) {
+        submitAccessUnit();
+    }
+    mAccessUnitRTPTime = rtpTime;
+
+    mPackets.push_back(buffer);
+    // hexdump(buffer->data(), buffer->size());
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void AMPEG4ElementaryAssembler::submitAccessUnit() {
+    CHECK(!mPackets.empty());
+
+#if BE_VERBOSE
+    LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
+#endif
+
+    uint64_t ntpTime;
+    CHECK((*mPackets.begin())->meta()->findInt64(
+                "ntp-time", (int64_t *)&ntpTime));
+
+    size_t totalSize = 0;
+    for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+         it != mPackets.end(); ++it) {
+        totalSize += (*it)->size();
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    size_t offset = 0;
+    for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+         it != mPackets.end(); ++it) {
+        sp<ABuffer> nal = *it;
+        memcpy(accessUnit->data() + offset, nal->data(), nal->size());
+        offset += nal->size();
+    }
+
+    accessUnit->meta()->setInt64("ntp-time", ntpTime);
+
+#if 0
+    printf(mAccessUnitDamaged ? "X" : ".");
+    fflush(stdout);
+#endif
+
+    if (mAccessUnitDamaged) {
+        accessUnit->meta()->setInt32("damaged", true);
+    }
+
+    mPackets.clear();
+    mAccessUnitDamaged = false;
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", accessUnit);
+    msg->post();
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    AssemblyStatus status = addPacket(source);
+    if (status == MALFORMED_PACKET) {
+        mAccessUnitDamaged = true;
+    }
+    return status;
+}
+
+void AMPEG4ElementaryAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    LOG(VERBOSE) << "packetLost (expected " << mNextExpectedSeqNo << ")";
+
+    ++mNextExpectedSeqNo;
+
+    mAccessUnitDamaged = true;
+}
+
+void AMPEG4ElementaryAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
new file mode 100644
index 0000000..1566d00
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_MPEG4_ELEM_ASSEMBLER_H_
+
+#define A_MPEG4_ELEM_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+struct AMPEG4ElementaryAssembler : public ARTPAssembler {
+    AMPEG4ElementaryAssembler(const sp<AMessage> &notify);
+
+protected:
+    virtual ~AMPEG4ElementaryAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    sp<AMessage> mNotifyMsg;
+
+    uint32_t mAccessUnitRTPTime;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+    bool mAccessUnitDamaged;
+    List<sp<ABuffer> > mPackets;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+    void submitAccessUnit();
+
+    DISALLOW_EVIL_CONSTRUCTORS(AMPEG4ElementaryAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_MPEG4_ELEM_ASSEMBLER_H_
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 224b4bf..8c56cb7 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -18,6 +18,11 @@
 
 #include "ASessionDescription.h"
 
+#include "avc_utils.h"
+
+#include <ctype.h>
+
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -37,6 +42,10 @@
     size_t keyLen = strlen(key);
 
     for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
         const char *colonPos = strchr(s, ';');
 
         size_t len =
@@ -90,7 +99,11 @@
     return buffer;
 }
 
-static sp<ABuffer> MakeAVCCodecSpecificData(const char *params) {
+static sp<ABuffer> MakeAVCCodecSpecificData(
+        const char *params, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
     AString val;
     if (!GetAttribute(params, "profile-level-id", &val)) {
         return NULL;
@@ -172,6 +185,11 @@
         memcpy(out, nal->data(), nal->size());
 
         out += nal->size();
+
+        if (i == 0) {
+            FindAVCDimensions(nal, width, height);
+            LOG(INFO) << "dimensions " << *width << "x" << *height;
+        }
     }
 
     *out++ = numPicParameterSets;
@@ -187,7 +205,7 @@
         out += nal->size();
     }
 
-    hexdump(csd->data(), csd->size());
+    // hexdump(csd->data(), csd->size());
 
     return csd;
 }
@@ -224,7 +242,162 @@
     csd->data()[sizeof(kStaticESDS)] = (x >> 8) & 0xff;
     csd->data()[sizeof(kStaticESDS) + 1] = x & 0xff;
 
-    hexdump(csd->data(), csd->size());
+    // hexdump(csd->data(), csd->size());
+
+    return csd;
+}
+
+static size_t GetSizeWidth(size_t x) {
+    size_t n = 1;
+    while (x > 127) {
+        ++n;
+        x >>= 7;
+    }
+    return n;
+}
+
+static uint8_t *EncodeSize(uint8_t *dst, size_t x) {
+    while (x > 127) {
+        *dst++ = (x & 0x7f) | 0x80;
+        x >>= 7;
+    }
+    *dst++ = x;
+    return dst;
+}
+
+static bool ExtractDimensionsFromVOLHeader(
+        const sp<ABuffer> &config, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
+    const uint8_t *ptr = config->data();
+    size_t offset = 0;
+    bool foundVOL = false;
+    while (offset + 3 < config->size()) {
+        if (memcmp("\x00\x00\x01", &ptr[offset], 3)
+                || (ptr[offset + 3] & 0xf0) != 0x20) {
+            ++offset;
+            continue;
+        }
+
+        foundVOL = true;
+        break;
+    }
+
+    if (!foundVOL) {
+        return false;
+    }
+
+    ABitReader br(&ptr[offset + 4], config->size() - offset - 4);
+    br.skipBits(1);  // random_accessible_vol
+    unsigned video_object_type_indication = br.getBits(8);
+
+    CHECK_NE(video_object_type_indication,
+             0x21u /* Fine Granularity Scalable */);
+
+    unsigned video_object_layer_verid;
+    unsigned video_object_layer_priority;
+    if (br.getBits(1)) {
+        video_object_layer_verid = br.getBits(4);
+        video_object_layer_priority = br.getBits(3);
+    }
+    unsigned aspect_ratio_info = br.getBits(4);
+    if (aspect_ratio_info == 0x0f /* extended PAR */) {
+        br.skipBits(8);  // par_width
+        br.skipBits(8);  // par_height
+    }
+    if (br.getBits(1)) {  // vol_control_parameters
+        br.skipBits(2);  // chroma_format
+        br.skipBits(1);  // low_delay
+        if (br.getBits(1)) {  // vbv_parameters
+            TRESPASS();
+        }
+    }
+    unsigned video_object_layer_shape = br.getBits(2);
+    CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */);
+
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned vop_time_increment_resolution = br.getBits(16);
+    CHECK(br.getBits(1));  // marker_bit
+
+    if (br.getBits(1)) {  // fixed_vop_rate
+        // range [0..vop_time_increment_resolution)
+
+        // vop_time_increment_resolution
+        // 2 => 0..1, 1 bit
+        // 3 => 0..2, 2 bits
+        // 4 => 0..3, 2 bits
+        // 5 => 0..4, 3 bits
+        // ...
+
+        CHECK_GT(vop_time_increment_resolution, 0u);
+        --vop_time_increment_resolution;
+
+        unsigned numBits = 0;
+        while (vop_time_increment_resolution > 0) {
+            ++numBits;
+            vop_time_increment_resolution >>= 1;
+        }
+
+        br.skipBits(numBits);  // fixed_vop_time_increment
+    }
+
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned video_object_layer_width = br.getBits(13);
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned video_object_layer_height = br.getBits(13);
+    CHECK(br.getBits(1));  // marker_bit
+
+    unsigned interlaced = br.getBits(1);
+
+    *width = video_object_layer_width;
+    *height = video_object_layer_height;
+
+    LOG(INFO) << "VOL dimensions = " << *width << "x" << *height;
+
+    return true;
+}
+
+sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
+        const char *params, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
+    AString val;
+    CHECK(GetAttribute(params, "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+
+    if (!ExtractDimensionsFromVOLHeader(config, width, height)) {
+        return NULL;
+    }
+
+    size_t len1 = config->size() + GetSizeWidth(config->size()) + 1;
+    size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13;
+    size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3;
+
+    sp<ABuffer> csd = new ABuffer(len3);
+    uint8_t *dst = csd->data();
+    *dst++ = 0x03;
+    dst = EncodeSize(dst, len2 + 3);
+    *dst++ = 0x00;  // ES_ID
+    *dst++ = 0x00;
+    *dst++ = 0x00;  // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+    *dst++ = 0x04;
+    dst = EncodeSize(dst, len1 + 13);
+    *dst++ = 0x01;  // Video ISO/IEC 14496-2 Simple Profile
+    for (size_t i = 0; i < 12; ++i) {
+        *dst++ = 0x00;
+    }
+
+    *dst++ = 0x05;
+    dst = EncodeSize(dst, config->size());
+    memcpy(dst, config->data(), config->size());
+    dst += config->size();
+
+    // hexdump(csd->data(), csd->size());
 
     return csd;
 }
@@ -253,25 +426,42 @@
         mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
 
         int32_t width, height;
-        sessionDesc->getDimensions(index, PT, &width, &height);
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            width = -1;
+            height = -1;
+        }
 
-        mFormat->setInt32(kKeyWidth, width);
-        mFormat->setInt32(kKeyHeight, height);
-
+        int32_t encWidth, encHeight;
         sp<ABuffer> codecSpecificData =
-            MakeAVCCodecSpecificData(params.c_str());
+            MakeAVCCodecSpecificData(params.c_str(), &encWidth, &encHeight);
 
         if (codecSpecificData != NULL) {
+            if (width < 0) {
+                // If no explicit width/height given in the sdp, use the dimensions
+                // extracted from the first sequence parameter set.
+                width = encWidth;
+                height = encHeight;
+            }
+
             mFormat->setData(
                     kKeyAVCC, 0,
                     codecSpecificData->data(), codecSpecificData->size());
+        } else if (width < 0) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
         }
+
+        mFormat->setInt32(kKeyWidth, width);
+        mFormat->setInt32(kKeyHeight, height);
     } else if (!strncmp(desc.c_str(), "H263-2000/", 10)
             || !strncmp(desc.c_str(), "H263-1998/", 10)) {
         mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
 
         int32_t width, height;
-        sessionDesc->getDimensions(index, PT, &width, &height);
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
 
         mFormat->setInt32(kKeyWidth, width);
         mFormat->setInt32(kKeyHeight, height);
@@ -317,6 +507,36 @@
         if (sampleRate != 16000 || numChannels != 1) {
             mInitCheck = ERROR_UNSUPPORTED;
         }
+    } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+        int32_t width, height;
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            width = -1;
+            height = -1;
+        }
+
+        int32_t encWidth, encHeight;
+        sp<ABuffer> codecSpecificData =
+            MakeMPEG4VideoCodecSpecificData(
+                    params.c_str(), &encWidth, &encHeight);
+
+        if (codecSpecificData != NULL) {
+            mFormat->setData(
+                    kKeyESDS, 0,
+                    codecSpecificData->data(), codecSpecificData->size());
+
+            if (width < 0) {
+                width = encWidth;
+                height = encHeight;
+            }
+        } else if (width < 0) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
+
+        mFormat->setInt32(kKeyWidth, width);
+        mFormat->setInt32(kKeyHeight, height);
     } else {
         mInitCheck = ERROR_UNSUPPORTED;
     }
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 469af3e..6816c45 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -321,6 +321,8 @@
 
     buffer->setRange(0, nbytes);
 
+    // LOG(INFO) << "received " << buffer->size() << " bytes.";
+
     status_t err;
     if (receiveRTP) {
         err = parseRTP(s, buffer);
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 225f6e8..775c4ee 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -20,6 +20,7 @@
 #include "AAVCAssembler.h"
 #include "AH263Assembler.h"
 #include "AMPEG4AudioAssembler.h"
+#include "AMPEG4ElementaryAssembler.h"
 #include "ASessionDescription.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
@@ -63,6 +64,9 @@
         mAssembler = new AAMRAssembler(notify, false /* isWide */, params);
     } else  if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
         mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
+    } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+        mAssembler = new AMPEG4ElementaryAssembler(notify);
+        mIssueFIRRequests = true;
     } else {
         TRESPASS();
     }
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index d6dd597..d4eed7c 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -134,10 +134,10 @@
     return OK;
 }
 
-void ARTPWriter::stop() {
+status_t ARTPWriter::stop() {
     Mutex::Autolock autoLock(mLock);
     if (!(mFlags & kFlagStarted)) {
-        return;
+        return OK;
     }
 
     (new AMessage(kWhatStop, mReflector->id()))->post();
@@ -145,9 +145,11 @@
     while (mFlags & kFlagStarted) {
         mCondition.wait(mLock);
     }
+    return OK;
 }
 
-void ARTPWriter::pause() {
+status_t ARTPWriter::pause() {
+    return OK;
 }
 
 static void StripStartcode(MediaBuffer *buffer) {
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index b1b8b45..fdc8d23 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -40,8 +40,8 @@
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual bool reachedEOS();
     virtual status_t start(MetaData *params);
-    virtual void stop();
-    virtual void pause();
+    virtual status_t stop();
+    virtual status_t pause();
 
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index e9162c0..5f8f5fd 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -136,6 +136,20 @@
     return true;
 }
 
+static void MakeSocketBlocking(int s, bool blocking) {
+    // Make socket non-blocking.
+    int flags = fcntl(s, F_GETFL, 0);
+    CHECK_NE(flags, -1);
+
+    if (blocking) {
+        flags &= ~O_NONBLOCK;
+    } else {
+        flags |= O_NONBLOCK;
+    }
+
+    CHECK_NE(fcntl(s, F_SETFL, flags), -1);
+}
+
 void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
     ++mConnectionID;
 
@@ -150,10 +164,7 @@
 
     mSocket = socket(AF_INET, SOCK_STREAM, 0);
 
-    // Make socket non-blocking.
-    int flags = fcntl(mSocket, F_GETFL, 0);
-    CHECK_NE(flags, -1);
-    CHECK_NE(fcntl(mSocket, F_SETFL, flags | O_NONBLOCK), -1);
+    MakeSocketBlocking(mSocket, false);
 
     AString url;
     CHECK(msg->findString("url", &url));
@@ -210,7 +221,7 @@
         mSocket = -1;
 
         flushPendingRequests();
-    } 
+    }
 
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
@@ -347,7 +358,13 @@
     CHECK_GE(res, 0);
 
     if (res == 1) {
-        if (!receiveRTSPReponse()) {
+        MakeSocketBlocking(mSocket, true);
+
+        bool success = receiveRTSPReponse();
+
+        MakeSocketBlocking(mSocket, false);
+
+        if (!success) {
             // Something horrible, irreparable has happened.
             flushPendingRequests();
             return;
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index ad813cd..4ea7fda 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -203,13 +203,18 @@
     }
 }
 
-void ASessionDescription::getDimensions(
+bool ASessionDescription::getDimensions(
         size_t index, unsigned long PT,
         int32_t *width, int32_t *height) const {
+    *width = 0;
+    *height = 0;
+
     char key[20];
     sprintf(key, "a=framesize:%lu", PT);
     AString value;
-    CHECK(findAttribute(index, key, &value));
+    if (!findAttribute(index, key, &value)) {
+        return false;
+    }
 
     const char *s = value.c_str();
     char *end;
@@ -221,6 +226,8 @@
     *height = strtoul(s, &end, 10);
     CHECK_GT(end, s);
     CHECK_EQ(*end, '\0');
+
+    return true;
 }
 
 bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
diff --git a/media/libstagefright/rtsp/ASessionDescription.h b/media/libstagefright/rtsp/ASessionDescription.h
index b26980f..a3fa79e 100644
--- a/media/libstagefright/rtsp/ASessionDescription.h
+++ b/media/libstagefright/rtsp/ASessionDescription.h
@@ -44,7 +44,7 @@
             size_t index, unsigned long *PT,
             AString *desc, AString *params) const;
 
-    void getDimensions(
+    bool getDimensions(
             size_t index, unsigned long PT,
             int32_t *width, int32_t *height) const;
 
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 7f3659f..ed16059 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -7,6 +7,7 @@
         AAVCAssembler.cpp           \
         AH263Assembler.cpp          \
         AMPEG4AudioAssembler.cpp    \
+        AMPEG4ElementaryAssembler.cpp \
         APacketSource.cpp           \
         ARTPAssembler.cpp           \
         ARTPConnection.cpp          \
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index f21c8dc..b19ad48 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -309,6 +309,16 @@
                 size_t trackIndex;
                 CHECK(msg->findSize("track-index", &trackIndex));
 
+                int32_t eos;
+                if (msg->findInt32("eos", &eos)) {
+                    LOG(INFO) << "received BYE on track index " << trackIndex;
+#if 0
+                    TrackInfo *track = &mTracks.editItemAt(trackIndex);
+                    track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
+#endif
+                    return;
+                }
+
                 sp<RefBase> obj;
                 CHECK(msg->findObject("access-unit", &obj));
 
diff --git a/native/android/Android.mk b/native/android/Android.mk
index bd2b27a..cc35a3a 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -12,7 +12,8 @@
     looper.cpp \
     native_activity.cpp \
     native_window.cpp \
-    sensor.cpp
+    sensor.cpp \
+    storage_manager.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
@@ -23,6 +24,9 @@
     libsurfaceflinger_client \
     libandroid_runtime
 
+LOCAL_STATIC_LIBRARIES := \
+    libstorage
+
 LOCAL_C_INCLUDES += \
     frameworks/base/native/include \
     frameworks/base/core/jni/android \
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
new file mode 100644
index 0000000..6dbe746
--- /dev/null
+++ b/native/android/storage_manager.cpp
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NStorage"
+
+#include <android/storage_manager.h>
+#include <storage/IMountService.h>
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+
+using namespace android;
+
+struct ObbActionListener : public BnObbActionListener {
+private:
+    sp<AStorageManager> mStorageManager;
+
+public:
+    ObbActionListener(AStorageManager* mgr) :
+            mStorageManager(mgr)
+    {}
+
+    virtual void onObbResult(const android::String16& filename, const android::String16& state) {
+        LOGD("Got obb result (%s, %s)\n", String8(filename).string(), String8(state).string());
+    }
+};
+
+struct AStorageManager : public RefBase {
+protected:
+    void* mObbCallback;
+    sp<ObbActionListener> mObbActionListener;
+    sp<IMountService> mMountService;
+
+public:
+    AStorageManager() :
+            mObbCallback(NULL)
+    {
+    }
+
+    bool initialize() {
+        sp<IServiceManager> sm = defaultServiceManager();
+        if (sm == NULL) {
+            LOGE("Couldn't get default ServiceManager\n");
+            return false;
+        }
+
+        mMountService = interface_cast<IMountService>(sm->getService(String16("mount")));
+        if (mMountService == NULL) {
+            LOGE("Couldn't get connection to MountService\n");
+            return false;
+        }
+
+        mObbActionListener = new ObbActionListener(this);
+
+        return true;
+    }
+
+    void setObbCallback(void* cb) {
+        mObbCallback = cb;
+    }
+
+    void mountObb(const char* filename, const char* key) {
+        String16 filename16(filename);
+        String16 key16(key);
+        mMountService->mountObb(filename16, key16, mObbActionListener);
+    }
+
+    void unmountObb(const char* filename, const bool force) {
+        String16 filename16(filename);
+        mMountService->unmountObb(filename16, force);
+    }
+
+    int isObbMounted(const char* filename) {
+        String16 filename16(filename);
+        return mMountService->isObbMounted(filename16);
+    }
+
+    const char* getMountedObbPath(const char* filename) {
+        String16 filename16(filename);
+        String16 path16;
+        if (mMountService->getMountedObbPath(filename16, path16)) {
+            return String8(path16).string();
+        } else {
+            return NULL;
+        }
+    }
+};
+
+
+AStorageManager* AStorageManager_new() {
+    sp<AStorageManager> mgr = new AStorageManager();
+    if (mgr == NULL || !mgr->initialize()) {
+        return NULL;
+    }
+    mgr->incStrong((void*)AStorageManager_new);
+    return static_cast<AStorageManager*>(mgr.get());
+}
+
+void AStorageManager_delete(AStorageManager* mgr) {
+    if (mgr) {
+        mgr->decStrong((void*)AStorageManager_new);
+    }
+}
+
+void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb) {
+    mgr->setObbCallback(cb);
+}
+
+void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {
+    mgr->mountObb(filename, key);
+}
+
+void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force) {
+    mgr->unmountObb(filename, force != 0);
+}
+
+int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename) {
+    return mgr->isObbMounted(filename) != 0;
+}
+
+const char* AStorageManager_getMountedObbPath(AStorageManager* mgr, const char* filename) {
+    return mgr->getMountedObbPath(filename);
+}
diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h
new file mode 100644
index 0000000..bbed8a4
--- /dev/null
+++ b/native/include/android/storage_manager.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_STORAGE_MANAGER_H
+#define ANDROID_STORAGE_MANAGER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AStorageManager;
+typedef struct AStorageManager AStorageManager;
+
+
+/**
+ * Obtains a new instance of AStorageManager.
+ */
+AStorageManager* AStorageManager_new();
+
+/**
+ * Release AStorageManager instance.
+ */
+void AStorageManager_delete(AStorageManager* mgr);
+
+/**
+ * Callback to call when requested OBB is complete.
+ */
+void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb);
+
+/**
+ * Attempts to mount an OBB file.
+ */
+void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key);
+
+/**
+ * Attempts to unmount an OBB file.
+ */
+void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force);
+
+/**
+ * Check whether an OBB is mounted.
+ */
+int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename);
+
+/**
+ * Get the mounted path for an OBB.
+ */
+const char* AStorageManager_getMountedObbPath(AStorageManager* mgr, const char* filename);
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif      // ANDROID_PACKAGE_MANAGER_H
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 4997dc8..7fbe9b5 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -685,8 +685,8 @@
             } while (num);
         }
         if (count) {
-            v0 = c->vc.vBuffer + 2 + num - 2;
-            v1 = c->vc.vBuffer + 2 + num - 1;
+            v0 = c->vc.vBuffer + 2 + vcs - 2;
+            v1 = c->vc.vBuffer + 2 + vcs - 1;
             if ((winding&2) == 0) {
                 // for strips copy back the two last compiled vertices
                 c->vc.vBuffer[0] = *v0;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 5bbe441..9e25681 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -576,41 +576,44 @@
     buffer = 0;
 
     // dequeue a new buffer
-    nativeWindow->dequeueBuffer(nativeWindow, &buffer);
-    
-    // TODO: lockBuffer should rather be executed when the very first
-    // direct rendering occurs.
-    nativeWindow->lockBuffer(nativeWindow, buffer);
-    
-    // reallocate the depth-buffer if needed
-    if ((width != buffer->width) || (height != buffer->height)) {
-        // TODO: we probably should reset the swap rect here
-        // if the window size has changed
-        width = buffer->width;
-        height = buffer->height;
-        if (depth.data) {
-            free(depth.data);
-            depth.width   = width;
-            depth.height  = height;
-            depth.stride  = buffer->stride;
-            depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
-            if (depth.data == 0) {
-                setError(EGL_BAD_ALLOC, EGL_FALSE);
-                return EGL_FALSE;
+    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) == NO_ERROR) {
+
+        // TODO: lockBuffer should rather be executed when the very first
+        // direct rendering occurs.
+        nativeWindow->lockBuffer(nativeWindow, buffer);
+
+        // reallocate the depth-buffer if needed
+        if ((width != buffer->width) || (height != buffer->height)) {
+            // TODO: we probably should reset the swap rect here
+            // if the window size has changed
+            width = buffer->width;
+            height = buffer->height;
+            if (depth.data) {
+                free(depth.data);
+                depth.width   = width;
+                depth.height  = height;
+                depth.stride  = buffer->stride;
+                depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
+                if (depth.data == 0) {
+                    setError(EGL_BAD_ALLOC, EGL_FALSE);
+                    return EGL_FALSE;
+                }
             }
         }
-    }
-    
-    // keep a reference on the buffer
-    buffer->common.incRef(&buffer->common);
 
-    // finally pin the buffer down
-    if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | 
-            GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
-        LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
-                buffer, buffer->width, buffer->height);
-        return setError(EGL_BAD_ACCESS, EGL_FALSE);
-        // FIXME: we should make sure we're not accessing the buffer anymore
+        // keep a reference on the buffer
+        buffer->common.incRef(&buffer->common);
+
+        // finally pin the buffer down
+        if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
+                GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
+            LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
+                    buffer, buffer->width, buffer->height);
+            return setError(EGL_BAD_ACCESS, EGL_FALSE);
+            // FIXME: we should make sure we're not accessing the buffer anymore
+        }
+    } else {
+        return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
     }
 
     return EGL_TRUE;
diff --git a/packages/DefaultContainerService/res/values-cs/strings.xml b/packages/DefaultContainerService/res/values-cs/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-cs/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-da/strings.xml b/packages/DefaultContainerService/res/values-da/strings.xml
new file mode 100644
index 0000000..5243028
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-da/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Hjælp til pakkeadgang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-de/strings.xml b/packages/DefaultContainerService/res/values-de/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-de/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-el/strings.xml b/packages/DefaultContainerService/res/values-el/strings.xml
new file mode 100644
index 0000000..a4d8144
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-el/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Βοηθός πρόσβασης πακέτου"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-es-rUS/strings.xml b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..670c2c5
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Asist. p/acceder al paq."</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-es/strings.xml b/packages/DefaultContainerService/res/values-es/strings.xml
new file mode 100644
index 0000000..022c461
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-es/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Ayudante acceso a paquete"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-fr/strings.xml b/packages/DefaultContainerService/res/values-fr/strings.xml
new file mode 100644
index 0000000..5c458bc
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-fr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Aide accès au package"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-it/strings.xml b/packages/DefaultContainerService/res/values-it/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-it/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ja/strings.xml b/packages/DefaultContainerService/res/values-ja/strings.xml
new file mode 100644
index 0000000..2f57e4e
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ja/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"パッケージアクセス支援ツール"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ko/strings.xml b/packages/DefaultContainerService/res/values-ko/strings.xml
new file mode 100644
index 0000000..0304972
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ko/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"패키지 액세스 도움말"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-nb/strings.xml b/packages/DefaultContainerService/res/values-nb/strings.xml
new file mode 100644
index 0000000..637f54d
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-nb/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Hjelpeprogram for pakketilgang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-nl/strings.xml b/packages/DefaultContainerService/res/values-nl/strings.xml
new file mode 100644
index 0000000..9ece040
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-nl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Helper voor pakkettoegang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pl/strings.xml b/packages/DefaultContainerService/res/values-pl/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..5c03669
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Ajuda p/ aceder pacotes"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pt/strings.xml b/packages/DefaultContainerService/res/values-pt/strings.xml
new file mode 100644
index 0000000..5fbd949
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Assistente de pacote"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ru/strings.xml b/packages/DefaultContainerService/res/values-ru/strings.xml
new file mode 100644
index 0000000..ccb0c53
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ru/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Мастер доступа к пакетам"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-sv/strings.xml b/packages/DefaultContainerService/res/values-sv/strings.xml
new file mode 100644
index 0000000..097a709
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-sv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Hjälp med paketåtkomst"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-tr/strings.xml b/packages/DefaultContainerService/res/values-tr/strings.xml
new file mode 100644
index 0000000..12ea674
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-tr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"Paket Erişim Yardımcısı"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rCN/strings.xml b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..65928b1
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"软件包访问帮助程序"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rTW/strings.xml b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..9a43509
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name" msgid="4841491635055379553">"套件存取輔助程式"</string>
+</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6a5290e..2e95932 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -222,16 +222,11 @@
             final String value = c.moveToNext() ? c.getString(0) : null;
             if (value == null) {
                 final SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
-                String serial = SystemProperties.get("ro.serialno");
-                if (serial != null) {
-                    try {
-                        random.setSeed(serial.getBytes("UTF-8"));
-                    } catch (UnsupportedEncodingException ignore) {
-                        // stick with default seed
-                    }
-                }
+                String serial = SystemProperties.get("ro.serialno", "");
+                random.setSeed(
+                    (serial + System.nanoTime() + new SecureRandom().nextLong()).getBytes());
                 final String newAndroidIdValue = Long.toHexString(random.nextLong());
-                Log.d(TAG, "Generated and saved new ANDROID_ID");
+                Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]");
                 final ContentValues values = new ContentValues();
                 values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID);
                 values.put(Settings.NameValueTable.VALUE, newAndroidIdValue);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 4f39ee4..e828f68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -1033,6 +1033,7 @@
                     iconId = sWifiSignalImages[mLastWifiSignalLevel];
                 }
 
+                mService.setIcon("wifi", iconId, 0);
                 // Show the icon since wi-fi is connected
                 mService.setIconVisibility("wifi", true);
 
@@ -1041,11 +1042,11 @@
                 mIsWifiConnected = false;
                 iconId = sWifiSignalImages[0];
 
+                mService.setIcon("wifi", iconId, 0);
                 // Hide the icon since we're not connected
                 mService.setIconVisibility("wifi", false);
             }
 
-            mService.setIcon("wifi", iconId, 0);
         } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
             int iconId;
             final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index 07bcce7..d2346e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -817,7 +817,8 @@
 
     void performCollapse() {
         if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
-                + " mExpandedVisible=" + mExpandedVisible);
+                + " mExpandedVisible=" + mExpandedVisible
+                + " mTicking=" + mTicking);
 
         if (!mExpandedVisible) {
             return;
@@ -832,7 +833,9 @@
         if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
             setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
         }
-        setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+        if (mDateView.getVisibility() == View.VISIBLE) {
+            setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+        }
 
         if (!mExpanded) {
             return;
@@ -1147,6 +1150,7 @@
 
         @Override
         void tickerStarting() {
+            if (SPEW) Slog.d(TAG, "tickerStarting");
             mTicking = true;
             mIcons.setVisibility(View.GONE);
             mTickerView.setVisibility(View.VISIBLE);
@@ -1159,38 +1163,30 @@
 
         @Override
         void tickerDone() {
+            if (SPEW) Slog.d(TAG, "tickerDone");
+            mTicking = false;
             mIcons.setVisibility(View.VISIBLE);
             mTickerView.setVisibility(View.GONE);
             mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
-                        mTickingDoneListener));
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, null));
             if (mExpandedVisible) {
                 setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
             }
         }
 
         void tickerHalting() {
+            if (SPEW) Slog.d(TAG, "tickerHalting");
+            mTicking = false;
             mIcons.setVisibility(View.VISIBLE);
             mTickerView.setVisibility(View.GONE);
             mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
-                        mTickingDoneListener));
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out, null));
             if (mExpandedVisible) {
                 setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
             }
         }
     }
 
-    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
-        public void onAnimationEnd(Animation animation) {
-            mTicking = false;
-        }
-        public void onAnimationRepeat(Animation animation) {
-        }
-        public void onAnimationStart(Animation animation) {
-        }
-    };
-
     private Animation loadAnim(int id, Animation.AnimationListener listener) {
         Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id);
         if (listener != null) {
@@ -1373,7 +1369,8 @@
     void updateExpandedViewPos(int expandedPosition) {
         if (SPEW) {
             Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
-                    + " mTrackingParams.y=" + mTrackingParams.y
+                    + " mTrackingParams.y=" 
+                    + ((mTrackingParams == null) ? "???" : mTrackingParams.y)
                     + " mTrackingPosition=" + mTrackingPosition);
         }
 
@@ -1518,6 +1515,9 @@
                 }
             }
         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+            Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: "
+                + (((net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0)
+                    ? "yes" : "no"));
             if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
                 mTicker.halt();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
index 07e8653..3a697a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
@@ -185,11 +185,11 @@
         final Segment newSegment = new Segment(n, icon, n.notification.tickerText);
 
         // If there's already a notification schedule for this package and id, remove it.
-        for (int i=0; i<initialCount; i++) {
+        for (int i=0; i<mSegments.size(); i++) {
             Segment seg = mSegments.get(i);
             if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
                 // just update that one to use this new data instead
-                mSegments.remove(i);
+                mSegments.remove(i--); // restart iteration here
             }
         }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index e2e6f1a..274124b 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2076,9 +2076,7 @@
                 return mDeskDockRotation;
             } else {
                 if (useSensorForOrientationLp(orientation)) {
-                    // If the user has enabled auto rotation by default, do it.
-                    int curRotation = mOrientationListener.getCurrentRotation();
-                    return curRotation >= 0 ? curRotation : lastRotation;
+                    return mOrientationListener.getCurrentRotation(lastRotation);
                 }
                 return Surface.ROTATION_0;
             }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6e7633e..ff31470 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1601,7 +1601,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
@@ -2021,6 +2021,11 @@
     return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
 }
 
+uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs()
+{
+    return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+}
+
 // ----------------------------------------------------------------------------
 AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
     :   PlaybackThread(audioFlinger, output, id, device)
@@ -2365,7 +2370,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
@@ -2486,6 +2491,18 @@
     return time;
 }
 
+uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
+{
+    uint32_t time;
+    if (AudioSystem::isLinearPCM(mFormat)) {
+        time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+    } else {
+        time = 10000;
+    }
+    return time;
+}
+
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
@@ -2612,7 +2629,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 5520551..51881f0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -664,6 +664,7 @@
         virtual void            deleteTrackName_l(int name) = 0;
         virtual uint32_t        activeSleepTimeUs() = 0;
         virtual uint32_t        idleSleepTimeUs() = 0;
+        virtual uint32_t        suspendSleepTimeUs() = 0;
 
     private:
 
@@ -724,6 +725,7 @@
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    activeSleepTimeUs();
         virtual     uint32_t    idleSleepTimeUs();
+        virtual     uint32_t    suspendSleepTimeUs();
 
         AudioMixer*                     mAudioMixer;
     };
@@ -744,6 +746,7 @@
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    activeSleepTimeUs();
         virtual     uint32_t    idleSleepTimeUs();
+        virtual     uint32_t    suspendSleepTimeUs();
 
     private:
         void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3b3904a..ea2c5d4 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -145,7 +145,12 @@
         return NULL;
     }
 
-    client = new Client(this, cameraClient, cameraId, callingPid);
+    sp<CameraHardwareInterface> hardware = HAL_openCameraHardware(cameraId);
+    if (hardware == NULL) {
+        LOGE("Fail to open camera hardware (id=%d)", cameraId);
+        return NULL;
+    }
+    client = new Client(this, cameraClient, hardware, cameraId, callingPid);
     mClient[cameraId] = client;
     LOG1("CameraService::connect X");
     return client;
@@ -285,16 +290,17 @@
 // ----------------------------------------------------------------------------
 
 CameraService::Client::Client(const sp<CameraService>& cameraService,
-        const sp<ICameraClient>& cameraClient, int cameraId, int clientPid) {
+        const sp<ICameraClient>& cameraClient,
+        const sp<CameraHardwareInterface>& hardware,
+        int cameraId, int clientPid) {
     int callingPid = getCallingPid();
     LOG1("Client::Client E (pid %d)", callingPid);
 
     mCameraService = cameraService;
     mCameraClient = cameraClient;
+    mHardware = hardware;
     mCameraId = cameraId;
     mClientPid = clientPid;
-
-    mHardware = HAL_openCameraHardware(cameraId);
     mUseOverlay = mHardware->useOverlay();
     mMsgEnabled = 0;
 
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 77ccf41..0d69836 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -106,6 +106,7 @@
         friend class CameraService;
                                 Client(const sp<CameraService>& cameraService,
                                        const sp<ICameraClient>& cameraClient,
+                                       const sp<CameraHardwareInterface>& hardware,
                                        int cameraId,
                                        int clientPid);
                                 ~Client();
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index c2c799b..f330d40 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -26,6 +26,7 @@
 import android.os.Environment;
 import android.os.LocalPowerManager;
 import android.os.PowerManager;
+import android.os.SystemProperties;
 import android.util.Slog;
 import android.util.Xml;
 import android.view.InputChannel;
@@ -47,9 +48,6 @@
 
 /*
  * Wraps the C++ InputManager and provides its callbacks.
- * 
- * XXX Tempted to promote this to a first-class service, ie. InputManagerService, to
- *     improve separation of concerns with respect to the window manager.
  */
 public class InputManager {
     static final String TAG = "InputManager";
@@ -507,5 +505,18 @@
             
             return names.toArray(new String[names.size()]);
         }
+        
+        @SuppressWarnings("unused")
+        public int getMaxEventsPerSecond() {
+            int result = 0;
+            try {
+                result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec"));
+            } catch (NumberFormatException e) {
+            }
+            if (result < 1) {
+                result = 60;
+            }
+            return result;
+        }
     }
 }
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 664dfa5..7e23422 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -348,7 +349,8 @@
     }
 
     public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String[] apnTypes, String interfaceName, int networkType) {
+            String reason, String apn, String[] apnTypes, String interfaceName, int networkType,
+            String gateway) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
@@ -372,7 +374,7 @@
             }
         }
         broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
-                apnTypes, interfaceName);
+                apnTypes, interfaceName, gateway);
     }
 
     public void notifyDataConnectionFailed(String reason) {
@@ -535,7 +537,7 @@
 
     private void broadcastDataConnectionStateChanged(int state,
             boolean isDataConnectivityPossible,
-            String reason, String apn, String[] apnTypes, String interfaceName) {
+            String reason, String apn, String[] apnTypes, String interfaceName, String gateway) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
@@ -558,6 +560,12 @@
         }
         intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
         intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
+        int gatewayAddr = 0;
+        if (gateway != null) {
+            gatewayAddr = NetworkUtils.v4StringToInt(gateway);
+        }
+        intent.putExtra(Phone.DATA_GATEWAY_KEY, gatewayAddr);
+
         mContext.sendStickyBroadcast(intent);
     }
 
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 509c789..0eca082 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -116,6 +116,8 @@
 
     private final LockList mLocks = new LockList();
     // some wifi lock statistics
+    private int mFullHighPerfLocksAcquired;
+    private int mFullHighPerfLocksReleased;
     private int mFullLocksAcquired;
     private int mFullLocksReleased;
     private int mScanLocksAcquired;
@@ -169,6 +171,7 @@
     private static final int MESSAGE_START_ACCESS_POINT = 6;
     private static final int MESSAGE_STOP_ACCESS_POINT  = 7;
     private static final int MESSAGE_SET_CHANNELS       = 8;
+    private static final int MESSAGE_ENABLE_NETWORKS    = 9;
 
 
     private final  WifiHandler mWifiHandler;
@@ -1661,6 +1664,12 @@
                 mDeviceIdle = false;
                 mScreenOff = false;
                 mWifiStateTracker.enableRssiPolling(true);
+                /* DHCP or other temporary failures in the past can prevent
+                 * a disabled network from being connected to, enable on screen on
+                 */
+                if (mWifiStateTracker.isAnyNetworkDisabled()) {
+                    sendEnableNetworksMessage();
+                }
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 Slog.d(TAG, "ACTION_SCREEN_OFF");
                 mScreenOff = true;
@@ -1782,8 +1791,8 @@
         msg.sendToTarget();
     }
 
-    private void sendStartMessage(boolean scanOnlyMode) {
-        Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
+    private void sendStartMessage(int lockMode) {
+        Message.obtain(mWifiHandler, MESSAGE_START_WIFI, lockMode, 0).sendToTarget();
     }
 
     private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
@@ -1792,6 +1801,10 @@
                 uid, 0, wifiConfig).sendToTarget();
     }
 
+    private void sendEnableNetworksMessage() {
+        Message.obtain(mWifiHandler, MESSAGE_ENABLE_NETWORKS).sendToTarget();
+    }
+
     private void updateWifiState() {
         // send a message so it's all serialized
         Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
@@ -1801,12 +1814,15 @@
         boolean wifiEnabled = getPersistedWifiEnabled();
         boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
         boolean lockHeld = mLocks.hasLocks();
-        int strongestLockMode;
+        int strongestLockMode = WifiManager.WIFI_MODE_FULL;
         boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
         boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
-        if (mDeviceIdle && lockHeld) {
+
+        if (lockHeld) {
             strongestLockMode = mLocks.getStrongestLockMode();
-        } else {
+        }
+        /* If device is not idle, lockmode cannot be scan only */
+        if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
             strongestLockMode = WifiManager.WIFI_MODE_FULL;
         }
 
@@ -1827,7 +1843,7 @@
                     sWakeLock.acquire();
                     sendEnableMessage(true, false, mLastEnableUid);
                     sWakeLock.acquire();
-                    sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
+                    sendStartMessage(strongestLockMode);
                 } else if (!mWifiStateTracker.isDriverStopped()) {
                     int wakeLockTimeout =
                             Settings.Secure.getInt(
@@ -1905,8 +1921,10 @@
                     break;
 
                 case MESSAGE_START_WIFI:
-                    mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
+                    mWifiStateTracker.setScanOnlyMode(msg.arg1 == WifiManager.WIFI_MODE_SCAN_ONLY);
                     mWifiStateTracker.restart();
+                    mWifiStateTracker.setHighPerfMode(msg.arg1 ==
+                            WifiManager.WIFI_MODE_FULL_HIGH_PERF);
                     sWakeLock.release();
                     break;
 
@@ -1949,6 +1967,10 @@
                     setNumAllowedChannelsBlocking(msg.arg1, msg.arg2 == 1);
                     break;
 
+                case MESSAGE_ENABLE_NETWORKS:
+                    mWifiStateTracker.enableAllNetworks(getConfiguredNetworks());
+                    break;
+
             }
         }
     }
@@ -1986,8 +2008,10 @@
         }
         pw.println();
         pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
+                mFullHighPerfLocksAcquired + " full high perf, " +
                 mScanLocksAcquired + " scan");
         pw.println("Locks released: " + mFullLocksReleased + " full, " +
+                mFullHighPerfLocksReleased + " full high perf, " +
                 mScanLocksReleased + " scan");
         pw.println();
         pw.println("Locks held:");
@@ -2042,11 +2066,15 @@
             if (mList.isEmpty()) {
                 return WifiManager.WIFI_MODE_FULL;
             }
-            for (WifiLock l : mList) {
-                if (l.mMode == WifiManager.WIFI_MODE_FULL) {
-                    return WifiManager.WIFI_MODE_FULL;
-                }
+
+            if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
+                return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
             }
+
+            if (mFullLocksAcquired > mFullLocksReleased) {
+                return WifiManager.WIFI_MODE_FULL;
+            }
+
             return WifiManager.WIFI_MODE_SCAN_ONLY;
         }
 
@@ -2085,7 +2113,11 @@
 
     public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
-        if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
+        if (lockMode != WifiManager.WIFI_MODE_FULL &&
+                lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
+                lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
+            Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
+            if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
             return false;
         }
         WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
@@ -2107,6 +2139,12 @@
                 ++mFullLocksAcquired;
                 mBatteryStats.noteFullWifiLockAcquired(uid);
                 break;
+            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+                ++mFullHighPerfLocksAcquired;
+                /* Treat high power as a full lock for battery stats */
+                mBatteryStats.noteFullWifiLockAcquired(uid);
+                break;
+
             case WifiManager.WIFI_MODE_SCAN_ONLY:
                 ++mScanLocksAcquired;
                 mBatteryStats.noteScanWifiLockAcquired(uid);
@@ -2146,6 +2184,10 @@
                         ++mFullLocksReleased;
                         mBatteryStats.noteFullWifiLockReleased(uid);
                         break;
+                    case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+                        ++mFullHighPerfLocksReleased;
+                        mBatteryStats.noteFullWifiLockReleased(uid);
+                        break;
                     case WifiManager.WIFI_MODE_SCAN_ONLY:
                         ++mScanLocksReleased;
                         mBatteryStats.noteScanWifiLockReleased(uid);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e5d1025..b37cd89 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import com.android.internal.R;
-import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.AttributeCache;
 import com.android.server.IntentResolver;
@@ -39,7 +38,6 @@
 import android.app.ApplicationErrorReport;
 import android.app.Dialog;
 import android.app.IActivityController;
-import android.app.IActivityManager;
 import android.app.IActivityWatcher;
 import android.app.IApplicationThread;
 import android.app.IInstrumentationWatcher;
@@ -50,7 +48,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.ResultInfo;
 import android.app.Service;
 import android.app.backup.IBackupManager;
 import android.content.ActivityNotFoundException;
@@ -94,7 +91,6 @@
 import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -109,6 +105,7 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.SparseArray;
+import android.util.TimeUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -1165,7 +1162,7 @@
                     } catch (RemoteException e) {
                     }
                 } catch (NameNotFoundException e) {
-                    Log.w(TAG, "Unable to create context for heavy notification", e);
+                    Slog.w(TAG, "Unable to create context for heavy notification", e);
                 }
             } break;
             case CANCEL_HEAVY_NOTIFICATION_MSG: {
@@ -1185,10 +1182,10 @@
             case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
                 synchronized (ActivityManagerService.this) {
                     checkExcessiveWakeLocksLocked(true);
-                    mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+                    removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
                     if (mSleeping) {
-                        Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                        mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+                        Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+                        sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
                     }
                 }
             } break;
@@ -2370,7 +2367,7 @@
             }
             
             if (proc == null) {
-                Log.w(TAG, "crashApplication: nothing for uid=" + uid
+                Slog.w(TAG, "crashApplication: nothing for uid=" + uid
                         + " initialPid=" + initialPid
                         + " packageName=" + packageName);
                 return;
@@ -4054,6 +4051,9 @@
                     return false;
                 }
             }
+            if (!pi.exported && pi.applicationInfo.uid != uid) {
+                return false;
+            }
             return true;
         } catch (RemoteException e) {
             return false;
@@ -4093,16 +4093,23 @@
         }
     }
 
-    void grantUriPermissionLocked(int callingUid,
-            String targetPkg, Uri uri, int modeFlags, ActivityRecord activity) {
+    /**
+     * Check if the targetPkg can be granted permission to access uri by
+     * the callingUid using the given modeFlags.  Throws a security exception
+     * if callingUid is not allowed to do this.  Returns the uid of the target
+     * if the URI permission grant should be performed; returns -1 if it is not
+     * needed (for example targetPkg already has permission to access the URI).
+     */
+    int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
+            Uri uri, int modeFlags) {
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         if (modeFlags == 0) {
-            return;
+            return -1;
         }
 
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Requested grant " + targetPkg + " permission to " + uri);
+                "Checking grant " + targetPkg + " permission to " + uri);
         
         final IPackageManager pm = AppGlobals.getPackageManager();
 
@@ -4110,7 +4117,7 @@
         if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                     "Can't grant URI permission for non-content URI: " + uri);
-            return;
+            return -1;
         }
 
         String name = uri.getAuthority();
@@ -4127,7 +4134,7 @@
         }
         if (pi == null) {
             Slog.w(TAG, "No content provider found for: " + name);
-            return;
+            return -1;
         }
 
         int targetUid;
@@ -4136,10 +4143,10 @@
             if (targetUid < 0) {
                 if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                         "Can't grant URI permission no uid for: " + targetPkg);
-                return;
+                return -1;
             }
         } catch (RemoteException ex) {
-            return;
+            return -1;
         }
 
         // First...  does the target actually need this permission?
@@ -4147,7 +4154,7 @@
             // No need to grant the target this permission.
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                     "Target " + targetPkg + " already has full permission to " + uri);
-            return;
+            return -1;
         }
 
         // Second...  is the provider allowing granting of URI permissions?
@@ -4184,12 +4191,23 @@
             }
         }
 
-        // Okay!  So here we are: the caller has the assumed permission
+        return targetUid;
+    }
+
+    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
+            Uri uri, int modeFlags, UriPermissionOwner owner) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        if (modeFlags == 0) {
+            return;
+        }
+
+        // So here we are: the caller has the assumed permission
         // to the uri, and the target doesn't.  Let's now give this to
         // the target.
 
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Granting " + targetPkg + " permission to " + uri);
+                "Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
         
         HashMap<Uri, UriPermission> targetUris
                 = mGrantedUriPermissions.get(targetUid);
@@ -4202,37 +4220,68 @@
         if (perm == null) {
             perm = new UriPermission(targetUid, uri);
             targetUris.put(uri, perm);
-
         }
+
         perm.modeFlags |= modeFlags;
-        if (activity == null) {
+        if (owner == null) {
             perm.globalModeFlags |= modeFlags;
         } else if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-            perm.readActivities.add(activity);
-            if (activity.readUriPermissions == null) {
-                activity.readUriPermissions = new HashSet<UriPermission>();
-            }
-            activity.readUriPermissions.add(perm);
+            perm.readOwners.add(owner);
+            owner.addReadPermission(perm);
         } else if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-            perm.writeActivities.add(activity);
-            if (activity.writeUriPermissions == null) {
-                activity.writeUriPermissions = new HashSet<UriPermission>();
-            }
-            activity.writeUriPermissions.add(perm);
+            perm.writeOwners.add(owner);
+            owner.addWritePermission(perm);
         }
     }
 
-    void grantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, ActivityRecord activity) {
-        if (intent == null) {
+    void grantUriPermissionLocked(int callingUid,
+            String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+        int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
+        if (targetUid < 0) {
             return;
         }
+
+        grantUriPermissionUncheckedLocked(targetUid, targetPkg, uri, modeFlags, owner);
+    }
+
+    /**
+     * Like checkGrantUriPermissionLocked, but takes an Intent.
+     */
+    int checkGrantUriPermissionFromIntentLocked(int callingUid,
+            String targetPkg, Intent intent) {
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                "Checking URI perm to " + (intent != null ? intent.getData() : null)
+                + " from " + intent + "; flags=0x"
+                + Integer.toHexString(intent != null ? intent.getFlags() : 0));
+
+        if (intent == null) {
+            return -1;
+        }
         Uri data = intent.getData();
         if (data == null) {
+            return -1;
+        }
+        return checkGrantUriPermissionLocked(callingUid, targetPkg, data,
+                intent.getFlags());
+    }
+
+    /**
+     * Like grantUriPermissionUncheckedLocked, but takes an Intent.
+     */
+    void grantUriPermissionUncheckedFromIntentLocked(int targetUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner) {
+        grantUriPermissionUncheckedLocked(targetUid, targetPkg, intent.getData(),
+                intent.getFlags(), owner);
+    }
+
+    void grantUriPermissionFromIntentLocked(int callingUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner) {
+        int targetUid = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg, intent);
+        if (targetUid < 0) {
             return;
         }
-        grantUriPermissionLocked(callingUid, targetPkg, data,
-                intent.getFlags(), activity);
+
+        grantUriPermissionUncheckedFromIntentLocked(targetUid, targetPkg, intent, owner);
     }
 
     public void grantUriPermission(IApplicationThread caller, String targetPkg,
@@ -4902,13 +4951,12 @@
     }
 
     private final String checkContentProviderPermissionLocked(
-            ProviderInfo cpi, ProcessRecord r, int mode) {
+            ProviderInfo cpi, ProcessRecord r) {
         final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
         final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.exported ? -1 : cpi.applicationInfo.uid)
-                == PackageManager.PERMISSION_GRANTED
-                && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+                == PackageManager.PERMISSION_GRANTED) {
             return null;
         }
         if (checkComponentPermission(cpi.writePermission, callingPid, callingUid,
@@ -4925,8 +4973,7 @@
                 PathPermission pp = pps[i];
                 if (checkComponentPermission(pp.getReadPermission(), callingPid, callingUid,
                         cpi.exported ? -1 : cpi.applicationInfo.uid)
-                        == PackageManager.PERMISSION_GRANTED
-                        && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+                        == PackageManager.PERMISSION_GRANTED) {
                     return null;
                 }
                 if (checkComponentPermission(pp.getWritePermission(), callingPid, callingUid,
@@ -4937,6 +4984,15 @@
             }
         }
         
+        HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+        if (perms != null) {
+            for (Map.Entry<Uri, UriPermission> uri : perms.entrySet()) {
+                if (uri.getKey().getAuthority().equals(cpi.authority)) {
+                    return null;
+                }
+            }
+        }
+
         String msg = "Permission Denial: opening provider " + cpi.name
                 + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
                 + ", uid=" + callingUid + ") requires "
@@ -4966,10 +5022,9 @@
             cpr = mProvidersByName.get(name);
             if (cpr != null) {
                 cpi = cpr.info;
-                if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
-                    return new ContentProviderHolder(cpi,
-                            cpi.readPermission != null
-                                    ? cpi.readPermission : cpi.writePermission);
+                String msg;
+                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                    throw new SecurityException(msg);
                 }
 
                 if (r != null && cpr.canRunHere(r)) {
@@ -5029,10 +5084,9 @@
                     return null;
                 }
 
-                if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
-                    return new ContentProviderHolder(cpi,
-                            cpi.readPermission != null
-                                    ? cpi.readPermission : cpi.writePermission);
+                String msg;
+                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                    throw new SecurityException(msg);
                 }
 
                 if (!mSystemReady && !mDidUpdate && !mWaitingUpdate
@@ -6183,7 +6237,7 @@
                 Binder.restoreCallingIdentity(origId);
             }
             int res = result.get();
-            Log.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+            Slog.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
         }
     }
 
@@ -8177,18 +8231,23 @@
             return;
         }
 
-        int i = 0;
-        while (i < N) {
+        while (r.pendingStarts.size() > 0) {
             try {
-                ServiceRecord.StartItem si = r.pendingStarts.get(i);
+                ServiceRecord.StartItem si = r.pendingStarts.remove(0);
                 if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: "
                         + r.name + " " + r.intent + " args=" + si.intent);
-                if (si.intent == null && N > 1) {
+                if (si.intent == null) {
                     // If somehow we got a dummy start at the front, then
                     // just drop it here.
-                    i++;
                     continue;
                 }
+                si.deliveredTime = SystemClock.uptimeMillis();
+                r.deliveredStarts.add(si);
+                si.deliveryCount++;
+                if (si.targetPermissionUid >= 0) {
+                    grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
+                            r.packageName, si.intent, si);
+                }
                 bumpServiceExecutingLocked(r);
                 if (!oomAdjusted) {
                     oomAdjusted = true;
@@ -8202,10 +8261,6 @@
                     flags |= Service.START_FLAG_REDELIVERY;
                 }
                 r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
-                si.deliveredTime = SystemClock.uptimeMillis();
-                r.deliveredStarts.add(si);
-                si.deliveryCount++;
-                i++;
             } catch (RemoteException e) {
                 // Remote process gone...  we'll let the normal cleanup take
                 // care of this.
@@ -8215,14 +8270,6 @@
                 break;
             }
         }
-        if (i == N) {
-            r.pendingStarts.clear();
-        } else {
-            while (i > 0) {
-                i--;
-                r.pendingStarts.remove(i);
-            }
-        }
     }
 
     private final boolean requestServiceBindingLocked(ServiceRecord r,
@@ -8305,7 +8352,7 @@
             if (r.lastStartId < 1) {
                 r.lastStartId = 1;
             }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, null));
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
         }
         
         sendServiceArgsLocked(r, true);
@@ -8325,6 +8372,7 @@
         if (N > 0) {
             for (int i=N-1; i>=0; i--) {
                 ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+                si.removeUriPermissionsLocked();
                 if (si.intent == null) {
                     // We'll generate this again if needed.
                 } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
@@ -8564,7 +8612,7 @@
         r.foregroundNoti = null;
         
         // Clear start entries.
-        r.deliveredStarts.clear();
+        r.clearDeliveredStartsLocked();
         r.pendingStarts.clear();
         
         if (r.app != null) {
@@ -8624,6 +8672,8 @@
                         ? res.permission : "private to package");
             }
             ServiceRecord r = res.record;
+            int targetPermissionUid = checkGrantUriPermissionFromIntentLocked(
+                    callingUid, r.packageName, service);
             if (unscheduleServiceRestartLocked(r)) {
                 if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: "
                         + r.shortName);
@@ -8634,7 +8684,8 @@
             if (r.lastStartId < 1) {
                 r.lastStartId = 1;
             }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, service));
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+                    service, targetPermissionUid));
             r.lastActivity = SystemClock.uptimeMillis();
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.startRunningLocked();
@@ -8759,7 +8810,9 @@
                     ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
                     if (si != null) {
                         while (r.deliveredStarts.size() > 0) {
-                            if (r.deliveredStarts.remove(0) == si) {
+                            ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
+                            cur.removeUriPermissionsLocked();
+                            if (cur == si) {
                                 break;
                             }
                         }
@@ -11304,7 +11357,7 @@
         final long curRealtime = SystemClock.elapsedRealtime();
         final long timeSince = curRealtime - mLastWakeLockCheckTime;
         mLastWakeLockCheckTime = curRealtime;
-        if (timeSince < 5*60*1000) {
+        if (timeSince < (WAKE_LOCK_CHECK_DELAY/3)) {
             doKills = false;
         }
         int i = mLruProcesses.size();
@@ -11318,9 +11371,19 @@
                             app.pid, curRealtime);
                 }
                 long timeUsed = wtime - app.lastWakeTime;
-                Slog.i(TAG, "Wake for " + app + ": over "
-                        + timeSince + " used " + timeUsed
-                        + " (" + ((timeUsed*100)/timeSince) + "%)");
+                if (false) {
+                    StringBuilder sb = new StringBuilder(128);
+                    sb.append("Wake for ");
+                    app.toShortString(sb);
+                    sb.append(": over ");
+                    TimeUtils.formatDuration(timeSince, sb);
+                    sb.append(" used ");
+                    TimeUtils.formatDuration(timeUsed, sb);
+                    sb.append(" (");
+                    sb.append((timeUsed*100)/timeSince);
+                    sb.append("%)");
+                    Slog.i(TAG, sb.toString());
+                }
                 // If a process has held a wake lock for more
                 // than 50% of the time during this period,
                 // that sounds pad.  Kill!
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 79756a7..80a41b7 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -29,6 +29,7 @@
 import android.os.Bundle;
 import android.os.Message;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.EventLog;
 import android.util.Log;
@@ -43,7 +44,7 @@
 /**
  * An entry in the history stack, representing an activity.
  */
-class ActivityRecord extends IApplicationToken.Stub {
+class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwner {
     final ActivityManagerService service; // owner
     final ActivityStack stack; // owner
     final ActivityInfo info; // all about me
@@ -340,16 +341,22 @@
      * Deliver a new Intent to an existing activity, so that its onNewIntent()
      * method will be called at the proper time.
      */
-    final void deliverNewIntentLocked(Intent intent) {
+    final void deliverNewIntentLocked(int callingUid, Intent intent) {
         boolean sent = false;
         if (state == ActivityState.RESUMED
                 && app != null && app.thread != null) {
             try {
                 ArrayList<Intent> ar = new ArrayList<Intent>();
-                ar.add(new Intent(intent));
+                intent = new Intent(intent);
+                ar.add(intent);
+                service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+                        intent, this);
                 app.thread.scheduleNewIntent(ar, this);
                 sent = true;
-            } catch (Exception e) {
+            } catch (RemoteException e) {
+                Slog.w(ActivityManagerService.TAG,
+                        "Exception thrown sending new intent to " + this, e);
+            } catch (NullPointerException e) {
                 Slog.w(ActivityManagerService.TAG,
                         "Exception thrown sending new intent to " + this, e);
             }
@@ -362,23 +369,25 @@
     void removeUriPermissionsLocked() {
         if (readUriPermissions != null) {
             for (UriPermission perm : readUriPermissions) {
-                perm.readActivities.remove(this);
-                if (perm.readActivities.size() == 0 && (perm.globalModeFlags
+                perm.readOwners.remove(this);
+                if (perm.readOwners.size() == 0 && (perm.globalModeFlags
                         &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
                     perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-                   service.removeUriPermissionIfNeededLocked(perm);
+                    service.removeUriPermissionIfNeededLocked(perm);
                 }
             }
+            readUriPermissions = null;
         }
         if (writeUriPermissions != null) {
             for (UriPermission perm : writeUriPermissions) {
-                perm.writeActivities.remove(this);
-                if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
+                perm.writeOwners.remove(this);
+                if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
                         &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
                     perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
                     service.removeUriPermissionIfNeededLocked(perm);
                 }
             }
+            writeUriPermissions = null;
         }
     }
 
@@ -569,6 +578,37 @@
                 state == ActivityState.RESUMED;
      }
     
+    @Override
+    public void addReadPermission(UriPermission perm) {
+        if (readUriPermissions == null) {
+            readUriPermissions = new HashSet<UriPermission>();
+        }
+        readUriPermissions.add(perm);
+    }
+
+    @Override
+    public void addWritePermission(UriPermission perm) {
+        if (writeUriPermissions == null) {
+            writeUriPermissions = new HashSet<UriPermission>();
+        }
+        writeUriPermissions.add(perm);
+    }
+
+    @Override
+    public void removeReadPermission(UriPermission perm) {
+        readUriPermissions.remove(perm);
+        if (readUriPermissions.size() == 0) {
+            readUriPermissions = null;
+        }
+    }
+
+    @Override
+    public void removeWritePermission(UriPermission perm) {
+        writeUriPermissions.remove(perm);
+        if (writeUriPermissions.size() == 0) {
+            writeUriPermissions = null;
+        }
+    }
     
     public String toString() {
         if (stringName != null) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index de7b15c..a5f7e96 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2033,16 +2033,6 @@
             }
         }
 
-        if (grantedUriPermissions != null && callingUid > 0) {
-            for (int i=0; i<grantedUriPermissions.length; i++) {
-                mService.grantUriPermissionLocked(callingUid, r.packageName,
-                        grantedUriPermissions[i], grantedMode, r);
-            }
-        }
-
-        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                intent, r);
-
         if (sourceRecord == null) {
             // This activity is not being started from another...  in this
             // case we -always- start a new task.
@@ -2150,7 +2140,7 @@
                                 top.task.setIntent(r.intent, r.info);
                             }
                             logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                            top.deliverNewIntentLocked(r.intent);
+                            top.deliverNewIntentLocked(callingUid, r.intent);
                         } else {
                             // A special case: we need to
                             // start the activity because it is not currently
@@ -2175,7 +2165,7 @@
                             if (taskTop.frontOfTask) {
                                 taskTop.task.setIntent(r.intent, r.info);
                             }
-                            taskTop.deliverNewIntentLocked(r.intent);
+                            taskTop.deliverNewIntentLocked(callingUid, r.intent);
                         } else if (!r.intent.filterEquals(taskTop.task.intent)) {
                             // In this case we are launching the root activity
                             // of the task, but with a different intent.  We
@@ -2243,7 +2233,7 @@
                                 // is the case, so this is it!
                                 return START_RETURN_INTENT_TO_CALLER;
                             }
-                            top.deliverNewIntentLocked(r.intent);
+                            top.deliverNewIntentLocked(callingUid, r.intent);
                             return START_DELIVERED_TO_TOP;
                         }
                     }
@@ -2288,7 +2278,7 @@
                         sourceRecord.task.taskId, r, launchFlags, true);
                 if (top != null) {
                     logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(r.intent);
+                    top.deliverNewIntentLocked(callingUid, r.intent);
                     // For paranoia, make sure we have correctly
                     // resumed the top activity.
                     if (doResume) {
@@ -2305,7 +2295,7 @@
                 if (where >= 0) {
                     ActivityRecord top = moveActivityToFrontLocked(where);
                     logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(r.intent);
+                    top.deliverNewIntentLocked(callingUid, r.intent);
                     if (doResume) {
                         resumeTopActivityLocked(null);
                     }
@@ -2333,6 +2323,17 @@
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in new guessed " + r.task);
         }
+
+        if (grantedUriPermissions != null && callingUid > 0) {
+            for (int i=0; i<grantedUriPermissions.length; i++) {
+                mService.grantUriPermissionLocked(callingUid, r.packageName,
+                        grantedUriPermissions[i], grantedMode, r);
+            }
+        }
+
+        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+                intent, r);
+
         if (newTask) {
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
         }
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index c3f0b3e..bac21b1 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -26,6 +26,7 @@
 import android.os.IBinder;
 import android.os.SystemClock;
 import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -73,61 +74,65 @@
     ActivityInfo curReceiver;   // info about the receiver that is currently running.
 
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + intent);
+        final long now = SystemClock.uptimeMillis();
+
+        pw.print(prefix); pw.println(this);
+        pw.print(prefix); pw.println(intent);
         if (sticky) {
             Bundle bundle = intent.getExtras();
             if (bundle != null) {
-                pw.println(prefix + "extras: " + bundle.toString());
+                pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
             }
         }
-        pw.println(prefix + "proc=" + callerApp);
-        pw.println(prefix + "caller=" + callerPackage
-                + " callingPid=" + callingPid
-                + " callingUid=" + callingUid);
+        pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.println(" ");
+                pw.println(callerApp != null ? callerApp.toShortString() : "null");
+                pw.print(" pid="); pw.print(callingPid);
+                pw.print(" uid="); pw.println(callingUid);
         if (requiredPermission != null) {
-            pw.println(prefix + "requiredPermission=" + requiredPermission);
+            pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
         }
-        pw.println(prefix + "dispatchTime=" + dispatchTime + " ("
-                + (SystemClock.uptimeMillis()-dispatchTime) + "ms since now)");
+        pw.print(prefix); pw.print("dispatchTime=");
+                TimeUtils.formatDuration(dispatchTime, now, pw);
         if (finishTime != 0) {
-            pw.println(prefix + "finishTime=" + finishTime + " ("
-                    + (SystemClock.uptimeMillis()-finishTime) + "ms since now)");
+            pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
         } else {
-            pw.println(prefix + "receiverTime=" + receiverTime + " ("
-                    + (SystemClock.uptimeMillis()-receiverTime) + "ms since now)");
+            pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
         }
+        pw.println("");
         if (anrCount != 0) {
-            pw.println(prefix + "anrCount=" + anrCount);
+            pw.print(prefix); pw.print("anrCount="); pw.println(anrCount);
         }
         if (resultTo != null || resultCode != -1 || resultData != null) {
-            pw.println(prefix + "resultTo=" + resultTo
-                  + " resultCode=" + resultCode + " resultData=" + resultData);
+            pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
+                    pw.print(" resultCode="); pw.print(resultCode);
+                    pw.print(" resultData="); pw.println(resultData);
         }
         if (resultExtras != null) {
-            pw.println(prefix + "resultExtras=" + resultExtras);
+            pw.print(prefix); pw.print("resultExtras="); pw.println(resultExtras);
         }
         if (resultAbort || ordered || sticky || initialSticky) {
-            pw.println(prefix + "resultAbort=" + resultAbort
-                    + " ordered=" + ordered + " sticky=" + sticky
-                    + " initialSticky=" + initialSticky);
+            pw.print(prefix); pw.print("resultAbort="); pw.print(resultAbort);
+                    pw.print(" ordered="); pw.print(ordered);
+                    pw.print(" sticky="); pw.print(sticky);
+                    pw.print(" initialSticky="); pw.println(initialSticky);
         }
         if (nextReceiver != 0 || receiver != null) {
-            pw.println(prefix + "nextReceiver=" + nextReceiver
-                  + " receiver=" + receiver);
+            pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver);
+                    pw.print(" receiver="); pw.println(receiver);
         }
         if (curFilter != null) {
-            pw.println(prefix + "curFilter=" + curFilter);
+            pw.print(prefix); pw.print("curFilter="); pw.println(curFilter);
         }
         if (curReceiver != null) {
-            pw.println(prefix + "curReceiver=" + curReceiver);
+            pw.print(prefix); pw.print("curReceiver="); pw.println(curReceiver);
         }
         if (curApp != null) {
-            pw.println(prefix + "curApp=" + curApp);
-            pw.println(prefix + "curComponent="
-                    + (curComponent != null ? curComponent.toShortString() : "--"));
+            pw.print(prefix); pw.print("curApp="); pw.println(curApp);
+            pw.print(prefix); pw.print("curComponent=");
+                    pw.println((curComponent != null ? curComponent.toShortString() : "--"));
             if (curReceiver != null && curReceiver.applicationInfo != null) {
-                pw.println(prefix + "curSourceDir=" + curReceiver.applicationInfo.sourceDir);
+                pw.print(prefix); pw.print("curSourceDir=");
+                        pw.println(curReceiver.applicationInfo.sourceDir);
             }
         }
         String stateStr = " (?)";
@@ -137,13 +142,14 @@
             case CALL_IN_RECEIVE:   stateStr=" (CALL_IN_RECEIVE)"; break;
             case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
         }
-        pw.println(prefix + "state=" + state + stateStr);
+        pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
         final int N = receivers != null ? receivers.size() : 0;
         String p2 = prefix + "  ";
         PrintWriterPrinter printer = new PrintWriterPrinter(pw);
         for (int i=0; i<N; i++) {
             Object o = receivers.get(i);
-            pw.println(prefix + "Receiver #" + i + ": " + o);
+            pw.print(prefix); pw.print("Receiver #"); pw.print(i);
+                    pw.print(": "); pw.println(o);
             if (o instanceof BroadcastFilter)
                 ((BroadcastFilter)o).dumpBrief(pw, p2);
             else if (o instanceof ResolveInfo)
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 73e489f..6d1fbab 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.Watchdog;
 
 import android.app.ActivityManager;
 import android.app.Dialog;
@@ -27,8 +26,9 @@
 import android.content.pm.ApplicationInfo;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -129,6 +129,8 @@
     ComponentName errorReportReceiver;
 
     void dump(PrintWriter pw, String prefix) {
+        final long now = SystemClock.uptimeMillis();
+
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
         }
@@ -158,7 +160,8 @@
                 pw.print(" curReceiver="); pw.println(curReceiver);
         pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
                 pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
-        pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime);
+        pw.print(prefix); pw.print("lastActivityTime=");
+                TimeUtils.formatDuration(lastActivityTime, now, pw);
                 pw.print(" lruWeight="); pw.print(lruWeight);
                 pw.print(" hidden="); pw.print(hidden);
                 pw.print(" empty="); pw.println(empty);
@@ -179,8 +182,10 @@
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.println(lruSeq);
         pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
-                pw.print(" lastRequestedGc="); pw.print(lastRequestedGc);
-                pw.print(" lastLowMemory="); pw.print(lastLowMemory);
+                pw.print(" lastRequestedGc=");
+                TimeUtils.formatDuration(lastRequestedGc, now, pw);
+                pw.print(" lastLowMemory=");
+                TimeUtils.formatDuration(lastLowMemory, now, pw);
                 pw.print(" reportLowMemory="); pw.println(reportLowMemory);
         if (killedBackground) {
             pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 35f18750..255fbe3 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -31,10 +31,12 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Slog;
+import android.util.TimeUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 
@@ -42,6 +44,12 @@
  * A running application service.
  */
 class ServiceRecord extends Binder {
+    // Maximum number of delivery attempts before giving up.
+    static final int MAX_DELIVERY_COUNT = 3;
+
+    // Maximum number of times it can fail during execution before giving up.
+    static final int MAX_DONE_EXECUTING_COUNT = 6;
+
     final ActivityManagerService ams;
     final BatteryStatsImpl.Uid.Pkg.Serv stats;
     final ComponentName name; // service component.
@@ -67,29 +75,6 @@
     final HashMap<IBinder, ConnectionRecord> connections
             = new HashMap<IBinder, ConnectionRecord>();
                             // IBinder -> ConnectionRecord of all bound clients
-    
-    // Maximum number of delivery attempts before giving up.
-    static final int MAX_DELIVERY_COUNT = 3;
-    
-    // Maximum number of times it can fail during execution before giving up.
-    static final int MAX_DONE_EXECUTING_COUNT = 6;
-    
-    static class StartItem {
-        final int id;
-        final Intent intent;
-        long deliveredTime;
-        int deliveryCount;
-        int doneExecutingCount;
-        
-        StartItem(int _id, Intent _intent) {
-            id = _id;
-            intent = _intent;
-        }
-    }
-    final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
-                            // start() arguments which been delivered.
-    final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
-                            // start() arguments that haven't yet been delivered.
 
     ProcessRecord app;      // where this service is running or null.
     boolean isForeground;   // is service currently in foreground mode?
@@ -111,22 +96,136 @@
 
     String stringName;      // caching of toString
     
+    static class StartItem implements UriPermissionOwner {
+        final ServiceRecord sr;
+        final int id;
+        final Intent intent;
+        final int targetPermissionUid;
+        long deliveredTime;
+        int deliveryCount;
+        int doneExecutingCount;
+
+        String stringName;      // caching of toString
+
+        HashSet<UriPermission> readUriPermissions; // special access to reading uris.
+        HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+
+        StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+            sr = _sr;
+            id = _id;
+            intent = _intent;
+            targetPermissionUid = _targetPermissionUid;
+        }
+
+        void removeUriPermissionsLocked() {
+            if (readUriPermissions != null) {
+                for (UriPermission perm : readUriPermissions) {
+                    perm.readOwners.remove(this);
+                    if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+                            &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+                        perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                        sr.ams.removeUriPermissionIfNeededLocked(perm);
+                    }
+                }
+                readUriPermissions = null;
+            }
+            if (writeUriPermissions != null) {
+                for (UriPermission perm : writeUriPermissions) {
+                    perm.writeOwners.remove(this);
+                    if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+                            &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+                        perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                        sr.ams.removeUriPermissionIfNeededLocked(perm);
+                    }
+                }
+                writeUriPermissions = null;
+            }
+        }
+
+        @Override
+        public void addReadPermission(UriPermission perm) {
+            if (readUriPermissions == null) {
+                readUriPermissions = new HashSet<UriPermission>();
+            }
+            readUriPermissions.add(perm);
+        }
+
+        @Override
+        public void addWritePermission(UriPermission perm) {
+            if (writeUriPermissions == null) {
+                writeUriPermissions = new HashSet<UriPermission>();
+            }
+            writeUriPermissions.add(perm);
+        }
+
+        @Override
+        public void removeReadPermission(UriPermission perm) {
+            readUriPermissions.remove(perm);
+            if (readUriPermissions.size() == 0) {
+                readUriPermissions = null;
+            }
+        }
+
+        @Override
+        public void removeWritePermission(UriPermission perm) {
+            writeUriPermissions.remove(perm);
+            if (writeUriPermissions.size() == 0) {
+                writeUriPermissions = null;
+            }
+        }
+
+        public String toString() {
+            if (stringName != null) {
+                return stringName;
+            }
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("ServiceRecord{")
+                .append(Integer.toHexString(System.identityHashCode(sr)))
+                .append(' ').append(sr.shortName)
+                .append(" StartItem ")
+                .append(Integer.toHexString(System.identityHashCode(this)))
+                .append(" id=").append(id).append('}');
+            return stringName = sb.toString();
+        }
+    }
+
+    final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
+                            // start() arguments which been delivered.
+    final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
+                            // start() arguments that haven't yet been delivered.
+
     void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
         final int N = list.size();
         for (int i=0; i<N; i++) {
             StartItem si = list.get(i);
             pw.print(prefix); pw.print("#"); pw.print(i);
                     pw.print(" id="); pw.print(si.id);
-                    if (now != 0) pw.print(" dur="); pw.print(now-si.deliveredTime);
+                    if (now != 0) {
+                        pw.print(" dur=");
+                        TimeUtils.formatDuration(si.deliveredTime, now, pw);
+                    }
                     if (si.deliveryCount != 0) {
                         pw.print(" dc="); pw.print(si.deliveryCount);
                     }
                     if (si.doneExecutingCount != 0) {
                         pw.print(" dxc="); pw.print(si.doneExecutingCount);
                     }
-                    pw.print(" ");
+                    pw.println("");
+            pw.print(prefix); pw.print("  intent=");
                     if (si.intent != null) pw.println(si.intent.toString());
                     else pw.println("null");
+            if (si.targetPermissionUid >= 0) {
+                pw.print(prefix); pw.print("  targetPermissionUid=");
+                        pw.println(si.targetPermissionUid);
+            }
+            if (si.readUriPermissions != null) {
+                pw.print(prefix); pw.print("  readUriPermissions=");
+                        pw.println(si.readUriPermissions);
+            }
+            if (si.writeUriPermissions != null) {
+                pw.print(prefix); pw.print("  writeUriPermissions=");
+                        pw.println(si.writeUriPermissions);
+            }
         }
     }
     
@@ -140,9 +239,10 @@
             pw.print(prefix); pw.print("permission="); pw.println(permission);
         }
         long now = SystemClock.uptimeMillis();
-        pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
-                if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
-                pw.print(" dataDir="); pw.println(dataDir);
+        long nowReal = SystemClock.elapsedRealtime();
+        pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
+        if (!resDir.equals(baseDir)) pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+        pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
         pw.print(prefix); pw.print("app="); pw.println(app);
         if (isForeground || foregroundId != 0) {
             pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
@@ -150,10 +250,15 @@
                     pw.print(" foregroundNoti="); pw.println(foregroundNoti);
         }
         pw.print(prefix); pw.print("createTime=");
-                pw.print(createTime-SystemClock.elapsedRealtime());
-                pw.print(" lastActivity="); pw.print(lastActivity-now);
-                pw.print(" executingStart="); pw.print(executingStart-now);
-                pw.print(" restartTime="); pw.println(restartTime);
+                TimeUtils.formatDuration(createTime, nowReal, pw);
+                pw.print(" lastActivity=");
+                TimeUtils.formatDuration(lastActivity, now, pw);
+                pw.println("");
+        pw.print(prefix); pw.print(" executingStart=");
+                TimeUtils.formatDuration(executingStart, now, pw);
+                pw.print(" restartTime=");
+                TimeUtils.formatDuration(restartTime, now, pw);
+                pw.println("");
         if (startRequested || lastStartId != 0) {
             pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
                     pw.print(" stopIfKilled="); pw.print(stopIfKilled);
@@ -164,13 +269,15 @@
                 || restartDelay != 0 || nextRestartTime != 0) {
             pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
                     pw.print(" restartCount="); pw.print(restartCount);
-                    pw.print(" restartDelay="); pw.print(restartDelay-now);
-                    pw.print(" nextRestartTime="); pw.print(nextRestartTime-now);
+                    pw.print(" restartDelay=");
+                    TimeUtils.formatDuration(restartDelay, now, pw);
+                    pw.print(" nextRestartTime=");
+                    TimeUtils.formatDuration(nextRestartTime, now, pw);
                     pw.print(" crashCount="); pw.println(crashCount);
         }
         if (deliveredStarts.size() > 0) {
             pw.print(prefix); pw.println("Delivered Starts:");
-            dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
+            dumpStartList(pw, prefix, deliveredStarts, now);
         }
         if (pendingStarts.size() > 0) {
             pw.print(prefix); pw.println("Pending Starts:");
@@ -312,6 +419,13 @@
         }
     }
     
+    public void clearDeliveredStartsLocked() {
+        for (int i=deliveredStarts.size()-1; i>=0; i--) {
+            deliveredStarts.get(i).removeUriPermissionsLocked();
+        }
+        deliveredStarts.clear();
+    }
+
     public String toString() {
         if (stringName != null) {
             return stringName;
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index 81450c5..93c59cc 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -22,13 +22,20 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 
+interface UriPermissionOwner {
+    void addReadPermission(UriPermission perm);
+    void addWritePermission(UriPermission perm);
+    void removeReadPermission(UriPermission perm);
+    void removeWritePermission(UriPermission perm);
+}
+
 class UriPermission {
     final int uid;
     final Uri uri;
     int modeFlags = 0;
     int globalModeFlags = 0;
-    final HashSet<ActivityRecord> readActivities = new HashSet<ActivityRecord>();
-    final HashSet<ActivityRecord> writeActivities = new HashSet<ActivityRecord>();
+    final HashSet<UriPermissionOwner> readOwners = new HashSet<UriPermissionOwner>();
+    final HashSet<UriPermissionOwner> writeOwners = new HashSet<UriPermissionOwner>();
     
     String stringName;
     
@@ -41,27 +48,21 @@
         if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
             globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
             modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            if (readActivities.size() > 0) {
-                for (ActivityRecord r : readActivities) {
-                    r.readUriPermissions.remove(this);
-                    if (r.readUriPermissions.size() == 0) {
-                        r.readUriPermissions = null;
-                    }
+            if (readOwners.size() > 0) {
+                for (UriPermissionOwner r : readOwners) {
+                    r.removeReadPermission(this);
                 }
-                readActivities.clear();
+                readOwners.clear();
             }
         }
         if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
             globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
             modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            if (readActivities.size() > 0) {
-                for (ActivityRecord r : readActivities) {
-                    r.writeUriPermissions.remove(this);
-                    if (r.writeUriPermissions.size() == 0) {
-                        r.writeUriPermissions = null;
-                    }
+            if (readOwners.size() > 0) {
+                for (UriPermissionOwner r : writeOwners) {
+                    r.removeWritePermission(this);
                 }
-                readActivities.clear();
+                readOwners.clear();
             }
         }
     }
@@ -85,11 +86,17 @@
                 pw.print(" uid="); pw.print(uid); 
                 pw.print(" globalModeFlags=0x");
                 pw.println(Integer.toHexString(globalModeFlags));
-        if (readActivities.size() != 0) {
-            pw.print(prefix); pw.print("readActivities="); pw.println(readActivities);
+        if (readOwners.size() != 0) {
+            pw.print(prefix); pw.println("readOwners:");
+            for (UriPermissionOwner owner : readOwners) {
+                pw.print(prefix); pw.print("  * "); pw.println(owner);
+            }
         }
-        if (writeActivities.size() != 0) {
-            pw.print(prefix); pw.print("writeActivities="); pw.println(writeActivities);
+        if (writeOwners.size() != 0) {
+            pw.print(prefix); pw.println("writeOwners:");
+            for (UriPermissionOwner owner : writeOwners) {
+                pw.print(prefix); pw.print("  * "); pw.println(owner);
+            }
         }
     }
 }
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index ea6aa94..c1165c7 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -197,6 +197,9 @@
     // capabilities of the GPS engine
     private int mEngineCapabilities;
 
+    // true if XTRA is supported
+    private boolean mSupportsXtra;
+
     // for calculating time to first fix
     private long mFixRequestTime = 0;
     // time to first fix for most recent session
@@ -635,6 +638,7 @@
         mEnabled = native_init();
 
         if (mEnabled) {
+            mSupportsXtra = native_supports_xtra();
             if (mSuplServerHost != null) {
                 native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
             }
@@ -839,7 +843,7 @@
             sendMessage(INJECT_NTP_TIME, 0, null);
             result = true;
         } else if ("force_xtra_injection".equals(command)) {
-            if (native_supports_xtra()) {
+            if (mSupportsXtra) {
                 xtraDownloadRequest();
                 result = true;
             }
@@ -1372,7 +1376,7 @@
                     handleInjectNtpTime();
                     break;
                 case DOWNLOAD_XTRA_DATA:
-                    if (native_supports_xtra()) {
+                    if (mSupportsXtra) {
                         handleDownloadXtraData();
                     }
                     break;
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index db3f536..d33558b 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -381,14 +381,29 @@
                     : listener);
         }
 
+        // process the command in a new thread
+        private void doCommandAsync(final EventObject command) {
+            new Thread(new Runnable() {
+                    public void run() {
+                        try {
+                            processCommand(command);
+                        } catch (SipException e) {
+                            // TODO: find a better way to do this
+                            if ((command instanceof RegisterCommand)
+                                    || (command == DEREGISTER)) {
+                                onRegistrationFailed(e);
+                            } else {
+                                onError(e);
+                            }
+                        }
+                    }
+            }).start();
+        }
+
         public void makeCall(SipProfile peerProfile,
                 SessionDescription sessionDescription) {
-            try {
-                processCommand(
-                        new MakeCallCommand(peerProfile, sessionDescription));
-            } catch (SipException e) {
-                onError(e);
-            }
+            doCommandAsync(
+                    new MakeCallCommand(peerProfile, sessionDescription));
         }
 
         public void answerCall(SessionDescription sessionDescription) {
@@ -401,36 +416,20 @@
         }
 
         public void endCall() {
-            try {
-                processCommand(END_CALL);
-            } catch (SipException e) {
-                onError(e);
-            }
+            doCommandAsync(END_CALL);
         }
 
         public void changeCall(SessionDescription sessionDescription) {
-            try {
-                processCommand(
-                        new MakeCallCommand(mPeerProfile, sessionDescription));
-            } catch (SipException e) {
-                onError(e);
-            }
+            doCommandAsync(
+                    new MakeCallCommand(mPeerProfile, sessionDescription));
         }
 
         public void register(int duration) {
-            try {
-                processCommand(new RegisterCommand(duration));
-            } catch (SipException e) {
-                onRegistrationFailed(e);
-            }
+            doCommandAsync(new RegisterCommand(duration));
         }
 
         public void unregister() {
-            try {
-                processCommand(DEREGISTER);
-            } catch (SipException e) {
-                onRegistrationFailed(e);
-            }
+            doCommandAsync(DEREGISTER);
         }
 
         public boolean isReRegisterRequired() {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index ebe71ab..3addc0d 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -139,6 +139,7 @@
     jmethodID filterJumpyTouchEvents;
     jmethodID getVirtualKeyDefinitions;
     jmethodID getExcludedDeviceNames;
+    jmethodID getMaxEventsPerSecond;
 } gCallbacksClassInfo;
 
 static struct {
@@ -249,6 +250,7 @@
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
     virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+    virtual int32_t getMaxEventsPerSecond();
 
 private:
     struct InputWindow {
@@ -310,6 +312,9 @@
     int32_t mFilterTouchEvents;
     int32_t mFilterJumpyTouchEvents;
 
+    // Cached throttling policy.
+    int32_t mMaxEventsPerSecond;
+
     // Cached display state.  (lock mDisplayLock)
     Mutex mDisplayLock;
     int32_t mDisplayWidth, mDisplayHeight;
@@ -319,9 +324,9 @@
     bool isScreenOn();
     bool isScreenBright();
 
-    // Weak references to all currently registered input channels by receive fd.
+    // Weak references to all currently registered input channels by connection pointer.
     Mutex mInputChannelRegistryLock;
-    KeyedVector<int, jweak> mInputChannelObjWeakByReceiveFd;
+    KeyedVector<InputChannel*, jweak> mInputChannelObjWeakTable;
 
     jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
@@ -400,6 +405,7 @@
 
 NativeInputManager::NativeInputManager(jobject callbacksObj) :
     mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+    mMaxEventsPerSecond(-1),
     mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
     mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
     mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
@@ -509,8 +515,7 @@
     {
         AutoMutex _l(mInputChannelRegistryLock);
 
-        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
-                inputChannel->getReceivePipeFd());
+        ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
         if (index >= 0) {
             LOGE("Input channel object '%s' has already been registered",
                     inputChannel->getName().string());
@@ -518,8 +523,7 @@
             goto DeleteWeakRef;
         }
 
-        mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(),
-                inputChannelObjWeak);
+        mInputChannelObjWeakTable.add(inputChannel.get(), inputChannelObjWeak);
     }
 
     status = mInputManager->registerInputChannel(inputChannel);
@@ -534,7 +538,7 @@
     // Failed!
     {
         AutoMutex _l(mInputChannelRegistryLock);
-        mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
+        mInputChannelObjWeakTable.removeItem(inputChannel.get());
     }
 
 DeleteWeakRef:
@@ -548,16 +552,15 @@
     {
         AutoMutex _l(mInputChannelRegistryLock);
 
-        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
-                inputChannel->getReceivePipeFd());
+        ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
         if (index < 0) {
             LOGE("Input channel object '%s' is not currently registered",
                     inputChannel->getName().string());
             return INVALID_OPERATION;
         }
 
-        inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
-        mInputChannelObjWeakByReceiveFd.removeItemsAt(index);
+        inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index);
+        mInputChannelObjWeakTable.removeItemsAt(index);
     }
 
     env->DeleteWeakGlobalRef(inputChannelObjWeak);
@@ -572,13 +575,12 @@
     {
         AutoMutex _l(mInputChannelRegistryLock);
 
-        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
-                inputChannel->getReceivePipeFd());
+        ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
         if (index < 0) {
             return NULL;
         }
 
-        jweak inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
+        jweak inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index);
         return env->NewLocalRef(inputChannelObjWeak);
     }
 }
@@ -925,6 +927,21 @@
     }
 }
 
+int32_t NativeInputManager::getMaxEventsPerSecond() {
+    if (mMaxEventsPerSecond < 0) {
+        JNIEnv* env = jniEnv();
+
+        jint result = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.getMaxEventsPerSecond);
+        if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) {
+            result = 60;
+        }
+
+        mMaxEventsPerSecond = result;
+    }
+    return mMaxEventsPerSecond;
+}
+
 void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
 #if DEBUG_FOCUS
     LOGD("setInputWindows");
@@ -2297,6 +2314,9 @@
     GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
             "getExcludedDeviceNames", "()[Ljava/lang/String;");
 
+    GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
+            "getMaxEventsPerSecond", "()I");
+
     // VirtualKeyDefinition
 
     FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index b80dbc5..146c177 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -22,6 +22,7 @@
 #include "jni.h"
 #include <limits.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <utils/Timers.h>
 #include "com_android_server_PowerManagerService.h"
 
 namespace android {
@@ -43,6 +44,11 @@
 static bool gScreenOn;
 static bool gScreenBright;
 
+static nsecs_t gLastEventTime[POWER_MANAGER_LAST_EVENT + 1];
+
+// Throttling interval for user activity calls.
+static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms
+
 // ----------------------------------------------------------------------------
 
 static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -67,6 +73,21 @@
 
 void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
     if (gPowerManagerServiceObj) {
+        // Throttle calls into user activity by event type.
+        // We're a little conservative about argument checking here in case the caller
+        // passes in bad data which could corrupt system state.
+        if (eventType >= 0 && eventType <= POWER_MANAGER_LAST_EVENT) {
+            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+            if (eventTime > now) {
+                eventTime = now;
+            }
+
+            if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) {
+                return;
+            }
+            gLastEventTime[eventType] = eventTime;
+        }
+
         JNIEnv* env = AndroidRuntime::getJNIEnv();
 
         env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity,
@@ -136,6 +157,12 @@
     GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz,
             "userActivity", "(JZIZ)V");
 
+    // Initialize
+    for (int i = 0; i < POWER_MANAGER_LAST_EVENT; i++) {
+        gLastEventTime[i] = LLONG_MIN;
+    }
+    gScreenOn = true;
+    gScreenBright = true;
     return 0;
 }
 
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
index 9b05f38..7c329b2 100644
--- a/services/jni/com_android_server_PowerManagerService.h
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -30,6 +30,8 @@
     POWER_MANAGER_LONG_TOUCH_EVENT = 3,
     POWER_MANAGER_TOUCH_UP_EVENT = 4,
     POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+
+    POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
 };
 
 extern bool android_server_PowerManagerService_isScreenOn();
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 79772ed..a14bfb5 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,7 +5,6 @@
     clz.cpp.arm \
     DisplayHardware/DisplayHardware.cpp \
     DisplayHardware/DisplayHardwareBase.cpp \
-    DisplayHardware/HWComposer.cpp \
     BlurFilter.cpp.arm \
     GLExtensions.cpp \
     Layer.cpp \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 166c528..2eac0a8 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -36,11 +36,11 @@
 
 #include "DisplayHardware/DisplayHardware.h"
 
+#include <hardware/copybit.h>
 #include <hardware/overlay.h>
 #include <hardware/gralloc.h>
 
 #include "GLExtensions.h"
-#include "HWComposer.h"
 
 using namespace android;
 
@@ -76,7 +76,7 @@
         const sp<SurfaceFlinger>& flinger,
         uint32_t dpy)
     : DisplayHardwareBase(flinger, dpy),
-      mFlags(0), mHwc(0)
+      mFlags(0)
 {
     init(dpy);
 }
@@ -262,17 +262,6 @@
 
     // Unbind the context from this thread
     eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-
-    // initialize the H/W composer
-    mHwc = new HWComposer();
-    if (mHwc->initCheck() == NO_ERROR) {
-        mHwc->setFrameBuffer(mDisplay, mSurface);
-    }
-}
-
-HWComposer& DisplayHardware::getHwComposer() const {
-    return *mHwc;
 }
 
 /*
@@ -328,12 +317,7 @@
     }
     
     mPageFlipCount++;
-
-    if (mHwc->initCheck() == NO_ERROR) {
-        mHwc->commit();
-    } else {
-        eglSwapBuffers(dpy, surface);
-    }
+    eglSwapBuffers(dpy, surface);
     checkEGLErrors("eglSwapBuffers");
 
     // for debugging
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index f2cfd2d..66bf521 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -34,11 +34,12 @@
 #include "DisplayHardware/DisplayHardwareBase.h"
 
 struct overlay_control_device_t;
+struct framebuffer_device_t;
+struct copybit_image_t;
 
 namespace android {
 
 class FramebufferNativeWindow;
-class HWComposer;
 
 class DisplayHardware : public DisplayHardwareBase
 {
@@ -79,9 +80,6 @@
     uint32_t getPageFlipCount() const;
     EGLDisplay getEGLDisplay() const { return mDisplay; }
     overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
-
-    // Hardware Composer
-    HWComposer& getHwComposer() const;
     
     status_t compositionComplete() const;
     
@@ -109,8 +107,6 @@
     GLint           mMaxViewportDims;
     GLint           mMaxTextureSize;
     
-    HWComposer*     mHwc;
-
     sp<FramebufferNativeWindow> mNativeWindow;
     overlay_control_device_t* mOverlayEngine;
 };
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
deleted file mode 100644
index 0291d78..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-
-#include <hardware/hardware.h>
-
-#include <cutils/log.h>
-
-#include <EGL/egl.h>
-
-#include "HWComposer.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-HWComposer::HWComposer()
-    : mModule(0), mHwc(0), mList(0), mCapacity(0),
-      mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
-{
-    int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
-    LOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
-    if (err == 0) {
-        err = hwc_open(mModule, &mHwc);
-        LOGE_IF(err, "%s device failed to initialize (%s)",
-                HWC_HARDWARE_COMPOSER, strerror(-err));
-    }
-}
-
-HWComposer::~HWComposer() {
-    free(mList);
-    if (mHwc) {
-        hwc_close(mHwc);
-    }
-}
-
-status_t HWComposer::initCheck() const {
-    return mHwc ? NO_ERROR : NO_INIT;
-}
-
-void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
-    mDpy = (hwc_display_t)dpy;
-    mSur = (hwc_surface_t)sur;
-}
-
-status_t HWComposer::createWorkList(size_t numLayers) {
-    if (mHwc) {
-        if (!mList || mCapacity < numLayers) {
-            free(mList);
-            size_t size = sizeof(hwc_layer_list) + numLayers*sizeof(hwc_layer_t);
-            mList = (hwc_layer_list_t*)malloc(size);
-            mCapacity = numLayers;
-        }
-        mList->flags = HWC_GEOMETRY_CHANGED;
-        mList->numHwLayers = numLayers;
-    }
-    return NO_ERROR;
-}
-
-status_t HWComposer::prepare() const {
-    int err = mHwc->prepare(mHwc, mList);
-    return (status_t)err;
-}
-
-status_t HWComposer::commit() const {
-    int err = mHwc->set(mHwc, mDpy, mSur, mList);
-    mList->flags &= ~HWC_GEOMETRY_CHANGED;
-    return (status_t)err;
-}
-
-size_t HWComposer::getNumLayers() const {
-    return mList ? mList->numHwLayers : 0;
-}
-
-hwc_layer_t* HWComposer::getLayers() const {
-    return mList ? mList->hwLayers : 0;
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
deleted file mode 100644
index c5d5c2b..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_HWCOMPOSER_H
-#define ANDROID_SF_HWCOMPOSER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <EGL/egl.h>
-
-#include <hardware/hwcomposer.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class HWComposer
-{
-public:
-
-    HWComposer();
-    ~HWComposer();
-
-    status_t initCheck() const;
-
-    // tells the HAL what the framebuffer is
-    void setFrameBuffer(EGLDisplay dpy, EGLSurface sur);
-
-    // create a work list for numLayers layer
-    status_t createWorkList(size_t numLayers);
-
-    // Asks the HAL what it can do
-    status_t prepare() const;
-
-    // commits the list
-    status_t commit() const;
-
-
-    size_t getNumLayers() const;
-    hwc_layer_t* getLayers() const;
-
-private:
-    hw_module_t const*      mModule;
-    hwc_composer_device_t*  mHwc;
-    hwc_layer_list_t*       mList;
-    size_t                  mCapacity;
-    hwc_display_t           mDpy;
-    hwc_surface_t           mSur;
-};
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3720e1669..629d993 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -35,7 +35,6 @@
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 #include "DisplayHardware/DisplayHardware.h"
-#include "DisplayHardware/HWComposer.h"
 
 
 #define DEBUG_RESIZE    0
@@ -178,62 +177,6 @@
     return NO_ERROR;
 }
 
-void Layer::setGeometry(hwc_layer_t* hwcl)
-{
-    hwcl->compositionType = HWC_FRAMEBUFFER;
-    hwcl->hints = 0;
-    hwcl->flags = 0;
-    hwcl->transform = 0;
-    hwcl->blending = HWC_BLENDING_NONE;
-
-    // we can't do alpha-fade with the hwc HAL
-    const State& s(drawingState());
-    if (s.alpha < 0xFF) {
-        hwcl->flags = HWC_SKIP_LAYER;
-        return;
-    }
-
-    // we can only handle simple transformation
-    if (mOrientation & Transform::ROT_INVALID) {
-        hwcl->flags = HWC_SKIP_LAYER;
-        return;
-    }
-
-    hwcl->transform = mOrientation;
-
-    if (needsBlending()) {
-        hwcl->blending = mPremultipliedAlpha ?
-                HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
-    }
-
-    hwcl->displayFrame.left   = mTransformedBounds.left;
-    hwcl->displayFrame.top    = mTransformedBounds.top;
-    hwcl->displayFrame.right  = mTransformedBounds.right;
-    hwcl->displayFrame.bottom = mTransformedBounds.bottom;
-
-    hwcl->visibleRegionScreen.rects =
-            reinterpret_cast<hwc_rect_t const *>(
-                    visibleRegionScreen.getArray(
-                            &hwcl->visibleRegionScreen.numRects));
-}
-
-void Layer::setPerFrameData(hwc_layer_t* hwcl) {
-    sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
-    if (buffer == NULL) {
-        // this situation can happen if we ran out of memory for instance.
-        // not much we can do. continue to use whatever texture was bound
-        // to this context.
-        hwcl->handle = NULL;
-        return;
-    }
-    hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
-    // TODO: set the crop value properly
-    hwcl->sourceCrop.left   = 0;
-    hwcl->sourceCrop.top    = 0;
-    hwcl->sourceCrop.right  = buffer->width;
-    hwcl->sourceCrop.bottom = buffer->height;
-}
-
 void Layer::reloadTexture(const Region& dirty)
 {
     sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 188da6a..e1d283b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,8 +68,6 @@
     bool isFixedSize() const;
 
     // LayerBase interface
-    virtual void setGeometry(hwc_layer_t* hwcl);
-    virtual void setPerFrameData(hwc_layer_t* hwcl);
     virtual void onDraw(const Region& clip) const;
     virtual uint32_t doTransaction(uint32_t transactionFlags);
     virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 043d54d..91ac915 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -307,15 +307,6 @@
     }
 }
 
-void LayerBase::setGeometry(hwc_layer_t* hwcl) {
-    hwcl->flags |= HWC_SKIP_LAYER;
-}
-
-void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
-    hwcl->compositionType = HWC_FRAMEBUFFER;
-    hwcl->handle = NULL;
-}
-
 void LayerBase::draw(const Region& clip) const
 {
     // reset GL state
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index dd1cd05..22bf857 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -35,8 +35,6 @@
 
 #include <pixelflinger/pixelflinger.h>
 
-#include <hardware/hwcomposer.h>
-
 #include "Transform.h"
 
 namespace android {
@@ -110,10 +108,6 @@
 
     virtual const char* getTypeId() const { return "LayerBase"; }
 
-    virtual void setGeometry(hwc_layer_t* hwcl);
-
-    virtual void setPerFrameData(hwc_layer_t* hwcl);
-
     /**
      * draw - performs some global clipping optimizations
      * and calls onDraw().
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d257897..637ae48 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -52,7 +52,6 @@
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/DisplayHardware.h"
-#include "DisplayHardware/HWComposer.h"
 
 /* ideally AID_GRAPHICS would be in a semi-public header
  * or there would be a way to map a user/group name to its id
@@ -77,7 +76,6 @@
         mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
         mDump("android.permission.DUMP"),
         mVisibleRegionsDirty(false),
-        mHwWorkListDirty(false),
         mDeferReleaseConsole(false),
         mFreezeDisplay(false),
         mFreezeCount(0),
@@ -370,11 +368,6 @@
     // post surfaces (if needed)
     handlePageFlip();
 
-    if (UNLIKELY(mHwWorkListDirty)) {
-        // build the h/w work list
-        handleWorkList();
-    }
-
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     if (LIKELY(hw.canDraw() && !isFrozen())) {
         // repaint the framebuffer (if needed)
@@ -450,7 +443,6 @@
         handleTransactionLocked(transactionFlags, ditchedLayers);
         mLastTransactionTime = systemTime() - now;
         mDebugInTransaction = 0;
-        mHwWorkListDirty = true;
         // here the transaction has been committed
     }
 
@@ -458,7 +450,6 @@
      * Clean-up all layers that went away
      * (do this without the lock held)
      */
-
     const size_t count = ditchedLayers.size();
     for (size_t i=0 ; i<count ; i++) {
         if (ditchedLayers[i] != 0) {
@@ -692,8 +683,8 @@
 void SurfaceFlinger::handlePageFlip()
 {
     bool visibleRegions = mVisibleRegionsDirty;
-    LayerVector& currentLayers(
-            const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
+    LayerVector& currentLayers = const_cast<LayerVector&>(
+            mDrawingState.layersSortedByZ);
     visibleRegions |= lockPageFlip(currentLayers);
 
         const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -716,7 +707,6 @@
 
             mWormholeRegion = screenRegion.subtract(opaqueRegion);
             mVisibleRegionsDirty = false;
-            mHwWorkListDirty = true;
         }
 
     unlockPageFlip(currentLayers);
@@ -747,20 +737,6 @@
     }
 }
 
-void SurfaceFlinger::handleWorkList()
-{
-    mHwWorkListDirty = false;
-    HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
-    if (hwc.initCheck() == NO_ERROR) {
-        const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
-        const size_t count = currentLayers.size();
-        hwc.createWorkList(count);
-        hwc_layer_t* const cur(hwc.getLayers());
-        for (size_t i=0 ; cur && i<count ; i++) {
-            currentLayers[i]->setGeometry(&cur[i]);
-        }
-    }
-}
 
 void SurfaceFlinger::handleRepaint()
 {
@@ -825,72 +801,9 @@
         // draw something...
         drawWormhole();
     }
-
-    status_t err = NO_ERROR;
     const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
-    size_t count = layers.size();
-
-    const DisplayHardware& hw(graphicPlane(0).displayHardware());
-    HWComposer& hwc(hw.getHwComposer());
-    hwc_layer_t* const cur(hwc.getLayers());
-
-    LOGE_IF(cur && hwc.getNumLayers() != count,
-            "HAL number of layers (%d) doesn't match surfaceflinger (%d)",
-            hwc.getNumLayers(), count);
-
-    // just to be extra-safe, use the smallest count
-    if (hwc.initCheck() == NO_ERROR) {
-        count = count < hwc.getNumLayers() ? count : hwc.getNumLayers();
-    }
-
-    /*
-     *  update the per-frame h/w composer data for each layer
-     *  and build the transparent region of the FB
-     */
-    Region transparent;
-    if (cur) {
-        for (size_t i=0 ; i<count ; i++) {
-            const sp<LayerBase>& layer(layers[i]);
-            layer->setPerFrameData(&cur[i]);
-            if (cur[i].hints & HWC_HINT_CLEAR_FB) {
-                if (!(layer->needsBlending())) {
-                    transparent.orSelf(layer->visibleRegionScreen);
-                }
-            }
-        }
-        err = hwc.prepare();
-        LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
-    }
-
-    /*
-     *  clear the area of the FB that need to be transparent
-     */
-    transparent.andSelf(dirty);
-    if (!transparent.isEmpty()) {
-        glClearColor(0,0,0,0);
-        Region::const_iterator it = transparent.begin();
-        Region::const_iterator const end = transparent.end();
-        const int32_t height = hw.getHeight();
-        while (it != end) {
-            const Rect& r(*it++);
-            const GLint sy = height - (r.top + r.height());
-            glScissor(r.left, sy, r.width(), r.height());
-            glClear(GL_COLOR_BUFFER_BIT);
-        }
-    }
-
-
-    /*
-     * and then, render the layers targeted at the framebuffer
-     */
-    for (size_t i=0 ; i<count ; i++) {
-        if (cur) {
-            if (!(cur[i].compositionType == HWC_FRAMEBUFFER) ||
-                    cur[i].flags & HWC_SKIP_LAYER) {
-                // skip layers handled by the HAL
-                continue;
-            }
-        }
+    const size_t count = layers.size();
+    for (size_t i=0 ; i<count ; ++i) {
         const sp<LayerBase>& layer(layers[i]);
         const Region clip(dirty.intersect(layer->visibleRegionScreen));
         if (!clip.isEmpty()) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8e286e5..8ecfc01 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -296,7 +296,6 @@
             void        handlePageFlip();
             bool        lockPageFlip(const LayerVector& currentLayers);
             void        unlockPageFlip(const LayerVector& currentLayers);
-            void        handleWorkList();
             void        handleRepaint();
             void        postFramebuffer();
             void        composeSurfaces(const Region& dirty);
@@ -371,7 +370,6 @@
                 Region                      mInvalidRegion;
                 Region                      mWormholeRegion;
                 bool                        mVisibleRegionsDirty;
-                bool                        mHwWorkListDirty;
                 bool                        mDeferReleaseConsole;
                 bool                        mFreezeDisplay;
                 int32_t                     mFreezeCount;
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
index 0f448e0..76f6159 100644
--- a/services/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -121,7 +121,6 @@
     case HAL_PIXEL_FORMAT_YCbCr_422_SP:
     case HAL_PIXEL_FORMAT_YCrCb_420_SP:
     case HAL_PIXEL_FORMAT_YCbCr_422_I:
-    case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
         return true;
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index aa916e0..27e08d4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -177,7 +177,7 @@
 
     /**
      * Returns the unique device ID, for example, the IMEI for GSM and the MEID
-     * for CDMA phones. Return null if device ID is not available.
+     * or ESN for CDMA phones. Return null if device ID is not available.
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 819cfbe..8e08592 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -16,16 +16,17 @@
 
 package com.android.internal.telephony;
 
+import com.android.internal.telephony.sip.SipPhone;
+
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RegistrantList;
-import android.util.Log;
-
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
-
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -52,7 +53,7 @@
  */
 public final class CallManager {
 
-    private static final String LOG_TAG ="GSM";
+    private static final String LOG_TAG ="Phone";
     private static final boolean LOCAL_DEBUG = true;
 
     private static final int EVENT_DISCONNECT = 100;
@@ -303,6 +304,32 @@
         }
     }
 
+    public void setAudioMode() {
+        Context context = getContext();
+        if (context == null) return;
+        AudioManager audioManager = (AudioManager)
+                context.getSystemService(Context.AUDIO_SERVICE);
+
+        int mode = AudioManager.MODE_NORMAL;
+        switch (getState()) {
+            case RINGING:
+                mode = AudioManager.MODE_RINGTONE;
+                break;
+            case OFFHOOK:
+                Phone fgPhone = getFgPhone();
+                if (!(fgPhone instanceof SipPhone)) {
+                    mode = AudioManager.MODE_IN_CALL;
+                }
+                break;
+        }
+        audioManager.setMode(mode);
+    }
+
+    private Context getContext() {
+        Phone defaultPhone = getDefaultPhone();
+        return ((defaultPhone == null) ? null : defaultPhone.getContext());
+    }
+
     private void registerForPhoneStates(Phone phone) {
         phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED, null);
         phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
@@ -453,7 +480,7 @@
             heldPhone = heldCall.getPhone();
         }
 
-        return (heldPhone == activePhone);
+        return heldPhone.getClass().equals(activePhone.getClass());
     }
 
     /**
@@ -466,10 +493,14 @@
      * In these cases, this operation may not be performed.
      */
     public void conference(Call heldCall) throws CallStateException {
-        if (canConference(heldCall))
+        Phone fgPhone = getFgPhone();
+        if (fgPhone instanceof SipPhone) {
+            ((SipPhone) fgPhone).conference(heldCall);
+        } else if (canConference(heldCall)) {
+            fgPhone.conference();
+        } else {
             throw(new CallStateException("Can't conference foreground and selected background call"));
-
-        heldCall.getPhone().conference();
+        }
     }
 
     /**
@@ -1288,7 +1319,10 @@
                     mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_INCOMING_RING:
-                    mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+                    // The event may come from RIL who's not aware of an ongoing fg call
+                    if (!hasActiveFgCall()) {
+                        mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+                    }
                     break;
                 case EVENT_RINGBACK_TONE:
                     mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 4da4b6a..057ba0a 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -102,7 +102,8 @@
                     sender.getActiveApnTypes(),
                     sender.getInterfaceName(null),
                     ((telephony!=null) ? telephony.getNetworkType() :
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN));
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN),
+                    sender.getGateway(null));
         } catch (RemoteException ex) {
             // system process is dead
         }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 5bf8e58..87e4b52 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,7 +32,8 @@
     void notifyCallForwardingChanged(boolean cfi);
     void notifyDataActivity(int state);
     void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, in String[] apnTypes, String interfaceName, int networkType);
+            String reason, String apn, in String[] apnTypes, String interfaceName, int networkType,
+            String gateway);
     void notifyDataConnectionFailed(String reason);
     void notifyCellLocation(in Bundle cellLocation);
 }
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 33f7c9d..9afade3 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -103,6 +103,7 @@
     static final String DATA_APN_KEY = "apn";
 
     static final String DATA_IFACE_NAME_KEY = "iface";
+    static final String DATA_GATEWAY_KEY = "gateway";
     static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
     static final String PHONE_IN_ECM_STATE = "phoneinECMState";
 
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index ca526a5..606b52d 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -44,10 +44,6 @@
 import android.util.Log;
 import android.view.WindowManager;
 
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SmsResponse;
-import com.android.internal.telephony.WapPushOverSms;
 import com.android.internal.util.HexDump;
 
 import java.io.ByteArrayOutputStream;
@@ -75,7 +71,7 @@
     private static final int DEFAULT_SMS_MAX_COUNT = 100;
 
     /** Default timeout for SMS sent query */
-    private static final int DEFAULT_SMS_TIMOUEOUT = 6000;
+    private static final int DEFAULT_SMS_TIMEOUT = 6000;
 
     protected static final String[] RAW_PROJECTION = new String[] {
         "pdu",
@@ -83,8 +79,6 @@
         "destination_port",
     };
 
-    static final int MAIL_SEND_SMS = 1;
-
     static final protected int EVENT_NEW_SMS = 1;
 
     static final protected int EVENT_SEND_SMS_COMPLETE = 2;
@@ -143,7 +137,7 @@
 
     private SmsCounter mCounter;
 
-    private ArrayList mSTrackers = new ArrayList(MO_MSG_QUEUE_LIMIT);
+    private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
 
     /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
     private PowerManager.WakeLock mWakeLock;
@@ -154,10 +148,6 @@
      */
     private final int WAKE_LOCK_TIMEOUT = 5000;
 
-    private static SmsMessage mSmsMessage;
-    private static SmsMessageBase mSmsMessageBase;
-    private SmsMessageBase.SubmitPduBase mSubmitPduBase;
-
     protected boolean mStorageAvailable = true;
     protected boolean mReportMemoryStatusPending = false;
 
@@ -345,7 +335,7 @@
             msg.obj = null;
             if (mSTrackers.isEmpty() == false) {
                 try {
-                    SmsTracker sTracker = (SmsTracker)mSTrackers.remove(0);
+                    SmsTracker sTracker = mSTrackers.remove(0);
                     sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
                 } catch (CanceledException ex) {
                     Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
@@ -358,7 +348,7 @@
 
         case EVENT_SEND_CONFIRMED_SMS:
             if (mSTrackers.isEmpty() == false) {
-                SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1);
+                SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
                 if (isMultipartTracker(sTracker)) {
                     sendMultipartSms(sTracker);
                 } else {
@@ -372,7 +362,7 @@
             if (mSTrackers.isEmpty() == false) {
                 // Remove the latest one.
                 try {
-                    SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1);
+                    SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
                     sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
                 } catch (CanceledException ex) {
                     Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
@@ -679,7 +669,7 @@
      * @param destPort the port to deliver the message to
      * @param data the body of the message to send
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is sucessfully sent, or failed.
+     *  broadcast when the message is successfully sent, or failed.
      *  The result code will be <code>Activity.RESULT_OK<code> for success,
      *  or one of these errors:<br>
      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
@@ -706,7 +696,7 @@
      *  the current default SMSC
      * @param text the body of the message to send
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is sucessfully sent, or failed.
+     *  broadcast when the message is successfully sent, or failed.
      *  The result code will be <code>Activity.RESULT_OK<code> for success,
      *  or one of these errors:<br>
      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
@@ -761,7 +751,7 @@
      *  defatult SMSC
      * @param pdu the raw PDU to send
      * @param sentIntent if not NULL this <code>Intent</code> is
-     *  broadcast when the message is sucessfully sent, or failed.
+     *  broadcast when the message is successfully sent, or failed.
      *  The result code will be <code>Activity.RESULT_OK<code> for success,
      *  or one of these errors:
      *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
@@ -837,7 +827,7 @@
 
         mSTrackers.add(tracker);
         sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d),
-                DEFAULT_SMS_TIMOUEOUT);
+                DEFAULT_SMS_TIMEOUT);
     }
 
     protected String getAppNameByIntent(PendingIntent intent) {
@@ -931,7 +921,7 @@
     }
 
     /**
-     * Keeps track of an SMS that has been sent to the RIL, until it it has
+     * Keeps track of an SMS that has been sent to the RIL, until it has
      * successfully been sent, or we're done trying.
      *
      */
@@ -972,27 +962,26 @@
             }
         };
 
-        private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
-                    mStorageAvailable = false;
-                    mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
-                } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
-                    mStorageAvailable = true;
-                    mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
-                } else {
-                    // Assume the intent is one of the SMS receive intents that
-                    // was sent as an ordered broadcast.  Check result and ACK.
-                    int rc = getResultCode();
-                    boolean success = (rc == Activity.RESULT_OK)
-                                        || (rc == Intents.RESULT_SMS_HANDLED);
+    private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
+                mStorageAvailable = false;
+                mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
+                mStorageAvailable = true;
+                mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+            } else {
+                // Assume the intent is one of the SMS receive intents that
+                // was sent as an ordered broadcast.  Check result and ACK.
+                int rc = getResultCode();
+                boolean success = (rc == Activity.RESULT_OK)
+                        || (rc == Intents.RESULT_SMS_HANDLED);
 
-                    // For a multi-part message, this only ACKs the last part.
-                    // Previous parts were ACK'd as they were received.
-                    acknowledgeLastIncomingSms(success, rc, null);
-                }
+                // For a multi-part message, this only ACKs the last part.
+                // Previous parts were ACK'd as they were received.
+                acknowledgeLastIncomingSms(success, rc, null);
             }
-
-        };
+        }
+    };
 }
diff --git a/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java
index 81e151e..4092c69 100644
--- a/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java
@@ -104,7 +104,8 @@
                     sender.getActiveApnTypes(),
                     sender.getInterfaceName(null),
                     ((telephony!=null) ? telephony.getNetworkType() :
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN));
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN),
+                    sender.getGateway(null));
         } catch (RemoteException ex) {
             // system process is dead
         }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 0c591e4..c21b6d9 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -447,9 +447,14 @@
         return mMeid;
     }
 
-    //returns MEID in CDMA
+    //returns MEID or ESN in CDMA
     public String getDeviceId() {
-        return getMeid();
+        String id = getMeid();
+        if ((id == null) || id.matches("^0*$")) {
+            Log.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN");
+            id = getEsn();
+        }
+        return id;
     }
 
     public String getDeviceSvn() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index ed93aea..8eaf4a2 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -28,21 +28,20 @@
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.preference.PreferenceManager;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
-import android.preference.PreferenceManager;
-import android.util.Config;
-import android.util.Log;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage.MessageClass;
+import android.util.Config;
+import android.util.Log;
 
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
-import com.android.internal.telephony.cdma.SmsMessage;
+import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
 import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.util.HexDump;
@@ -51,20 +50,16 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.lang.Boolean;
 
 
 final class CdmaSMSDispatcher extends SMSDispatcher {
     private static final String TAG = "CDMA";
 
-    private CDMAPhone mCdmaPhone;
-
     private byte[] mLastDispatchedSmsFingerprint;
     private byte[] mLastAcknowledgedSmsFingerprint;
 
     CdmaSMSDispatcher(CDMAPhone phone) {
         super(phone);
-        mCdmaPhone = phone;
     }
 
     /**
@@ -129,7 +124,7 @@
             Log.d(TAG, "Voicemail count=" + voicemailCount);
             // Store the voicemail count in preferences.
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
-                    ((CDMAPhone) mPhone).getContext());
+                    mPhone.getContext());
             SharedPreferences.Editor editor = sp.edit();
             editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
             editor.commit();
@@ -176,7 +171,7 @@
          * TODO(cleanup): Why are we using a getter method for this
          * (and for so many other sms fields)?  Trivial getters and
          * setters like this are direct violations of the style guide.
-         * If the purpose is to protect agaist writes (by not
+         * If the purpose is to protect against writes (by not
          * providing a setter) then any protection is illusory (and
          * hence bad) for cases where the values are not primitives,
          * such as this call for the header.  Since this is an issue
@@ -440,7 +435,7 @@
     protected void sendSms(SmsTracker tracker) {
         HashMap map = tracker.mData;
 
-        byte smsc[] = (byte[]) map.get("smsc");
+        // byte smsc[] = (byte[]) map.get("smsc");  // unused for CDMA
         byte pdu[] = (byte[]) map.get("pdu");
 
         Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index d720516..3079a64 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -27,13 +27,12 @@
 import android.util.Config;
 import android.util.Log;
 
-import com.android.internal.telephony.IccUtils;
-import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
-import com.android.internal.telephony.gsm.SmsMessage;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -97,20 +96,20 @@
         if (sms.isTypeZero()) {
             // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
             // Displayed/Stored/Notified. They should only be acknowledged.
-            Log.d(TAG, "Received short message type 0, Dont display or store it. Send Ack");
+            Log.d(TAG, "Received short message type 0, Don't display or store it. Send Ack");
             return Intents.RESULT_SMS_HANDLED;
         }
 
         // Special case the message waiting indicator messages
         if (sms.isMWISetMessage()) {
             mGsmPhone.updateMessageWaitingIndicator(true);
-            handled |= sms.isMwiDontStore();
+            handled = sms.isMwiDontStore();
             if (Config.LOGD) {
                 Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
             }
         } else if (sms.isMWIClearMessage()) {
             mGsmPhone.updateMessageWaitingIndicator(false);
-            handled |= sms.isMwiDontStore();
+            handled = sms.isMwiDontStore();
             if (Config.LOGD) {
                 Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
             }
@@ -301,7 +300,7 @@
             map.put("smsc", pdus.encodedScAddress);
             map.put("pdu", pdus.encodedMessage);
 
-            SmsTracker tracker =  SmsTrackerFactory(map, sentIntent, deliveryIntent);
+            SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent);
             sendSms(tracker);
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index a94518f..a052db0 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -21,6 +21,7 @@
 import android.content.SharedPreferences;
 import android.net.Uri;
 import android.net.rtp.AudioGroup;
+import android.net.rtp.AudioStream;
 import android.net.sip.SipAudioCall;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
@@ -221,7 +222,21 @@
     }
 
     public void conference() throws CallStateException {
-        // TODO
+        if ((foregroundCall.getState() != SipCall.State.ACTIVE)
+                || (foregroundCall.getState() != SipCall.State.ACTIVE)) {
+            throw new CallStateException("wrong state to merge calls: fg="
+                    + foregroundCall.getState() + ", bg="
+                    + backgroundCall.getState());
+        }
+        foregroundCall.merge(backgroundCall);
+    }
+
+    public void conference(Call that) throws CallStateException {
+        if (!(that instanceof SipCall)) {
+            throw new CallStateException("expect " + SipCall.class
+                    + ", cannot merge with " + that.getClass());
+        }
+        foregroundCall.merge((SipCall) that);
     }
 
     public boolean canTransfer() {
@@ -467,6 +482,18 @@
             return (audioGroup.getMode() == AudioGroup.MODE_MUTED);
         }
 
+        void merge(SipCall that) throws CallStateException {
+            AudioGroup myGroup = getAudioGroup();
+            for (Connection c : that.connections) {
+                SipConnection conn = (SipConnection) c;
+                conn.mergeTo(myGroup);
+                connections.add(conn);
+                conn.changeOwner(this);
+            }
+            that.connections.clear();
+            that.setState(Call.State.IDLE);
+        }
+
         void sendDtmf(char c) {
             AudioGroup audioGroup = getAudioGroup();
             if (audioGroup == null) return;
@@ -541,6 +568,7 @@
     private class SipConnection extends SipConnectionBase {
         private SipCall mOwner;
         private SipAudioCall mSipAudioCall;
+        private AudioGroup mOriginalGroup;
         private Call.State mState = Call.State.IDLE;
         private SipProfile mPeer;
         private boolean mIncoming = false;
@@ -660,6 +688,16 @@
             }
         }
 
+        void mergeTo(AudioGroup group) throws CallStateException {
+            AudioStream stream = mSipAudioCall.getAudioStream();
+            if (stream == null) {
+                throw new CallStateException("wrong state to merge: "
+                        + mSipAudioCall.getState());
+            }
+            if (mOriginalGroup == null) mOriginalGroup = getAudioGroup();
+            stream.join(group);
+        }
+
         @Override
         protected void setState(Call.State state) {
             if (state == mState) return;
@@ -694,6 +732,8 @@
 
         @Override
         public void hangup() throws CallStateException {
+            // TODO: need to pull AudioStream out of the AudioGroup in case
+            // this conn was part of a conf call
             Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
                     + ": on phone " + getPhone());
             try {
diff --git a/tests/BatteryWaster/res/layout/battery_waster.xml b/tests/BatteryWaster/res/layout/battery_waster.xml
index 36aa68b..57a5b55 100644
--- a/tests/BatteryWaster/res/layout/battery_waster.xml
+++ b/tests/BatteryWaster/res/layout/battery_waster.xml
@@ -25,6 +25,7 @@
         android:layout_height="wrap_content"
         android:layout_marginLeft="25dp"
         android:layout_marginTop="25dp"
+        android:saveEnabled="false"
         android:textSize="18sp"
         android:textColor="#ffffffff"
         android:text="@string/waste_away"
@@ -35,6 +36,7 @@
         android:layout_height="wrap_content"
         android:layout_marginLeft="25dp"
         android:layout_marginTop="25dp"
+        android:saveEnabled="false"
         android:textSize="18sp"
         android:textColor="#ffffffff"
         android:text="@string/wake_away"
diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
index 499330f..48c4520 100644
--- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
+++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
@@ -39,6 +39,7 @@
     DateFormat mDateFormat;
     IntentFilter mFilter;
     PowerManager.WakeLock mWakeLock;
+    PowerManager.WakeLock mPartialWakeLock;
     SpinThread mThread;
 
     boolean mWasting, mWaking;
@@ -66,6 +67,8 @@
         PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "BatteryWaster");
         mWakeLock.setReferenceCounted(false);
+        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BatteryWaster");
+        mPartialWakeLock.setReferenceCounted(false);
     }
 
     @Override
@@ -80,6 +83,9 @@
         if (mWakeLock.isHeld()) {
             mWakeLock.release();
         }
+        if (mPartialWakeLock.isHeld()) {
+            mPartialWakeLock.release();
+        }
     }
 
     View.OnClickListener mClickListener = new View.OnClickListener() {
@@ -133,7 +139,7 @@
     }
 
     void updateWakeLock() {
-        if (mWasting || mWaking) {
+        if (mWasting) {
             if (!mWakeLock.isHeld()) {
                 mWakeLock.acquire();
             }
@@ -142,6 +148,15 @@
                 mWakeLock.release();
             }
         }
+        if (mWaking) {
+            if (!mPartialWakeLock.isHeld()) {
+                mPartialWakeLock.acquire();
+            }
+        } else {
+            if (mPartialWakeLock.isHeld()) {
+                mPartialWakeLock.release();
+            }
+        }
     }
 
     void log(String s) {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 4d071e3..21f3be4 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -637,6 +637,20 @@
             }
         },
 
+        new Test("Ticker") {
+            public void run() {
+                Notification not = new Notification(
+                    R.drawable.app_gmail, 
+                    "New mail from joeo@example.com, on the topic of very long ticker texts",
+                    System.currentTimeMillis());
+                not.setLatestEventInfo(NotificationTestList.this,
+                    "A new message awaits",
+                    "The contents are very interesting and important",
+                    makeIntent());
+                mNM.notify(1, not);
+            }
+        },
+
         new Test("Crash") {
             public void run()
             {
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 83057b8..35124aa 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -336,6 +336,7 @@
     SMALL_SCREEN_ATTR = 0x01010284,
     NORMAL_SCREEN_ATTR = 0x01010285,
     LARGE_SCREEN_ATTR = 0x01010286,
+    XLARGE_SCREEN_ATTR = 0x010102bf,
     REQUIRED_ATTR = 0x0101028e,
 };
 
@@ -569,6 +570,7 @@
             int smallScreen = 1;
             int normalScreen = 1;
             int largeScreen = 1;
+            int xlargeScreen = 1;
             String8 pkg;
             String8 activityName;
             String8 activityLabel;
@@ -751,6 +753,8 @@
                                 NORMAL_SCREEN_ATTR, NULL, 1);
                         largeScreen = getIntegerAttribute(tree,
                                 LARGE_SCREEN_ATTR, NULL, 1);
+                        xlargeScreen = getIntegerAttribute(tree,
+                                XLARGE_SCREEN_ATTR, NULL, 1);
                     } else if (tag == "uses-feature") {
                         String8 name = getAttribute(tree, NAME_ATTR, &error);
 
@@ -1079,10 +1083,15 @@
             if (largeScreen > 0) {
                 largeScreen = targetSdk >= 4 ? -1 : 0;
             }
+            if (xlargeScreen > 0) {
+                // Introduced in Honeycomb.
+                xlargeScreen = targetSdk >= 10 ? -1 : 0;
+            }
             printf("supports-screens:");
             if (smallScreen != 0) printf(" 'small'");
             if (normalScreen != 0) printf(" 'normal'");
             if (largeScreen != 0) printf(" 'large'");
+            if (xlargeScreen != 0) printf(" 'xlarge'");
             printf("\n");
 
             printf("locales:");
diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp
index 2a9bf04..49e077f 100644
--- a/tools/obbtool/Main.cpp
+++ b/tools/obbtool/Main.cpp
@@ -29,7 +29,7 @@
 static int wantUsage = 0;
 static int wantVersion = 0;
 
-#define ADD_OPTS "n:v:f:c:"
+#define ADD_OPTS "n:v:o"
 static const struct option longopts[] = {
     {"help",       no_argument, &wantUsage,   1},
     {"version",    no_argument, &wantVersion, 1},
@@ -37,8 +37,7 @@
     /* Args for "add" */
     {"name",       required_argument, NULL, 'n'},
     {"version",    required_argument, NULL, 'v'},
-    {"filesystem", required_argument, NULL, 'f'},
-    {"crypto",     required_argument, NULL, 'c'},
+    {"overlay",    optional_argument, NULL, 'o'},
 
     {NULL, 0, NULL, '\0'}
 };
@@ -46,8 +45,7 @@
 struct package_info_t {
     char* packageName;
     int packageVersion;
-    char* filesystem;
-    char* crypto;
+    bool overlay;
 };
 
 /*
@@ -77,6 +75,7 @@
 
     obb->setPackageName(String8(info->packageName));
     obb->setVersion(info->packageVersion);
+    obb->setOverlay(info->overlay);
 
     if (!obb->writeTo(filename)) {
         fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
@@ -112,6 +111,8 @@
     printf("OBB info for '%s':\n", filename);
     printf("Package name: %s\n", obb->getPackageName().string());
     printf("     Version: %d\n", obb->getVersion());
+    printf("       Flags: 0x%08x\n", obb->getFlags());
+    printf("     Overlay: %s\n", obb->isOverlay() ? "true" : "false");
 }
 
 /*
@@ -143,7 +144,7 @@
         case 'n':
             package_info.packageName = optarg;
             break;
-        case 'v':
+        case 'v': {
             char *end;
             package_info.packageVersion = strtol(optarg, &end, 10);
             if (*optarg == '\0' || *end != '\0') {
@@ -152,11 +153,9 @@
                 goto bail;
             }
             break;
-        case 'f':
-            package_info.filesystem = optarg;
-            break;
-        case 'c':
-            package_info.crypto = optarg;
+        }
+        case 'o':
+            package_info.overlay = true;
             break;
         case '?':
             wantUsage = 1;
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index dc86082..37cc121 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -49,27 +49,28 @@
     synchronized void add(AudioStream stream, AudioCodec codec, int codecType, int dtmfType) {
         if (!mStreams.containsKey(stream)) {
             try {
-                int id = add(stream.getMode(), stream.dup(),
+                int socket = stream.dup();
+                add(stream.getMode(), socket,
                         stream.getRemoteAddress().getHostAddress(), stream.getRemotePort(),
                         codec.name, codec.sampleRate, codec.sampleCount, codecType, dtmfType);
-                mStreams.put(stream, id);
+                mStreams.put(stream, socket);
             } catch (NullPointerException e) {
                 throw new IllegalStateException(e);
             }
         }
     }
 
-    private native int add(int mode, int socket, String remoteAddress, int remotePort,
+    private native void add(int mode, int socket, String remoteAddress, int remotePort,
             String codecName, int sampleRate, int sampleCount, int codecType, int dtmfType);
 
     synchronized void remove(AudioStream stream) {
-        Integer id = mStreams.remove(stream);
-        if (id != null) {
-            remove(id);
+        Integer socket = mStreams.remove(stream);
+        if (socket != null) {
+            remove(socket);
         }
     }
 
-    private native void remove(int id);
+    private native void remove(int socket);
 
     /**
      * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index abdc9d7..f4be839 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -191,11 +191,8 @@
      */
     void continueCall() throws SipException;
 
-    /** Puts the device to in-call mode. */
-    void setInCallMode();
-
     /** Puts the device to speaker mode. */
-    void setSpeakerMode();
+    void setSpeakerMode(boolean speakerMode);
 
     /** Toggles mute. */
     void toggleMute();
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 474bc4b..7161309 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -485,16 +485,9 @@
         return mMuted;
     }
 
-    public synchronized void setInCallMode() {
+    public synchronized void setSpeakerMode(boolean speakerMode) {
         ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
-                .setSpeakerphoneOn(false);
-        ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
-                .setMode(AudioManager.MODE_NORMAL);
-    }
-
-    public synchronized void setSpeakerMode() {
-        ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
-                .setSpeakerphoneOn(true);
+                .setSpeakerphoneOn(speakerMode);
     }
 
     public void sendDtmf(int code) {
@@ -587,8 +580,15 @@
                     Log.d(TAG, "   not sending");
                     audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
                 }
+            } else {
+                /* The recorder volume will be very low if the device is in
+                 * IN_CALL mode. Therefore, we have to set the mode to NORMAL
+                 * in order to have the normal microphone level.
+                 */
+                ((AudioManager) mContext.getSystemService
+                        (Context.AUDIO_SERVICE))
+                        .setMode(AudioManager.MODE_NORMAL);
             }
-            setInCallMode();
 
             AudioGroup audioGroup = new AudioGroup();
             audioStream.join(audioGroup);
@@ -614,7 +614,6 @@
                 mRtpSession = null;
             }
         }
-        setInCallMode();
     }
 
     private int getLocalMediaPort() {
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index fc1ed9b..08a8d1c 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -367,7 +367,7 @@
             MSG_TRUNC | MSG_DONTWAIT);
 
         // Do we need to check SSRC, sequence, and timestamp? They are not
-        // reliable but at least they can be used to identity duplicates?
+        // reliable but at least they can be used to identify duplicates?
         if (length < 12 || length > (int)sizeof(buffer) ||
             (ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
             LOGD("stream[%d] malformed packet", mSocket);
@@ -526,70 +526,6 @@
     LOGD("group[%d] is dead", mDeviceSocket);
 }
 
-#define FROYO_COMPATIBLE
-#ifdef FROYO_COMPATIBLE
-
-// Copied from AudioRecord.cpp.
-status_t AudioRecord_getMinFrameCount(
-        int* frameCount,
-        uint32_t sampleRate,
-        int format,
-        int channelCount)
-{
-    size_t size = 0;
-    if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &size)
-            != NO_ERROR) {
-        LOGE("AudioSystem could not query the input buffer size.");
-        return NO_INIT;
-    }
-
-    if (size == 0) {
-        LOGE("Unsupported configuration: sampleRate %d, format %d, channelCount %d",
-            sampleRate, format, channelCount);
-        return BAD_VALUE;
-    }
-
-    // We double the size of input buffer for ping pong use of record buffer.
-    size <<= 1;
-
-    if (AudioSystem::isLinearPCM(format)) {
-        size /= channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1);
-    }
-
-    *frameCount = size;
-    return NO_ERROR;
-}
-
-// Copied from AudioTrack.cpp.
-status_t AudioTrack_getMinFrameCount(
-        int* frameCount,
-        int streamType,
-        uint32_t sampleRate)
-{
-    int afSampleRate;
-    if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-    int afFrameCount;
-    if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-    uint32_t afLatency;
-    if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-
-    // Ensure that buffer depth covers at least audio hardware latency
-    uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
-    if (minBufCount < 2) minBufCount = 2;
-
-    *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
-              afFrameCount * minBufCount * sampleRate / afSampleRate;
-    return NO_ERROR;
-}
-
-#endif
-
 bool AudioGroup::set(int sampleRate, int sampleCount)
 {
     mEventQueue = epoll_create(2);
@@ -603,15 +539,6 @@
     // Find out the frame count for AudioTrack and AudioRecord.
     int output = 0;
     int input = 0;
-#ifdef FROYO_COMPATIBLE
-    if (AudioTrack_getMinFrameCount(&output, AudioSystem::VOICE_CALL,
-        sampleRate) != NO_ERROR || output <= 0 ||
-        AudioRecord_getMinFrameCount(&input, sampleRate,
-        AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
-        LOGE("cannot compute frame count");
-        return false;
-    }
-#else
     if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL,
         sampleRate) != NO_ERROR || output <= 0 ||
         AudioRecord::getMinFrameCount(&input, sampleRate,
@@ -619,7 +546,6 @@
         LOGE("cannot compute frame count");
         return false;
     }
-#endif
     LOGD("reported frame count: output %d, input %d", output, input);
 
     output = (output + sampleCount - 1) / sampleCount * sampleCount;
@@ -771,6 +697,10 @@
     for (AudioStream *stream = mChain; stream->mNext; stream = stream->mNext) {
         AudioStream *target = stream->mNext;
         if (target->mSocket == socket) {
+            if (epoll_ctl(mEventQueue, EPOLL_CTL_DEL, socket, NULL)) {
+                LOGE("epoll_ctl: %s", strerror(errno));
+                return false;
+            }
             stream->mNext = target->mNext;
             LOGD("stream[%d] leaves group[%d]", socket, mDeviceSocket);
             delete target;
@@ -874,7 +804,7 @@
 static jfieldID gNative;
 static jfieldID gMode;
 
-jint add(JNIEnv *env, jobject thiz, jint mode,
+void add(JNIEnv *env, jobject thiz, jint mode,
     jint socket, jstring jRemoteAddress, jint remotePort,
     jstring jCodecName, jint sampleRate, jint sampleCount,
     jint codecType, jint dtmfType)
@@ -887,7 +817,7 @@
     sockaddr_storage remote;
     if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
         // Exception already thrown.
-        return -1;
+        goto error;
     }
     if (sampleRate < 0 || sampleCount < 0 || codecType < 0 || codecType > 127) {
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
@@ -895,12 +825,12 @@
     }
     if (!jCodecName) {
         jniThrowNullPointerException(env, "codecName");
-        return -1;
+        goto error;
     }
     codecName = env->GetStringUTFChars(jCodecName, NULL);
     if (!codecName) {
         // Exception already thrown.
-        return -1;
+        goto error;
     }
 
     // Create audio stream.
@@ -909,8 +839,10 @@
         codecType, dtmfType)) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "cannot initialize audio stream");
+        env->ReleaseStringUTFChars(jCodecName, codecName);
         goto error;
     }
+    env->ReleaseStringUTFChars(jCodecName, codecName);
     socket = -1;
 
     // Create audio group.
@@ -934,16 +866,13 @@
 
     // Succeed.
     env->SetIntField(thiz, gNative, (int)group);
-    env->ReleaseStringUTFChars(jCodecName, codecName);
-    return socket;
+    return;
 
 error:
     delete group;
     delete stream;
     close(socket);
     env->SetIntField(thiz, gNative, NULL);
-    env->ReleaseStringUTFChars(jCodecName, codecName);
-    return -1;
 }
 
 void remove(JNIEnv *env, jobject thiz, jint socket)
@@ -976,7 +905,7 @@
 }
 
 JNINativeMethod gMethods[] = {
-    {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)I", (void *)add},
+    {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)V", (void *)add},
     {"remove", "(I)V", (void *)remove},
     {"setMode", "(I)V", (void *)setMode},
     {"sendDtmf", "(I)V", (void *)sendDtmf},
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4a22b68..9d21521 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -307,6 +307,16 @@
     public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
 
     /**
+     * In this Wi-Fi lock mode, Wi-Fi will behave as in the mode
+     * {@link #WIFI_MODE_FULL} but it operates at high performance
+     * at the expense of power. This mode should be used
+     * only when the wifi connection needs to have minimum loss and low
+     * latency as it can impact the battery life.
+     * @hide
+     */
+    public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
+
+    /**
      * In this Wi-Fi lock mode, Wi-Fi will be kept active,
      * and will behave normally, i.e., it will attempt to automatically
      * establish a connection to a remembered access point that is
@@ -993,8 +1003,9 @@
     /**
      * Creates a new WifiLock.
      *
-     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
-     * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
+     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
+     * and {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
+     *
      * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is 
      *            never shown to the user under normal conditions, but should be descriptive 
      *            enough to identify your application and the specific WifiLock within it, if it
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 7a3282c..25f05c0 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -149,6 +149,8 @@
 
     public native static String getDhcpError();
 
+    public native static boolean setSuspendOptimizationsCommand(boolean enabled);
+
     /**
      * Wait for the supplicant to send an event, returning the event string.
      * @return the event string sent by the supplicant.
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index f57df62..22dbda3 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -59,6 +59,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Track the state of Wifi connectivity. All event handling is done here,
@@ -216,6 +217,9 @@
     private boolean mUseStaticIp = false;
     private int mReconnectCount;
 
+    /* Tracks if any network in the configuration is disabled */
+    private AtomicBoolean mIsAnyNetworkDisabled = new AtomicBoolean(false);
+
     // used to store the (non-persisted) num determined during device boot 
     // (from mcc or other phone info) before the driver is started.
     private int mNumAllowedChannels = 0;
@@ -276,6 +280,9 @@
     
     private boolean mIsScanModeActive;
     private boolean mEnableRssiPolling;
+    private boolean mIsHighPerfEnabled;
+    private int mPowerModeRefCount = 0;
+    private int mOptimizationsDisabledRefCount = 0;
 
     /**
      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
@@ -659,6 +666,67 @@
         }
     }
 
+    /**
+     * Set suspend mode optimizations. These include:
+     * - packet filtering
+     * - turn off roaming
+     * - DTIM settings
+     *
+     * Uses reference counting to keep the suspend optimizations disabled
+     * as long as one entity wants optimizations disabled.
+     *
+     * For example, WifiLock can keep suspend optimizations disabled
+     * or the user setting (wifi never sleeps) can keep suspend optimizations
+     * disabled. As long as one entity wants it disabled, it should stay
+     * that way
+     *
+     * @param enabled true if optimizations need enabled, false otherwise
+     */
+    public synchronized void setSuspendModeOptimizations(boolean enabled) {
+
+        /* It is good to plumb suspend optimization enable
+         * or disable even if ref count indicates already done
+         * since we could have a case of previous failure.
+         */
+        if (!enabled) {
+            mOptimizationsDisabledRefCount++;
+        } else {
+            mOptimizationsDisabledRefCount--;
+            if (mOptimizationsDisabledRefCount > 0) {
+                return;
+            } else {
+                /* Keep refcount from becoming negative */
+                mOptimizationsDisabledRefCount = 0;
+            }
+        }
+
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
+            return;
+        }
+
+        WifiNative.setSuspendOptimizationsCommand(enabled);
+    }
+
+
+    /**
+     * Set high performance mode of operation. This would mean
+     * use active power mode and disable suspend optimizations
+     * @param enabled true if enabled, false otherwise
+     */
+    public synchronized void setHighPerfMode(boolean enabled) {
+        if (mIsHighPerfEnabled != enabled) {
+            if (enabled) {
+                setPowerMode(DRIVER_POWER_MODE_ACTIVE);
+                setSuspendModeOptimizations(false);
+            } else {
+                setPowerMode(DRIVER_POWER_MODE_AUTO);
+                setSuspendModeOptimizations(true);
+            }
+            mIsHighPerfEnabled = enabled;
+            Log.d(TAG,"high performance mode: " + enabled);
+        }
+    }
+
 
     private void checkIsBluetoothPlaying() {
         boolean isBluetoothPlaying = false;
@@ -744,9 +812,13 @@
                 dhcpThread.start();
                 mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
                 mIsScanModeActive = true;
+                mIsHighPerfEnabled = false;
+                mOptimizationsDisabledRefCount = 0;
+                mPowerModeRefCount = 0;
                 mTornDownByConnMgr = false;
                 mLastBssid = null;
                 mLastSsid = null;
+                mIsAnyNetworkDisabled.set(false);
                 requestConnectionInfo();
                 SupplicantState supplState = mWifiInfo.getSupplicantState();
                 /**
@@ -1518,6 +1590,10 @@
         mWifiState.set(wifiState);
     }
 
+    public boolean isAnyNetworkDisabled() {
+        return mIsAnyNetworkDisabled.get();
+    }
+
    /**
      * The WifiNative interface functions are listed below.
      * The only native call that is not synchronized on
@@ -1718,10 +1794,28 @@
         if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
+        if (disableOthers) mIsAnyNetworkDisabled.set(true);
         return WifiNative.enableNetworkCommand(netId, disableOthers);
     }
 
     /**
+     * Enable all networks
+     *
+     * @param networks list of configured networks
+     */
+    public synchronized void enableAllNetworks(List<WifiConfiguration> networks) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
+            return;
+        }
+        mIsAnyNetworkDisabled.set(false);
+        for (WifiConfiguration config : networks) {
+            if (config.status == WifiConfiguration.Status.DISABLED) {
+                WifiNative.enableNetworkCommand(config.networkId, false);
+            }
+        }
+    }
+
+    /**
      * Disable a network
      *
      * @param netId network id of the network
@@ -1731,6 +1825,7 @@
         if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
+        mIsAnyNetworkDisabled.set(true);
         return WifiNative.disableNetworkCommand(netId);
     }
 
@@ -1947,13 +2042,41 @@
      * @param mode
      *     DRIVER_POWER_MODE_AUTO
      *     DRIVER_POWER_MODE_ACTIVE
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     *
+     * Uses reference counting to keep power mode active
+     * as long as one entity wants power mode to be active.
+     *
+     * For example, WifiLock high perf mode can keep power mode active
+     * or a DHCP session can keep it active. As long as one entity wants
+     * it enabled, it should stay that way
+     *
      */
-    public synchronized boolean setPowerMode(int mode) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
+    private synchronized void setPowerMode(int mode) {
+
+        /* It is good to plumb power mode change
+         * even if ref count indicates already done
+         * since we could have a case of previous failure.
+         */
+        switch(mode) {
+            case DRIVER_POWER_MODE_ACTIVE:
+                mPowerModeRefCount++;
+                break;
+            case DRIVER_POWER_MODE_AUTO:
+                mPowerModeRefCount--;
+                if (mPowerModeRefCount > 0) {
+                    return;
+                } else {
+                    /* Keep refcount from becoming negative */
+                    mPowerModeRefCount = 0;
+                }
+                break;
         }
-        return WifiNative.setPowerModeCommand(mode);
+
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
+            return;
+        }
+
+        WifiNative.setPowerModeCommand(mode);
     }
 
     /**