summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.xml86
-rw-r--r--core/java/android/util/JsonReader.java34
-rw-r--r--core/java/android/util/JsonWriter.java54
-rw-r--r--core/java/android/util/MalformedJsonException.java31
-rw-r--r--core/java/android/webkit/WebView.java39
-rw-r--r--core/java/android/widget/TextView.java34
-rw-r--r--core/jni/com_android_internal_os_ZygoteInit.cpp35
-rw-r--r--core/tests/coretests/src/android/util/JsonReaderTest.java53
-rw-r--r--core/tests/coretests/src/android/util/JsonWriterTest.java44
-rw-r--r--include/gui/SurfaceTexture.h7
-rw-r--r--include/media/stagefright/ACodec.h4
-rw-r--r--include/media/stagefright/foundation/AMessage.h14
-rw-r--r--libs/gui/SurfaceTexture.cpp18
-rw-r--r--media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp12
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp45
-rw-r--r--media/libstagefright/ACodec.cpp112
-rw-r--r--media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp5
-rw-r--r--media/libstagefright/foundation/AMessage.cpp28
-rw-r--r--tests/CoreTests/android/core/HeapTest.java90
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java3
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java30
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java92
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java13
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java137
24 files changed, 700 insertions, 320 deletions
diff --git a/api/current.xml b/api/current.xml
index 57a76de65994..eb021b90e643 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -198616,6 +198616,17 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="isLenient"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="nextBoolean"
return="boolean"
abstract="false"
@@ -198746,21 +198757,6 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
-<method name="syntaxError"
- return="java.io.IOException"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
</class>
<class name="JsonToken"
extends="java.lang.Enum"
@@ -198893,6 +198889,17 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="isLenient"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="name"
return="android.util.JsonWriter"
abstract="false"
@@ -198934,6 +198941,19 @@
<parameter name="indent" type="java.lang.String">
</parameter>
</method>
+<method name="setLenient"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="lenient" type="boolean">
+</parameter>
+</method>
<method name="value"
return="android.util.JsonWriter"
abstract="false"
@@ -198994,6 +199014,21 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="value"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.Number">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
</class>
<class name="Log"
extends="java.lang.Object"
@@ -199373,6 +199408,25 @@
</parameter>
</method>
</class>
+<class name="MalformedJsonException"
+ extends="java.io.IOException"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="MalformedJsonException"
+ type="android.util.MalformedJsonException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
<class name="MonthDisplayHelper"
extends="java.lang.Object"
abstract="false"
@@ -258003,7 +258057,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index 80255452ca6e..8f44895bab01 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,6 +16,7 @@
package android.util;
+import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.io.Closeable;
@@ -249,6 +250,13 @@ public final class JsonReader implements Closeable {
}
/**
+ * Returns true if this parser is liberal in what it accepts.
+ */
+ public boolean isLenient() {
+ return lenient;
+ }
+
+ /**
* Consumes the next token from the JSON stream and asserts that it is the
* beginning of a new array.
*/
@@ -311,7 +319,7 @@ public final class JsonReader implements Closeable {
case EMPTY_DOCUMENT:
replaceTop(JsonScope.NONEMPTY_DOCUMENT);
JsonToken firstToken = nextValue();
- if (token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) {
+ if (!lenient && token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) {
throw new IOException(
"Expected JSON document to start with '[' or '{' but was " + token);
}
@@ -327,7 +335,15 @@ public final class JsonReader implements Closeable {
case NONEMPTY_OBJECT:
return nextInObject(false);
case NONEMPTY_DOCUMENT:
- return token = JsonToken.END_DOCUMENT;
+ try {
+ JsonToken token = nextValue();
+ if (lenient) {
+ return token;
+ }
+ throw syntaxError("Expected EOF");
+ } catch (EOFException e) {
+ return token = JsonToken.END_DOCUMENT; // TODO: avoid throwing here?
+ }
case CLOSED:
throw new IllegalStateException("JsonReader is closed");
default:
@@ -758,7 +774,7 @@ public final class JsonReader implements Closeable {
}
}
- throw syntaxError("End of input");
+ throw new EOFException("End of input");
}
private void checkLenient() throws IOException {
@@ -1030,8 +1046,6 @@ public final class JsonReader implements Closeable {
* form -12.34e+56. Fractional and exponential parts are optional. Leading
* zeroes are not allowed in the value or exponential part, but are allowed
* in the fraction.
- *
- * <p>This has a side effect of setting isInteger.
*/
private JsonToken decodeNumber(char[] chars, int offset, int length) {
int i = offset;
@@ -1085,8 +1099,8 @@ public final class JsonReader implements Closeable {
* Throws a new IO exception with the given message and a context snippet
* with this reader's content.
*/
- public IOException syntaxError(String message) throws IOException {
- throw new JsonSyntaxException(message + " near " + getSnippet());
+ private IOException syntaxError(String message) throws IOException {
+ throw new MalformedJsonException(message + " near " + getSnippet());
}
private CharSequence getSnippet() {
@@ -1097,10 +1111,4 @@ public final class JsonReader implements Closeable {
snippet.append(buffer, pos, afterPos);
return snippet;
}
-
- private static class JsonSyntaxException extends IOException {
- private JsonSyntaxException(String s) {
- super(s);
- }
- }
}
diff --git a/core/java/android/util/JsonWriter.java b/core/java/android/util/JsonWriter.java
index 89cad79eb858..47e84c51dd25 100644
--- a/core/java/android/util/JsonWriter.java
+++ b/core/java/android/util/JsonWriter.java
@@ -138,6 +138,8 @@ public final class JsonWriter implements Closeable {
*/
private String separator = ":";
+ private boolean lenient;
+
/**
* Creates a new instance that writes a JSON-encoded stream to {@code out}.
* For best performance, ensure {@link Writer} is buffered; wrapping in
@@ -169,6 +171,29 @@ public final class JsonWriter implements Closeable {
}
/**
+ * Configure this writer to relax its syntax rules. By default, this writer
+ * only emits well-formed JSON as specified by <a
+ * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer
+ * to lenient permits the following:
+ * <ul>
+ * <li>Top-level values of any type. With strict writing, the top-level
+ * value must be an object or an array.
+ * <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
+ * Double#isInfinite() infinities}.
+ * </ul>
+ */
+ public void setLenient(boolean lenient) {
+ this.lenient = lenient;
+ }
+
+ /**
+ * Returns true if this writer has relaxed syntax rules.
+ */
+ public boolean isLenient() {
+ return lenient;
+ }
+
+ /**
* Begins encoding a new array. Each call to this method must be paired with
* a call to {@link #endArray}.
*
@@ -306,11 +331,11 @@ public final class JsonWriter implements Closeable {
* Encodes {@code value}.
*
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
- * {@link Double#isInfinite() infinities}.
+ * {@link Double#isInfinite() infinities} unless this writer is lenient.
* @return this writer.
*/
public JsonWriter value(double value) throws IOException {
- if (Double.isNaN(value) || Double.isInfinite(value)) {
+ if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
}
beforeValue(false);
@@ -330,6 +355,28 @@ public final class JsonWriter implements Closeable {
}
/**
+ * Encodes {@code value}.
+ *
+ * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
+ * {@link Double#isInfinite() infinities} unless this writer is lenient.
+ * @return this writer.
+ */
+ public JsonWriter value(Number value) throws IOException {
+ if (value == null) {
+ return nullValue();
+ }
+
+ String string = value.toString();
+ if (!lenient &&
+ (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
+ throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+ }
+ beforeValue(false);
+ out.append(string);
+ return this;
+ }
+
+ /**
* Ensures all buffered data is written to the underlying {@link Writer}
* and flushes that writer.
*/
@@ -364,7 +411,6 @@ public final class JsonWriter implements Closeable {
switch (c) {
case '"':
case '\\':
- case '/':
out.write('\\');
out.write(c);
break;
@@ -439,7 +485,7 @@ public final class JsonWriter implements Closeable {
private void beforeValue(boolean root) throws IOException {
switch (peek()) {
case EMPTY_DOCUMENT: // first in document
- if (!root) {
+ if (!lenient && !root) {
throw new IllegalStateException(
"JSON must start with an array or an object.");
}
diff --git a/core/java/android/util/MalformedJsonException.java b/core/java/android/util/MalformedJsonException.java
new file mode 100644
index 000000000000..63c19ff86cd4
--- /dev/null
+++ b/core/java/android/util/MalformedJsonException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.io.IOException;
+
+/**
+ * Thrown when a reader encounters malformed JSON. Some syntax errors can be
+ * ignored by calling {@link JsonReader#setLenient(boolean)}.
+ */
+public final class MalformedJsonException extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ public MalformedJsonException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 61f0bce4b90a..f98ad74de7eb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2961,12 +2961,12 @@ public class WebView extends AbsoluteLayout
* @return boolean True if the find dialog is shown, false otherwise.
*/
public boolean showFindDialog(String text, boolean showIme) {
- mFindCallback = new FindActionModeCallback(mContext);
- if (startActionMode(mFindCallback) == null) {
+ FindActionModeCallback callback = new FindActionModeCallback(mContext);
+ if (startActionMode(callback) == null) {
// Could not start the action mode, so end Find on page
- mFindCallback = null;
return false;
}
+ mFindCallback = callback;
setFindIsUp(true);
mFindCallback.setWebView(this);
if (showIme) {
@@ -4564,14 +4564,6 @@ public class WebView extends AbsoluteLayout
return true;
}
- if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
- || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
- if (!nativePageShouldHandleShiftAndArrows() && !nativeCursorWantsKeyEvents()
- && !mSelectingText) {
- setUpSelect();
- }
- }
-
if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
if (event.hasNoModifiers()) {
pageUp(false);
@@ -4662,12 +4654,6 @@ public class WebView extends AbsoluteLayout
if (!wantsKeyEvents) return false;
}
- if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
- && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
- // turn off copy select if a shift-key combo is pressed
- selectionDone();
- }
-
if (getSettings().getNavDump()) {
switch (keyCode) {
case KeyEvent.KEYCODE_4:
@@ -4766,14 +4752,6 @@ public class WebView extends AbsoluteLayout
return true;
}
- if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
- || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
- if (!nativePageShouldHandleShiftAndArrows() && copySelection()) {
- selectionDone();
- return true;
- }
- }
-
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
if (nativePageShouldHandleShiftAndArrows()) {
@@ -5234,7 +5212,8 @@ public class WebView extends AbsoluteLayout
// Textfields, plugins, and contentEditable nodes need to receive the
// shift up key even if another key was released while the shift key
// was held down.
- if (!inEditingMode() && (mNativeClass == 0
+ boolean inEditingMode = inEditingMode();
+ if (!inEditingMode && (mNativeClass == 0
|| !nativePageShouldHandleShiftAndArrows())) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
mGotKeyDown = true;
@@ -5251,7 +5230,13 @@ public class WebView extends AbsoluteLayout
}
if (dispatch) {
- return super.dispatchKeyEvent(event);
+ if (inEditingMode) {
+ // Ensure that the WebTextView gets the event, even if it does
+ // not currently have a bounds.
+ return mWebTextView.dispatchKeyEvent(event);
+ } else {
+ return super.dispatchKeyEvent(event);
+ }
} else {
// We didn't dispatch, so let something else handle the key
return false;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cc5be00d19e5..7675e0cb7c52 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4780,7 +4780,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
hideControllers();
- stopSelectionActionMode();
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
@@ -5119,6 +5118,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mInputMethodState != null) {
mInputMethodState.mExtracting = req;
}
+ // This stops a possible text selection mode. Maybe not intended.
hideControllers();
}
@@ -6781,7 +6781,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
- // Hide the controller if the amount of content changed
+ // Hide the controllers if the amount of content changed
if (before != after) {
hideControllers();
}
@@ -8207,9 +8207,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
selectCurrentWord();
}
- final InputMethodManager imm = (InputMethodManager)
- getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(this, 0, null);
+ if (!mTextIsSelectable) {
+ // Show the IME, except when selection non editable text.
+ final InputMethodManager imm = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(this, 0, null);
+ }
ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
mSelectionActionMode = startActionMode(actionModeCallback);
@@ -8233,6 +8236,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private void stopSelectionActionMode() {
if (mSelectionActionMode != null) {
+ // This will hide the mSelectionModifierCursorController
mSelectionActionMode.finish();
}
}
@@ -8356,8 +8360,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mCustomSelectionActionModeCallback != null) {
mCustomSelectionActionModeCallback.onDestroyActionMode(mode);
}
- Selection.setSelection((Spannable) mText, getSelectionStart());
- hideSelectionModifierCursorController();
+ Selection.setSelection((Spannable) mText, getSelectionEnd());
+
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.hide();
+ }
+
mSelectionActionMode = null;
}
}
@@ -9173,16 +9181,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private void hideSelectionModifierCursorController() {
- // No need to create the controller to hide it.
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.hide();
- }
- }
-
+ /**
+ * Hides the insertion controller and stops text selection mode, hiding the selection controller
+ */
private void hideControllers() {
hideInsertionPointCursorController();
- hideSelectionModifierCursorController();
+ stopSelectionActionMode();
}
/**
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index ada4dd339be2..52e8f4251bb9 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -45,14 +45,10 @@ namespace android {
static jint com_android_internal_os_ZygoteInit_setreuid(
JNIEnv* env, jobject clazz, jint ruid, jint euid)
{
- int err;
-
- errno = 0;
- err = setreuid(ruid, euid);
-
- //LOGI("setreuid(%d,%d) err %d errno %d", ruid, euid, err, errno);
-
- return errno;
+ if (setreuid(ruid, euid) < 0) {
+ return errno;
+ }
+ return 0;
}
/*
@@ -62,14 +58,10 @@ static jint com_android_internal_os_ZygoteInit_setreuid(
static jint com_android_internal_os_ZygoteInit_setregid(
JNIEnv* env, jobject clazz, jint rgid, jint egid)
{
- int err;
-
- errno = 0;
- err = setregid(rgid, egid);
-
- //LOGI("setregid(%d,%d) err %d errno %d", rgid, egid, err, errno);
-
- return errno;
+ if (setregid(rgid, egid) < 0) {
+ return errno;
+ }
+ return 0;
}
/*
@@ -79,13 +71,10 @@ static jint com_android_internal_os_ZygoteInit_setregid(
static jint com_android_internal_os_ZygoteInit_setpgid(
JNIEnv* env, jobject clazz, jint pid, jint pgid)
{
- int err;
-
- errno = 0;
-
- err = setpgid(pid, pgid);
-
- return errno;
+ if (setpgid(pid, pgid) < 0) {
+ return errno;
+ }
+ return 0;
}
/*
diff --git a/core/tests/coretests/src/android/util/JsonReaderTest.java b/core/tests/coretests/src/android/util/JsonReaderTest.java
index 216a772dc412..b5c2c2743f33 100644
--- a/core/tests/coretests/src/android/util/JsonReaderTest.java
+++ b/core/tests/coretests/src/android/util/JsonReaderTest.java
@@ -16,11 +16,10 @@
package android.util;
-import java.util.Arrays;
-import junit.framework.TestCase;
-
import java.io.IOException;
import java.io.StringReader;
+import java.util.Arrays;
+import junit.framework.TestCase;
public final class JsonReaderTest extends TestCase {
@@ -677,7 +676,7 @@ public final class JsonReaderTest extends TestCase {
try {
reader.nextString();
fail();
- } catch (IOException expected) {
+ } catch (MalformedJsonException expected) {
}
}
@@ -811,4 +810,50 @@ public final class JsonReaderTest extends TestCase {
reader.nextNull();
reader.endArray();
}
+
+ public void testStrictMultipleTopLevelValues() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[] []"));
+ reader.beginArray();
+ reader.endArray();
+ try {
+ reader.peek();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientMultipleTopLevelValues() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[] true {}"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.endArray();
+ assertEquals(true, reader.nextBoolean());
+ reader.beginObject();
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testStrictTopLevelValueType() {
+ JsonReader reader = new JsonReader(new StringReader("true"));
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientTopLevelValueType() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("true"));
+ reader.setLenient(true);
+ assertEquals(true, reader.nextBoolean());
+ }
+
+ public void testStrictNonExecutePrefix() {
+ JsonReader reader = new JsonReader(new StringReader(")]}'\n []"));
+ try {
+ reader.beginArray();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/util/JsonWriterTest.java b/core/tests/coretests/src/android/util/JsonWriterTest.java
index fa840239d7b8..b29e2fdde08c 100644
--- a/core/tests/coretests/src/android/util/JsonWriterTest.java
+++ b/core/tests/coretests/src/android/util/JsonWriterTest.java
@@ -16,10 +16,11 @@
package android.util;
-import junit.framework.TestCase;
-
import java.io.IOException;
import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import junit.framework.TestCase;
public final class JsonWriterTest extends TestCase {
@@ -119,7 +120,7 @@ public final class JsonWriterTest extends TestCase {
JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.beginObject();
jsonWriter.name("a");
- jsonWriter.value(null);
+ jsonWriter.value((String) null);
jsonWriter.endObject();
assertEquals("{\"a\":null}", stringWriter.toString());
}
@@ -145,6 +146,27 @@ public final class JsonWriterTest extends TestCase {
}
}
+ public void testNonFiniteBoxedDoubles() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ try {
+ jsonWriter.value(new Double(Double.NaN));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ jsonWriter.value(new Double(Double.NEGATIVE_INFINITY));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ jsonWriter.value(new Double(Double.POSITIVE_INFINITY));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
public void testDoubles() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
@@ -189,6 +211,22 @@ public final class JsonWriterTest extends TestCase {
+ "9223372036854775807]", stringWriter.toString());
}
+ public void testNumbers() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.value(new BigInteger("0"));
+ jsonWriter.value(new BigInteger("9223372036854775808"));
+ jsonWriter.value(new BigInteger("-9223372036854775809"));
+ jsonWriter.value(new BigDecimal("3.141592653589793238462643383"));
+ jsonWriter.endArray();
+ jsonWriter.close();
+ assertEquals("[0,"
+ + "9223372036854775808,"
+ + "-9223372036854775809,"
+ + "3.141592653589793238462643383]", stringWriter.toString());
+ }
+
public void testBooleans() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index ff92e0858943..cbc15d81b980 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -113,8 +113,11 @@ private:
int mBufferCount;
// mCurrentTexture is the buffer slot index of the buffer that is currently
- // bound to the OpenGL texture. A value of INVALID_BUFFER_SLOT, indicating
- // that no buffer is currently bound to the texture.
+ // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
+ // indicating that no buffer slot is currently bound to the texture. Note,
+ // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+ // that no buffer is bound to the texture. A call to setBufferCount will
+ // reset mCurrentTexture to INVALID_BUFFER_SLOT.
int mCurrentTexture;
// mLastQueued is the buffer slot index of the most recently enqueued buffer.
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index b3815c4f24ac..4599d70d1faf 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -101,6 +101,8 @@ private:
List<sp<AMessage> > mDeferredQueue;
+ bool mSentFormat;
+
status_t allocateBuffersOnPort(OMX_U32 portIndex);
status_t freeBuffersOnPort(OMX_U32 portIndex);
status_t freeBuffer(OMX_U32 portIndex, size_t i);
@@ -145,6 +147,8 @@ private:
void deferMessage(const sp<AMessage> &msg);
void processDeferredMessages();
+ void sendFormatChange();
+
DISALLOW_EVIL_CONSTRUCTORS(ACodec);
};
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 91ec60c894f3..72dc730191fd 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -52,6 +52,10 @@ struct AMessage : public RefBase {
void setObject(const char *name, const sp<RefBase> &obj);
void setMessage(const char *name, const sp<AMessage> &obj);
+ void setRect(
+ const char *name,
+ int32_t left, int32_t top, int32_t right, int32_t bottom);
+
bool findInt32(const char *name, int32_t *value) const;
bool findInt64(const char *name, int64_t *value) const;
bool findSize(const char *name, size_t *value) const;
@@ -62,6 +66,10 @@ struct AMessage : public RefBase {
bool findObject(const char *name, sp<RefBase> *obj) const;
bool findMessage(const char *name, sp<AMessage> *obj) const;
+ bool findRect(
+ const char *name,
+ int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
+
void post(int64_t delayUs = 0);
// Performs a deep-copy of "this", contained messages are in turn "dup'ed".
@@ -85,11 +93,16 @@ private:
kTypeString,
kTypeObject,
kTypeMessage,
+ kTypeRect,
};
uint32_t mWhat;
ALooper::handler_id mTarget;
+ struct Rect {
+ int32_t mLeft, mTop, mRight, mBottom;
+ };
+
struct Item {
union {
int32_t int32Value;
@@ -100,6 +113,7 @@ private:
void *ptrValue;
RefBase *refValue;
AString *stringValue;
+ Rect rectValue;
} u;
const char *mName;
Type mType;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 9579996342cc..11a48d989425 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "SurfaceTexture"
+//#define LOG_NDEBUG 0
#define GL_GLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES
@@ -36,21 +37,32 @@ namespace android {
SurfaceTexture::SurfaceTexture(GLuint tex) :
mBufferCount(MIN_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT),
mLastQueued(INVALID_BUFFER_SLOT), mTexName(tex) {
+ LOGV("SurfaceTexture::SurfaceTexture");
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
+ mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
+ mSlots[i].mOwnedByClient = false;
+ }
}
SurfaceTexture::~SurfaceTexture() {
+ LOGV("SurfaceTexture::~SurfaceTexture");
freeAllBuffers();
}
status_t SurfaceTexture::setBufferCount(int bufferCount) {
+ LOGV("SurfaceTexture::setBufferCount");
Mutex::Autolock lock(mMutex);
freeAllBuffers();
mBufferCount = bufferCount;
+ mCurrentTexture = INVALID_BUFFER_SLOT;
+ mLastQueued = INVALID_BUFFER_SLOT;
return OK;
}
sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+ LOGV("SurfaceTexture::requestBuffer");
Mutex::Autolock lock(mMutex);
if (buf < 0 || mBufferCount <= buf) {
LOGE("requestBuffer: slot index out of range [0, %d]: %d",
@@ -75,6 +87,7 @@ sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
}
status_t SurfaceTexture::dequeueBuffer(int *buf) {
+ LOGV("SurfaceTexture::dequeueBuffer");
Mutex::Autolock lock(mMutex);
int found = INVALID_BUFFER_SLOT;
for (int i = 0; i < mBufferCount; i++) {
@@ -92,6 +105,7 @@ status_t SurfaceTexture::dequeueBuffer(int *buf) {
}
status_t SurfaceTexture::queueBuffer(int buf) {
+ LOGV("SurfaceTexture::queueBuffer");
Mutex::Autolock lock(mMutex);
if (buf < 0 || mBufferCount <= buf) {
LOGE("queueBuffer: slot index out of range [0, %d]: %d",
@@ -111,6 +125,7 @@ status_t SurfaceTexture::queueBuffer(int buf) {
}
void SurfaceTexture::cancelBuffer(int buf) {
+ LOGV("SurfaceTexture::cancelBuffer");
Mutex::Autolock lock(mMutex);
if (buf < 0 || mBufferCount <= buf) {
LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount,
@@ -124,18 +139,21 @@ void SurfaceTexture::cancelBuffer(int buf) {
}
status_t SurfaceTexture::setCrop(const Rect& reg) {
+ LOGV("SurfaceTexture::setCrop");
Mutex::Autolock lock(mMutex);
// XXX: How should we handle crops?
return OK;
}
status_t SurfaceTexture::setTransform(uint32_t transform) {
+ LOGV("SurfaceTexture::setTransform");
Mutex::Autolock lock(mMutex);
// XXX: How should we handle transforms?
return OK;
}
status_t SurfaceTexture::updateTexImage() {
+ LOGV("SurfaceTexture::updateTexImage");
Mutex::Autolock lock(mMutex);
// We always bind the texture even if we don't update its contents.
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
index 4d4cd8dd807c..802d1fb1b263 100644
--- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -309,6 +309,18 @@ void DecoderWrapper::WrapperReader::sendFormatChange() {
realNotify->setInt32("width", width);
realNotify->setInt32("height", height);
+
+ int32_t cropLeft, cropTop, cropRight, cropBottom;
+ if (!meta->findRect(
+ kKeyCropRect,
+ &cropLeft, &cropTop, &cropRight, &cropBottom)) {
+ cropLeft = 0;
+ cropTop = 0;
+ cropRight = width - 1;
+ cropBottom = height - 1;
+ }
+
+ realNotify->setRect("crop", cropLeft, cropTop, cropRight, cropBottom);
}
notify->post();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index e1b371eb7c73..7f534c07115b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -271,22 +271,43 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
finishFlushIfPossible();
} else if (what == ACodec::kWhatOutputFormatChanged) {
- CHECK(audio);
-
- int32_t numChannels;
- CHECK(codecRequest->findInt32("channel-count", &numChannels));
+ if (audio) {
+ int32_t numChannels;
+ CHECK(codecRequest->findInt32("channel-count", &numChannels));
- int32_t sampleRate;
- CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
+ int32_t sampleRate;
+ CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
- LOGV("Audio output format changed to %d Hz, %d channels",
- sampleRate, numChannels);
+ LOGV("Audio output format changed to %d Hz, %d channels",
+ sampleRate, numChannels);
- mAudioSink->close();
- CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
- mAudioSink->start();
+ mAudioSink->close();
+ CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
+ mAudioSink->start();
- mRenderer->signalAudioSinkChanged();
+ mRenderer->signalAudioSinkChanged();
+ } else {
+ // video
+
+ int32_t width, height;
+ CHECK(codecRequest->findInt32("width", &width));
+ CHECK(codecRequest->findInt32("height", &height));
+
+ int32_t cropLeft, cropTop, cropRight, cropBottom;
+ CHECK(codecRequest->findRect(
+ "crop",
+ &cropLeft, &cropTop, &cropRight, &cropBottom));
+
+ LOGV("Video output format changed to %d x %d "
+ "(crop: %d, %d, %d, %d)",
+ width, height,
+ cropLeft, cropTop, cropRight, cropBottom);
+
+ notifyListener(
+ MEDIA_SET_VIDEO_SIZE,
+ cropRight - cropLeft + 1,
+ cropBottom - cropTop + 1);
+ }
} else if (what == ACodec::kWhatShutdownCompleted) {
LOGV("%s shutdown completed", audio ? "audio" : "video");
if (audio) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 39e0c5105b44..dfb4e000e6bf 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -299,7 +299,8 @@ private:
////////////////////////////////////////////////////////////////////////////////
ACodec::ACodec()
- : mNode(NULL) {
+ : mNode(NULL),
+ mSentFormat(false) {
mUninitializedState = new UninitializedState(this);
mLoadedToIdleState = new LoadedToIdleState(this);
mIdleToExecutingState = new IdleToExecutingState(this);
@@ -980,6 +981,103 @@ void ACodec::processDeferredMessages() {
}
}
+void ACodec::sendFormatChange() {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatOutputFormatChanged);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ CHECK_EQ(mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)),
+ (status_t)OK);
+
+ CHECK_EQ((int)def.eDir, (int)OMX_DirOutput);
+
+ switch (def.eDomain) {
+ case OMX_PortDomainVideo:
+ {
+ OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video;
+
+ notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW);
+ notify->setInt32("width", videoDef->nFrameWidth);
+ notify->setInt32("height", videoDef->nFrameHeight);
+
+ OMX_CONFIG_RECTTYPE rect;
+ InitOMXParams(&rect);
+ rect.nPortIndex = kPortIndexOutput;
+
+ if (mOMX->getConfig(
+ mNode, OMX_IndexConfigCommonOutputCrop,
+ &rect, sizeof(rect)) != OK) {
+ rect.nLeft = 0;
+ rect.nTop = 0;
+ rect.nWidth = videoDef->nFrameWidth;
+ rect.nHeight = videoDef->nFrameHeight;
+ }
+
+ CHECK_GE(rect.nLeft, 0);
+ CHECK_GE(rect.nTop, 0);
+ CHECK_GE(rect.nWidth, 0u);
+ CHECK_GE(rect.nHeight, 0u);
+ CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth);
+ CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight);
+
+ notify->setRect(
+ "crop",
+ rect.nLeft,
+ rect.nTop,
+ rect.nLeft + rect.nWidth - 1,
+ rect.nTop + rect.nHeight - 1);
+
+ if (mNativeWindow != NULL) {
+ android_native_rect_t crop;
+ crop.left = rect.nLeft;
+ crop.top = rect.nTop;
+ crop.right = rect.nLeft + rect.nWidth - 1;
+ crop.bottom = rect.nTop + rect.nHeight - 1;
+
+ CHECK_EQ(0, native_window_set_crop(
+ mNativeWindow.get(), &crop));
+ }
+ break;
+ }
+
+ case OMX_PortDomainAudio:
+ {
+ OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
+ CHECK_EQ((int)audioDef->eEncoding, (int)OMX_AUDIO_CodingPCM);
+
+ OMX_AUDIO_PARAM_PCMMODETYPE params;
+ InitOMXParams(&params);
+ params.nPortIndex = kPortIndexOutput;
+
+ CHECK_EQ(mOMX->getParameter(
+ mNode, OMX_IndexParamAudioPcm,
+ &params, sizeof(params)),
+ (status_t)OK);
+
+ CHECK(params.nChannels == 1 || params.bInterleaved);
+ CHECK_EQ(params.nBitPerSample, 16u);
+ CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned);
+ CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear);
+
+ notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW);
+ notify->setInt32("channel-count", params.nChannels);
+ notify->setInt32("sample-rate", params.nSamplingRate);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+
+ notify->post();
+
+ mSentFormat = true;
+}
+
////////////////////////////////////////////////////////////////////////////////
ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState)
@@ -1305,6 +1403,10 @@ bool ACodec::BaseState::onOMXFillBufferDone(
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
}
} else {
+ if (!mCodec->mSentFormat) {
+ mCodec->sendFormatChange();
+ }
+
if (mCodec->mNativeWindow == NULL) {
info->mData->setRange(rangeOffset, rangeLength);
}
@@ -1717,7 +1819,7 @@ bool ACodec::ExecutingState::onOMXEvent(
{
CHECK_EQ(data1, (OMX_U32)kPortIndexOutput);
- if (data2 == OMX_IndexParamPortDefinition) {
+ if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode,
OMX_CommandPortDisable, kPortIndexOutput),
@@ -1729,6 +1831,8 @@ bool ACodec::ExecutingState::onOMXEvent(
}
mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
+ } else if (data2 == OMX_IndexConfigCommonOutputCrop) {
+ mCodec->mSentFormat = false;
} else {
LOGV("[%s] OMX_EventPortSettingsChanged 0x%08lx",
mCodec->mComponentName.c_str(), data2);
@@ -1816,6 +1920,8 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
} else if (data1 == (OMX_U32)OMX_CommandPortEnable) {
CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+ mCodec->mSentFormat = false;
+
LOGV("[%s] Output port now reenabled.",
mCodec->mComponentName.c_str());
@@ -1869,6 +1975,8 @@ bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
void ACodec::ExecutingToIdleState::stateEntered() {
LOGV("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
+
+ mCodec->mSentFormat = false;
}
bool ACodec::ExecutingToIdleState::onOMXEvent(
diff --git a/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp b/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp
index 0f08f6e9fd18..38778fb3aa71 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp
@@ -132,7 +132,10 @@ status_t M4vH263Decoder::start(MetaData *) {
}
MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle);
- CHECK_EQ(mode, actualMode);
+ if (mode != actualMode) {
+ PVCleanUpVideoDecoder(mHandle);
+ return UNKNOWN_ERROR;
+ }
PVSetPostProcType((VideoDecControls *) mHandle, 0);
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 0e40acc505d2..b592c3fc3e06 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -171,6 +171,18 @@ void AMessage::setMessage(const char *name, const sp<AMessage> &obj) {
item->u.refValue = obj.get();
}
+void AMessage::setRect(
+ const char *name,
+ int32_t left, int32_t top, int32_t right, int32_t bottom) {
+ Item *item = allocateItem(name);
+ item->mType = kTypeRect;
+
+ item->u.rectValue.mLeft = left;
+ item->u.rectValue.mTop = top;
+ item->u.rectValue.mRight = right;
+ item->u.rectValue.mBottom = bottom;
+}
+
bool AMessage::findString(const char *name, AString *value) const {
const Item *item = findItem(name, kTypeString);
if (item) {
@@ -198,6 +210,22 @@ bool AMessage::findMessage(const char *name, sp<AMessage> *obj) const {
return false;
}
+bool AMessage::findRect(
+ const char *name,
+ int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const {
+ const Item *item = findItem(name, kTypeRect);
+ if (item == NULL) {
+ return false;
+ }
+
+ *left = item->u.rectValue.mLeft;
+ *top = item->u.rectValue.mTop;
+ *right = item->u.rectValue.mRight;
+ *bottom = item->u.rectValue.mBottom;
+
+ return true;
+}
+
void AMessage::post(int64_t delayUs) {
extern ALooperRoster gLooperRoster;
diff --git a/tests/CoreTests/android/core/HeapTest.java b/tests/CoreTests/android/core/HeapTest.java
index 6116f5e0b5e6..98cb28ae26fe 100644
--- a/tests/CoreTests/android/core/HeapTest.java
+++ b/tests/CoreTests/android/core/HeapTest.java
@@ -540,94 +540,4 @@ public class HeapTest extends TestCase {
objSize = (objSize * 4) / 5;
}
}
-
- // TODO: flaky test
- //@SmallTest
- public void testExternalOomeLarge() {
- /* Just shy of the typical max heap size so that it will actually
- * try to allocate it instead of short-circuiting.
- */
- final int HUGE_SIZE = (16 * 1024 * 1024 - 32);
-
- assertFalse(VMRuntime.getRuntime().trackExternalAllocation(HUGE_SIZE));
- }
-
- /**
- * "Allocates" external memory in progressively smaller chunks until there's
- * only roughly 16 bytes left.
- *
- * @return the number of bytes allocated
- */
- private long allocateMaxExternal() {
- final VMRuntime runtime = VMRuntime.getRuntime();
- final int SIXTEEN_MB = (16 * 1024 * 1024);
- final int MIN_SIZE = 16;
- long totalAllocated = 0;
- boolean success;
-
- success = false;
- try {
- /* "Allocate" progressively smaller chunks to "fill up" the entire heap.
- */
- int objSize = 1 * 1024 * 1024;
- while (objSize >= MIN_SIZE) {
- boolean sawFailure = false;
- for (int i = 0; i < SIXTEEN_MB / objSize; i++) {
- if (runtime.trackExternalAllocation(objSize)) {
- totalAllocated += objSize;
- } else {
- sawFailure = true;
- break;
- }
- }
-
- if (!sawFailure) {
- throw new RuntimeException("Test failed: " +
- "no failure while filling heap");
- }
-
- objSize = (objSize * 4) / 5;
- }
- success = true;
- } finally {
- if (!success) {
- runtime.trackExternalFree(totalAllocated);
- totalAllocated = 0;
- }
- }
- return totalAllocated;
- }
-
- public void xxtest00ExternalOomeSmall() {
- VMRuntime.getRuntime().trackExternalFree(allocateMaxExternal());
- }
-
- /**
- * Allocates as much external memory as possible, then allocates from the heap
- * until an OOME is caught.
- *
- * It's nice to run this test while the real heap is small, hence the '00' in its
- * name to force it to run before testOomeSmall().
- */
- public void xxtest00CombinedOomeSmall() {
- long totalAllocated = 0;
- boolean sawEx = false;
- try {
- totalAllocated = allocateMaxExternal();
- LinkedList<Object> list = new LinkedList<Object>();
- try {
- while (true) {
- list.add((Object)new byte[8192]);
- }
- /*NOTREACHED*/
- } catch (OutOfMemoryError oom) {
- sawEx = true;
- }
- } finally {
- VMRuntime.getRuntime().trackExternalFree(totalAllocated);
- }
- assertTrue(sawEx);
- }
-
- //TODO: test external alloc debugging/inspection
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index e1bf9251e0b1..c042327ebf1b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -195,7 +195,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Capability.RENDER,
Capability.EMBEDDED_LAYOUT,
Capability.VIEW_MANIPULATION,
- Capability.ANIMATE);
+ Capability.PLAY_ANIMATION,
+ Capability.ANIMATED_VIEW_MANIPULATION);
BridgeAssetManager.initSystem();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 72692274b0c2..b0eea4cacb49 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -92,7 +92,7 @@ public final class BridgeContext extends Activity {
// cache for TypedArray generated from IStyleResourceValue object
private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache;
- private BridgeInflater mInflater;
+ private BridgeInflater mBridgeInflater;
private final IProjectCallback mProjectCallback;
private BridgeContentResolver mContentResolver;
@@ -160,7 +160,7 @@ public final class BridgeContext extends Activity {
}
public void setBridgeInflater(BridgeInflater inflater) {
- mInflater = inflater;
+ mBridgeInflater = inflater;
}
public void addViewKey(View view, Object viewKey) {
@@ -221,7 +221,7 @@ public final class BridgeContext extends Activity {
@Override
public LayoutInflater getLayoutInflater() {
- return mInflater;
+ return mBridgeInflater;
}
// ------------ Context methods
@@ -244,7 +244,7 @@ public final class BridgeContext extends Activity {
@Override
public Object getSystemService(String service) {
if (LAYOUT_INFLATER_SERVICE.equals(service)) {
- return mInflater;
+ return mBridgeInflater;
}
// AutoCompleteTextView and MultiAutoCompleteTextView want a window
@@ -591,7 +591,7 @@ public final class BridgeContext extends Activity {
boolean frameworkOnly = false;
- // eleminate the prefix from the string
+ // eliminate the prefix from the string
if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_THEME_REF)) {
frameworkOnly = true;
reference = reference.substring(BridgeConstants.PREFIX_ANDROID_THEME_REF.length());
@@ -677,7 +677,7 @@ public final class BridgeContext extends Activity {
* project resources
*/
private ResourceValue findResValue(String resType, String resName, boolean frameworkOnly) {
- // map of IResouceValue for the given type
+ // map of ResouceValue for the given type
Map<String, ResourceValue> typeMap;
// if allowed, search in the project resources first.
@@ -701,6 +701,13 @@ public final class BridgeContext extends Activity {
}
// didn't find the resource anywhere.
+ // This is normal if the resource is an ID that is generated automatically.
+ // For other resources, we output a warning
+ if ("+id".equals(resType) == false && "+android:id".equals(resType) == false) { //$NON-NLS-1$ //$NON-NLS-2$
+ Bridge.getLog().warning("resources", //$NON-NLS-1$
+ "Couldn't resolve resource @" +
+ (frameworkOnly ? "android:" : "") + resType + "/" + resName);
+ }
return null;
}
@@ -1303,4 +1310,15 @@ public final class BridgeContext extends Activity {
public Context getApplicationContext() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void startActivities(Intent[] arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean isRestricted() {
+ return false;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index 6ae7e032c259..886ef2ac5729 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -43,25 +43,25 @@ import java.util.Map;
*/
public final class BridgeTypedArray extends TypedArray {
- private BridgeResources mResources;
+ private BridgeResources mBridgeResources;
private BridgeContext mContext;
- private ResourceValue[] mData;
+ private ResourceValue[] mResourceData;
private String[] mNames;
private final boolean mPlatformFile;
public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
boolean platformFile) {
super(null, null, null, 0);
- mResources = resources;
+ mBridgeResources = resources;
mContext = context;
mPlatformFile = platformFile;
- mData = new ResourceValue[len];
+ mResourceData = new ResourceValue[len];
mNames = new String[len];
}
/** A bridge-specific method that sets a value in the type array */
public void bridgeSetValue(int index, String name, ResourceValue value) {
- mData[index] = value;
+ mResourceData[index] = value;
mNames[index] = name;
}
@@ -75,7 +75,7 @@ public final class BridgeTypedArray extends TypedArray {
// fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
// first count the array size
int count = 0;
- for (ResourceValue data : mData) {
+ for (ResourceValue data : mResourceData) {
if (data != null) {
count++;
}
@@ -87,8 +87,8 @@ public final class BridgeTypedArray extends TypedArray {
// fill the array with the indices.
int index = 1;
- for (int i = 0 ; i < mData.length ; i++) {
- if (mData[i] != null) {
+ for (int i = 0 ; i < mResourceData.length ; i++) {
+ if (mResourceData[i] != null) {
mIndices[index++] = i;
}
}
@@ -99,7 +99,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int length() {
- return mData.length;
+ return mResourceData.length;
}
/**
@@ -107,7 +107,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public Resources getResources() {
- return mResources;
+ return mBridgeResources;
}
/**
@@ -120,9 +120,9 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public CharSequence getText(int index) {
- if (mData[index] != null) {
+ if (mResourceData[index] != null) {
// FIXME: handle styled strings!
- return mData[index].getValue();
+ return mResourceData[index].getValue();
}
return null;
@@ -138,8 +138,8 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public String getString(int index) {
- if (mData[index] != null) {
- return mData[index].getValue();
+ if (mResourceData[index] != null) {
+ return mResourceData[index].getValue();
}
return null;
@@ -155,11 +155,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean getBoolean(int index, boolean defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s != null) {
return XmlUtils.convertValueToBoolean(s, defValue);
}
@@ -177,11 +177,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getInt(int index, int defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
try {
return (s == null) ? defValue : XmlUtils.convertValueToInt(s, defValue);
@@ -225,11 +225,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getFloat(int index, float defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s != null) {
try {
@@ -259,11 +259,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getColor(int index, int defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
try {
return ResourceHelper.getColor(s);
} catch (NumberFormatException e) {
@@ -288,11 +288,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public ColorStateList getColorStateList(int index) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return null;
}
- String value = mData[index].getValue();
+ String value = mResourceData[index].getValue();
if (value == null) {
return null;
@@ -347,11 +347,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getInteger(int index, int defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s != null) {
try {
@@ -386,11 +386,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getDimension(int index, float defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s == null) {
return defValue;
@@ -402,7 +402,7 @@ public final class BridgeTypedArray extends TypedArray {
}
if (ResourceHelper.stringToFloat(s, mValue)) {
- return mValue.getDimension(mResources.mMetrics);
+ return mValue.getDimension(mBridgeResources.mMetrics);
}
// looks like we were unable to resolve the dimension value
@@ -456,11 +456,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getDimensionPixelSize(int index, int defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s == null) {
return defValue;
@@ -522,11 +522,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getFraction(int index, int base, int pbase, float defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String value = mData[index].getValue();
+ String value = mResourceData[index].getValue();
if (value == null) {
return defValue;
}
@@ -560,8 +560,8 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getResourceId(int index, int defValue) {
- // get the IResource for this index
- ResourceValue resValue = mData[index];
+ // get the Resource for this index
+ ResourceValue resValue = mResourceData[index];
// no data, return the default value.
if (resValue == null) {
@@ -575,7 +575,7 @@ public final class BridgeTypedArray extends TypedArray {
}
// if the attribute was a reference to an id, and not a declaration of an id (@+id), then
- // the xml attribute value was "resolved" which leads us to a IResourceValue with
+ // the xml attribute value was "resolved" which leads us to a ResourceValue with
// getType() returning "id" and getName() returning the id name
// (and getValue() returning null!). We need to handle this!
if (resValue.getType() != null && resValue.getType().equals(BridgeConstants.RES_ID)) {
@@ -662,17 +662,17 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public Drawable getDrawable(int index) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return null;
}
- ResourceValue value = mData[index];
+ ResourceValue value = mResourceData[index];
String stringValue = value.getValue();
if (stringValue == null || BridgeConstants.REFERENCE_NULL.equals(stringValue)) {
return null;
}
- Drawable d = ResourceHelper.getDrawable(value, mContext, mData[index].isFramework());
+ Drawable d = ResourceHelper.getDrawable(value, mContext, mResourceData[index].isFramework());
if (d != null) {
return d;
@@ -700,18 +700,18 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public CharSequence[] getTextArray(int index) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return null;
}
- String value = mData[index].getValue();
+ String value = mResourceData[index].getValue();
if (value != null) {
return new CharSequence[] { value };
}
Bridge.getLog().warning(null, String.format(
String.format("Unknown value for getTextArray(%d) => %s", //DEBUG
- index, mData[index].getName())));
+ index, mResourceData[index].getName())));
return null;
}
@@ -727,11 +727,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean getValue(int index, TypedValue outValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return false;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
return ResourceHelper.stringToFloat(s, outValue);
}
@@ -745,7 +745,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean hasValue(int index) {
- return mData[index] != null;
+ return mResourceData[index] != null;
}
/**
@@ -792,6 +792,6 @@ public final class BridgeTypedArray extends TypedArray {
@Override
public String toString() {
- return mData.toString();
+ return mResourceData.toString();
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
index 125c1e6b6b70..033e065cb479 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -28,7 +28,7 @@ import android.os.Handler_Delegate;
import android.os.Message;
import android.os.Handler_Delegate.IHandlerCallback;
-import java.util.LinkedList;
+import java.util.PriorityQueue;
import java.util.Queue;
/**
@@ -45,7 +45,7 @@ import java.util.Queue;
*/
public abstract class AnimationThread extends Thread {
- private static class MessageBundle {
+ private static class MessageBundle implements Comparable<MessageBundle> {
final Handler mTarget;
final Message mMessage;
final long mUptimeMillis;
@@ -55,11 +55,18 @@ public abstract class AnimationThread extends Thread {
mMessage = message;
mUptimeMillis = uptimeMillis;
}
+
+ public int compareTo(MessageBundle bundle) {
+ if (mUptimeMillis < bundle.mUptimeMillis) {
+ return -1;
+ }
+ return 1;
+ }
}
private final RenderSessionImpl mSession;
- private Queue<MessageBundle> mQueue = new LinkedList<MessageBundle>();
+ private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
private final IAnimationListener mListener;
public AnimationThread(RenderSessionImpl scene, String threadName,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 025a318f0e57..55a5bc084899 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -630,7 +630,7 @@ public class RenderSessionImpl {
* @see LayoutScene#moveChild(Object, Object, int, Map, IAnimationListener)
*/
public Result moveChild(final ViewGroup newParentView, final View childView, final int index,
- Map<String, String> layoutParamsMap, IAnimationListener listener) {
+ Map<String, String> layoutParamsMap, final IAnimationListener listener) {
checkLock();
invalidateRenderingSize();
@@ -647,42 +647,73 @@ public class RenderSessionImpl {
if (listener != null) {
final LayoutParams params = layoutParams;
- new AnimationThread(this, "moveChild", listener) {
- @Override
- public Result preAnimation() {
- // set up the transition for the previous parent.
- LayoutTransition removeTransition = new LayoutTransition();
- previousParent.setLayoutTransition(removeTransition);
-
- // no fade-out. Because we can't rely on layout transition listeners when
- // there is no Animator at all, instead we keep the animator but set its
- // duration to 0.
- // Note: Cannot user Animation.setDuration() directly. Have to set it
- // on the LayoutTransition.
- removeTransition.setDuration(LayoutTransition.DISAPPEARING, 0);
-
- if (previousParent != newParentView) {
- // different parent, set a Layout transition on the new parent.
- newParentView.setLayoutTransition(new LayoutTransition());
+ // there is no support for animating views across layouts, so in case the new and old
+ // parent views are different we fake the animation through a no animation thread.
+ if (previousParent != newParentView) {
+ new Thread("not animated moveChild") {
+ @Override
+ public void run() {
+ Result result = moveView(previousParent, newParentView, childView, index,
+ params);
+ if (result.isSuccess() == false) {
+ listener.done(result);
+ }
+
+ // ready to do the work, acquire the scene.
+ result = acquire(250);
+ if (result.isSuccess() == false) {
+ listener.done(result);
+ return;
+ }
+
+ try {
+ result = render();
+ if (result.isSuccess()) {
+ listener.onNewFrame(RenderSessionImpl.this.getSession());
+ }
+ } finally {
+ release();
+ }
+
+ listener.done(result);
}
+ }.start();
+ } else {
+ new AnimationThread(this, "moveChild", listener) {
- // no fade-in. Because we can't rely on layout transition listeners when
- // there is no Animator at all, instead we keep the animator but set its
- // duration to 0.
- // Note: Cannot user Animation.setDuration() directly. Have to set it
- // on the LayoutTransition.
- newParentView.getLayoutTransition().setDuration(LayoutTransition.APPEARING, 0);
+ @Override
+ public Result preAnimation() {
+ // set up the transition for the parent.
+ LayoutTransition transition = new LayoutTransition();
+ previousParent.setLayoutTransition(transition);
- return moveView(previousParent, newParentView, childView, index, params);
- }
+ // tweak the animation durations and start delays (to match the duration of
+ // animation playing just before).
+ // Note: Cannot user Animation.setDuration() directly. Have to set it
+ // on the LayoutTransition.
+ transition.setDuration(LayoutTransition.DISAPPEARING, 100);
+ // CHANGE_DISAPPEARING plays after DISAPPEARING
+ transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 100);
- @Override
- public void postAnimation() {
- previousParent.setLayoutTransition(null);
- newParentView.setLayoutTransition(null);
- }
- }.start();
+ transition.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 100);
+
+ transition.setDuration(LayoutTransition.CHANGE_APPEARING, 100);
+ // CHANGE_APPEARING plays after CHANGE_APPEARING
+ transition.setStartDelay(LayoutTransition.APPEARING, 100);
+
+ transition.setDuration(LayoutTransition.APPEARING, 100);
+
+ return moveView(previousParent, newParentView, childView, index, params);
+ }
+
+ @Override
+ public void postAnimation() {
+ previousParent.setLayoutTransition(null);
+ newParentView.setLayoutTransition(null);
+ }
+ }.start();
+ }
// always return success since the real status will come through the listener.
return SUCCESS.createResult(layoutParams);
@@ -707,7 +738,7 @@ public class RenderSessionImpl {
*
* @param previousParent the previous parent, still owning the child at the time of the call.
* @param newParent the new parent
- * @param view the view to move
+ * @param movedView the view to move
* @param index the new location in the new parent
* @param params an option (can be null) {@link LayoutParams} instance.
*
@@ -715,12 +746,12 @@ public class RenderSessionImpl {
* {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
* adding views.
*/
- private Result moveView(ViewGroup previousParent, final ViewGroup newParent, View view,
- final int index, final LayoutParams params) {
+ private Result moveView(ViewGroup previousParent, final ViewGroup newParent,
+ final View movedView, final int index, final LayoutParams params) {
try {
// check if there is a transition on the previousParent.
- LayoutTransition transition = previousParent.getLayoutTransition();
- if (transition != null) {
+ LayoutTransition previousTransition = previousParent.getLayoutTransition();
+ if (previousTransition != null) {
// in this case there is an animation. This means we have to wait for the child's
// parent reference to be null'ed out so that we can add it to the new parent.
// It is technically removed right before the DISAPPEARING animation is done (if
@@ -730,48 +761,50 @@ public class RenderSessionImpl {
// parent, we need to wait until the CHANGE_DISAPPEARING animation is done before
// adding the child or the child will appear in its new location before the
// other children have made room for it.
- // If the parents are different, then we can add the child to its new parent right
- // after the DISAPPEARING animation is done.
-
- final int waitForType = newParent == previousParent ?
- LayoutTransition.CHANGE_DISAPPEARING : LayoutTransition.DISAPPEARING;
// add a listener to the transition to be notified of the actual removal.
- transition.addTransitionListener(new TransitionListener() {
+ previousTransition.addTransitionListener(new TransitionListener() {
+ private int mChangeDisappearingCount = 0;
public void startTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) {
- // don't care.
+ if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
+ mChangeDisappearingCount++;
+ }
}
public void endTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) {
- if (transitionType == waitForType) {
+ if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
+ mChangeDisappearingCount--;
+ }
+
+ if (transitionType == LayoutTransition.CHANGE_DISAPPEARING &&
+ mChangeDisappearingCount == 0) {
// add it to the parentView in the correct location
if (params != null) {
- newParent.addView(view, index, params);
+ newParent.addView(movedView, index, params);
} else {
- newParent.addView(view, index);
+ newParent.addView(movedView, index);
}
-
}
}
});
// remove the view from the current parent.
- previousParent.removeView(view);
+ previousParent.removeView(movedView);
// and return since adding the view to the new parent is done in the listener.
return SUCCESS.createResult();
} else {
// standard code with no animation. pretty simple.
- previousParent.removeView(view);
+ previousParent.removeView(movedView);
// add it to the parentView in the correct location
if (params != null) {
- newParent.addView(view, index, params);
+ newParent.addView(movedView, index, params);
} else {
- newParent.addView(view, index);
+ newParent.addView(movedView, index);
}
return SUCCESS.createResult();