Merge change I17b7ffe9

* changes:
  Do not back up / restore the END_BUTTON_BEHAVIOR setting
diff --git a/Android.mk b/Android.mk
index 0cf1229..8a1013f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -351,6 +351,7 @@
     -since ./frameworks/base/api/4.xml 4 \
     -since ./frameworks/base/api/5.xml 5 \
     -since ./frameworks/base/api/6.xml 6 \
+    -since ./frameworks/base/api/7.xml 7 \
 		-error 1 -error 2 -warning 3 -error 4 -error 6 -error 8 \
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 376f3d9..ad6540a 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -345,12 +345,12 @@
         sp<IOMX> omx = service->getOMX();
         CHECK(omx.get() != NULL);
 
-        List<String8> list;
+        List<IOMX::ComponentInfo> list;
         omx->listNodes(&list);
 
-        for (List<String8>::iterator it = list.begin();
+        for (List<IOMX::ComponentInfo>::iterator it = list.begin();
              it != list.end(); ++it) {
-            printf("%s\n", (*it).string());
+            printf("%s\n", (*it).mName.string());
         }
     }
 
diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java
index 5f1a445..11a94d1 100644
--- a/core/java/android/gesture/GestureStore.java
+++ b/core/java/android/gesture/GestureStore.java
@@ -65,7 +65,12 @@
 
     // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures
     public static final int ORIENTATION_INVARIANT = 1;
+    // at most 2 directions can be recognized
     public static final int ORIENTATION_SENSITIVE = 2;
+    // at most 4 directions can be recognized
+    static final int ORIENTATION_SENSITIVE_4 = 4;
+    // at most 8 directions can be recognized
+    static final int ORIENTATION_SENSITIVE_8 = 8;
 
     private static final short FILE_FORMAT_VERSION = 1;
 
@@ -131,7 +136,7 @@
     public ArrayList<Prediction> recognize(Gesture gesture) {
         Instance instance = Instance.createInstance(mSequenceType,
                 mOrientationStyle, gesture, null);
-        return mClassifier.classify(mSequenceType, instance.vector);
+        return mClassifier.classify(mSequenceType, mOrientationStyle, instance.vector);
     }
 
     /**
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java
index 40d7029..f1dcd89 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -366,6 +366,38 @@
         }
         return Math.acos(sum);
     }
+    
+    /**
+     * Calculate the "minimum" cosine distance between two instances
+     * 
+     * @param vector1
+     * @param vector2
+     * @param numOrientations the maximum number of orientation allowed
+     * @return the distance between the two instances (between 0 and Math.PI)
+     */
+    static double minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) {
+        final int len = vector1.length;
+        double a = 0;
+        double b = 0;
+        for (int i = 0; i < len; i += 2) {
+            a += vector1[i] * vector2[i] + vector1[i + 1] * vector2[i + 1];
+            b += vector1[i] * vector2[i + 1] - vector1[i + 1] * vector2[i];
+        }
+        if (a != 0) {
+            final double tan = b/a;
+            final double angle = Math.atan(tan);
+            if (numOrientations > 2 && Math.abs(angle) >= Math.PI / numOrientations) {
+                return Math.acos(a);
+            } else {
+                final double cosine = Math.cos(angle);
+                final double sine = cosine * tan; 
+                return Math.acos(a * cosine + b * sine);
+            }
+        } else {
+            return Math.PI / 2;
+        }
+    }
+
 
     static OrientedBoundingBox computeOrientedBoundingBox(ArrayList<GesturePoint> pts) {
         GestureStroke stroke = new GestureStroke(pts);
diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java
index ef208ac..68a2985 100755
--- a/core/java/android/gesture/Instance.java
+++ b/core/java/android/gesture/Instance.java
@@ -94,7 +94,7 @@
         float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]);
 
         float adjustment = -orientation;
-        if (orientationType == GestureStore.ORIENTATION_SENSITIVE) {
+        if (orientationType != GestureStore.ORIENTATION_INVARIANT) {
             int count = ORIENTATIONS.length;
             for (int i = 0; i < count; i++) {
                 float delta = ORIENTATIONS[i] - orientation;
diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java
index b93b76f..9987e69 100644
--- a/core/java/android/gesture/InstanceLearner.java
+++ b/core/java/android/gesture/InstanceLearner.java
@@ -41,7 +41,7 @@
     };
 
     @Override
-    ArrayList<Prediction> classify(int sequenceType, float[] vector) {
+    ArrayList<Prediction> classify(int sequenceType, int orientationType, float[] vector) {
         ArrayList<Prediction> predictions = new ArrayList<Prediction>();
         ArrayList<Instance> instances = getInstances();
         int count = instances.size();
@@ -53,7 +53,7 @@
             }
             double distance;
             if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) {
-                distance = GestureUtilities.cosineDistance(sample.vector, vector);
+                distance = GestureUtilities.minimumCosineDistance(sample.vector, vector, orientationType);
             } else {
                 distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);
             }
diff --git a/core/java/android/gesture/Learner.java b/core/java/android/gesture/Learner.java
index feacde5..60997e0 100755
--- a/core/java/android/gesture/Learner.java
+++ b/core/java/android/gesture/Learner.java
@@ -79,5 +79,5 @@
         instances.removeAll(toDelete);
     }
 
-    abstract ArrayList<Prediction> classify(int gestureType, float[] vector);
+    abstract ArrayList<Prediction> classify(int sequenceType, int orientationType, float[] vector);
 }
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 238ece1..7c7bfeb 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -46,7 +46,7 @@
 }
  
 static void Typeface_unref(JNIEnv* env, jobject obj, SkTypeface* face) {
-    face->unref();
+    SkSafeUnref(face);
 }
 
 static int Typeface_getStyle(JNIEnv* env, jobject obj, SkTypeface* face) {
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 2757850..883f64a 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -35,7 +35,7 @@
 <p>This page provides data about the relative number of active devices
 running a given version of the Android platform. This can help you
 understand the landscape of device distribution and decide how to prioritize
-the development of your application features for the devices currently in the
+the development of your application features for the devices currently in
 the hands of users.</p>
 
 <p class="note"><strong>Note:</strong> This data is based on the number
diff --git a/docs/html/resources/faq/troubleshooting.jd b/docs/html/resources/faq/troubleshooting.jd
index 0cf1ab0..f3252b0 100644
--- a/docs/html/resources/faq/troubleshooting.jd
+++ b/docs/html/resources/faq/troubleshooting.jd
@@ -22,7 +22,10 @@
     <li><a href="#eclipse">Eclipse isn't talking to the emulator</a></li>
     <li><a href="#majorminor">When I go to preferences in Eclipse and select "Android", I get the following error message: Unsupported major.minor version 49.0.</a></li>
     <li><a href="#apidemosreinstall">I can't install ApiDemos apps in my IDE because of a signing error</a></li>
+    <li><a href="#gesturebuilderinstall">I can't install the GestureBuilder sample
+app in the emulator</a></li>
     <li><a href="#signingcalendar">I can't compile my app because the build tools generated an expired debug certificate</a></li>
+    <li><a href="#manifestfiles">Unable to view manifest files from within Eclipse</a></li>
 </ul>
 
 <a name="installeclipsecomponents" id="installeclipsecomponents"></a><h2>ADT Installation Error: "requires plug-in org.eclipse.wst.sse.ui".</h2>
@@ -233,6 +236,55 @@
 <p>For more information about adb, see the <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a> 
 documentation.</p>
 
+<h2 id="gesturebuilderinstall">I can't install the GestureBuilder sample 
+app in the emulator</a></h2>
+
+<p>This is similar to the ApiDemos problem described above, except that
+you cannot fix it by uninstalling GestureBuilder from the emulator. The 
+GestureBuilder app cannot be uninstalled because it is currently installed 
+within the system files themselves.</p>
+
+<p><strong>Symptoms</strong></p>
+
+<ul><li><p>You cannot run GestureBuilder in the emulator:</p>
+
+<pre>[2009-12-10 14:57:19 - GestureBuilderActivity]Re-installation failed due to different application signatures.
+[2009-12-10 14:57:19 - GestureBuilderActivity]You must perform a full uninstall of the application. WARNING: This will remove the application data!
+[2009-12-10 14:57:19 - GestureBuilderActivity]Please execute 'adb uninstall com.android.gesture.builder' in a shell.</pre>
+</li>
+
+<li><p>Running <code>adb uninstall com.android.gesture.builder</code> fails:</p>
+<pre>$ adb uninstall com.android.gesture.builder
+	Failure</pre>
+</li></ul>
+
+<p>For now, the work-around is to change the sample's package name
+so that the system can install it as a new app rather than as a 
+replacement for the existing GestureBuilder app. To change the 
+package name, open the manifest file and modify the package attribute 
+of the manifest element. Next, update imports and other references to 
+the package name, rebuild the app, and run it in an AVD.</p>
+
+<p>For example, here's how you could do this in Eclipse:</p>
+
+<ol>
+  <li>Right-click on the package name
+(<code>src/com.android.gesture.builder</code>).</li>
+  <li>Select <strong>Refactor &gt; Rename</strong> and change the name, for example to
+<code>com.android.gestureNEW.builder</code>. </li>
+  <li>Open the manifest file. Inside the <code>&lt;manifest&gt;</code>
+tag, change the package name to
+<code>com.android.gestureNEW.builder</code>.</li>
+  <li>Open each of the two Activity files and do Ctrl-Shift-O to add
+missing import packages, then save each file.</li>
+<li>Run the GestureBuilder application on the emulator.</li>
+</ol>
+
+<p>If you get an error message such as "Could not load /sdcard/gestures. 
+Make sure you have a mounted SD card," be sure that your target AVD has an 
+SD card. To create an AVD that has an SD card, use the
+<a href="{@docRoot}guide/developing/tools/avd.html#options"><code>-c</code> 
+option</a> in the <code>android create avd</code> command.</p> 
 
 <h2 id="signingcalendar">I can't compile my app because the build tools generated an expired debug certificate</h2>
 
@@ -259,3 +311,25 @@
 <p>For general information about signing Android applications, see 
 <a href="{@docRoot}guide/publishing/app-signing.html">Signing Your Applications</a>. </p>
 
+<h2 id="manifestfiles">Unable to view manifest files from within
+Eclipse</a></h2>
+
+<p>When you try to open an application's manifest file from within
+Eclipse, you might get an error such as this one:</p>
+<pre>An error has occurred. See error log for more details.
+org.eclipse.wst.sse.ui.StructuredTextEditor.isBlockSelectionModeEnabled()Z</pre>
+
+<p>Try reverting to the 3.0 version of the Eclipse XML Editors and
+Tools. If this does not work, remove the 3.1 version of the tool. To do
+this in Eclipse 3.4:</p>
+
+<ol>
+	<li>Select <strong>Help > Software Updates...</strong></li>
+	<li>Select the <strong>Installed Software</strong> tab.</li>
+	<li>Select <strong>Eclipse XML Editors and Tools</strong>.</li>
+	<li>Click <strong>Uninstall</strong>.</li>
+	<li>Click <strong>Finish</strong>.</li>
+</ol>
+
+<p>When you restart Eclipse, you should be able to view the manifest
+files. </p>
\ No newline at end of file
diff --git a/docs/html/resources/samples/index.jd b/docs/html/resources/samples/index.jd
index f5550734..8ab6f43b 100644
--- a/docs/html/resources/samples/index.jd
+++ b/docs/html/resources/samples/index.jd
@@ -1,13 +1,11 @@
-guide=true
 page.title=Sample Code
 @jd:body
 
 
-<p>Sometimes, the best way to learn how things are done is to look at some code.
-Here, you can browse the source of some sample Android applications that are included
-in the Android SDK.</p>
+<p>Sometimes, the best way to learn how things are done is to look at some code.</p>
 
-<p>Each version of the Android platform available for the SDK includes a full set of sample
+<p>Here, you can browse the source of some sample Android applications. Also, each version of the
+Android platform available for the SDK includes a set of sample
 applications (which may vary between different versions of the platform).
 You can find the samples in your SDK at:</p>
 
@@ -53,7 +51,7 @@
   <dd>A home screen replacement application.</dd>
   
  <dt><a href="JetBoy/index.html">JetBoy</a></dt>
-  <dd>JetBoy is a game that demonstrates the SONiVOX JET interactive music technology,
+  <dd>A game that demonstrates the SONiVOX JET interactive music technology,
   with {@link android.media.JetPlayer}.</dd>
     
  <dt><a href="LunarLander/index.html">Lunar Lander</a></dt>
@@ -77,7 +75,7 @@
  <dt><a href="SoftKeyboard/index.html">Soft Keyboard</a></dt>
   <dd>An example of writing an input method for a software keyboard.</dd>
 
- <dt><a href=Wiktionary/index.html">Wiktionary</a></dt>
+ <dt><a href="Wiktionary/index.html">Wiktionary</a></dt>
   <dd>An example of creating interactive widgets for display on the Android
   home screen.</dd>
 
diff --git a/docs/html/shareables/latest_samples.zip b/docs/html/shareables/latest_samples.zip
index e09e9c0..42fad99 100644
--- a/docs/html/shareables/latest_samples.zip
+++ b/docs/html/shareables/latest_samples.zip
Binary files differ
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f0d5a6a..bc92d3a 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -143,6 +143,9 @@
 
     // don't allow clients to call this directly
     private Typeface(int ni) {
+        if (0 == ni) {
+            throw new RuntimeException("native typeface cannot be made");
+        }
         native_instance = ni;
     }
     
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 6f3ba1c..39bd5b1 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -42,7 +42,11 @@
     typedef void *buffer_id;
     typedef void *node_id;
 
-    virtual status_t listNodes(List<String8> *list) = 0;
+    struct ComponentInfo {
+        String8 mName;
+        List<String8> mRoles;
+    };
+    virtual status_t listNodes(List<ComponentInfo> *list) = 0;
 
     virtual status_t allocateNode(
             const char *name, const sp<IOMXObserver> &observer,
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 76a9e7d..b43e48f 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -75,7 +75,7 @@
         : BpInterface<IOMX>(impl) {
     }
 
-    virtual status_t listNodes(List<String8> *list) {
+    virtual status_t listNodes(List<ComponentInfo> *list) {
         list->clear();
 
         Parcel data, reply;
@@ -84,9 +84,14 @@
 
         int32_t n = reply.readInt32();
         for (int32_t i = 0; i < n; ++i) {
-            String8 s = reply.readString8();
+            list->push_back(ComponentInfo());
+            ComponentInfo &info = *--list->end();
 
-            list->push_back(s);
+            info.mName = reply.readString8();
+            int32_t numRoles = reply.readInt32();
+            for (int32_t j = 0; j < numRoles; ++j) {
+                info.mRoles.push_back(reply.readString8());
+            }
         }
 
         return OK;
@@ -368,13 +373,20 @@
         {
             CHECK_INTERFACE(IOMX, data, reply);
 
-            List<String8> list;
+            List<ComponentInfo> list;
             listNodes(&list);
 
             reply->writeInt32(list.size());
-            for (List<String8>::iterator it = list.begin();
+            for (List<ComponentInfo>::iterator it = list.begin();
                  it != list.end(); ++it) {
-                reply->writeString8(*it);
+                ComponentInfo &cur = *it;
+
+                reply->writeString8(cur.mName);
+                reply->writeInt32(cur.mRoles.size());
+                for (List<String8>::iterator role_it = cur.mRoles.begin();
+                     role_it != cur.mRoles.end(); ++role_it) {
+                    reply->writeString8(*role_it);
+                }
             }
 
             return NO_ERROR;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 7969710..143e8ee 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -534,8 +534,8 @@
             uint16_t sample_size = U16_AT(&buffer[18]);
             uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
 
-            printf("*** coding='%s' %d channels, size %d, rate %d\n",
-                   chunk, num_channels, sample_size, sample_rate);
+            // printf("*** coding='%s' %d channels, size %d, rate %d\n",
+            //        chunk, num_channels, sample_size, sample_rate);
 
             mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
             mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
@@ -576,8 +576,8 @@
             uint16_t width = U16_AT(&buffer[6 + 18]);
             uint16_t height = U16_AT(&buffer[6 + 20]);
 
-            printf("*** coding='%s' width=%d height=%d\n",
-                   chunk, width, height);
+            // printf("*** coding='%s' width=%d height=%d\n",
+            //        chunk, width, height);
 
             mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
             mLastTrack->meta->setInt32(kKeyWidth, width);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 99c39f8..47bbda3 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -425,14 +425,9 @@
         esds.getCodecSpecificInfo(
                 &codec_specific_data, &codec_specific_data_size);
 
-        printf("found codec-specific data of size %d\n",
-               codec_specific_data_size);
-
         codec->addCodecSpecificData(
                 codec_specific_data, codec_specific_data_size);
     } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
-        printf("found avcc of size %d\n", size);
-
         // Parse the AVCDecoderConfigurationRecord
 
         const uint8_t *ptr = (const uint8_t *)data;
@@ -1223,7 +1218,7 @@
              portIndex == kPortIndexInput ? "input" : "output");
     }
 
-    dumpPortStatus(portIndex);
+    // dumpPortStatus(portIndex);
 
     return OK;
 }
@@ -1273,7 +1268,6 @@
                 CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
                 drainInputBuffer(&buffers->editItemAt(i));
             }
-
             break;
         }
 
@@ -1282,12 +1276,10 @@
             IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
             OMX_U32 flags = msg.u.extended_buffer_data.flags;
 
-            CODEC_LOGV("FILL_BUFFER_DONE(buffer: %p, size: %ld, flags: 0x%08lx)",
+            CODEC_LOGV("FILL_BUFFER_DONE(buffer: %p, size: %ld, flags: 0x%08lx, timestamp: %lld us (%.2f secs))",
                  buffer,
                  msg.u.extended_buffer_data.range_length,
-                 flags);
-
-            CODEC_LOGV("FILL_BUFFER_DONE(timestamp: %lld us (%.2f secs))",
+                 flags,
                  msg.u.extended_buffer_data.timestamp,
                  msg.u.extended_buffer_data.timestamp / 1E6);
 
@@ -1315,11 +1307,13 @@
                 CHECK_EQ(err, OK);
 
                 buffers->removeAt(i);
+#if 0
             } else if (mPortStatus[kPortIndexOutput] == ENABLED
                        && (flags & OMX_BUFFERFLAG_EOS)) {
                 CODEC_LOGV("No more output data.");
                 mNoMoreOutputData = true;
                 mBufferFilled.signal();
+#endif
             } else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) {
                 CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
 
@@ -1351,6 +1345,11 @@
 
                 mFilledBuffers.push_back(i);
                 mBufferFilled.signal();
+
+                if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_EOS) {
+                    CODEC_LOGV("No more output data.");
+                    mNoMoreOutputData = true;
+                }
             }
 
             break;
@@ -1374,7 +1373,7 @@
 
         case OMX_EventError:
         {
-            LOGE("ERROR(%ld, %ld)", data1, data2);
+            LOGE("ERROR(0x%08lx, %ld)", data1, data2);
 
             setState(ERROR);
             break;
@@ -1386,6 +1385,7 @@
             break;
         }
 
+#if 0
         case OMX_EventBufferFlag:
         {
             CODEC_LOGV("EVENT_BUFFER_FLAG(%ld)", data1);
@@ -1395,6 +1395,7 @@
             }
             break;
         }
+#endif
 
         default:
         {
@@ -1565,13 +1566,6 @@
                     CODEC_LOGV("Finished flushing both ports, now continuing from"
                          " seek-time.");
 
-                    // Clear this flag in case the decoder sent us either
-                    // the EVENT_BUFFER_FLAG(1) or an output buffer with
-                    // the EOS flag set _while_ flushing. Since we're going
-                    // to submit "fresh" input data now, this flag no longer
-                    // applies to our future.
-                    mNoMoreOutputData = false;
-
                     drainInputBuffers();
                     fillOutputBuffers();
                 }
@@ -1832,6 +1826,8 @@
             memcpy(info->mMem->pointer(), specific->mData, specific->mSize);
         }
 
+        mNoMoreOutputData = false;
+
         status_t err = mOMX->emptyBuffer(
                 mNode, info->mBuffer, 0, size,
                 OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
@@ -1849,7 +1845,9 @@
     if (mSeekTimeUs >= 0) {
         MediaSource::ReadOptions options;
         options.setSeekTo(mSeekTimeUs);
+
         mSeekTimeUs = -1;
+        mBufferFilled.signal();
 
         err = mSource->read(&srcBuffer, &options);
     } else {
@@ -1866,6 +1864,8 @@
 
         mSignalledEOS = true;
     } else {
+        mNoMoreOutputData = false;
+
         srcLength = srcBuffer->range_length();
 
         if (info->mMem->size() < srcLength) {
@@ -1878,10 +1878,10 @@
                srcLength);
 
         if (srcBuffer->meta_data()->findInt64(kKeyTime, &timestampUs)) {
-            CODEC_LOGV("Calling emptyBuffer on buffer %p (length %d)",
-                 info->mBuffer, srcLength);
-            CODEC_LOGV("Calling emptyBuffer with timestamp %lld us (%.2f secs)",
-                 timestampUs, timestampUs / 1E6);
+            CODEC_LOGV("Calling emptyBuffer on buffer %p (length %d), "
+                       "timestamp %lld us (%.2f secs)",
+                       info->mBuffer, srcLength,
+                       timestampUs, timestampUs / 1E6);
         }
     }
 
@@ -2298,7 +2298,6 @@
         CODEC_LOGV("seeking to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
 
         mSignalledEOS = false;
-        mNoMoreOutputData = false;
 
         CHECK(seekTimeUs >= 0);
         mSeekTimeUs = seekTimeUs;
@@ -2317,6 +2316,10 @@
         if (emulateOutputFlushCompletion) {
             onCmdComplete(OMX_CommandFlush, kPortIndexOutput);
         }
+
+        while (mSeekTimeUs >= 0) {
+            mBufferFilled.wait(mLock);
+        }
     }
 
     while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 01b8e7a..ce0b0d55aa 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -31,7 +31,7 @@
 public:
     OMX();
 
-    virtual status_t listNodes(List<String8> *list);
+    virtual status_t listNodes(List<ComponentInfo> *list);
 
     virtual status_t allocateNode(
             const char *name, const sp<IOMXObserver> &observer, node_id *node);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 19d3940..923b801 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -85,6 +85,7 @@
     OMX::node_id mNodeID;
     OMX_HANDLETYPE mHandle;
     sp<IOMXObserver> mObserver;
+    bool mDying;
 
     struct ActiveBuffer {
         OMX_U32 mPortIndex;
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 4f88d99..2473731 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -47,3 +47,6 @@
 LOCAL_MODULE:= libstagefright_omx
 
 include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 94dbb6d..8c3f252 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -208,39 +208,27 @@
     instance->onObserverDied(mMaster);
 }
 
-#if 0
-static void dumpRoles(OMXMaster *master, const char *name) {
-    Vector<String8> roles;
-    OMX_ERRORTYPE err = master->getRolesOfComponent(name, &roles);
-
-    if (err != OMX_ErrorNone) {
-        LOGE("Could not get roles for component '%s'.", name);
-        return;
-    }
-
-    if (roles.isEmpty()) {
-        LOGE("Component '%s' has NO roles!", name);
-        return;
-    }
-
-    LOGI("Component '%s' has the following roles:", name);
-
-    for (size_t i = 0; i < roles.size(); ++i) {
-        LOGI("%d) %s", i + 1, roles[i].string());
-    }
-}
-#endif
-
-status_t OMX::listNodes(List<String8> *list) {
+status_t OMX::listNodes(List<ComponentInfo> *list) {
     list->clear();
 
     OMX_U32 index = 0;
     char componentName[256];
     while (mMaster->enumerateComponents(
                 componentName, sizeof(componentName), index) == OMX_ErrorNone) {
-        list->push_back(String8(componentName));
+        list->push_back(ComponentInfo());
+        ComponentInfo &info = *--list->end();
 
-        // dumpRoles(mMaster, componentName);
+        info.mName = componentName;
+
+        Vector<String8> roles;
+        OMX_ERRORTYPE err =
+            mMaster->getRolesOfComponent(componentName, &roles);
+
+        if (err == OMX_ErrorNone) {
+            for (OMX_U32 i = 0; i < roles.size(); ++i) {
+                info.mRoles.push_back(roles[i]);
+            }
+        }
 
         ++index;
     }
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 288710e..4eb6417 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -78,7 +78,8 @@
     : mOwner(owner),
       mNodeID(NULL),
       mHandle(NULL),
-      mObserver(observer) {
+      mObserver(observer),
+      mDying(false) {
 }
 
 OMXNodeInstance::~OMXNodeInstance() {
@@ -114,6 +115,11 @@
     // for components that don't do this themselves on a call to
     // "FreeHandle".
 
+    // The code below may trigger some more events to be dispatched
+    // by the OMX component - we want to ignore them as our client
+    // does not expect them.
+    mDying = true;
+
     OMX_STATETYPE state;
     CHECK_EQ(OMX_GetState(mHandle, &state), OMX_ErrorNone);
     switch (state) {
@@ -406,6 +412,9 @@
         OMX_IN OMX_U32 nData2,
         OMX_IN OMX_PTR pEventData) {
     OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
+    if (instance->mDying) {
+        return OMX_ErrorNone;
+    }
     return instance->owner()->OnEvent(
             instance->nodeID(), eEvent, nData1, nData2, pEventData);
 }
@@ -416,6 +425,9 @@
         OMX_IN OMX_PTR pAppData,
         OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
     OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
+    if (instance->mDying) {
+        return OMX_ErrorNone;
+    }
     return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer);
 }
 
@@ -425,6 +437,9 @@
         OMX_IN OMX_PTR pAppData,
         OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
     OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
+    if (instance->mDying) {
+        return OMX_ErrorNone;
+    }
     return instance->owner()->OnFillBufferDone(instance->nodeID(), pBuffer);
 }
 
diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk
new file mode 100644
index 0000000..cd654510
--- /dev/null
+++ b/media/libstagefright/omx/tests/Android.mk
@@ -0,0 +1,23 @@
+ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES =       \
+	OMXHarness.cpp  \
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright
+
+LOCAL_C_INCLUDES:= \
+	$(JNI_H_INCLUDE) \
+	frameworks/base/media/libstagefright \
+	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
+
+LOCAL_MODULE:= omx_tests
+
+include $(BUILD_EXECUTABLE)
+
+endif
+
+
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
new file mode 100644
index 0000000..2e23899
--- /dev/null
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXHarness"
+#include <utils/Log.h>
+
+#include "OMXHarness.h"
+
+#include <sys/time.h>
+
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXCodec.h>
+
+#define DEFAULT_TIMEOUT         500000
+
+namespace android {
+
+static int64_t getNowUs() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+
+    return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
+}
+
+Harness::Harness()
+    : mInitCheck(NO_INIT) {
+    mInitCheck = initOMX();
+}
+
+Harness::~Harness() {
+}
+
+status_t Harness::initCheck() const {
+    return mInitCheck;
+}
+
+status_t Harness::initOMX() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("media.player"));
+    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+    mOMX = service->getOMX();
+
+    return mOMX != 0 ? OK : NO_INIT;
+}
+
+void Harness::onMessage(const omx_message &msg) {
+    Mutex::Autolock autoLock(mLock);
+    mMessageQueue.push_back(msg);
+    mMessageAddedCondition.signal();
+}
+
+status_t Harness::dequeueMessageForNode(
+        IOMX::node_id node, omx_message *msg, int64_t timeoutUs) {
+    return dequeueMessageForNodeIgnoringBuffers(
+            node, NULL, NULL, msg, timeoutUs);
+}
+
+// static
+bool Harness::handleBufferMessage(
+        const omx_message &msg,
+        Vector<Buffer> *inputBuffers,
+        Vector<Buffer> *outputBuffers) {
+    switch (msg.type) {
+        case omx_message::EMPTY_BUFFER_DONE:
+        {
+            if (inputBuffers) {
+                for (size_t i = 0; i < inputBuffers->size(); ++i) {
+                    if ((*inputBuffers)[i].mID == msg.u.buffer_data.buffer) {
+                        inputBuffers->editItemAt(i).mFlags &= ~kBufferBusy;
+                        return true;
+                    }
+                }
+                CHECK(!"should not be here");
+            }
+            break;
+        }
+
+        case omx_message::FILL_BUFFER_DONE:
+        {
+            if (outputBuffers) {
+                for (size_t i = 0; i < outputBuffers->size(); ++i) {
+                    if ((*outputBuffers)[i].mID == msg.u.buffer_data.buffer) {
+                        outputBuffers->editItemAt(i).mFlags &= ~kBufferBusy;
+                        return true;
+                    }
+                }
+                CHECK(!"should not be here");
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    return false;
+}
+
+status_t Harness::dequeueMessageForNodeIgnoringBuffers(
+        IOMX::node_id node,
+        Vector<Buffer> *inputBuffers,
+        Vector<Buffer> *outputBuffers,
+        omx_message *msg, int64_t timeoutUs) {
+    int64_t finishBy = getNowUs() + timeoutUs;
+
+    for (;;) {
+        Mutex::Autolock autoLock(mLock);
+        List<omx_message>::iterator it = mMessageQueue.begin();
+        while (it != mMessageQueue.end()) {
+            if ((*it).node == node) {
+                if (handleBufferMessage(*it, inputBuffers, outputBuffers)) {
+                    it = mMessageQueue.erase(it);
+                    continue;
+                }
+
+                *msg = *it;
+                mMessageQueue.erase(it);
+
+                return OK;
+            }
+
+            ++it;
+        }
+
+        status_t err = (timeoutUs < 0)
+            ? mMessageAddedCondition.wait(mLock)
+            : mMessageAddedCondition.waitRelative(
+                    mLock, (finishBy - getNowUs()) * 1000);
+
+        if (err == TIMED_OUT) {
+            return err;
+        }
+        CHECK_EQ(err, OK);
+    }
+}
+
+status_t Harness::getPortDefinition(
+        IOMX::node_id node, OMX_U32 portIndex,
+        OMX_PARAM_PORTDEFINITIONTYPE *def) {
+    def->nSize = sizeof(*def);
+    def->nVersion.s.nVersionMajor = 1;
+    def->nVersion.s.nVersionMinor = 0;
+    def->nVersion.s.nRevision = 0;
+    def->nVersion.s.nStep = 0;
+    def->nPortIndex = portIndex;
+    return mOMX->getParameter(
+            node, OMX_IndexParamPortDefinition, def, sizeof(*def));
+}
+
+#define EXPECT(condition, info) \
+    if (!(condition)) {         \
+        LOGE(info); printf("\n  * " info "\n"); return UNKNOWN_ERROR; \
+    }
+
+#define EXPECT_SUCCESS(err, info) \
+    EXPECT((err) == OK, info " failed")
+
+status_t Harness::allocatePortBuffers(
+        const sp<MemoryDealer> &dealer,
+        IOMX::node_id node, OMX_U32 portIndex,
+        Vector<Buffer> *buffers) {
+    buffers->clear();
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    status_t err = getPortDefinition(node, portIndex, &def);
+    EXPECT_SUCCESS(err, "getPortDefinition");
+
+    for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+        Buffer buffer;
+        buffer.mMemory = dealer->allocate(def.nBufferSize);
+        buffer.mFlags = 0;
+        CHECK(buffer.mMemory != NULL);
+
+        err = mOMX->allocateBufferWithBackup(
+                node, portIndex, buffer.mMemory, &buffer.mID);
+        EXPECT_SUCCESS(err, "allocateBuffer");
+
+        buffers->push(buffer);
+    }
+
+    return OK;
+}
+
+status_t Harness::setRole(IOMX::node_id node, const char *role) {
+    OMX_PARAM_COMPONENTROLETYPE params;
+    params.nSize = sizeof(params);
+    params.nVersion.s.nVersionMajor = 1;
+    params.nVersion.s.nVersionMinor = 0;
+    params.nVersion.s.nRevision = 0;
+    params.nVersion.s.nStep = 0;
+    strncpy((char *)params.cRole, role, OMX_MAX_STRINGNAME_SIZE - 1);
+    params.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+    return mOMX->setParameter(
+            node, OMX_IndexParamStandardComponentRole,
+            &params, sizeof(params));
+}
+
+struct NodeReaper {
+    NodeReaper(const sp<Harness> &harness, IOMX::node_id node)
+        : mHarness(harness),
+          mNode(node) {
+    }
+
+    ~NodeReaper() {
+        if (mNode != 0) {
+            mHarness->mOMX->freeNode(mNode);
+            mNode = 0;
+        }
+    }
+
+    void disarm() {
+        mNode = 0;
+    }
+
+private:
+    sp<Harness> mHarness;
+    IOMX::node_id mNode;
+
+    NodeReaper(const NodeReaper &);
+    NodeReaper &operator=(const NodeReaper &);
+};
+
+static sp<MediaSource> MakeSource(
+        const char *uri,
+        const char *mimeType) {
+    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri);
+
+    if (extractor == NULL) {
+        return NULL;
+    }
+
+    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+        const char *trackMIME;
+        CHECK(meta->findCString(kKeyMIMEType, &trackMIME));
+
+        if (!strcasecmp(trackMIME, mimeType)) {
+            return extractor->getTrack(i);
+        }
+    }
+
+    return NULL;
+}
+
+status_t Harness::testStateTransitions(
+        const char *componentName, const char *componentRole) {
+    if (strncmp(componentName, "OMX.", 4)) {
+        // Non-OMX components, i.e. software decoders won't execute this
+        // test.
+        return OK;
+    }
+
+    sp<MemoryDealer> dealer = new MemoryDealer(8 * 1024 * 1024);
+    IOMX::node_id node;
+
+    status_t err =
+        mOMX->allocateNode(componentName, this, &node);
+    EXPECT_SUCCESS(err, "allocateNode");
+
+    NodeReaper reaper(this, node);
+
+    err = setRole(node, componentRole);
+    EXPECT_SUCCESS(err, "setRole");
+
+    // Initiate transition Loaded->Idle
+    err = mOMX->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle);
+    EXPECT_SUCCESS(err, "sendCommand(go-to-Idle)");
+
+    omx_message msg;
+    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
+    // Make sure node doesn't just transition to idle before we are done
+    // allocating all input and output buffers.
+    EXPECT(err == TIMED_OUT,
+            "Component must not transition from loaded to idle before "
+            "all input and output buffers are allocated.");
+
+    // Now allocate buffers.
+    Vector<Buffer> inputBuffers;
+    err = allocatePortBuffers(dealer, node, 0, &inputBuffers);
+    EXPECT_SUCCESS(err, "allocatePortBuffers(input)");
+
+    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
+    CHECK_EQ(err, TIMED_OUT);
+
+    Vector<Buffer> outputBuffers;
+    err = allocatePortBuffers(dealer, node, 1, &outputBuffers);
+    EXPECT_SUCCESS(err, "allocatePortBuffers(output)");
+
+    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
+    EXPECT(err == OK
+            && msg.type == omx_message::EVENT
+            && msg.u.event_data.event == OMX_EventCmdComplete
+            && msg.u.event_data.data1 == OMX_CommandStateSet
+            && msg.u.event_data.data2 == OMX_StateIdle,
+           "Component did not properly transition to idle state "
+           "after all input and output buffers were allocated.");
+
+    // Initiate transition Idle->Executing
+    err = mOMX->sendCommand(node, OMX_CommandStateSet, OMX_StateExecuting);
+    EXPECT_SUCCESS(err, "sendCommand(go-to-Executing)");
+
+    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
+    EXPECT(err == OK
+            && msg.type == omx_message::EVENT
+            && msg.u.event_data.event == OMX_EventCmdComplete
+            && msg.u.event_data.data1 == OMX_CommandStateSet
+            && msg.u.event_data.data2 == OMX_StateExecuting,
+           "Component did not properly transition from idle to "
+           "executing state.");
+
+    for (size_t i = 0; i < outputBuffers.size(); ++i) {
+        err = mOMX->fillBuffer(node, outputBuffers[i].mID);
+        EXPECT_SUCCESS(err, "fillBuffer");
+
+        outputBuffers.editItemAt(i).mFlags |= kBufferBusy;
+    }
+
+    err = mOMX->sendCommand(node, OMX_CommandFlush, 1);
+    EXPECT_SUCCESS(err, "sendCommand(flush-output-port)");
+
+    err = dequeueMessageForNodeIgnoringBuffers(
+            node, &inputBuffers, &outputBuffers, &msg, DEFAULT_TIMEOUT);
+    EXPECT(err == OK
+            && msg.type == omx_message::EVENT
+            && msg.u.event_data.event == OMX_EventCmdComplete
+            && msg.u.event_data.data1 == OMX_CommandFlush
+            && msg.u.event_data.data2 == 1,
+           "Component did not properly acknowledge flushing the output port.");
+
+    for (size_t i = 0; i < outputBuffers.size(); ++i) {
+        EXPECT((outputBuffers[i].mFlags & kBufferBusy) == 0,
+               "Not all output buffers have been returned to us by the time "
+               "we received the flush-complete notification.");
+    }
+
+    for (size_t i = 0; i < outputBuffers.size(); ++i) {
+        err = mOMX->fillBuffer(node, outputBuffers[i].mID);
+        EXPECT_SUCCESS(err, "fillBuffer");
+
+        outputBuffers.editItemAt(i).mFlags |= kBufferBusy;
+    }
+
+    // Initiate transition Executing->Idle
+    err = mOMX->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle);
+    EXPECT_SUCCESS(err, "sendCommand(go-to-Idle)");
+
+    err = dequeueMessageForNodeIgnoringBuffers(
+            node, &inputBuffers, &outputBuffers, &msg, DEFAULT_TIMEOUT);
+    EXPECT(err == OK
+            && msg.type == omx_message::EVENT
+            && msg.u.event_data.event == OMX_EventCmdComplete
+            && msg.u.event_data.data1 == OMX_CommandStateSet
+            && msg.u.event_data.data2 == OMX_StateIdle,
+           "Component did not properly transition to from executing to "
+           "idle state.");
+
+    for (size_t i = 0; i < inputBuffers.size(); ++i) {
+        EXPECT((inputBuffers[i].mFlags & kBufferBusy) == 0,
+                "Not all input buffers have been returned to us by the "
+                "time we received the transition-to-idle complete "
+                "notification.");
+    }
+
+    for (size_t i = 0; i < outputBuffers.size(); ++i) {
+        EXPECT((outputBuffers[i].mFlags & kBufferBusy) == 0,
+                "Not all output buffers have been returned to us by the "
+                "time we received the transition-to-idle complete "
+                "notification.");
+    }
+
+    // Initiate transition Idle->Loaded
+    err = mOMX->sendCommand(node, OMX_CommandStateSet, OMX_StateLoaded);
+    EXPECT_SUCCESS(err, "sendCommand(go-to-Loaded)");
+
+    // Make sure node doesn't just transition to loaded before we are done
+    // freeing all input and output buffers.
+    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
+    CHECK_EQ(err, TIMED_OUT);
+
+    for (size_t i = 0; i < inputBuffers.size(); ++i) {
+        err = mOMX->freeBuffer(node, 0, inputBuffers[i].mID);
+        EXPECT_SUCCESS(err, "freeBuffer");
+    }
+
+    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
+    CHECK_EQ(err, TIMED_OUT);
+
+    for (size_t i = 0; i < outputBuffers.size(); ++i) {
+        err = mOMX->freeBuffer(node, 1, outputBuffers[i].mID);
+        EXPECT_SUCCESS(err, "freeBuffer");
+    }
+
+    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
+    EXPECT(err == OK
+            && msg.type == omx_message::EVENT
+            && msg.u.event_data.event == OMX_EventCmdComplete
+            && msg.u.event_data.data1 == OMX_CommandStateSet
+            && msg.u.event_data.data2 == OMX_StateLoaded,
+           "Component did not properly transition to from idle to "
+           "loaded state after freeing all input and output buffers.");
+
+    err = mOMX->freeNode(node);
+    EXPECT_SUCCESS(err, "freeNode");
+
+    reaper.disarm();
+
+    node = 0;
+
+    return OK;
+}
+
+static const char *GetMimeFromComponentRole(const char *componentRole) {
+    struct RoleToMime {
+        const char *mRole;
+        const char *mMime;
+    };
+    const RoleToMime kRoleToMime[] = {
+        { "video_decoder.avc", "video/avc" },
+        { "video_decoder.mpeg4", "video/mp4v-es" },
+        { "video_decoder.h263", "video/3gpp" },
+
+        // we appear to use this as a synonym to amrnb.
+        { "audio_decoder.amr", "audio/3gpp" },
+
+        { "audio_decoder.amrnb", "audio/3gpp" },
+        { "audio_decoder.amrwb", "audio/amr-wb" },
+        { "audio_decoder.aac", "audio/mp4a-latm" },
+        { "audio_decoder.mp3", "audio/mpeg" }
+    };
+
+    for (size_t i = 0; i < sizeof(kRoleToMime) / sizeof(kRoleToMime[0]); ++i) {
+        if (!strcmp(componentRole, kRoleToMime[i].mRole)) {
+            return kRoleToMime[i].mMime;
+        }
+    }
+
+    return NULL;
+}
+
+static const char *GetURLForMime(const char *mime) {
+    struct MimeToURL {
+        const char *mMime;
+        const char *mURL;
+    };
+    static const MimeToURL kMimeToURL[] = {
+        { "video/avc",
+          "file:///sdcard/media_api/video/H264_AAC.3gp" },
+        { "video/mp4v-es", "file:///sdcard/media_api/video/gingerkids.MP4" },
+        { "video/3gpp",
+          "file:///sdcard/media_api/video/H263_500_AMRNB_12.3gp" },
+        { "audio/3gpp",
+          "file:///sdcard/media_api/video/H263_500_AMRNB_12.3gp" },
+        { "audio/amr-wb",
+          "file:///sdcard/media_api/music_perf/AMRWB/"
+          "NIN_AMR-WB_15.85kbps_16kbps.amr" },
+        { "audio/mp4a-latm",
+          "file:///sdcard/media_api/music_perf/AAC/"
+          "WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4" },
+        { "audio/mpeg",
+          "file:///sdcard/media_api/music_perf/MP3/"
+          "WC_256kbps_44.1khz_mono_CBR_DPA.mp3" }
+    };
+
+    for (size_t i = 0; i < sizeof(kMimeToURL) / sizeof(kMimeToURL[0]); ++i) {
+        if (!strcasecmp(kMimeToURL[i].mMime, mime)) {
+            return kMimeToURL[i].mURL;
+        }
+    }
+
+    return NULL;
+}
+
+static sp<MediaSource> CreateSourceForMime(const char *mime) {
+    const char *url = GetURLForMime(mime);
+    CHECK(url != NULL);
+
+    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(url);
+
+    CHECK(extractor != NULL);
+
+    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        sp<MetaData> meta = extractor->getTrackMetaData(i);
+        CHECK(meta != NULL);
+
+        const char *trackMime;
+        CHECK(meta->findCString(kKeyMIMEType, &trackMime));
+
+        if (!strcasecmp(mime, trackMime)) {
+            return extractor->getTrack(i);
+        }
+    }
+
+    return NULL;
+}
+
+static double uniform_rand() {
+    return (double)rand() / RAND_MAX;
+}
+
+static bool CloseEnough(int64_t time1Us, int64_t time2Us) {
+#if 0
+    int64_t diff = time1Us - time2Us;
+    if (diff < 0) {
+        diff = -diff;
+    }
+
+    return diff <= 50000;
+#else
+    return time1Us == time2Us;
+#endif
+}
+
+status_t Harness::testSeek(
+        const char *componentName, const char *componentRole) {
+    bool isEncoder =
+        !strncmp(componentRole, "audio_encoder.", 14)
+        || !strncmp(componentRole, "video_encoder.", 14);
+
+    if (isEncoder) {
+        // Not testing seek behaviour for encoders.
+
+        printf("  * Not testing seek functionality for encoders.\n");
+        return OK;
+    }
+
+    const char *mime = GetMimeFromComponentRole(componentRole);
+
+    if (!mime) {
+        LOGI("Cannot perform seek test with this componentRole (%s)",
+             componentRole);
+
+        return OK;
+    }
+
+    sp<MediaSource> source = CreateSourceForMime(mime);
+
+    sp<MediaSource> seekSource = CreateSourceForMime(mime);
+    CHECK_EQ(seekSource->start(), OK);
+
+    sp<MediaSource> codec = OMXCodec::Create(
+            mOMX, source->getFormat(), false /* createEncoder */,
+            source, componentName);
+
+    CHECK(codec != NULL);
+
+    CHECK_EQ(codec->start(), OK);
+
+    int64_t durationUs;
+    CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
+
+    LOGI("stream duration is %lld us (%.2f secs)",
+         durationUs, durationUs / 1E6);
+
+    static const int32_t kNumIterations = 5000;
+
+    for (int32_t i = 0; i < kNumIterations; ++i) {
+        int64_t requestedSeekTimeUs;
+        int64_t actualSeekTimeUs;
+        MediaSource::ReadOptions options;
+
+        double r = uniform_rand();
+
+        if (r < 0.5) {
+            // 50% chance of just continuing to decode from last position.
+
+            requestedSeekTimeUs = -1;
+
+            LOGI("requesting linear read");
+        } else {
+            if (r < 0.55) {
+                // 5% chance of seeking beyond end of stream.
+
+                requestedSeekTimeUs = durationUs;
+
+                LOGI("requesting seek beyond EOF");
+            } else {
+                requestedSeekTimeUs =
+                    (int64_t)(uniform_rand() * durationUs);
+
+                LOGI("requesting seek to %lld us (%.2f secs)",
+                     requestedSeekTimeUs, requestedSeekTimeUs / 1E6);
+            }
+
+            MediaBuffer *buffer;
+            options.setSeekTo(requestedSeekTimeUs);
+            if (seekSource->read(&buffer, &options) != OK) {
+                CHECK_EQ(buffer, NULL);
+                actualSeekTimeUs = -1;
+            } else {
+                CHECK(buffer != NULL);
+                CHECK(buffer->meta_data()->findInt64(kKeyTime, &actualSeekTimeUs));
+                CHECK(actualSeekTimeUs >= 0);
+
+                buffer->release();
+                buffer = NULL;
+            }
+
+            LOGI("nearest keyframe is at %lld us (%.2f secs)",
+                 actualSeekTimeUs, actualSeekTimeUs / 1E6);
+        }
+
+        status_t err;
+        MediaBuffer *buffer;
+        for (;;) {
+            err = codec->read(&buffer, &options);
+            options.clearSeekTo();
+            if (err == INFO_FORMAT_CHANGED) {
+                CHECK_EQ(buffer, NULL);
+                continue;
+            }
+            if (err == OK) {
+                CHECK(buffer != NULL);
+                if (buffer->range_length() == 0) {
+                    buffer->release();
+                    buffer = NULL;
+                    continue;
+                }
+            } else {
+                CHECK_EQ(buffer, NULL);
+            }
+
+            break;
+        }
+
+        if (requestedSeekTimeUs < 0) {
+            // Linear read.
+            if (err != OK) {
+                CHECK_EQ(buffer, NULL);
+            } else {
+                CHECK(buffer != NULL);
+                buffer->release();
+                buffer = NULL;
+            }
+        } else if (actualSeekTimeUs < 0) {
+            CHECK(err != OK);
+            CHECK_EQ(buffer, NULL);
+        } else {
+            EXPECT(err == OK,
+                   "Expected a valid buffer to be returned from "
+                   "OMXCodec::read.");
+            CHECK(buffer != NULL);
+
+            int64_t bufferTimeUs;
+            CHECK(buffer->meta_data()->findInt64(kKeyTime, &bufferTimeUs));
+            if (!CloseEnough(bufferTimeUs, actualSeekTimeUs)) {
+                printf("\n  * Attempted seeking to %lld us (%.2f secs)",
+                       requestedSeekTimeUs, requestedSeekTimeUs / 1E6);
+                printf("\n  * Nearest keyframe is at %lld us (%.2f secs)",
+                       actualSeekTimeUs, actualSeekTimeUs / 1E6);
+                printf("\n  * Returned buffer was at %lld us (%.2f secs)\n\n",
+                       bufferTimeUs, bufferTimeUs / 1E6);
+
+                buffer->release();
+                buffer = NULL;
+
+                CHECK_EQ(codec->stop(), OK);
+
+                return UNKNOWN_ERROR;
+            }
+
+            buffer->release();
+            buffer = NULL;
+        }
+    }
+
+    CHECK_EQ(codec->stop(), OK);
+
+    return OK;
+}
+
+status_t Harness::test(
+        const char *componentName, const char *componentRole) {
+    printf("testing %s [%s] ... ", componentName, componentRole);
+    LOGI("testing %s [%s].", componentName, componentRole);
+
+    status_t err1 = testStateTransitions(componentName, componentRole);
+    status_t err2 = testSeek(componentName, componentRole);
+
+    if (err1 != OK) {
+        return err1;
+    }
+
+    return err2;
+}
+
+status_t Harness::testAll() {
+    List<IOMX::ComponentInfo> componentInfos;
+    status_t err = mOMX->listNodes(&componentInfos);
+    EXPECT_SUCCESS(err, "listNodes");
+
+    for (List<IOMX::ComponentInfo>::iterator it = componentInfos.begin();
+         it != componentInfos.end(); ++it) {
+        const IOMX::ComponentInfo &info = *it;
+        const char *componentName = info.mName.string();
+
+        for (List<String8>::const_iterator role_it = info.mRoles.begin();
+             role_it != info.mRoles.end(); ++role_it) {
+            const char *componentRole = (*role_it).string();
+
+            err = test(componentName, componentRole);
+
+            if (err == OK) {
+                printf("OK\n");
+            }
+        }
+    }
+
+    return OK;
+}
+
+}  // namespace android
+
+static void usage(const char *me) {
+    fprintf(stderr, "usage: %s\n"
+                    "  -h(elp)  Show this information\n"
+                    "  -s(eed)  Set the random seed\n"
+                    "    [ component role ]\n\n"
+                    "When launched without specifying a specific component "
+                    "and role, tool will test all available OMX components "
+                    "in all their supported roles. To determine available "
+                    "component names, use \"stagefright -l\"\n"
+                    "It's also a good idea to run a separate \"adb logcat\""
+                    " for additional debug and progress information.", me);
+
+    exit(0);
+}
+
+int main(int argc, char **argv) {
+    using namespace android;
+
+    android::ProcessState::self()->startThreadPool();
+    DataSource::RegisterDefaultSniffers();
+
+    const char *me = argv[0];
+
+    unsigned long seed = 0xdeadbeef;
+
+    int res;
+    while ((res = getopt(argc, argv, "hs:")) >= 0) {
+        switch (res) {
+            case 's':
+            {
+                char *end;
+                unsigned long x = strtoul(optarg, &end, 10);
+
+                if (*end != '\0' || end == optarg) {
+                    fprintf(stderr, "Malformed seed.\n");
+                    return 1;
+                }
+
+                seed = x;
+                break;
+            }
+
+            case '?':
+                fprintf(stderr, "\n");
+                // fall through
+
+            case 'h':
+            default:
+            {
+                usage(me);
+                exit(1);
+                break;
+            }
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    printf("To reproduce the conditions for this test, launch "
+           "with \"%s -s %lu\"\n", me, seed);
+
+    srand(seed);
+
+    sp<Harness> h = new Harness;
+    CHECK_EQ(h->initCheck(), OK);
+
+    if (argc == 0) {
+        h->testAll();
+    } else if (argc == 2) {
+        if (h->test(argv[0], argv[1]) == OK) {
+            printf("OK\n");
+        }
+    }
+
+    return 0;
+}
diff --git a/media/libstagefright/omx/tests/OMXHarness.h b/media/libstagefright/omx/tests/OMXHarness.h
new file mode 100644
index 0000000..bb8fd0c
--- /dev/null
+++ b/media/libstagefright/omx/tests/OMXHarness.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OMX_HARNESS_H_
+
+#define OMX_HARNESS_H_
+
+#include <media/IOMX.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+class MemoryDealer;
+
+struct Harness : public BnOMXObserver {
+    enum BufferFlags {
+        kBufferBusy = 1
+    };
+    struct Buffer {
+        IOMX::buffer_id mID;
+        sp<IMemory> mMemory;
+        uint32_t mFlags;
+    };
+
+    Harness();
+
+    status_t initCheck() const;
+
+    status_t dequeueMessageForNode(
+            IOMX::node_id node, omx_message *msg, int64_t timeoutUs = -1);
+
+    status_t dequeueMessageForNodeIgnoringBuffers(
+            IOMX::node_id node,
+            Vector<Buffer> *inputBuffers,
+            Vector<Buffer> *outputBuffers,
+            omx_message *msg, int64_t timeoutUs = -1);
+
+    status_t getPortDefinition(
+            IOMX::node_id node, OMX_U32 portIndex,
+            OMX_PARAM_PORTDEFINITIONTYPE *def);
+
+    status_t allocatePortBuffers(
+            const sp<MemoryDealer> &dealer,
+            IOMX::node_id node, OMX_U32 portIndex,
+            Vector<Buffer> *buffers);
+
+    status_t setRole(IOMX::node_id node, const char *role);
+
+    status_t testStateTransitions(
+            const char *componentName, const char *componentRole);
+
+    status_t testSeek(
+            const char *componentName, const char *componentRole);
+
+    status_t test(
+            const char *componentName, const char *componentRole);
+
+    status_t testAll();
+
+    virtual void onMessage(const omx_message &msg);
+
+protected:
+    virtual ~Harness();
+
+private:
+    friend struct NodeReaper;
+
+    Mutex mLock;
+
+    status_t mInitCheck;
+    sp<IOMX> mOMX;
+    List<omx_message> mMessageQueue;
+    Condition mMessageAddedCondition;
+
+    status_t initOMX();
+
+    bool handleBufferMessage(
+            const omx_message &msg,
+            Vector<Buffer> *inputBuffers,
+            Vector<Buffer> *outputBuffers);
+
+    Harness(const Harness &);
+    Harness &operator=(const Harness &);
+};
+
+}  // namespace android
+
+#endif  // OMX_HARNESS_H_
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
index aca90bb..503d440 100644
--- a/obex/javax/obex/ServerSession.java
+++ b/obex/javax/obex/ServerSession.java
@@ -315,7 +315,7 @@
                 if (!((flags & 1) == 0)) {
                     backup = true;
                 }
-                if ((flags & 2) == 0) {
+                if (!((flags & 2) == 0)) {
                     create = false;
                 }