Merge "media: hook up MediaCodec.setSurface" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index b47a2f5..de401b2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -32624,6 +32624,21 @@
     method public int getTopPadding();
   }
 
+  public static final class StaticLayout.Builder {
+    method public android.text.StaticLayout build();
+    method public static android.text.StaticLayout.Builder obtain(java.lang.CharSequence, int, int, android.text.TextPaint, int);
+    method public android.text.StaticLayout.Builder setAlignment(android.text.Layout.Alignment);
+    method public android.text.StaticLayout.Builder setBreakStrategy(int);
+    method public android.text.StaticLayout.Builder setEllipsize(android.text.TextUtils.TruncateAt);
+    method public android.text.StaticLayout.Builder setEllipsizedWidth(int);
+    method public android.text.StaticLayout.Builder setIncludePad(boolean);
+    method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+    method public android.text.StaticLayout.Builder setLineSpacing(float, float);
+    method public android.text.StaticLayout.Builder setMaxLines(int);
+    method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
+    method public android.text.StaticLayout.Builder setTextDir(android.text.TextDirectionHeuristic);
+  }
+
   public abstract interface TextDirectionHeuristic {
     method public abstract boolean isRtl(char[], int, int);
     method public abstract boolean isRtl(java.lang.CharSequence, int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 99c8c90..59578c2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -34834,6 +34834,21 @@
     method public int getTopPadding();
   }
 
+  public static final class StaticLayout.Builder {
+    method public android.text.StaticLayout build();
+    method public static android.text.StaticLayout.Builder obtain(java.lang.CharSequence, int, int, android.text.TextPaint, int);
+    method public android.text.StaticLayout.Builder setAlignment(android.text.Layout.Alignment);
+    method public android.text.StaticLayout.Builder setBreakStrategy(int);
+    method public android.text.StaticLayout.Builder setEllipsize(android.text.TextUtils.TruncateAt);
+    method public android.text.StaticLayout.Builder setEllipsizedWidth(int);
+    method public android.text.StaticLayout.Builder setIncludePad(boolean);
+    method public android.text.StaticLayout.Builder setIndents(int[], int[]);
+    method public android.text.StaticLayout.Builder setLineSpacing(float, float);
+    method public android.text.StaticLayout.Builder setMaxLines(int);
+    method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
+    method public android.text.StaticLayout.Builder setTextDir(android.text.TextDirectionHeuristic);
+  }
+
   public abstract interface TextDirectionHeuristic {
     method public abstract boolean isRtl(char[], int, int);
     method public abstract boolean isRtl(java.lang.CharSequence, int, int);
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 239b386..fc65f63 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -283,15 +283,14 @@
 
         if (reflowed == null) {
             reflowed = new StaticLayout(null);
-            b = StaticLayout.Builder.obtain(text, where, where + after, getWidth());
+            b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth());
         }
 
         b.setText(text, where, where + after)
                 .setPaint(getPaint())
                 .setWidth(getWidth())
                 .setTextDir(getTextDirectionHeuristic())
-                .setSpacingMult(getSpacingMultiplier())
-                .setSpacingAdd(getSpacingAdd())
+                .setLineSpacing(getSpacingAdd(), getSpacingMultiplier())
                 .setEllipsizedWidth(mEllipsizedWidth)
                 .setEllipsize(mEllipsizeAt)
                 .setBreakStrategy(mBreakStrategy);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 67794b1..451abea 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.annotation.Nullable;
 import android.graphics.Paint;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
@@ -46,18 +47,31 @@
     static final String TAG = "StaticLayout";
 
     /**
-     * Builder for static layouts. It would be better if this were a public
-     * API (as it would offer much greater flexibility for adding new options)
-     * but for the time being it's just internal.
-     *
-     * @hide
+     * Builder for static layouts. The builder is a newer pattern for constructing
+     * StaticLayout objects and should be preferred over the constructors,
+     * particularly to access newer features. To build a static layout, first
+     * call {@link #obtain} with the required arguments (text, paint, and width),
+     * then call setters for optional parameters, and finally {@link #build}
+     * to build the StaticLayout object. Parameters not explicitly set will get
+     * default values.
      */
     public final static class Builder {
         private Builder() {
             mNativePtr = nNewBuilder();
         }
 
-        public static Builder obtain(CharSequence source, int start, int end, int width) {
+        /**
+         * Obtain a builder for constructing StaticLayout objects
+         *
+         * @param source The text to be laid out, optionally with spans
+         * @param start The index of the start of the text
+         * @param end The index + 1 of the end of the text
+         * @param paint The base paint used for layout
+         * @param width The width in pixels
+         * @return a builder object used for constructing the StaticLayout
+         */
+        public static Builder obtain(CharSequence source, int start, int end, TextPaint paint,
+                int width) {
             Builder b = sPool.acquire();
             if (b == null) {
                 b = new Builder();
@@ -67,6 +81,7 @@
             b.mText = source;
             b.mStart = start;
             b.mEnd = end;
+            b.mPaint = paint;
             b.mWidth = width;
             b.mAlignment = Alignment.ALIGN_NORMAL;
             b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
@@ -98,6 +113,18 @@
             return setText(source, 0, source.length());
         }
 
+        /**
+         * Set the text. Only useful when re-using the builder, which is done for
+         * the internal implementation of {@link DynamicLayout} but not as part
+         * of normal {@link StaticLayout} usage.
+         *
+         * @param source The text to be laid out, optionally with spans
+         * @param start The index of the start of the text
+         * @param end The index + 1 of the end of the text
+         * @return this builder, useful for chaining
+         *
+         * @hide
+         */
         public Builder setText(CharSequence source, int start, int end) {
             mText = source;
             mStart = start;
@@ -105,11 +132,27 @@
             return this;
         }
 
+        /**
+         * Set the paint. Internal for reuse cases only.
+         *
+         * @param paint The base paint used for layout
+         * @return this builder, useful for chaining
+         *
+         * @hide
+         */
         public Builder setPaint(TextPaint paint) {
             mPaint = paint;
             return this;
         }
 
+        /**
+         * Set the width. Internal for reuse cases only.
+         *
+         * @param width The width in pixels
+         * @return this builder, useful for chaining
+         *
+         * @hide
+         */
         public Builder setWidth(int width) {
             mWidth = width;
             if (mEllipsize == null) {
@@ -118,53 +161,126 @@
             return this;
         }
 
+        /**
+         * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}.
+         *
+         * @param alignment Alignment for the resulting {@link StaticLayout}
+         * @return this builder, useful for chaining
+         */
         public Builder setAlignment(Alignment alignment) {
             mAlignment = alignment;
             return this;
         }
 
+        /**
+         * Set the text direction heuristic. The text direction heuristic is used to
+         * resolve text direction based per-paragraph based on the input text. The default is
+         * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+         *
+         * @param textDir text direction heuristic for resolving BiDi behavior.
+         * @return this builder, useful for chaining
+         */
         public Builder setTextDir(TextDirectionHeuristic textDir) {
             mTextDir = textDir;
             return this;
         }
 
-        // TODO: combine the following, as they're almost always set together?
-        public Builder setSpacingMult(float spacingMult) {
+        /**
+         * Set line spacing parameters. The default is 0.0 for {@code spacingAdd}
+         * and 1.0 for {@code spacingMult}.
+         *
+         * @param spacingAdd line spacing add
+         * @param spacingMult line spacing multiplier
+         * @return this builder, useful for chaining
+         * @see android.widget.TextView#setLineSpacing
+         */
+        public Builder setLineSpacing(float spacingAdd, float spacingMult) {
+            mSpacingAdd = spacingAdd;
             mSpacingMult = spacingMult;
             return this;
         }
 
-        public Builder setSpacingAdd(float spacingAdd) {
-            mSpacingAdd = spacingAdd;
-            return this;
-        }
-
+        /**
+         * Set whether to include extra space beyond font ascent and descent (which is
+         * needed to avoid clipping in some languages, such as Arabic and Kannada). The
+         * default is {@code true}.
+         *
+         * @param includePad whether to include padding
+         * @return this builder, useful for chaining
+         * @see android.widget.TextView#setIncludeFontPadding
+         */
         public Builder setIncludePad(boolean includePad) {
             mIncludePad = includePad;
             return this;
         }
 
-        // TODO: combine the following?
+        /**
+         * Set the width as used for ellipsizing purposes, if it differs from the
+         * normal layout width. The default is the {@code width}
+         * passed to {@link #obtain}.
+         *
+         * @param ellipsizedWidth width used for ellipsizing, in pixels
+         * @return this builder, useful for chaining
+         * @see android.widget.TextView#setEllipsize
+         */
         public Builder setEllipsizedWidth(int ellipsizedWidth) {
             mEllipsizedWidth = ellipsizedWidth;
             return this;
         }
 
-        public Builder setEllipsize(TextUtils.TruncateAt ellipsize) {
+        /**
+         * Set ellipsizing on the layout. Causes words that are longer than the view
+         * is wide, or exceeding the number of lines (see #setMaxLines) in the case
+         * of {@link android.text.TextUtils.TruncateAt#END} or
+         * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead
+         * of broken. The default is
+         * {@code null}, indicating no ellipsis is to be applied.
+         *
+         * @param ellipsize type of ellipsis behavior
+         * @return this builder, useful for chaining
+         * @see android.widget.TextView#setEllipsize
+         */
+        public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
             mEllipsize = ellipsize;
             return this;
         }
 
+        /**
+         * Set maximum number of lines. This is particularly useful in the case of
+         * ellipsizing, where it changes the layout of the last line. The default is
+         * unlimited.
+         *
+         * @param maxLines maximum number of lines in the layout
+         * @return this builder, useful for chaining
+         * @see android.widget.TextView#setMaxLines
+         */
         public Builder setMaxLines(int maxLines) {
             mMaxLines = maxLines;
             return this;
         }
 
+        /**
+         * Set break strategy, useful for selecting high quality or balanced paragraph
+         * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
+         *
+         * @param breakStrategy break strategy for paragraph layout
+         * @return this builder, useful for chaining
+         * @see android.widget.TextView#setBreakStrategy
+         */
         public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
             mBreakStrategy = breakStrategy;
             return this;
         }
 
+        /**
+         * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
+         * pixels. For lines past the last element in the array, the last element repeats.
+         *
+         * @param leftIndents array of indent values for left margin, in pixels
+         * @param rightIndents array of indent values for right margin, in pixels
+         * @return this builder, useful for chaining
+         * @see android.widget.TextView#setIndents
+         */
         public Builder setIndents(int[] leftIndents, int[] rightIndents) {
             int leftLen = leftIndents == null ? 0 : leftIndents.length;
             int rightLen = rightIndents == null ? 0 : rightIndents.length;
@@ -218,6 +334,15 @@
             nAddReplacementRun(mNativePtr, start, end, width);
         }
 
+        /**
+         * Build the {@link StaticLayout} after options have been set.
+         *
+         * <p>Note: the builder object must not be reused in any way after calling this
+         * method. Setting parameters after calling this method, or calling it a second
+         * time on the same builder object, will likely lead to unexpected results.
+         *
+         * @return the newly constructed {@link StaticLayout} object
+         */
         public StaticLayout build() {
             StaticLayout result = new StaticLayout(this);
             Builder.recycle(this);
@@ -327,12 +452,10 @@
                     : new Ellipsizer(source),
               paint, outerwidth, align, textDir, spacingmult, spacingadd);
 
-        Builder b = Builder.obtain(source, bufstart, bufend, outerwidth)
-            .setPaint(paint)
+        Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
             .setAlignment(align)
             .setTextDir(textDir)
-            .setSpacingMult(spacingmult)
-            .setSpacingAdd(spacingadd)
+            .setLineSpacing(spacingadd, spacingmult)
             .setIncludePad(includepad)
             .setEllipsizedWidth(ellipsizedWidth)
             .setEllipsize(ellipsize)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 774a864..449173f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6630,12 +6630,10 @@
             // TODO: code duplication with makeSingleLayout()
             if (mHintLayout == null) {
                 StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
-                        mHint.length(), hintWidth)
-                        .setPaint(mTextPaint)
+                        mHint.length(), mTextPaint, hintWidth)
                         .setAlignment(alignment)
                         .setTextDir(mTextDir)
-                        .setSpacingMult(mSpacingMult)
-                        .setSpacingAdd(mSpacingAdd)
+                        .setLineSpacing(mSpacingAdd, mSpacingMult)
                         .setIncludePad(mIncludePad)
                         .setBreakStrategy(mBreakStrategy);
                 if (mLeftIndents != null || mRightIndents != null) {
@@ -6721,12 +6719,10 @@
         }
         if (result == null) {
             StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
-                    0, mTransformed.length(), wantWidth)
-                    .setPaint(mTextPaint)
+                    0, mTransformed.length(), mTextPaint, wantWidth)
                     .setAlignment(alignment)
                     .setTextDir(mTextDir)
-                    .setSpacingMult(mSpacingMult)
-                    .setSpacingAdd(mSpacingAdd)
+                    .setLineSpacing(mSpacingAdd, mSpacingMult)
                     .setIncludePad(mIncludePad)
                     .setBreakStrategy(mBreakStrategy);
             if (mLeftIndents != null || mRightIndents != null) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0b8b280..7270b2c 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2138,4 +2138,7 @@
 
     <!-- This config is used to force VoiceInteractionService to start on certain low ram devices. -->
     <bool name="config_forceEnableVoiceInteractionService">false</bool>
+
+    <!-- This config is ued to determine whether animations are allowed in low power mode. -->
+    <bool name="config_allowAnimationsInLowPowerMode">false</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 50d2f80..f8d276f 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -281,6 +281,7 @@
   <java-symbol type="bool" name="config_bluetooth_default_profiles" />
   <java-symbol type="bool" name="config_enableWifiDisplay" />
   <java-symbol type="bool" name="config_forceEnableVoiceInteractionService" />
+  <java-symbol type="bool" name="config_allowAnimationsInLowPowerMode" />
   <java-symbol type="bool" name="config_useDevInputEventForAudioJack" />
   <java-symbol type="bool" name="config_safe_media_volume_enabled" />
   <java-symbol type="bool" name="config_camera_sound_forced" />
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 31d2efb..118f4b8 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -4,7 +4,7 @@
 header.hide=1
 page.metaDescription=Download the official Android IDE and developer tools to build apps for Android phones, tablets, wearables, TVs, and more.
 
-studio.version=1.2.0
+studio.version=1.2.0.12
 
 studio.linux_bundle_download=android-studio-ide-141.1890965-linux.zip
 studio.linux_bundle_bytes=259139652 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c875b1d..8220a74 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -681,12 +681,20 @@
      */
     @NonNull
     public static Surface createPersistentInputSurface() {
-        // TODO implement this
-        return new PersistentSurface();
+        return native_createPersistentInputSurface();
     }
 
     static class PersistentSurface extends Surface {
-        PersistentSurface() {}
+        @SuppressWarnings("unused")
+        PersistentSurface() {} // used by native
+
+        @Override
+        public void release() {
+            native_releasePersistentInputSurface(this);
+            super.release();
+        }
+
+        private long mPersistentObject;
     };
 
     /**
@@ -700,9 +708,17 @@
      *           {@link #createPersistentInputSurface}.
      */
     public void usePersistentInputSurface(@NonNull Surface surface) {
-        throw new IllegalArgumentException("not implemented");
+        if (!(surface instanceof PersistentSurface)) {
+            throw new IllegalArgumentException("not a PersistentSurface");
+        }
+        native_usePersistentInputSurface(surface);
     }
 
+    @NonNull
+    private static native final PersistentSurface native_createPersistentInputSurface();
+    private static native final void native_releasePersistentInputSurface(@NonNull Surface surface);
+    private native final void native_usePersistentInputSurface(@NonNull Surface surface);
+
     private native final void native_setCallback(@Nullable Callback cb);
 
     private native final void native_configure(
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 1b054cc..a2f596b 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.Application;
@@ -142,22 +143,28 @@
 
     /**
      * Configures the recorder to use a persistent surface when using SURFACE video source.
-     * <p> May only be called after {@link #prepare} in lieu of {@link #getSurface}.
-     * Frames rendered to the Surface before {@link #start} will be discarded.</p>
+     * <p> May only be called before {@link #prepare}. If called, {@link #getSurface} should
+     * not be used and will throw IllegalStateException. Frames rendered to the Surface
+     * before {@link #start} will be discarded.</p>
 
      * @param surface a persistent input surface created by
      *           {@link MediaCodec#createPersistentInputSurface}
-     * @throws IllegalStateException if it is called before {@link #prepare}, after
-     * {@link #stop}, or is called when VideoSource is not set to SURFACE.
+     * @throws IllegalStateException if it is called after {@link #prepare} and before
+     * {@link #stop}.
      * @throws IllegalArgumentException if the surface was not created by
      *           {@link MediaCodec#createPersistentInputSurface}.
      * @see MediaCodec#createPersistentInputSurface
      * @see MediaRecorder.VideoSource
      */
-    public void usePersistentSurface(Surface surface) {
-        throw new IllegalArgumentException("not implemented");
+    public void usePersistentSurface(@NonNull Surface surface) {
+        if (!(surface instanceof MediaCodec.PersistentSurface)) {
+            throw new IllegalArgumentException("not a PersistentSurface");
+        }
+        native_usePersistentSurface(surface);
     }
 
+    private native final void native_usePersistentSurface(@NonNull Surface surface);
+
     /**
      * Sets a Surface to show a preview of recorded media (video). Calls this
      * before prepare() to make sure that the desirable preview display is
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 39b79de..f808c0d1 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -39,7 +39,7 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/MediaErrors.h>
-
+#include <media/stagefright/PersistentSurface.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 #include <system/window.h>
@@ -75,6 +75,14 @@
     jint reasonReclaimed;
 } gExceptionReason;
 
+static struct {
+    jclass clazz;
+    jfieldID mLock;
+    jfieldID mPersistentObject;
+    jmethodID ctor;
+    jmethodID setNativeObjectLocked;
+} gPersistentSurfaceClassInfo;
+
 struct fields_t {
     jfieldID context;
     jmethodID postEventFromNativeID;
@@ -87,6 +95,7 @@
 };
 
 static fields_t gFields;
+static const void *sRefBaseOwner;
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -263,6 +272,11 @@
     return mCodec->createInputSurface(bufferProducer);
 }
 
+status_t JMediaCodec::usePersistentInputSurface(
+        const sp<PersistentSurface> &surface) {
+    return mCodec->usePersistentInputSurface(surface);
+}
+
 status_t JMediaCodec::start() {
     return mCodec->start();
 }
@@ -915,6 +929,120 @@
     throwExceptionAsNecessary(env, err);
 }
 
+sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface(
+        JNIEnv* env, jobject object) {
+    sp<PersistentSurface> persistentSurface;
+
+    jobject lock = env->GetObjectField(
+            object, gPersistentSurfaceClassInfo.mLock);
+    if (env->MonitorEnter(lock) == JNI_OK) {
+        persistentSurface = reinterpret_cast<PersistentSurface *>(
+                env->GetLongField(object,
+                        gPersistentSurfaceClassInfo.mPersistentObject));
+        env->MonitorExit(lock);
+    }
+    env->DeleteLocalRef(lock);
+
+    return persistentSurface;
+}
+
+static jobject android_media_MediaCodec_createPersistentInputSurface(
+        JNIEnv* env, jclass /* clazz */) {
+    ALOGV("android_media_MediaCodec_createPersistentInputSurface");
+    sp<PersistentSurface> persistentSurface =
+        MediaCodec::CreatePersistentInputSurface();
+
+    if (persistentSurface == NULL) {
+        return NULL;
+    }
+
+    sp<Surface> surface = new Surface(
+            persistentSurface->getBufferProducer(), true);
+    if (surface == NULL) {
+        return NULL;
+    }
+
+    jobject object = env->NewObject(
+            gPersistentSurfaceClassInfo.clazz,
+            gPersistentSurfaceClassInfo.ctor);
+
+    if (object == NULL) {
+        if (env->ExceptionCheck()) {
+            ALOGE("Could not create PersistentSurface.");
+            env->ExceptionClear();
+        }
+        return NULL;
+    }
+
+    jobject lock = env->GetObjectField(
+            object, gPersistentSurfaceClassInfo.mLock);
+    if (env->MonitorEnter(lock) == JNI_OK) {
+        env->CallVoidMethod(
+                object,
+                gPersistentSurfaceClassInfo.setNativeObjectLocked,
+                (jlong)surface.get());
+        env->SetLongField(
+                object,
+                gPersistentSurfaceClassInfo.mPersistentObject,
+                (jlong)persistentSurface.get());
+        env->MonitorExit(lock);
+    } else {
+        env->DeleteLocalRef(object);
+        object = NULL;
+    }
+    env->DeleteLocalRef(lock);
+
+    if (object != NULL) {
+        surface->incStrong(&sRefBaseOwner);
+        persistentSurface->incStrong(&sRefBaseOwner);
+    }
+
+    return object;
+}
+
+static void android_media_MediaCodec_releasePersistentInputSurface(
+        JNIEnv* env, jclass /* clazz */, jobject object) {
+    sp<PersistentSurface> persistentSurface;
+
+    jobject lock = env->GetObjectField(
+            object, gPersistentSurfaceClassInfo.mLock);
+    if (env->MonitorEnter(lock) == JNI_OK) {
+        persistentSurface = reinterpret_cast<PersistentSurface *>(
+            env->GetLongField(
+                    object, gPersistentSurfaceClassInfo.mPersistentObject));
+        env->SetLongField(
+                object,
+                gPersistentSurfaceClassInfo.mPersistentObject,
+                (jlong)0);
+        env->MonitorExit(lock);
+    }
+    env->DeleteLocalRef(lock);
+
+    if (persistentSurface != NULL) {
+        persistentSurface->decStrong(&sRefBaseOwner);
+    }
+    // no need to release surface as it will be released by Surface's jni
+}
+
+static void android_media_MediaCodec_usePersistentInputSurface(
+        JNIEnv* env, jobject thiz, jobject object) {
+    ALOGV("android_media_MediaCodec_usePersistentInputSurface");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+    if (codec == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    sp<PersistentSurface> persistentSurface =
+        android_media_MediaCodec_getPersistentInputSurface(env, object);
+
+    status_t err = codec->usePersistentInputSurface(persistentSurface);
+    if (err != NO_ERROR) {
+        throwExceptionAsNecessary(env, err);
+    }
+}
+
 static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
         jobject thiz) {
     ALOGV("android_media_MediaCodec_createInputSurface");
@@ -1517,6 +1645,29 @@
     CHECK(field != NULL);
     gExceptionReason.reasonReclaimed =
         env->GetStaticIntField(clazz.get(), field);
+
+    clazz.reset(env->FindClass("android/view/Surface"));
+    CHECK(clazz.get() != NULL);
+
+    field = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
+    CHECK(field != NULL);
+    gPersistentSurfaceClassInfo.mLock = field;
+
+    jmethodID method = env->GetMethodID(clazz.get(), "setNativeObjectLocked", "(J)V");
+    CHECK(method != NULL);
+    gPersistentSurfaceClassInfo.setNativeObjectLocked = method;
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$PersistentSurface"));
+    CHECK(clazz.get() != NULL);
+    gPersistentSurfaceClassInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    method = env->GetMethodID(clazz.get(), "<init>", "()V");
+    CHECK(method != NULL);
+    gPersistentSurfaceClassInfo.ctor = method;
+
+    field = env->GetFieldID(clazz.get(), "mPersistentObject", "J");
+    CHECK(field != NULL);
+    gPersistentSurfaceClassInfo.mPersistentObject = field;
 }
 
 static void android_media_MediaCodec_native_setup(
@@ -1567,6 +1718,17 @@
 
     { "native_reset", "()V", (void *)android_media_MediaCodec_reset },
 
+    { "native_releasePersistentInputSurface",
+      "(Landroid/view/Surface;)V",
+       (void *)android_media_MediaCodec_releasePersistentInputSurface},
+
+    { "native_createPersistentInputSurface",
+      "()Landroid/media/MediaCodec$PersistentSurface;",
+      (void *)android_media_MediaCodec_createPersistentInputSurface },
+
+    { "native_usePersistentInputSurface", "(Landroid/view/Surface;)V",
+      (void *)android_media_MediaCodec_usePersistentInputSurface },
+
     { "native_setCallback",
       "(Landroid/media/MediaCodec$Callback;)V",
       (void *)android_media_MediaCodec_native_setCallback },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 5041dac..bf61f42 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -33,6 +33,7 @@
 struct ICrypto;
 struct IGraphicBufferProducer;
 struct MediaCodec;
+struct PersistentSurface;
 class Surface;
 
 struct JMediaCodec : public AHandler {
@@ -57,6 +58,7 @@
             const sp<IGraphicBufferProducer> &surface);
 
     status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+    status_t usePersistentInputSurface(const sp<PersistentSurface> &surface);
 
     status_t start();
     status_t stop();
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 02297fc..0044bed 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -29,6 +29,7 @@
 #include <camera/ICameraService.h>
 #include <camera/Camera.h>
 #include <media/mediarecorder.h>
+#include <media/stagefright/PersistentSurface.h>
 #include <utils/threads.h>
 
 #include <ScopedUtfChars.h>
@@ -48,6 +49,8 @@
 
 // helper function to extract a native Camera object from a Camera Java object
 extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct JNICameraContext** context);
+extern sp<PersistentSurface>
+android_media_MediaCodec_getPersistentInputSurface(JNIEnv* env, jobject object);
 
 struct fields_t {
     jfieldID    context;
@@ -115,6 +118,12 @@
     return android_view_Surface_getSurface(env, clazz);
 }
 
+static sp<PersistentSurface> get_persistentSurface(JNIEnv* env, jobject object)
+{
+    ALOGV("get_persistentSurface");
+    return android_media_MediaCodec_getPersistentInputSurface(env, object);
+}
+
 // Returns true if it throws an exception.
 static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message)
 {
@@ -487,6 +496,18 @@
     android_media_MediaRecorder_release(env, thiz);
 }
 
+void android_media_MediaRecorder_usePersistentSurface(
+        JNIEnv* env, jobject thiz, jobject object) {
+    ALOGV("android_media_MediaRecorder_usePersistentSurface");
+
+    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+
+    sp<PersistentSurface> persistentSurface = get_persistentSurface(env, object);
+
+    process_media_recorder_call(env, mr->usePersistentSurface(persistentSurface),
+            "java/lang/IllegalArgumentException", "native_usePersistentSurface failed.");
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -513,6 +534,7 @@
     {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
                                                                 (void *)android_media_MediaRecorder_native_setup},
     {"native_finalize",      "()V",                             (void *)android_media_MediaRecorder_native_finalize},
+    {"native_usePersistentSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_usePersistentSurface },
 };
 
 // This function only registers the native methods, and is called from
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
index e730329..563b0f3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
@@ -27,6 +27,7 @@
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.hardware.Camera;
+import android.media.MediaCodec;
 import android.media.MediaMetadataRetriever;
 import android.media.MediaPlayer;
 import android.media.MediaRecorder;
@@ -225,10 +226,12 @@
 
     private boolean recordVideoFromSurface(
             int frameRate, int captureRate, int width, int height,
-            int videoFormat, int outFormat, String outFile, boolean videoOnly) {
+            int videoFormat, int outFormat, String outFile, boolean videoOnly,
+            Surface persistentSurface) {
         Log.v(TAG,"recordVideoFromSurface");
         MediaRecorder recorder = new MediaRecorder();
         int sleepTime = 33; // normal capture at 33ms / frame
+        Surface surface = null;
         try {
             if (!videoOnly) {
                 recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
@@ -246,8 +249,15 @@
             if (!videoOnly) {
                 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
             }
+            if (persistentSurface != null) {
+                Log.v(TAG, "using persistent surface");
+                surface = persistentSurface;
+                recorder.usePersistentSurface(surface);
+            }
             recorder.prepare();
-            Surface surface = recorder.getSurface();
+            if (persistentSurface == null) {
+                surface = recorder.getSurface();
+            }
 
             Paint paint = new Paint();
             paint.setTextSize(16);
@@ -283,11 +293,15 @@
 
             Log.v(TAG, "stop");
             recorder.stop();
-            recorder.release();
         } catch (Exception e) {
-            Log.v("record video failed ", e.toString());
-            recorder.release();
+            Log.v(TAG, "record video failed: " + e.toString());
             return false;
+        } finally {
+            recorder.release();
+            // release surface if not using persistent surface
+            if (persistentSurface == null && surface != null) {
+                surface.release();
+            }
         }
         return true;
     }
@@ -550,7 +564,7 @@
 
                 success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
                         MediaRecorder.OutputFormat.THREE_GPP, filename,
-                        k == 0 ? true : false /* videoOnly */);
+                        k == 0 ? true : false /* videoOnly */, null);
                 if (success) {
                     success = validateVideo(filename, 352, 288);
                 }
@@ -564,6 +578,40 @@
         assertTrue("testSurfaceRecording", noOfFailure == 0);
     }
 
+    public void testPersistentSurfaceRecording() {
+        boolean success = false;
+        int noOfFailure = 0;
+        Surface surface = null;
+        try {
+            int codec = MediaRecorder.VideoEncoder.H264;
+            int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec);
+            surface = MediaCodec.createPersistentInputSurface();
+            for (int k = 0; k < 2; k++) {
+                String filename = "/sdcard/surface_persistent" + k + ".3gp";
+
+                Log.v(TAG, "test persistent surface - round " + k);
+                success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
+                        MediaRecorder.OutputFormat.THREE_GPP, filename,
+                        true /* videoOnly */, surface);
+                if (success) {
+                    success = validateVideo(filename, 352, 288);
+                }
+                if (!success) {
+                    noOfFailure++;
+                }
+            }
+        } catch (Exception e) {
+            Log.v(TAG, e.toString());
+        } finally {
+            if (surface != null) {
+                Log.v(TAG, "releasing persistent surface");
+                surface.release();
+                surface = null;
+            }
+        }
+        assertTrue("testPersistentSurfaceRecording", noOfFailure == 0);
+    }
+
     // Test recording from surface source with/without audio
     public void testSurfaceRecordingTimeLapse() {
         boolean success = false;
@@ -583,7 +631,7 @@
                 success = recordVideoFromSurface(
                         frameRate, captureRate, 352, 288, codec,
                         MediaRecorder.OutputFormat.THREE_GPP,
-                        filename, false /* videoOnly */);
+                        filename, false /* videoOnly */, null);
                 if (success) {
                     success = validateVideo(filename, 352, 288);
                     if (success) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d4b3dab..6bf68e8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -332,6 +332,7 @@
 
     final boolean mHasPermanentDpad;
     final long mDrawLockTimeoutMillis;
+    final boolean mAllowAnimationsInLowPowerMode;
 
     final boolean mAllowBootMessages;
 
@@ -899,6 +900,8 @@
                 com.android.internal.R.bool.config_defaultInTouchMode);
         mDrawLockTimeoutMillis = context.getResources().getInteger(
                 com.android.internal.R.integer.config_drawLockTimeoutMillis);
+        mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplaySettings = new DisplaySettings();
@@ -924,7 +927,7 @@
             @Override
             public void onLowPowerModeChanged(boolean enabled) {
                 synchronized (mWindowMap) {
-                    if (mAnimationsDisabled != enabled) {
+                    if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {
                         mAnimationsDisabled = enabled;
                         dispatchNewAnimatorScaleLocked(null);
                     }