Merge "Pressing the power button quickly needs to turn the screen on and off correctly." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index bdc7f2e..70f2bff 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -705,6 +705,17 @@
  visibility="public"
 >
 </field>
+<field name="NFC"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.NFC&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PERSISTENT_ACTIVITY"
  type="java.lang.String"
  transient="false"
diff --git a/core/java/android/nfc/NdefTagConnection.java b/core/java/android/nfc/NdefTagConnection.java
index 4e9865c..4795fa7 100644
--- a/core/java/android/nfc/NdefTagConnection.java
+++ b/core/java/android/nfc/NdefTagConnection.java
@@ -25,6 +25,9 @@
  * A connection to an NDEF target on an {@link NdefTag}.
  * <p>You can acquire this kind of connection with {@link NfcAdapter#createNdefTagConnection
  * createNdefTagConnection()}. Use the connection to read or write {@link NdefMessage}s.
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
  */
 public class NdefTagConnection extends RawTagConnection {
     public static final int NDEF_MODE_READ_ONCE = 1;
@@ -69,7 +72,7 @@
      * This will always return the most up to date payload, and can block.
      * It can be canceled with {@link RawTagConnection#close}.
      * Most NDEF tags will contain just one NDEF message.
-     * <p>
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      * @throws FormatException if the tag is not NDEF formatted
      * @throws IOException if the target is lost or connection closed
      * @throws FormatException
@@ -109,8 +112,9 @@
      * message. Use {@link NdefRecord} to write several records to a single tag.
      * For write-many tags, use {@link #makeReadOnly} after this method to attempt
      * to prevent further modification. For write-once tags this is not
-     * neccesary.
-     * Requires NFC_WRITE permission.
+     * necessary.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
+     *
      * @throws FormatException if the tag is not suitable for NDEF messages
      * @throws IOException if the target is lost or connection closed or the
      *                     write failed
@@ -138,7 +142,7 @@
      * Attempts to make the NDEF data in this tag read-only.
      * This method will block until the action is complete. It can be canceled
      * with {@link RawTagConnection#close}.
-     * Requires NFC_WRITE permission.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      * @return true if the tag is now read-only
      * @throws IOException if the target is lost, or connection closed
      */
@@ -164,7 +168,8 @@
 
     /**
      * Read/Write mode hint.
-     * Provides a hint if further reads or writes are likely to suceed.
+     * Provides a hint if further reads or writes are likely to succeed.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      * @return one of NDEF_MODE
      * @throws IOException if the target is lost or connection closed
      */
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index fb079f4..d76ac41 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -15,14 +15,16 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.ActivityThread;
 import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.nfc.INfcAdapter;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
 
-//TODO(npelly) permission {@link android.Manifest.permission#NFC_MODIFY}
 /**
  * Represents the device's local NFC adapter.
  * <p>
@@ -35,7 +37,7 @@
  * to NFC Tags.
  * <p class="note">
  * <strong>Note:</strong> Some methods require the
- * TODO permission.
+ * {@link android.Manifest.permission#NFC} permission.
  */
 public final class NfcAdapter {
     /**
@@ -153,6 +155,26 @@
     }
 
     /**
+     * Helper to check if this device has FEATURE_NFC, but without using
+     * a context.
+     * Equivalent to
+     * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)
+     */
+    private static boolean hasNfcFeature() {
+        IPackageManager pm = ActivityThread.getPackageManager();
+        if (pm == null) {
+            Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
+            return false;
+        }
+        try {
+            return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
+            return false;
+        }
+    }
+
+    /**
      * Get a handle to the default NFC Adapter on this Android device.
      * <p>
      * Most Android devices will only have one NFC Adapter (NFC Controller).
@@ -166,9 +188,16 @@
             }
             sIsInitialized = true;
 
+            /* is this device meant to have NFC */
+            if (!hasNfcFeature()) {
+                Log.v(TAG, "this device does not have NFC support");
+                return null;
+            }
+
+            /* get a handle to NFC service */
             IBinder b = ServiceManager.getService("nfc");
             if (b == null) {
-                Log.d(TAG, "NFC Service not available");
+                Log.e(TAG, "could not retrieve NFC service");
                 return null;
             }
 
@@ -231,8 +260,7 @@
      * <li>provide the NDEF message on over LLCP to peer NFC adapters
      * </ul>
      * The NDEF message is preserved across reboot.
-     * <p>
-     * Requires NFC_WRITE permission
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      *
      * @param message NDEF message to make public
      */
@@ -246,8 +274,7 @@
 
     /**
      * Get the NDEF Message that this adapter appears as to Tag readers.
-     * <p>
-     * Requires NFC_WRITE permission
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      *
      * @return NDEF Message that is publicly readable
      */
@@ -262,6 +289,7 @@
 
     /**
      * Create a raw tag connection to the default Target
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
     public RawTagConnection createRawTagConnection(Tag tag) {
         try {
@@ -274,6 +302,7 @@
 
     /**
      * Create a raw tag connection to the specified Target
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
     public RawTagConnection createRawTagConnection(Tag tag, String target) {
         try {
@@ -286,6 +315,7 @@
 
     /**
      * Create an NDEF tag connection to the default Target
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
     public NdefTagConnection createNdefTagConnection(NdefTag tag) {
         try {
@@ -298,6 +328,7 @@
 
     /**
      * Create an NDEF tag connection to the specified Target
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
     public NdefTagConnection createNdefTagConnection(NdefTag tag, String target) {
         try {
diff --git a/core/java/android/nfc/RawTagConnection.java b/core/java/android/nfc/RawTagConnection.java
index 37d700c..265eb1b 100644
--- a/core/java/android/nfc/RawTagConnection.java
+++ b/core/java/android/nfc/RawTagConnection.java
@@ -30,7 +30,7 @@
  * Applications must implement their own protocol stack on top of {@link #transceive transceive()}.
  *
  * <p class="note"><strong>Note:</strong>
- * Most methods require the TODO
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
  * permission.
  */
 public class RawTagConnection {
@@ -71,11 +71,15 @@
 
     /**
      * Get the {@link Tag} this connection is associated with.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
     public Tag getTag() {
         return mTag;
     }
 
+    /**
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
+     */
     public String getTagTarget() {
         return mSelectedTarget;
     }
@@ -84,7 +88,7 @@
      * Helper to indicate if {@link #transceive transceive()} calls might succeed.
      * <p>
      * Does not cause RF activity, and does not block.
-     * <p>
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      * @return true if {@link #connect} has completed successfully and the {@link Tag} is believed
      * to be within range. Applications must still handle {@link java.io.IOException}
      * while using {@link #transceive transceive()}, in case connection is lost after this method
@@ -104,7 +108,7 @@
      * <p>
      * {@link #close} can be called from another thread to cancel this connection
      * attempt.
-     *
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      * @throws IOException if the target is lost, or connect canceled
      */
     public void connect() throws IOException {
@@ -120,6 +124,7 @@
      * <p>
      * Once this method is called, this object cannot be re-used and should be discarded. Further
      * calls to {@link #transceive transceive()} or {@link #connect} will fail.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
     public void close() {
         mIsConnected = false;
@@ -135,8 +140,7 @@
      * <p>
      * This method will block until the response is received. It can be canceled
      * with {@link #close}.
-     * <p>
-     * Requires NFC_WRITE permission.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
      *
      * @param data bytes to send
      * @return bytes received in response
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
new file mode 100644
index 0000000..d292881
--- /dev/null
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnBufferingUpdateListener;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.media.MediaPlayer.OnSeekCompleteListener;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * <p>HTML5 support class for Audio.
+ */
+class HTML5Audio extends Handler
+                 implements MediaPlayer.OnBufferingUpdateListener,
+                            MediaPlayer.OnCompletionListener,
+                            MediaPlayer.OnErrorListener,
+                            MediaPlayer.OnPreparedListener,
+                            MediaPlayer.OnSeekCompleteListener {
+    // Logging tag.
+    private static final String LOGTAG = "HTML5Audio";
+
+    private MediaPlayer mMediaPlayer;
+
+    // The C++ MediaPlayerPrivateAndroid object.
+    private int mNativePointer;
+
+    private static int IDLE        =  0;
+    private static int INITIALIZED =  1;
+    private static int PREPARED    =  2;
+    private static int STARTED     =  4;
+    private static int COMPLETE    =  5;
+    private static int PAUSED      =  6;
+    private static int STOPPED     = -2;
+    private static int ERROR       = -1;
+
+    private int mState = IDLE;
+
+    private String mUrl;
+    private boolean mAskToPlay = false;
+
+    // Timer thread -> UI thread
+    private static final int TIMEUPDATE = 100;
+
+    // The spec says the timer should fire every 250 ms or less.
+    private static final int TIMEUPDATE_PERIOD = 250;  // ms
+    // The timer for timeupate events.
+    // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
+    private Timer mTimer;
+    private final class TimeupdateTask extends TimerTask {
+        public void run() {
+            HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget();
+        }
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case TIMEUPDATE: {
+                try {
+                    if (mState != ERROR && mMediaPlayer.isPlaying()) {
+                        int position = mMediaPlayer.getCurrentPosition();
+                        nativeOnTimeupdate(position, mNativePointer);
+                    }
+                } catch (IllegalStateException e) {
+                    mState = ERROR;
+                }
+            }
+        }
+    }
+
+    // event listeners for MediaPlayer
+    // Those are called from the same thread we created the MediaPlayer
+    // (i.e. the webviewcore thread here)
+
+    // MediaPlayer.OnBufferingUpdateListener
+    public void onBufferingUpdate(MediaPlayer mp, int percent) {
+        nativeOnBuffering(percent, mNativePointer);
+    }
+
+    // MediaPlayer.OnCompletionListener;
+    public void onCompletion(MediaPlayer mp) {
+        resetMediaPlayer();
+        mState = IDLE;
+        nativeOnEnded(mNativePointer);
+    }
+
+    // MediaPlayer.OnErrorListener
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        mState = ERROR;
+        resetMediaPlayer();
+        mState = IDLE;
+        return false;
+    }
+
+    // MediaPlayer.OnPreparedListener
+    public void onPrepared(MediaPlayer mp) {
+        mState = PREPARED;
+        if (mTimer != null) {
+            mTimer.schedule(new TimeupdateTask(),
+                            TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD);
+        }
+        nativeOnPrepared(mp.getDuration(), 0, 0, mNativePointer);
+        if (mAskToPlay) {
+            mAskToPlay = false;
+            play();
+        }
+    }
+
+    // MediaPlayer.OnSeekCompleteListener
+    public void onSeekComplete(MediaPlayer mp) {
+        nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer);
+    }
+
+
+    /**
+     * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object.
+     */
+    public HTML5Audio(int nativePtr) {
+        // Save the native ptr
+        mNativePointer = nativePtr;
+        resetMediaPlayer();
+    }
+
+    private void resetMediaPlayer() {
+        if (mMediaPlayer == null) {
+            mMediaPlayer = new MediaPlayer();
+        } else {
+            mMediaPlayer.reset();
+        }
+        mMediaPlayer.setOnBufferingUpdateListener(this);
+        mMediaPlayer.setOnCompletionListener(this);
+        mMediaPlayer.setOnErrorListener(this);
+        mMediaPlayer.setOnPreparedListener(this);
+        mMediaPlayer.setOnSeekCompleteListener(this);
+
+        if (mTimer != null) {
+            mTimer.cancel();
+        }
+        mTimer = new Timer();
+        mState = IDLE;
+    }
+
+    private void setDataSource(String url) {
+        mUrl = url;
+        try {
+            if (mState != IDLE) {
+                resetMediaPlayer();
+            }
+            mMediaPlayer.setDataSource(url);
+            mState = INITIALIZED;
+            mMediaPlayer.prepareAsync();
+        } catch (IOException e) {
+            Log.e(LOGTAG, "couldn't load the resource: " + url + " exc: " + e);
+            resetMediaPlayer();
+        }
+    }
+
+    private void play() {
+        if ((mState == ERROR || mState == IDLE) && mUrl != null) {
+            resetMediaPlayer();
+            setDataSource(mUrl);
+            mAskToPlay = true;
+        }
+
+        if (mState >= PREPARED) {
+            mMediaPlayer.start();
+            mState = STARTED;
+        }
+    }
+
+    private void pause() {
+        if (mState == STARTED) {
+            if (mTimer != null) {
+                mTimer.purge();
+            }
+            mMediaPlayer.pause();
+            mState = PAUSED;
+        }
+    }
+
+    private void seek(int msec) {
+        if (mState >= PREPARED) {
+            mMediaPlayer.seekTo(msec);
+        }
+    }
+
+    private void teardown() {
+        mMediaPlayer.release();
+        mState = ERROR;
+        mNativePointer = 0;
+    }
+
+    private float getMaxTimeSeekable() {
+        return mMediaPlayer.getDuration() / 1000.0f;
+    }
+
+    private native void nativeOnBuffering(int percent, int nativePointer);
+    private native void nativeOnEnded(int nativePointer);
+    private native void nativeOnPrepared(int duration, int width, int height, int nativePointer);
+    private native void nativeOnTimeupdate(int position, int nativePointer);
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9080f96..d719783 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2956,6 +2956,25 @@
         if (imm != null) imm.restartInput(this);
     }
 
+    /**
+     * It would be better to rely on the input type for everything. A password inputType should have
+     * a password transformation. We should hence use isPasswordInputType instead of this method.
+     *
+     * We should:
+     * - Call setInputType in setKeyListener instead of changing the input type directly (which
+     * would install the correct transformation).
+     * - Refuse the installation of a non-password transformation in setTransformation if the input
+     * type is password.
+     *
+     * However, this is like this for legacy reasons and we cannot break existing apps. This method
+     * is useful since it matches what the user can see (obfuscated text or not).
+     *
+     * @return true if the current transformation method is of the password type.
+     */
+    private boolean hasPasswordTransformationMethod() {
+        return mTransformation instanceof PasswordTransformationMethod;
+    }
+
     private boolean isPasswordInputType(int inputType) {
         final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS
                 | EditorInfo.TYPE_MASK_VARIATION);
@@ -7135,7 +7154,7 @@
     }
 
     private boolean canCut() {
-        if (mTransformation instanceof PasswordTransformationMethod) {
+        if (hasPasswordTransformationMethod()) {
             return false;
         }
 
@@ -7149,7 +7168,7 @@
     }
 
     private boolean canCopy() {
-        if (mTransformation instanceof PasswordTransformationMethod) {
+        if (hasPasswordTransformationMethod()) {
             return false;
         }
 
@@ -7398,8 +7417,13 @@
             MenuHandler handler = new MenuHandler();
 
             if (canSelectText()) {
-                menu.add(0, ID_START_SELECTING_TEXT, 0, com.android.internal.R.string.selectText).
-                     setOnMenuItemClickListener(handler);
+                if (!hasPasswordTransformationMethod()) {
+                    // selectCurrentWord is not available on a password field and would return an
+                    // arbitrary 10-charater selection around pressed position. Discard it.
+                    // SelectAll is still useful to be able to clear the field using the delete key.
+                    menu.add(0, ID_START_SELECTING_TEXT, 0, com.android.internal.R.string.selectText).
+                    setOnMenuItemClickListener(handler);
+                }
                 menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
                      setOnMenuItemClickListener(handler).
                      setAlphabeticShortcut('a');
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ef4ee11..a22c827 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -341,29 +341,12 @@
         android:description="@string/permdesc_bluetooth"
         android:label="@string/permlab_bluetooth" />
 
-    <!-- Allows applications to access remote NFC devices
-         @hide -->
-    <permission android:name="com.trustedlogic.trustednfc.permission.NFC_RAW"
+    <!-- Allows applications to directly communicate over NFC -->
+    <permission android:name="android.permission.NFC"
         android:permissionGroup="android.permission-group.NETWORK"
         android:protectionLevel="dangerous"
-        android:description="@string/permdesc_nfcRaw"
-        android:label="@string/permlab_nfcRaw" />
-
-    <!-- Allows applications to be notified of remote NFC devices
-         @hide -->
-    <permission android:name="com.trustedlogic.trustednfc.permission.NFC_NOTIFY"
-        android:permissionGroup="android.permission-group.NETWORK"
-        android:protectionLevel="dangerous"
-        android:description="@string/permdesc_nfcNotify"
-        android:label="@string/permlab_nfcNotify" />
-
-    <!-- Allows applications to be notified of remote NFC LLCP devices
-         @hide -->
-    <permission android:name="com.trustedlogic.trustednfc.permission.NFC_LLCP"
-        android:permissionGroup="android.permission-group.NETWORK"
-        android:protectionLevel="dangerous"
-        android:description="@string/permdesc_nfcLlcp"
-        android:label="@string/permlab_nfcLlcp" />
+        android:description="@string/permdesc_nfc"
+        android:label="@string/permlab_nfc" />
 
     <!-- Allows an application to use SIP service -->
     <permission android:name="android.permission.USE_SIP"
@@ -875,14 +858,6 @@
         android:description="@string/permdesc_bluetoothAdmin"
         android:label="@string/permlab_bluetoothAdmin" />
 
-    <!-- Allows applications to change NFC connectivity settings 
-         @hide -->
-    <permission android:name="com.trustedlogic.trustednfc.permission.NFC_ADMIN"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="dangerous"
-        android:description="@string/permdesc_nfcAdmin"
-        android:label="@string/permlab_nfcAdmin" />
-
     <!-- Allows an application to clear the caches of all installed
          applications on the device.  -->
     <permission android:name="android.permission.CLEAR_APP_CACHE"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e1b5d93..075cc66 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1153,28 +1153,10 @@
       connections with paired devices.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_nfcAdmin">NFC administration</string>
+    <string name="permlab_nfc">control Near Field Communication</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_nfcAdmin">Allows an application to configure
-      the local NFC phone.</string>
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_nfcRaw">NFC full access to remote device</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_nfcRaw">Allows an application to access
-      remote NFC devices.</string>
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_nfcNotify">NFC notification from remote device</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_nfcNotify">Allows an application to be notified
-      of operations related to remote NFC devices.</string>
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_nfcLlcp">NFC notification from remote LLCP device</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_nfcLlcp">Allows an application to be notified
-      of LLCP operations related to remote NFC devices.</string>
+    <string name="permdesc_nfc">Allows an application to communicate
+      with Near Field Communication (NFC) tags, cards, and readers.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_disableKeyguard">disable keylock</string>
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 2bb7783..1d94160 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -33,7 +33,11 @@
                   public MediaBufferObserver {
     enum CreationFlags {
         kPreferSoftwareCodecs    = 1,
-        kIgnoreCodecSpecificData = 2
+        kIgnoreCodecSpecificData = 2,
+
+        // The client wants to access the output buffer's video
+        // data for example for thumbnail extraction.
+        kClientNeedsFramebuffer  = 4,
     };
     static sp<MediaSource> Create(
             const sp<IOMX> &omx,
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index b3daf67..81a2b0d 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -725,6 +725,14 @@
 
     size_t PES_packet_length = accessUnit->size() + 8;
 
+    if (PES_packet_length >= 65536) {
+        // This really should only happen for video.
+        CHECK_EQ(stream_id, 0xe0u);
+
+        // It's valid to set this to 0 for video according to the specs.
+        PES_packet_length = 0;
+    }
+
     uint8_t *ptr = buffer->data();
     *ptr++ = 0x47;
     *ptr++ = 0x40 | (PID >> 8);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4648ad3..9a49a9b 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -493,12 +493,29 @@
 
         LOGV("Attempting to allocate OMX node '%s'", componentName);
 
+        uint32_t quirks = getComponentQuirks(componentName, createEncoder);
+
+        if (!createEncoder
+                && (quirks & kOutputBuffersAreUnreadable)
+                && (flags & kClientNeedsFramebuffer)) {
+            if (strncmp(componentName, "OMX.SEC.", 8)) {
+                // For OMX.SEC.* decoders we can enable a special mode that
+                // gives the client access to the framebuffer contents.
+
+                LOGW("Component '%s' does not give the client access to "
+                     "the framebuffer contents. Skipping.",
+                     componentName);
+
+                continue;
+            }
+        }
+
         status_t err = omx->allocateNode(componentName, observer, &node);
         if (err == OK) {
             LOGV("Successfully allocated OMX node '%s'", componentName);
 
             sp<OMXCodec> codec = new OMXCodec(
-                    omx, node, getComponentQuirks(componentName, createEncoder),
+                    omx, node, quirks,
                     createEncoder, mime, componentName,
                     source);
 
@@ -681,6 +698,33 @@
 
     initOutputFormat(meta);
 
+    if ((flags & kClientNeedsFramebuffer)
+            && !strncmp(mComponentName, "OMX.SEC.", 8)) {
+        OMX_INDEXTYPE index;
+
+        status_t err =
+            mOMX->getExtensionIndex(
+                    mNode,
+                    "OMX.SEC.index.ThumbnailMode",
+                    &index);
+
+        if (err != OK) {
+            return err;
+        }
+
+        OMX_BOOL enable = OMX_TRUE;
+        err = mOMX->setConfig(mNode, index, &enable, sizeof(enable));
+
+        if (err != OK) {
+            CODEC_LOGE("setConfig('OMX.SEC.index.ThumbnailMode') "
+                       "returned error 0x%08x", err);
+
+            return err;
+        }
+
+        mQuirks &= ~kOutputBuffersAreUnreadable;
+    }
+
     return OK;
 }
 
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index af9c70c..a800a93 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -112,7 +112,7 @@
     sp<MediaSource> decoder =
         OMXCodec::Create(
                 client->interface(), source->getFormat(), false, source,
-                NULL, flags);
+                NULL, flags | OMXCodec::kClientNeedsFramebuffer);
 
     if (decoder.get() == NULL) {
         LOGV("unable to instantiate video decoder.");
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index 9866876..d118bd7 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -29,21 +29,18 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 
-# Non-Linux hosts might not have OpenSSL libcrypto
-ifeq ($(HOST_OS),linux)
-    include $(CLEAR_VARS)
+#####################################################
+include $(CLEAR_VARS)
 
-    LOCAL_MODULE := pbkdf2gen
+LOCAL_MODULE := pbkdf2gen
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_SRC_FILES := pbkdf2gen.cpp
+LOCAL_LDLIBS += -ldl
+LOCAL_C_INCLUDES := external/openssl/include $(LOCAL_C_INCLUDES)
+LOCAL_STATIC_LIBRARIES := libcrypto_static
 
-    LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_EXECUTABLE)
 
-    LOCAL_CFLAGS := -Wall -Werror
-
-    LOCAL_SRC_FILES := pbkdf2gen.cpp
-
-    LOCAL_SHARED_LIBRARIES := libcrypto
-
-    include $(BUILD_HOST_EXECUTABLE)
-endif # HOST_OS == linux
-
+#######################################################
 endif # TARGET_BUILD_APPS