Merge "Fix 3106227: use WeakReferences for receivers in DigitalClock class" into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 48816af..4006e67 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -29401,6 +29401,23 @@
  visibility="public"
 >
 </method>
+<method name="listenUsingInsecureRfcommWithServiceRecord"
+ return="android.bluetooth.BluetoothServerSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="uuid" type="java.util.UUID">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
 <method name="listenUsingRfcommWithServiceRecord"
  return="android.bluetooth.BluetoothServerSocket"
  abstract="false"
@@ -30587,6 +30604,21 @@
 >
 <implements name="android.os.Parcelable">
 </implements>
+<method name="createInsecureRfcommSocketToServiceRecord"
+ return="android.bluetooth.BluetoothSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uuid" type="java.util.UUID">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
 <method name="createRfcommSocketToServiceRecord"
  return="android.bluetooth.BluetoothSocket"
  abstract="false"
@@ -86115,6 +86147,385 @@
 </parameter>
 </method>
 </interface>
+<class name="MediaMetadataRetriever"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="MediaMetadataRetriever"
+ type="android.media.MediaMetadataRetriever"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="extractMetadata"
+ return="java.lang.String"
+ abstract="false"
+ native="true"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+</method>
+<method name="getEmbeddedPicture"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFrameAtTime"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timeUs" type="long">
+</parameter>
+<parameter name="option" type="int">
+</parameter>
+</method>
+<method name="getFrameAtTime"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timeUs" type="long">
+</parameter>
+</method>
+<method name="getFrameAtTime"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="release"
+ return="void"
+ abstract="false"
+ native="true"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setDataSource"
+ return="void"
+ abstract="false"
+ native="true"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+</method>
+<method name="setDataSource"
+ return="void"
+ abstract="false"
+ native="true"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="offset" type="long">
+</parameter>
+<parameter name="length" type="long">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+</method>
+<method name="setDataSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+</method>
+<method name="setDataSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
+<exception name="SecurityException" type="java.lang.SecurityException">
+</exception>
+</method>
+<field name="METADATA_KEY_ALBUM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_ALBUMARTIST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="13"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_ARTIST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_AUTHOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_CD_TRACK_NUMBER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_COMPILATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="15"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_COMPOSER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_DATE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_DISC_NUMBER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="14"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_DURATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_GENRE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_MIMETYPE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="12"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_NUM_TRACKS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_TITLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_WRITER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="METADATA_KEY_YEAR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OPTION_CLOSEST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OPTION_CLOSEST_SYNC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OPTION_NEXT_SYNC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OPTION_PREVIOUS_SYNC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="MediaPlayer"
  extends="java.lang.Object"
  abstract="false"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index f55b746..ff92431 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -596,21 +596,19 @@
             const char *filename = argv[k];
 
             CHECK_EQ(retriever->setDataSource(filename), (status_t)OK);
-            CHECK_EQ(retriever->setMode(
-                        METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL),
-                     (status_t)OK);
-
-            sp<IMemory> mem = retriever->captureFrame();
+            sp<IMemory> mem =
+                    retriever->getFrameAtTime(-1,
+                                    MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
 
             if (mem != NULL) {
-                printf("captureFrame(%s) => OK\n", filename);
+                printf("getFrameAtTime(%s) => OK\n", filename);
             } else {
                 mem = retriever->extractAlbumArt();
 
                 if (mem != NULL) {
                     printf("extractAlbumArt(%s) => OK\n", filename);
                 } else {
-                    printf("both captureFrame and extractAlbumArt "
+                    printf("both getFrameAtTime and extractAlbumArt "
                            "failed on file '%s'.\n", filename);
                 }
             }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 3040319..a7175e3 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -729,6 +729,15 @@
      * Create a listening, secure RFCOMM Bluetooth socket.
      * <p>A remote device connecting to this socket will be authenticated and
      * communication on this socket will be encrypted.
+     * <p> Use this socket only if an authenticated socket link is possible.
+     * Authentication refers to the authentication of the link key to
+     * prevent man-in-the-middle type of attacks.
+     * For example, for Bluetooth 2.1 devices, if any of the devices does not
+     * have an input and output capability or just has the ability to
+     * display a numeric key, a secure socket connection is not possible.
+     * In such a case, use {#link listenUsingInsecureRfcommOn}.
+     * For more details, refer to the Security Model section 5.2 (vol 3) of
+     * Bluetooth Core Specification version 2.1 + EDR.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
      * connections from a listening {@link BluetoothServerSocket}.
      * <p>Valid RFCOMM channels are in range 1 to 30.
@@ -756,6 +765,15 @@
      * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
      * <p>A remote device connecting to this socket will be authenticated and
      * communication on this socket will be encrypted.
+     * <p> Use this socket only if an authenticated socket link is possible.
+     * Authentication refers to the authentication of the link key to
+     * prevent man-in-the-middle type of attacks.
+     * For example, for Bluetooth 2.1 devices, if any of the devices does not
+     * have an input and output capability or just has the ability to
+     * display a numeric key, a secure socket connection is not possible.
+     * In such a case, use {#link listenUsingInsecureRfcommWithServiceRecord}.
+     * For more details, refer to the Security Model section 5.2 (vol 3) of
+     * Bluetooth Core Specification version 2.1 + EDR.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
      * connections from a listening {@link BluetoothServerSocket}.
      * <p>The system will assign an unused RFCOMM channel to listen on.
@@ -776,6 +794,42 @@
      */
     public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
             throws IOException {
+        return createNewRfcommSocketAndRecord(name, uuid, true, true);
+    }
+
+    /**
+     * Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
+     * <p>The link key will be unauthenticated i.e the communication is
+     * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices,
+     * the link key will be encrypted, as encryption is mandartory.
+     * For legacy devices (pre Bluetooth 2.1 devices) the link key will not
+     * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
+     * encrypted and authenticated communication channel is desired.
+     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
+     * connections from a listening {@link BluetoothServerSocket}.
+     * <p>The system will assign an unused RFCOMM channel to listen on.
+     * <p>The system will also register a Service Discovery
+     * Protocol (SDP) record with the local SDP server containing the specified
+     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
+     * can use the same UUID to query our SDP server and discover which channel
+     * to connect to. This SDP record will be removed when this socket is
+     * closed, or if this application closes unexpectedly.
+     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
+     * connect to this socket from another device using the same {@link UUID}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     * @param name service name for SDP record
+     * @param uuid uuid for SDP record
+     * @return a listening RFCOMM BluetoothServerSocket
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions, or channel in use.
+     */
+    public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
+            throws IOException {
+        return createNewRfcommSocketAndRecord(name, uuid, false, false);
+    }
+
+    private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
+            boolean auth, boolean encrypt) throws IOException {
         RfcommChannelPicker picker = new RfcommChannelPicker(uuid);
 
         BluetoothServerSocket socket;
@@ -789,7 +843,7 @@
             }
 
             socket = new BluetoothServerSocket(
-                    BluetoothSocket.TYPE_RFCOMM, true, true, channel);
+                    BluetoothSocket.TYPE_RFCOMM, auth, encrypt, channel);
             errno = socket.mSocket.bindListen();
             if (errno == 0) {
                 if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel);
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index ada3c24..aee6ad8 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -693,6 +693,15 @@
      * outgoing connection to this remote device on given channel.
      * <p>The remote device will be authenticated and communication on this
      * socket will be encrypted.
+     * <p> Use this socket only if an authenticated socket link is possible.
+     * Authentication refers to the authentication of the link key to
+     * prevent man-in-the-middle type of attacks.
+     * For example, for Bluetooth 2.1 devices, if any of the devices does not
+     * have an input and output capability or just has the ability to
+     * display a numeric key, a secure socket connection is not possible.
+     * In such a case, use {#link createInsecureRfcommSocket}.
+     * For more details, refer to the Security Model section 5.2 (vol 3) of
+     * Bluetooth Core Specification version 2.1 + EDR.
      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
      * connection.
      * <p>Valid RFCOMM channels are in range 1 to 30.
@@ -720,6 +729,48 @@
      * determine which channel to connect to.
      * <p>The remote device will be authenticated and communication on this
      * socket will be encrypted.
+     * <p> Use this socket only if an authenticated socket link is possible.
+     * Authentication refers to the authentication of the link key to
+     * prevent man-in-the-middle type of attacks.
+     * For example, for Bluetooth 2.1 devices, if any of the devices does not
+     * have an input and output capability or just has the ability to
+     * display a numeric key, a secure socket connection is not possible.
+     * In such a case, use {#link createInsecureRfcommSocketToServiceRecord}.
+     * For more details, refer to the Security Model section 5.2 (vol 3) of
+     * Bluetooth Core Specification version 2.1 + EDR.
+     * <p>Hint: If you are connecting to a Bluetooth serial board then try
+     * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
+     * However if you are connecting to an Android peer then please generate
+     * your own unique UUID.
+         * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param uuid service record uuid to lookup RFCOMM channel
+     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions
+     */
+    public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
+        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
+                new ParcelUuid(uuid));
+    }
+
+    /**
+     * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure
+     * outgoing connection to this remote device using SDP lookup of uuid.
+     * <p> The communication channel will not have an authenticated link key
+     * i.e it will be subject to man-in-the-middle attacks. For Bluetooth 2.1
+     * devices, the link key will be encrypted, as encryption is mandatory.
+     * For legacy devices (pre Bluetooth 2.1 devices) the link key will
+     * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an
+     * encrypted and authenticated communication channel is desired.
+     * <p>This is designed to be used with {@link
+     * BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord} for peer-peer
+     * Bluetooth applications.
+     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
+     * connection. This will also perform an SDP lookup of the given uuid to
+     * determine which channel to connect to.
+     * <p>The remote device will be authenticated and communication on this
+     * socket will be encrypted.
      * <p>Hint: If you are connecting to a Bluetooth serial board then try
      * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
      * However if you are connecting to an Android peer then please generate
@@ -731,8 +782,8 @@
      * @throws IOException on error, for example Bluetooth not available, or
      *                     insufficient permissions
      */
-    public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
+    public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
+        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1,
                 new ParcelUuid(uuid));
     }
 
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c9d6af8..c0c0462 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -317,6 +317,7 @@
      * @hide
      */
     public INfcAdapter getService() {
+        isEnabled();  // NOP call to recover sService if it is stale
         return sService;
     }
 
@@ -325,6 +326,7 @@
      * @hide
      */
     public INfcTag getTagService() {
+        isEnabled();  // NOP call to recover sTagService if it is stale
         return sTagService;
     }
 
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 2a76e33..6095a64 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -234,6 +234,14 @@
         // high time constant.
         private static final float MAX_DEVIATION_FROM_GRAVITY = 1.5f;
 
+        // Minimum acceleration considered, in m/s^2. Below this threshold sensor noise will have
+        // significant impact on the calculations and in case of the vector (0, 0, 0) there is no
+        // defined rotation or tilt at all. Low or zero readings can happen when space travelling
+        // or free falling, but more commonly when shaking or getting bad readings from the sensor.
+        // The accelerometer is turned off when not used and polling it too soon after it is
+        // turned on may result in (0, 0, 0).
+        private static final float MIN_ABS_ACCELERATION = 1.5f;
+
         // Actual sampling period corresponding to SensorManager.SENSOR_DELAY_NORMAL.  There's no
         // way to get this information from SensorManager.
         // Note the actual period is generally 3-30ms larger than this depending on the device, but
@@ -347,6 +355,9 @@
             float deviation = Math.abs(magnitude - SensorManager.STANDARD_GRAVITY);
 
             handleAccelerationDistrust(deviation);
+            if (magnitude < MIN_ABS_ACCELERATION) {
+                return; // Ignore tilt and orientation when (0, 0, 0) or low reading
+            }
 
             // only filter tilt when we're accelerating
             float alpha = 1;
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index 9baba8e..8e3cdbb 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -32,9 +32,7 @@
     virtual void            disconnect() = 0;
     virtual status_t        setDataSource(const char* srcUrl) = 0;
     virtual status_t        setDataSource(int fd, int64_t offset, int64_t length) = 0;
-    virtual status_t        setMode(int mode) = 0;
-    virtual status_t        getMode(int* mode) const = 0;
-    virtual sp<IMemory>     captureFrame() = 0;
+    virtual sp<IMemory>     getFrameAtTime(int64_t timeUs, int option) = 0;
     virtual sp<IMemory>     extractAlbumArt() = 0;
     virtual const char*     extractMetadata(int keyCode) = 0;
 };
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index ff57774..0449122 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -32,9 +32,7 @@
     virtual             ~MediaMetadataRetrieverBase() {}
     virtual status_t    setDataSource(const char *url) = 0;
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) = 0;
-    virtual status_t    setMode(int mode) = 0;
-    virtual status_t    getMode(int* mode) const = 0;
-    virtual VideoFrame* captureFrame() = 0;
+    virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) = 0;
     virtual MediaAlbumArt* extractAlbumArt() = 0;
     virtual const char* extractMetadata(int keyCode) = 0;
 };
@@ -43,35 +41,12 @@
 class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase
 {
 public:
-    MediaMetadataRetrieverInterface()
-        : mMode(0) {
-    }
+    MediaMetadataRetrieverInterface() {}
 
     virtual             ~MediaMetadataRetrieverInterface() {}
-
-    // @param mode The intended mode of operations:
-    // can be any of the following:
-    // METADATA_MODE_NOOP: Experimental - just add and remove data source.
-    // METADATA_MODE_FRAME_CAPTURE_ONLY: For capture frame/thumbnail only.
-    // METADATA_MODE_METADATA_RETRIEVAL_ONLY: For meta data retrieval only.
-    // METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL: For both frame
-    //     capture and meta data retrieval.
-    virtual status_t    setMode(int mode) {
-                            if (mode < METADATA_MODE_NOOP ||
-                                mode > METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL) {
-                                return BAD_VALUE;
-                            }
-
-                            mMode = mode;
-                            return NO_ERROR;
-                        }
-
-    virtual status_t    getMode(int* mode) const { *mode = mMode; return NO_ERROR; }
-    virtual VideoFrame* captureFrame() { return NULL; }
+    virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) { return NULL; }
     virtual MediaAlbumArt* extractAlbumArt() { return NULL; }
     virtual const char* extractMetadata(int keyCode) { return NULL; }
-
-    uint32_t mMode;
 };
 
 }; // namespace android
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index ddc07f6..e905006 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -42,37 +42,14 @@
     METADATA_KEY_YEAR            = 8,
     METADATA_KEY_DURATION        = 9,
     METADATA_KEY_NUM_TRACKS      = 10,
-    METADATA_KEY_IS_DRM_CRIPPLED = 11,
-    METADATA_KEY_CODEC           = 12,
-    METADATA_KEY_RATING          = 13,
-    METADATA_KEY_COMMENT         = 14,
-    METADATA_KEY_COPYRIGHT       = 15,
-    METADATA_KEY_BIT_RATE        = 16,
-    METADATA_KEY_FRAME_RATE      = 17,
-    METADATA_KEY_VIDEO_FORMAT    = 18,
-    METADATA_KEY_VIDEO_HEIGHT    = 19,
-    METADATA_KEY_VIDEO_WIDTH     = 20,
-    METADATA_KEY_WRITER          = 21,
-    METADATA_KEY_MIMETYPE        = 22,
-    METADATA_KEY_DISC_NUMBER     = 23,
-    METADATA_KEY_ALBUMARTIST     = 24,
-    METADATA_KEY_COMPILATION     = 25,
+    METADATA_KEY_WRITER          = 11,
+    METADATA_KEY_MIMETYPE        = 12,
+    METADATA_KEY_ALBUMARTIST     = 13,
+    METADATA_KEY_DISC_NUMBER     = 14,
+    METADATA_KEY_COMPILATION     = 15,
     // Add more here...
 };
 
-// The intended mode of operations:$
-// METADATA_MODE_NOOP: Experimental - just add and remove data source.$
-// METADATA_MODE_FRAME_CAPTURE_ONLY: For capture frame/thumbnail only.$
-// METADATA_MODE_METADATA_RETRIEVAL_ONLY: For meta data retrieval only.$
-// METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL: For both frame capture
-//   and meta data retrieval.$
-enum {
-    METADATA_MODE_NOOP                                 = 0x00,
-    METADATA_MODE_METADATA_RETRIEVAL_ONLY              = 0x01,
-    METADATA_MODE_FRAME_CAPTURE_ONLY                   = 0x02,
-    METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL = 0x03
-};
-
 class MediaMetadataRetriever: public RefBase
 {
 public:
@@ -81,9 +58,7 @@
     void disconnect();
     status_t setDataSource(const char* dataSourceUrl);
     status_t setDataSource(int fd, int64_t offset, int64_t length);
-    status_t setMode(int mode);
-    status_t getMode(int* mode);
-    sp<IMemory> captureFrame();
+    sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
     sp<IMemory> extractAlbumArt();
     const char* extractMetadata(int keyCode);
 
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 008528d..77e939e 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -29,7 +29,6 @@
 /**
  * MediaMetadataRetriever class provides a unified interface for retrieving
  * frame and meta data from an input media file.
- * {@hide}
  */
 public class MediaMetadataRetriever
 {
@@ -42,41 +41,13 @@
     @SuppressWarnings("unused")
     private int mNativeContext;
  
+    private static final int EMBEDDED_PICTURE_TYPE_ANY = 0xFFFF;
+
     public MediaMetadataRetriever() {
         native_setup();
     }
 
     /**
-     * Call this method before setDataSource() so that the mode becomes
-     * effective for subsequent operations. This method can be called only once
-     * at the beginning if the intended mode of operation for a
-     * MediaMetadataRetriever object remains the same for its whole lifetime,
-     * and thus it is unnecessary to call this method each time setDataSource()
-     * is called. If this is not never called (which is allowed), by default the
-     * intended mode of operation is to both capture frame and retrieve meta
-     * data (i.e., MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY).
-     * Often, this may not be what one wants, since doing this has negative
-     * performance impact on execution time of a call to setDataSource(), since
-     * both types of operations may be time consuming.
-     * 
-     * @param mode The intended mode of operation. Can be any combination of 
-     * MODE_GET_METADATA_ONLY and MODE_CAPTURE_FRAME_ONLY:
-     * 1. MODE_GET_METADATA_ONLY & MODE_CAPTURE_FRAME_ONLY: 
-     *    For neither frame capture nor meta data retrieval
-     * 2. MODE_GET_METADATA_ONLY: For meta data retrieval only
-     * 3. MODE_CAPTURE_FRAME_ONLY: For frame capture only
-     * 4. MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY: 
-     *    For both frame capture and meta data retrieval
-     */
-    public native void setMode(int mode);
-    
-    /**
-     * @return the current mode of operation. A negative return value indicates
-     * some runtime error has occurred.
-     */
-    public native int getMode();
-
-    /**
      * Sets the data source (file pathname) to use. Call this
      * method before the rest of the methods in this class. This method may be
      * time-consuming.
@@ -190,22 +161,99 @@
 
     /**
      * Call this method after setDataSource(). This method finds a
-     * representative frame if successful and returns it as a bitmap. This is
-     * useful for generating a thumbnail for an input media source.
-     * 
+     * representative frame close to the given time position by considering
+     * the given option if possible, and returns it as a bitmap. This is
+     * useful for generating a thumbnail for an input data source or just
+     * obtain and display a frame at the given time position.
+     *
+     * @param timeUs The time position where the frame will be retrieved.
+     * When retrieving the frame at the given time position, there is no
+     * guarantee that the data source has a frame located at the position.
+     * When this happens, a frame nearby will be returned. If timeUs is
+     * negative, time position and option will ignored, and any frame
+     * that the implementation considers as representative may be returned.
+     *
+     * @param option a hint on how the frame is found. Use
+     * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp earlier than or the same as timeUs. Use
+     * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp later than or the same as timeUs. Use
+     * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp closest to or the same as timeUs. Use
+     * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
+     * or may not be a sync frame but is closest to or the same as timeUs.
+     * {@link #OPTION_CLOSEST} often has larger performance overhead compared
+     * to the other options if there is no sync frame located at timeUs.
+     *
      * @return A Bitmap containing a representative video frame, which 
      *         can be null, if such a frame cannot be retrieved.
      */
-    public native Bitmap captureFrame();
+    public Bitmap getFrameAtTime(long timeUs, int option) {
+        if (option < OPTION_PREVIOUS_SYNC ||
+            option > OPTION_CLOSEST) {
+            throw new IllegalArgumentException("Unsupported option: " + option);
+        }
+
+        return _getFrameAtTime(timeUs, option);
+    }
+
+    /**
+     * Call this method after setDataSource(). This method finds a
+     * representative frame close to the given time position if possible,
+     * and returns it as a bitmap. This is useful for generating a thumbnail
+     * for an input data source. Call this method if one does not care
+     * how the frame is found as long as it is close to the given time;
+     * otherwise, please call {@link #getFrameAtTime(long, int)}.
+     *
+     * @param timeUs The time position where the frame will be retrieved.
+     * When retrieving the frame at the given time position, there is no
+     * guarentee that the data source has a frame located at the position.
+     * When this happens, a frame nearby will be returned. If timeUs is
+     * negative, time position and option will ignored, and any frame
+     * that the implementation considers as representative may be returned.
+     *
+     * @return A Bitmap containing a representative video frame, which
+     *         can be null, if such a frame cannot be retrieved.
+     *
+     * @see #getFrameAtTime(long, int)
+     */
+    public Bitmap getFrameAtTime(long timeUs) {
+        return getFrameAtTime(timeUs, OPTION_CLOSEST_SYNC);
+    }
+
+    /**
+     * Call this method after setDataSource(). This method finds a
+     * representative frame at any time position if possible,
+     * and returns it as a bitmap. This is useful for generating a thumbnail
+     * for an input data source. Call this method if one does not
+     * care about where the frame is located; otherwise, please call
+     * {@link #getFrameAtTime(long)} or {@link #getFrameAtTime(long, int)}
+     *
+     * @return A Bitmap containing a representative video frame, which
+     *         can be null, if such a frame cannot be retrieved.
+     *
+     * @see #getFrameAtTime(long)
+     * @see #getFrameAtTime(long, int)
+     */
+    public Bitmap getFrameAtTime() {
+        return getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
+    }
+
+    private native Bitmap _getFrameAtTime(long timeUs, int option);
+
     
     /**
      * Call this method after setDataSource(). This method finds the optional
-     * graphic or album art associated (embedded or external url linked) the 
-     * related data source.
+     * graphic or album art associated associated with the data source. If
+     * there are more than one pictures, (any) one of them is returned.
      * 
      * @return null if no such graphic is found.
      */
-    public native byte[] extractAlbumArt();
+    public byte[] getEmbeddedPicture() {
+        return getEmbeddedPicture(EMBEDDED_PICTURE_TYPE_ANY);
+    }
+
+    private native byte[] getEmbeddedPicture(int pictureType);
 
     /**
      * Call it when one is done with the object. This method releases the memory
@@ -226,38 +274,129 @@
         }
     }
 
-    public static final int MODE_GET_METADATA_ONLY  = 0x01;
-    public static final int MODE_CAPTURE_FRAME_ONLY = 0x02;
+    /**
+     * Option used in method {@link #getFrameAtTime(long, int)} to get a
+     * frame at a specified location.
+     *
+     * @see #getFrameAtTime(long, int)
+     */
+    /* Do not change these option values without updating their counterparts
+     * in include/media/stagefright/MediaSource.h!
+     */
+    /**
+     * This option is used with {@link #getFrameAtTime(long, int)} to retrieve
+     * a sync (or key) frame associated with a data source that is located
+     * right before or at the given time.
+     *
+     * @see #getFrameAtTime(long, int)
+     */
+    public static final int OPTION_PREVIOUS_SYNC    = 0x00;
+    /**
+     * This option is used with {@link #getFrameAtTime(long, int)} to retrieve
+     * a sync (or key) frame associated with a data source that is located
+     * right after or at the given time.
+     *
+     * @see #getFrameAtTime(long, int)
+     */
+    public static final int OPTION_NEXT_SYNC        = 0x01;
+    /**
+     * This option is used with {@link #getFrameAtTime(long, int)} to retrieve
+     * a sync (or key) frame associated with a data source that is located
+     * closest to (in time) or at the given time.
+     *
+     * @see #getFrameAtTime(long, int)
+     */
+    public static final int OPTION_CLOSEST_SYNC     = 0x02;
+    /**
+     * This option is used with {@link #getFrameAtTime(long, int)} to retrieve
+     * a frame (not necessarily a key frame) associated with a data source that
+     * is located closest to or at the given time.
+     *
+     * @see #getFrameAtTime(long, int)
+     */
+    public static final int OPTION_CLOSEST          = 0x03;
 
     /*
-     * Do not change these values without updating their counterparts
-     * in include/media/mediametadataretriever.h!
+     * Do not change these metadata key values without updating their
+     * counterparts in include/media/mediametadataretriever.h!
+     */
+    /**
+     * The metadata key to retrieve the numberic string describing the
+     * order of the audio data source on its original recording.
      */
     public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
+    /**
+     * The metadata key to retrieve the information about the album title
+     * of the data source.
+     */
     public static final int METADATA_KEY_ALBUM           = 1;
+    /**
+     * The metadata key to retrieve the information about the artist of
+     * the data source.
+     */
     public static final int METADATA_KEY_ARTIST          = 2;
+    /**
+     * The metadata key to retrieve the information about the author of
+     * the data source.
+     */
     public static final int METADATA_KEY_AUTHOR          = 3;
+    /**
+     * The metadata key to retrieve the information about the composer of
+     * the data source.
+     */
     public static final int METADATA_KEY_COMPOSER        = 4;
+    /**
+     * The metadata key to retrieve the date when the data source was created
+     * or modified.
+     */
     public static final int METADATA_KEY_DATE            = 5;
+    /**
+     * The metadata key to retrieve the content type or genre of the data
+     * source.
+     */
     public static final int METADATA_KEY_GENRE           = 6;
+    /**
+     * The metadata key to retrieve the data source title.
+     */
     public static final int METADATA_KEY_TITLE           = 7;
+    /**
+     * The metadata key to retrieve the year when the data source was created
+     * or modified.
+     */
     public static final int METADATA_KEY_YEAR            = 8;
+    /**
+     * The metadata key to retrieve the playback duration of the data source.
+     */
     public static final int METADATA_KEY_DURATION        = 9;
+    /**
+     * The metadata key to retrieve the number of tracks, such as audio, video,
+     * text, in the data source, such as a mp4 or 3gpp file.
+     */
     public static final int METADATA_KEY_NUM_TRACKS      = 10;
-    public static final int METADATA_KEY_IS_DRM_CRIPPLED = 11;
-    public static final int METADATA_KEY_CODEC           = 12;
-    public static final int METADATA_KEY_RATING          = 13;
-    public static final int METADATA_KEY_COMMENT         = 14;
-    public static final int METADATA_KEY_COPYRIGHT       = 15;
-    public static final int METADATA_KEY_BIT_RATE        = 16;
-    public static final int METADATA_KEY_FRAME_RATE      = 17;
-    public static final int METADATA_KEY_VIDEO_FORMAT    = 18;
-    public static final int METADATA_KEY_VIDEO_HEIGHT    = 19;
-    public static final int METADATA_KEY_VIDEO_WIDTH     = 20;
-    public static final int METADATA_KEY_WRITER          = 21;
-    public static final int METADATA_KEY_MIMETYPE        = 22;
-    public static final int METADATA_KEY_DISCNUMBER      = 23;
-    public static final int METADATA_KEY_ALBUMARTIST     = 24;
-    public static final int METADATA_KEY_COMPILATION     = 25;
+    /**
+     * The metadata key to retrieve the information of the writer (such as
+     * lyricist) of the data source.
+     */
+    public static final int METADATA_KEY_WRITER          = 11;
+    /**
+     * The metadata key to retrieve the mime type of the data source. Some
+     * example mime types include: "video/mp4", "audio/mp4", "audio/amr-wb",
+     * etc.
+     */
+    public static final int METADATA_KEY_MIMETYPE        = 12;
+    /**
+     * The metadata key to retrieve the information about the performers or
+     * artist associated with the data source.
+     */
+    public static final int METADATA_KEY_ALBUMARTIST     = 13;
+    /**
+     * The metadata key to retrieve the numberic string that describes which
+     * part of a set the audio data source comes from.
+     */
+    public static final int METADATA_KEY_DISC_NUMBER     = 14;
+    /**
+     * The metadata key to retrieve the music album compilation status.
+     */
+    public static final int METADATA_KEY_COMPILATION     = 15;
     // Add more here...
 }
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 3d85e31..494b4cb 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -146,9 +146,8 @@
         Bitmap bitmap = null;
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         try {
-            retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
             retriever.setDataSource(filePath);
-            bitmap = retriever.captureFrame();
+            bitmap = retriever.getFrameAtTime(-1);
         } catch (IllegalArgumentException ex) {
             // Assume this is a corrupt video file
         } catch (RuntimeException ex) {
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 63e9dc8..c5e58f7 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -131,33 +131,9 @@
     process_media_retriever_call(env, retriever->setDataSource(fd, offset, length), "java/lang/RuntimeException", "setDataSource failed");
 }
 
-static void android_media_MediaMetadataRetriever_setMode(JNIEnv *env, jobject thiz, jint mode)
+static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, jobject thiz, jlong timeUs, jint option)
 {
-    LOGV("setMode");
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
-    if (retriever == 0) {
-        jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
-        return;
-    }
-    process_media_retriever_call(env, retriever->setMode(mode), "java/lang/RuntimeException", "setMode failed");
-}
-
-static int android_media_MediaMetadataRetriever_getMode(JNIEnv *env, jobject thiz)
-{
-    LOGV("getMode");
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
-    if (retriever == 0) {
-        jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
-        return -1;  // Error
-    }
-    int mode = -1;
-    retriever->getMode(&mode);
-    return mode;
-}
-
-static jobject android_media_MediaMetadataRetriever_captureFrame(JNIEnv *env, jobject thiz)
-{
-    LOGV("captureFrame");
+    LOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
@@ -166,12 +142,12 @@
 
     // Call native method to retrieve a video frame
     VideoFrame *videoFrame = NULL;
-    sp<IMemory> frameMemory = retriever->captureFrame();
+    sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
     if (frameMemory != 0) {  // cast the shared structure to a VideoFrame object
         videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
     }
     if (videoFrame == NULL) {
-        LOGE("captureFrame: videoFrame is a NULL pointer");
+        LOGE("getFrameAtTime: videoFrame is a NULL pointer");
         return NULL;
     }
 
@@ -213,7 +189,7 @@
     // Create a SkBitmap to hold the pixels
     SkBitmap *bitmap = new SkBitmap();
     if (bitmap == NULL) {
-        LOGE("captureFrame: cannot instantiate a SkBitmap object.");
+        LOGE("getFrameAtTime: cannot instantiate a SkBitmap object.");
         return NULL;
     }
     bitmap->setConfig(SkBitmap::kRGB_565_Config, videoFrame->mDisplayWidth, videoFrame->mDisplayHeight);
@@ -242,21 +218,26 @@
                 false);                         // filter
 }
 
-static jbyteArray android_media_MediaMetadataRetriever_extractAlbumArt(JNIEnv *env, jobject thiz)
+static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
+        JNIEnv *env, jobject thiz, jint pictureType)
 {
-    LOGV("extractAlbumArt");
+    LOGV("getEmbeddedPicture: %d", pictureType);
     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
         return NULL;
     }
     MediaAlbumArt* mediaAlbumArt = NULL;
+
+    // FIXME:
+    // Use pictureType to retrieve the intended embedded picture and also change
+    // the method name to getEmbeddedPicture().
     sp<IMemory> albumArtMemory = retriever->extractAlbumArt();
     if (albumArtMemory != 0) {  // cast the shared structure to a MediaAlbumArt object
         mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer());
     }
     if (mediaAlbumArt == NULL) {
-        LOGE("extractAlbumArt: Call to extractAlbumArt failed.");
+        LOGE("getEmbeddedPicture: Call to getEmbeddedPicture failed.");
         return NULL;
     }
 
@@ -264,7 +245,7 @@
     char* data = (char*) mediaAlbumArt + sizeof(MediaAlbumArt);
     jbyteArray array = env->NewByteArray(len);
     if (!array) {  // OutOfMemoryError exception has already been thrown.
-        LOGE("extractAlbumArt: OutOfMemoryError is thrown.");
+        LOGE("getEmbeddedPicture: OutOfMemoryError is thrown.");
     } else {
         jbyte* bytes = env->GetByteArrayElements(array, NULL);
         if (bytes != NULL) {
@@ -365,11 +346,9 @@
 static JNINativeMethod nativeMethods[] = {
         {"setDataSource",   "(Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSource},
         {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
-        {"setMode",         "(I)V", (void *)android_media_MediaMetadataRetriever_setMode},
-        {"getMode",         "()I",  (void *)android_media_MediaMetadataRetriever_getMode},
-        {"captureFrame",    "()Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_captureFrame},
+        {"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
         {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
-        {"extractAlbumArt", "()[B", (void *)android_media_MediaMetadataRetriever_extractAlbumArt},
+        {"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
         {"release",         "()V", (void *)android_media_MediaMetadataRetriever_release},
         {"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize},
         {"native_setup",    "()V", (void *)android_media_MediaMetadataRetriever_native_setup},
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index e529d25..d5298c9 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -81,9 +81,7 @@
     DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
     SET_DATA_SOURCE_URL,
     SET_DATA_SOURCE_FD,
-    SET_MODE,
-    GET_MODE,
-    CAPTURE_FRAME,
+    GET_FRAME_AT_TIME,
     EXTRACT_ALBUM_ART,
     EXTRACT_METADATA,
 };
@@ -124,32 +122,17 @@
         return reply.readInt32();
     }
 
-    status_t setMode(int mode)
+    sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
     {
+        LOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
         Parcel data, reply;
         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
-        data.writeInt32(mode);
-        remote()->transact(SET_MODE, data, &reply);
-        return reply.readInt32();
-    }
-
-    status_t getMode(int* mode) const
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
-        remote()->transact(GET_MODE, data, &reply);
-        *mode = reply.readInt32();
-        return reply.readInt32();
-    }
-
-    sp<IMemory> captureFrame()
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
+        data.writeInt64(timeUs);
+        data.writeInt32(option);
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
         sendSchedPolicy(data);
 #endif
-        remote()->transact(CAPTURE_FRAME, data, &reply);
+        remote()->transact(GET_FRAME_AT_TIME, data, &reply);
         status_t ret = reply.readInt32();
         if (ret != NO_ERROR) {
             return NULL;
@@ -216,26 +199,15 @@
             reply->writeInt32(setDataSource(fd, offset, length));
             return NO_ERROR;
         } break;
-        case SET_MODE: {
+        case GET_FRAME_AT_TIME: {
             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
-            int mode = data.readInt32();
-            reply->writeInt32(setMode(mode));
-            return NO_ERROR;
-        } break;
-        case GET_MODE: {
-            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
-            int mode;
-            status_t status = getMode(&mode);
-            reply->writeInt32(mode);
-            reply->writeInt32(status);
-            return NO_ERROR;
-        } break;
-        case CAPTURE_FRAME: {
-            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
+            int64_t timeUs = data.readInt64();
+            int option = data.readInt32();
+            LOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
             setSchedPolicy(data);
 #endif
-            sp<IMemory> bitmap = captureFrame();
+            sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
             if (bitmap != 0) {  // Don't send NULL across the binder interface
                 reply->writeInt32(NO_ERROR);
                 reply->writeStrongBinder(bitmap->asBinder());
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index e2712ba..8dfcb3b 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -123,37 +123,15 @@
     return mRetriever->setDataSource(fd, offset, length);
 }
 
-status_t MediaMetadataRetriever::setMode(int mode)
+sp<IMemory> MediaMetadataRetriever::getFrameAtTime(int64_t timeUs, int option)
 {
-    LOGV("setMode(%d)", mode);
-    Mutex::Autolock _l(mLock);
-    if (mRetriever == 0) {
-        LOGE("retriever is not initialized");
-        return INVALID_OPERATION;
-    }
-    return mRetriever->setMode(mode);
-}
-
-status_t MediaMetadataRetriever::getMode(int* mode)
-{
-    LOGV("getMode");
-    Mutex::Autolock _l(mLock);
-    if (mRetriever == 0) {
-        LOGE("retriever is not initialized");
-        return INVALID_OPERATION;
-    }
-    return mRetriever->getMode(mode);
-}
-
-sp<IMemory> MediaMetadataRetriever::captureFrame()
-{
-    LOGV("captureFrame");
+    LOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option);
     Mutex::Autolock _l(mLock);
     if (mRetriever == 0) {
         LOGE("retriever is not initialized");
         return NULL;
     }
-    return mRetriever->captureFrame();
+    return mRetriever->getFrameAtTime(timeUs, option);
 }
 
 const char* MediaMetadataRetriever::extractMetadata(int keyCode)
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 39fce81..713e441 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -65,7 +65,6 @@
     mThumbnail = NULL;
     mAlbumArt = NULL;
     mRetriever = NULL;
-    mMode = METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL;
 }
 
 MetadataRetrieverClient::~MetadataRetrieverClient()
@@ -80,7 +79,7 @@
     char buffer[SIZE];
     String8 result;
     result.append(" MetadataRetrieverClient\n");
-    snprintf(buffer, 255, "  pid(%d) mode(%d)\n", mPid, mMode);
+    snprintf(buffer, 255, "  pid(%d)\n", mPid);
     result.append(buffer);
     write(fd, result.string(), result.size());
     write(fd, "\n", 1);
@@ -94,7 +93,6 @@
     mRetriever.clear();
     mThumbnail.clear();
     mAlbumArt.clear();
-    mMode = METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL;
     IPCThreadState::self()->flushCommands();
 }
 
@@ -140,10 +138,7 @@
     LOGV("player type = %d", playerType);
     sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
     if (p == NULL) return NO_INIT;
-    status_t ret = p->setMode(mMode);
-    if (ret == NO_ERROR) {
-        ret = p->setDataSource(url);
-    }
+    status_t ret = p->setDataSource(url);
     if (ret == NO_ERROR) mRetriever = p;
     return ret;
 }
@@ -181,55 +176,22 @@
         ::close(fd);
         return NO_INIT;
     }
-    status_t status = p->setMode(mMode);
-    if (status == NO_ERROR) {
-        p->setDataSource(fd, offset, length);
-    }
+    status_t status = p->setDataSource(fd, offset, length);
     if (status == NO_ERROR) mRetriever = p;
     ::close(fd);
     return status;
 }
 
-status_t MetadataRetrieverClient::setMode(int mode)
+sp<IMemory> MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option)
 {
-    LOGV("setMode");
-    Mutex::Autolock lock(mLock);
-    if (mode < METADATA_MODE_NOOP ||
-        mode > METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL) {
-        LOGE("invalid mode %d", mode);
-        return BAD_VALUE;
-    }
-    mMode = mode;
-    return NO_ERROR;
-}
-
-status_t MetadataRetrieverClient::getMode(int* mode) const
-{
-    LOGV("getMode");
-    Mutex::Autolock lock(mLock);
-
-    // TODO:
-    // This may not be necessary.
-    // If setDataSource() has not been called, return the cached value
-    // otherwise, return the value retrieved from the retriever
-    if (mRetriever == NULL) {
-        *mode = mMode;
-    } else {
-        mRetriever->getMode(mode);
-    }
-    return NO_ERROR;
-}
-
-sp<IMemory> MetadataRetrieverClient::captureFrame()
-{
-    LOGV("captureFrame");
+    LOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option);
     Mutex::Autolock lock(mLock);
     mThumbnail.clear();
     if (mRetriever == NULL) {
         LOGE("retriever is not initialized");
         return NULL;
     }
-    VideoFrame *frame = mRetriever->captureFrame();
+    VideoFrame *frame = mRetriever->getFrameAtTime(timeUs, option);
     if (frame == NULL) {
         LOGE("failed to capture a video frame");
         return NULL;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index 4aab94f..b834715 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -43,9 +43,7 @@
     virtual void                    disconnect();
     virtual status_t                setDataSource(const char *url);
     virtual status_t                setDataSource(int fd, int64_t offset, int64_t length);
-    virtual status_t                setMode(int mode);
-    virtual status_t                getMode(int* mode) const;
-    virtual sp<IMemory>             captureFrame();
+    virtual sp<IMemory>             getFrameAtTime(int64_t timeUs, int option);
     virtual sp<IMemory>             extractAlbumArt();
     virtual const char*             extractMetadata(int keyCode);
 
@@ -60,7 +58,6 @@
     mutable Mutex                          mLock;
     sp<MediaMetadataRetrieverBase>         mRetriever;
     pid_t                                  mPid;
-    int                                    mMode;
 
     // Keep the shared memory copy of album art and capture frame (for thumbnail)
     sp<IMemory>                            mAlbumArt;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index d3aa2f6..10c6e41 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -130,9 +130,7 @@
         return HandleMIDI(path, &client);
     }
 
-    if (mRetriever->setDataSource(path) == OK
-            && mRetriever->setMode(
-                METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) {
+    if (mRetriever->setDataSource(path) == OK) {
         const char *value;
         if ((value = mRetriever->extractMetadata(
                         METADATA_KEY_MIMETYPE)) != NULL) {
@@ -181,9 +179,7 @@
     }
     lseek(fd, 0, SEEK_SET);
 
-    if (mRetriever->setDataSource(fd, 0, size) == OK
-            && mRetriever->setMode(
-                METADATA_MODE_FRAME_CAPTURE_ONLY) == OK) {
+    if (mRetriever->setDataSource(fd, 0, size) == OK) {
         sp<IMemory> mem = mRetriever->extractAlbumArt();
 
         if (mem != NULL) {
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index ac208cd..e8f4839 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -108,7 +108,10 @@
 static VideoFrame *extractVideoFrameWithCodecFlags(
         OMXClient *client,
         const sp<MetaData> &trackMeta,
-        const sp<MediaSource> &source, uint32_t flags) {
+        const sp<MediaSource> &source,
+        uint32_t flags,
+        int64_t frameTimeUs,
+        int seekMode) {
     sp<MediaSource> decoder =
         OMXCodec::Create(
                 client->interface(), source->getFormat(), false, source,
@@ -130,11 +133,22 @@
     // and spurious empty buffers.
 
     MediaSource::ReadOptions options;
+    if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
+        seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
+
+        LOGE("Unknown seek mode: %d", seekMode);
+        return NULL;
+    }
+
+    MediaSource::ReadOptions::SeekMode mode =
+            static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
+
     int64_t thumbNailTime;
-    if (trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
-        options.setSeekTo(thumbNailTime);
+    if (frameTimeUs < 0 && trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
+        options.setSeekTo(thumbNailTime, mode);
     } else {
         thumbNailTime = -1;
+        options.setSeekTo(frameTimeUs, mode);
     }
 
     MediaBuffer *buffer = NULL;
@@ -226,14 +240,10 @@
     return frame;
 }
 
-VideoFrame *StagefrightMetadataRetriever::captureFrame() {
-    LOGV("captureFrame");
+VideoFrame *StagefrightMetadataRetriever::getFrameAtTime(
+        int64_t timeUs, int option) {
 
-    if (0 == (mMode & METADATA_MODE_FRAME_CAPTURE_ONLY)) {
-        LOGV("captureFrame disabled by mode (0x%08x)", mMode);
-
-        return NULL;
-    }
+    LOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
 
     if (mExtractor.get() == NULL) {
         LOGV("no extractor.");
@@ -270,13 +280,15 @@
 
     VideoFrame *frame =
         extractVideoFrameWithCodecFlags(
-                &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs);
+                &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs,
+                timeUs, option);
 
     if (frame == NULL) {
         LOGV("Software decoder failed to extract thumbnail, "
              "trying hardware decoder.");
 
-        frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0);
+        frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0,
+                        timeUs, option);
     }
 
     return frame;
@@ -285,12 +297,6 @@
 MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
     LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
 
-    if (0 == (mMode & METADATA_MODE_METADATA_RETRIEVAL_ONLY)) {
-        LOGV("extractAlbumArt/metadata retrieval disabled by mode");
-
-        return NULL;
-    }
-
     if (mExtractor == NULL) {
         return NULL;
     }
@@ -309,12 +315,6 @@
 }
 
 const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
-    if (0 == (mMode & METADATA_MODE_METADATA_RETRIEVAL_ONLY)) {
-        LOGV("extractAlbumArt/metadata retrieval disabled by mode");
-
-        return NULL;
-    }
-
     if (mExtractor == NULL) {
         return NULL;
     }
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index b80387f..07b1ec8 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -35,7 +35,7 @@
     virtual status_t setDataSource(const char *url);
     virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
 
-    virtual VideoFrame *captureFrame();
+    virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option);
     virtual MediaAlbumArt *extractAlbumArt();
     virtual const char *extractMetadata(int keyCode);
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
index 0c0974c..2eea206 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
@@ -532,7 +532,7 @@
                 e.printStackTrace();
                 return false;
             }
-            Bitmap outThumbnail = mMediaMetadataRetriever.captureFrame();
+            Bitmap outThumbnail = mMediaMetadataRetriever.getFrameAtTime(-1);
 
             //Verify the thumbnail 
             Bitmap goldenBitmap = mBitmapFactory.decodeFile(goldenPath);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
index 95dbb97..6ded74d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
@@ -202,7 +202,6 @@
         }
         String value = null;
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         try {
             retriever.setDataSource(meta_data_file[fileIndex][0]);
         } catch(Exception e) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
index 7174e2b..dec21d74 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
@@ -36,13 +36,12 @@
    
     // Test album art extraction.
     @MediumTest
-    public static void testAlbumArt() throws Exception {
-        Log.v(TAG, "testAlbumArt starts.");
+    public static void testGetEmbeddedPicture() throws Exception {
+        Log.v(TAG, "testGetEmbeddedPicture starts.");
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         boolean supportWMA = MediaProfileReader.getWMAEnable();
         boolean hasFailed = false;
         boolean supportWMV = MediaProfileReader.getWMVEnable();
-        retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         for (int i = 0, n = MediaNames.ALBUMART_TEST_FILES.length; i < n; ++i) {
             try {
                 Log.v(TAG, "File " + i + ": " + MediaNames.ALBUMART_TEST_FILES[i]);
@@ -53,13 +52,13 @@
                     continue;
                 }
                 retriever.setDataSource(MediaNames.ALBUMART_TEST_FILES[i]);
-                byte[] albumArt = retriever.extractAlbumArt();
+                byte[] albumArt = retriever.getEmbeddedPicture();
 
                 // TODO:
                 // A better test would be to compare the retrieved album art with the
                 // known result.
                 if (albumArt == null) {  // Do we have expect in JUnit?
-                    Log.e(TAG, "Fails to extract album art for " + MediaNames.ALBUMART_TEST_FILES[i]);
+                    Log.e(TAG, "Fails to get embedded picture for " + MediaNames.ALBUMART_TEST_FILES[i]);
                     hasFailed = true;
                 }
             } catch(Exception e) {
@@ -69,7 +68,7 @@
             Thread.yield();  // Don't be evil
         }
         retriever.release();
-        Log.v(TAG, "testAlbumArt completes.");
+        Log.v(TAG, "testGetEmbeddedPicture completes.");
         assertTrue(!hasFailed);
     }
 
@@ -92,7 +91,7 @@
                     continue;
                 }
                 retriever.setDataSource(MediaNames.THUMBNAIL_CAPTURE_TEST_FILES[i]);
-                Bitmap bitmap = retriever.captureFrame();
+                Bitmap bitmap = retriever.getFrameAtTime(-1);
                 assertTrue(bitmap != null);
                 try {
                     java.io.OutputStream stream = new FileOutputStream(MediaNames.THUMBNAIL_CAPTURE_TEST_FILES[i] + ".jpg");
@@ -120,7 +119,6 @@
         boolean supportWMV = MediaProfileReader.getWMVEnable();
         boolean hasFailed = false;
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         for(int i = 0, n = MediaNames.METADATA_RETRIEVAL_TEST_FILES.length; i < n; ++i) {
             try {
                 Log.v(TAG, "File " + i + ": " + MediaNames.METADATA_RETRIEVAL_TEST_FILES[i]);
@@ -148,12 +146,9 @@
     public static void testBasicNormalMethodCallSequence() throws Exception {
         boolean hasFailed = false;
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         try {
             retriever.setDataSource(MediaNames.TEST_PATH_1);
-            /*
-             * captureFrame() fails due to lack of permission to access hardware decoder devices
-            Bitmap bitmap = retriever.captureFrame();
+            Bitmap bitmap = retriever.getFrameAtTime(-1);
             assertTrue(bitmap != null);
             try {
                 java.io.OutputStream stream = new FileOutputStream("/sdcard/thumbnailout.jpg");
@@ -162,7 +157,6 @@
             } catch (Exception e) {
                 throw new Exception("Fails to convert the bitmap to a JPEG file for " + MediaNames.TEST_PATH_1, e);
             }
-            */
             extractAllSupportedMetadataValues(retriever);
         } catch(Exception e) {
             Log.e(TAG, "Fails to setDataSource for " + MediaNames.TEST_PATH_1, e);
@@ -172,18 +166,17 @@
         assertTrue(!hasFailed);
     }
 
-    // If setDataSource() has not been called, both captureFrame() and extractMetadata() must
+    // If setDataSource() has not been called, both getFrameAtTime() and extractMetadata() must
     // return null.
     @MediumTest
     public static void testBasicAbnormalMethodCallSequence() {
         boolean hasFailed = false;
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         if (retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) != null) {
             Log.e(TAG, "No album metadata expected, but is available");
             hasFailed = true;
         }
-        if (retriever.captureFrame() != null) {
+        if (retriever.getFrameAtTime(-1) != null) {
             Log.e(TAG, "No frame expected, but is available");
             hasFailed = true;
         }
@@ -194,7 +187,6 @@
     @MediumTest
     public static void testSetDataSource() {
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         boolean hasFailed = false;
 
         // Null pointer argument
@@ -259,14 +251,13 @@
         // By default, capture frame and retrieve metadata
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         boolean hasFailed = false;
-        // retriever.setDataSource(MediaNames.TEST_PATH_1);
-        // assertTrue(retriever.captureFrame() != null);
-        // assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null);
+        retriever.setDataSource(MediaNames.TEST_PATH_1);
+        assertTrue(retriever.getFrameAtTime(-1) != null);
+        assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null);
 
         // Do not capture frame or retrieve metadata
-        retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY & MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         retriever.setDataSource(MediaNames.TEST_PATH_1);
-        if (retriever.captureFrame() != null) {
+        if (retriever.getFrameAtTime(-1) != null) {
             Log.e(TAG, "No frame expected, but is available");
             hasFailed = true;
         }
@@ -276,23 +267,20 @@
         }
 
         // Capture frame only
-        // retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
-        // retriever.setDataSource(MediaNames.TEST_PATH_1);
-        // assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) == null);
+        retriever.setDataSource(MediaNames.TEST_PATH_1);
+        assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) == null);
 
         // Retriever metadata only
-        retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         retriever.setDataSource(MediaNames.TEST_PATH_1);
-        if (retriever.captureFrame() != null) {
+        if (retriever.getFrameAtTime(-1) != null) {
             Log.e(TAG, "No frame expected, but is available");
             hasFailed = true;
         }
 
         // Capture frame and retrieve metadata
-        // retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY | MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
-        // retriever.setDataSource(MediaNames.TEST_PATH_1);
-        // assertTrue(retriever.captureFrame() != null);
-        // assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null);
+        retriever.setDataSource(MediaNames.TEST_PATH_1);
+        assertTrue(retriever.getFrameAtTime(-1) != null);
+        assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null);
         retriever.release();
         assertTrue(!hasFailed);
     }
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 1f06dcc..5e33f05 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -221,8 +221,7 @@
         final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
 
         ab.setAdapter(mAdapter, this)
-                .setInverseBackgroundForced(true)
-                .setTitle(R.string.global_actions);
+                .setInverseBackgroundForced(true);
 
         final AlertDialog dialog = ab.create();
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
@@ -249,6 +248,7 @@
         } else {
             mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
         }
+        mDialog.setTitle(R.string.global_actions);
     }
 
 
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index d00c043..39c7540 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -7408,6 +7408,12 @@
     static class PackageSignatures {
         private Signature[] mSignatures;
 
+        PackageSignatures(PackageSignatures orig) {
+            if (orig != null && orig.mSignatures != null) {
+                mSignatures = orig.mSignatures.clone();
+            }
+        }
+
         PackageSignatures(Signature[] sigs) {
             assignSignatures(sigs);
         }
@@ -7743,6 +7749,15 @@
             setFlags(pkgFlags);
         }
 
+        GrantedPermissions(GrantedPermissions base) {
+            pkgFlags = base.pkgFlags;
+            grantedPermissions = (HashSet<String>) base.grantedPermissions.clone();
+
+            if (base.gids != null) {
+                gids = base.gids.clone();
+            }
+        }
+
         void setFlags(int pkgFlags) {
             this.pkgFlags = pkgFlags & (
                     ApplicationInfo.FLAG_SYSTEM |
@@ -7770,7 +7785,7 @@
         int versionCode;
 
         boolean uidError;
-        
+
         PackageSignatures signatures = new PackageSignatures();
 
         boolean permissionsFixed;
@@ -7796,6 +7811,48 @@
             init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
         }
 
+        /**
+         * New instance of PackageSetting with one-level-deep cloning.
+         */
+        PackageSettingBase(PackageSettingBase base) {
+            super(base);
+
+            name = base.name;
+            realName = base.realName;
+            codePath = base.codePath;
+            codePathString = base.codePathString;
+            resourcePath = base.resourcePath;
+            resourcePathString = base.resourcePathString;
+            nativeLibraryPathString = base.nativeLibraryPathString;
+
+            if (base.obbPathStrings != null) {
+                obbPathStrings = base.obbPathStrings.clone();
+            }
+
+            timeStamp = base.timeStamp;
+            firstInstallTime = base.firstInstallTime;
+            lastUpdateTime = base.lastUpdateTime;
+            versionCode = base.versionCode;
+
+            uidError = base.uidError;
+
+            signatures = new PackageSignatures(base.signatures);
+
+            permissionsFixed = base.permissionsFixed;
+            haveGids = base.haveGids;
+
+            disabledComponents = (HashSet<String>) base.disabledComponents.clone();
+
+            enabledComponents = (HashSet<String>) base.enabledComponents.clone();
+
+            enabled = base.enabled;
+            installStatus = base.installStatus;
+
+            origPackage = base.origPackage;
+
+            installerPackageName = base.installerPackageName;
+        }
+
         void init(File codePath, File resourcePath, String nativeLibraryPathString,
                 int pVersionCode) {
             this.codePath = codePath;
@@ -7826,6 +7883,9 @@
             timeStamp = newStamp;
         }
 
+        /**
+         * Make a shallow copy of this package settings.
+         */
         public void copyFrom(PackageSettingBase base) {
             grantedPermissions = base.grantedPermissions;
             gids = base.gids;
@@ -7885,18 +7945,16 @@
                     pkgFlags);
         }
 
+        /**
+         * New instance of PackageSetting replicating the original settings.
+         * Note that it keeps the same PackageParser.Package instance.
+         */
         PackageSetting(PackageSetting orig) {
-            super(orig.name, orig.realName, orig.codePath, orig.resourcePath,
-                    orig.nativeLibraryPathString, orig.versionCode, orig.pkgFlags);
-            copyFrom(orig);
-        }
+            super(orig);
 
-        public void copyFrom(PackageSetting base) {
-            super.copyFrom((PackageSettingBase) base);
-
-            userId = base.userId;
-            sharedUser = base.sharedUser;
-            pkg = base.pkg;
+            userId = orig.userId;
+            pkg = orig.pkg;
+            sharedUser = orig.sharedUser;
         }
 
         @Override
@@ -8128,7 +8186,7 @@
                 // a little trick...  when we install the new package, we don't
                 // want to modify the existing PackageSetting for the built-in
                 // version.  so at this point we need a new PackageSetting that
-                // is okay to much with.
+                // is okay to muck with.
                 PackageSetting newp = new PackageSetting(p);
                 replacePackageLP(name, newp);
                 return true;