diff options
| -rw-r--r-- | api/current.txt | 15 | ||||
| -rw-r--r-- | api/system-current.txt | 15 | ||||
| -rw-r--r-- | core/java/android/text/DynamicLayout.java | 5 | ||||
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 161 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 12 | ||||
| -rwxr-xr-x | core/res/res/values/config.xml | 3 | ||||
| -rwxr-xr-x | core/res/res/values/symbols.xml | 1 | ||||
| -rw-r--r-- | docs/html/sdk/index.jd | 2 | ||||
| -rw-r--r-- | media/java/android/media/MediaCodec.java | 24 | ||||
| -rw-r--r-- | media/java/android/media/MediaRecorder.java | 19 | ||||
| -rw-r--r-- | media/jni/android_media_MediaCodec.cpp | 164 | ||||
| -rw-r--r-- | media/jni/android_media_MediaCodec.h | 2 | ||||
| -rw-r--r-- | media/jni/android_media_MediaRecorder.cpp | 22 | ||||
| -rw-r--r-- | media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java | 62 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/WindowManagerService.java | 5 |
15 files changed, 462 insertions, 50 deletions
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 @@ package android.text { 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 @@ package android.text { 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 @@ public class DynamicLayout extends Layout 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 @@ public class StaticLayout extends Layout { 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 @@ public class StaticLayout extends Layout { 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 @@ public class StaticLayout extends Layout { 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 @@ public class StaticLayout extends Layout { 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 @@ public class StaticLayout extends Layout { 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) { - mSpacingMult = spacingMult; - return this; - } - - public Builder setSpacingAdd(float spacingAdd) { + /** + * 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; } + /** + * 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 @@ public class StaticLayout extends Layout { 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 @@ public class StaticLayout extends Layout { : 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } 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 @@ page.template=sdk 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 @@ final public class MediaCodec { */ @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 @@ final public class MediaCodec { * {@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 @@ public class MediaRecorder /** * 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..f808c0d 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 @@ static struct ExceptionReason { 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 @@ struct fields_t { }; static fields_t gFields; +static const void *sRefBaseOwner; //////////////////////////////////////////////////////////////////////////////// @@ -263,6 +272,11 @@ status_t JMediaCodec::createInputSurface( 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 @@ static void android_media_MediaCodec_native_setSurface( 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 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { 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 @@ static JNINativeMethod gMethods[] = { { "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 AString; struct ICrypto; struct IGraphicBufferProducer; struct MediaCodec; +struct PersistentSurface; class Surface; struct JMediaCodec : public AHandler { @@ -57,6 +58,7 @@ struct JMediaCodec : public AHandler { 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 @@ using namespace android; // 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 @@ static sp<Surface> get_surface(JNIEnv* env, jobject clazz) 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_native_finalize(JNIEnv *env, jobject thiz) 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 @@ static JNINativeMethod gMethods[] = { {"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.Color; 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 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra 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 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra 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 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra 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 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra 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 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra 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 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase2<MediaFra 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 @@ public class WindowManagerService extends IWindowManager.Stub final boolean mHasPermanentDpad; final long mDrawLockTimeoutMillis; + final boolean mAllowAnimationsInLowPowerMode; final boolean mAllowBootMessages; @@ -899,6 +900,8 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void onLowPowerModeChanged(boolean enabled) { synchronized (mWindowMap) { - if (mAnimationsDisabled != enabled) { + if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) { mAnimationsDisabled = enabled; dispatchNewAnimatorScaleLocked(null); } |
