diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index 49b72fee22d3f..eb941e43fe637 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -73,7 +73,18 @@ public final class Message implements Parcelable { * receiver. */ public Messenger replyTo; - + + /** If set message is in use */ + /*package*/ static final int FLAG_IN_USE = 1; + + /** Flags reserved for future use (All are reserved for now) */ + /*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE; + + /** Flags to clear in the copyFrom method */ + /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE; + + /*package*/ int flags; + /*package*/ long when; /*package*/ Bundle data; @@ -253,6 +264,7 @@ public final class Message implements Parcelable { * target/callback of the original message. */ public void copyFrom(Message o) { + this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM; this.what = o.what; this.arg1 = o.arg1; this.arg2 = o.arg2; @@ -350,6 +362,7 @@ public final class Message implements Parcelable { } /*package*/ void clearForRecycle() { + flags = 0; what = 0; arg1 = 0; arg2 = 0; @@ -361,6 +374,14 @@ public final class Message implements Parcelable { data = null; } + /*package*/ boolean isInUse() { + return ((flags & FLAG_IN_USE) == FLAG_IN_USE); + } + + /*package*/ void markInUse() { + flags |= FLAG_IN_USE; + } + /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). */ public Message() { @@ -453,4 +474,3 @@ public final class Message implements Parcelable { replyTo = Messenger.readMessengerOrNullFromParcel(source); } } - diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index adb11c82f9878..75dfdd2549605 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -120,6 +120,7 @@ public class MessageQueue { now = SystemClock.uptimeMillis(); Message msg = pullNextLocked(now); if (msg != null) { + msg.markInUse(); return msg; } @@ -192,7 +193,7 @@ public class MessageQueue { } final boolean enqueueMessage(Message msg, long when) { - if (msg.when != 0) { + if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java index b7c2d1f1c1495..f82bfce6ef4ea 100644 --- a/core/tests/coretests/src/android/os/MessageQueueTest.java +++ b/core/tests/coretests/src/android/os/MessageQueueTest.java @@ -41,6 +41,10 @@ public class MessageQueueTest extends TestCase { } public void handleMessage(Message msg) { + if (!msg.isInUse()) { + failure(new RuntimeException( + "msg.isInuse is false, should always be true, #" + msg.what)); + } if (mCount <= mLastMessage) { if (msg.what != mCount) { failure(new RuntimeException( @@ -99,5 +103,174 @@ public class MessageQueueTest extends TestCase { tester.doTest(1000); } -} + private static class TestFieldIntegrityHandler extends TestHandlerThread { + Handler mHandler; + int mLastMessage; + int mCount; + + public TestFieldIntegrityHandler() { + } + + public void go() { + mHandler = new Handler() { + public void handleMessage(Message msg) { + TestFieldIntegrityHandler.this.handleMessage(msg); + } + }; + } + + public void handleMessage(Message msg) { + if (!msg.isInUse()) { + failure(new RuntimeException( + "msg.isInuse is false, should always be true, #" + msg.what)); + } + if (mCount <= mLastMessage) { + if (msg.what != mCount) { + failure(new RuntimeException( + "Expected message #" + mCount + + ", received #" + msg.what)); + } else if (mCount == mLastMessage) { + success(); + } + mCount++; + } else { + failure(new RuntimeException( + "Message received after done, #" + msg.what)); + } + } + } + + @MediumTest + public void testFieldIntegrity() throws Exception { + + TestHandlerThread tester = new TestFieldIntegrityHandler() { + Bundle mBundle; + + public void go() { + super.go(); + mLastMessage = 1; + mCount = 0; + mHandler.sendMessage(mHandler.obtainMessage(0)); + } + + public void handleMessage(Message msg) { + super.handleMessage(msg); + if (msg.what == 0) { + msg.flags = -1; + msg.what = 1; + msg.arg1 = 456; + msg.arg2 = 789; + msg.obj = this; + msg.replyTo = null; + mBundle = new Bundle(); + msg.data = mBundle; + msg.data.putString("key", "value"); + + Message newMsg = mHandler.obtainMessage(); + newMsg.copyFrom(msg); + if (newMsg.isInUse() != false) { + failure(new RuntimeException( + "newMsg.isInUse is true should be false after copyFrom")); + } + if (newMsg.flags != 0) { + failure(new RuntimeException(String.format( + "newMsg.flags is %d should be 0 after copyFrom", newMsg.flags))); + } + if (newMsg.what != 1) { + failure(new RuntimeException(String.format( + "newMsg.what is %d should be %d after copyFrom", newMsg.what, 1))); + } + if (newMsg.arg1 != 456) { + failure(new RuntimeException(String.format( + "newMsg.arg1 is %d should be %d after copyFrom", msg.arg1, 456))); + } + if (newMsg.arg2 != 789) { + failure(new RuntimeException(String.format( + "newMsg.arg2 is %d should be %d after copyFrom", msg.arg2, 789))); + } + if (newMsg.obj != this) { + failure(new RuntimeException( + "newMsg.obj should be 'this' after copyFrom")); + } + if (newMsg.replyTo != null) { + failure(new RuntimeException( + "newMsg.replyTo should be null after copyFrom")); + } + if (newMsg.data == mBundle) { + failure(new RuntimeException( + "newMsg.data should NOT be mBundle after copyFrom")); + } + if (!newMsg.data.getString("key").equals(mBundle.getString("key"))) { + failure(new RuntimeException(String.format( + "newMsg.data.getString(\"key\") is %s and does not equal" + + " mBundle.getString(\"key\") which is %s after copyFrom", + newMsg.data.getString("key"), mBundle.getString("key")))); + } + if (newMsg.when != 0) { + failure(new RuntimeException(String.format( + "newMsg.when is %d should be 0 after copyFrom", newMsg.when))); + } + if (newMsg.target != mHandler) { + failure(new RuntimeException( + "newMsg.target is NOT mHandler after copyFrom")); + } + if (newMsg.callback != null) { + failure(new RuntimeException( + "newMsg.callback is NOT null after copyFrom")); + } + + mHandler.sendMessage(newMsg); + } else if (msg.what == 1) { + if (msg.isInUse() != true) { + failure(new RuntimeException(String.format( + "msg.isInUse is false should be true after when processing %d", + msg.what))); + } + if (msg.arg1 != 456) { + failure(new RuntimeException(String.format( + "msg.arg1 is %d should be %d when processing # %d", + msg.arg1, 456, msg.what))); + } + if (msg.arg2 != 789) { + failure(new RuntimeException(String.format( + "msg.arg2 is %d should be %d when processing # %d", + msg.arg2, 789, msg.what))); + } + if (msg.obj != this) { + failure(new RuntimeException(String.format( + "msg.obj should be 'this' when processing # %d", msg.what))); + } + if (msg.replyTo != null) { + failure(new RuntimeException(String.format( + "msg.replyTo should be null when processing # %d", msg.what))); + } + if (!msg.data.getString("key").equals(mBundle.getString("key"))) { + failure(new RuntimeException(String.format( + "msg.data.getString(\"key\") is %s and does not equal" + + " mBundle.getString(\"key\") which is %s when processing # %d", + msg.data.getString("key"), mBundle.getString("key"), msg.what))); + } + if (msg.when != 0) { + failure(new RuntimeException(String.format( + "msg.when is %d should be 0 when processing # %d", + msg.when, msg.what))); + } + if (msg.target != null) { + failure(new RuntimeException(String.format( + "msg.target is NOT null when processing # %d", msg.what))); + } + if (msg.callback != null) { + failure(new RuntimeException(String.format( + "msg.callback is NOT null when processing # %d", msg.what))); + } + } else { + failure(new RuntimeException(String.format( + "Unexpected msg.what is %d" + msg.what))); + } + } + }; + + tester.doTest(1000); + } +}