Merge "Earlier processing of DrmInfo notification"
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1ebbe85..fb37f9f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1251,6 +1251,11 @@
     public void prepare() throws IOException, IllegalStateException {
         _prepare();
         scanInternalSubtitleTracks();
+
+        // DrmInfo, if any, has been resolved by now.
+        synchronized (mDrmLock) {
+            mDrmInfoResolved = true;
+        }
     }
 
     private native void _prepare() throws IOException, IllegalStateException;
@@ -3149,12 +3154,6 @@
                     sendMessage(msg2);
                 }
 
-                // MEDIA_DRM_INFO is fired (if available) before MEDIA_PREPARED.
-                // An empty mDrmInfo indicates prepared is done but the source is not DRM protected.
-                // Setting this before the callback so onPreparedListener can call getDrmInfo to
-                // get the right state
-                mDrmInfoResolved = true;
-
                 OnPreparedListener onPreparedListener = mOnPreparedListener;
                 if (onPreparedListener != null)
                     onPreparedListener.onPrepared(mMediaPlayer);
@@ -3166,12 +3165,14 @@
                 if (msg.obj == null) {
                     Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
                 } else if (msg.obj instanceof Parcel) {
-                    Parcel parcel = (Parcel)msg.obj;
-                    DrmInfo drmInfo = new DrmInfo(parcel);
+                    // The parcel was parsed already in postEventFromNative
+                    DrmInfo drmInfo = null;
 
                     OnDrmInfoHandlerDelegate onDrmInfoHandlerDelegate;
                     synchronized (mDrmLock) {
-                        mDrmInfo = drmInfo.makeCopy();
+                        if (mOnDrmInfoHandlerDelegate != null && mDrmInfo != null) {
+                            drmInfo = mDrmInfo.makeCopy();
+                        }
                         // local copy while keeping the lock
                         onDrmInfoHandlerDelegate = mOnDrmInfoHandlerDelegate;
                     }
@@ -3366,10 +3367,43 @@
             return;
         }
 
-        if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
-            // this acquires the wakelock if needed, and sets the client side state
-            mp.start();
+        switch (what) {
+        case MEDIA_INFO:
+            if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
+                // this acquires the wakelock if needed, and sets the client side state
+                mp.start();
+            }
+            break;
+
+        case MEDIA_DRM_INFO:
+            // We need to derive mDrmInfo before prepare() returns so processing it here
+            // before the notification is sent to EventHandler below. EventHandler runs in the
+            // notification looper so its handleMessage might process the event after prepare()
+            // has returned.
+            Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
+            if (obj instanceof Parcel) {
+                Parcel parcel = (Parcel)obj;
+                DrmInfo drmInfo = new DrmInfo(parcel);
+                synchronized (mp.mDrmLock) {
+                    mp.mDrmInfo = drmInfo;
+                }
+            } else {
+                Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj);
+            }
+            break;
+
+        case MEDIA_PREPARED:
+            // By this time, we've learned about DrmInfo's presence or absence. This is meant
+            // mainly for prepareAsync() use case. For prepare(), this still can run to a race
+            // condition b/c MediaPlayerNative releases the prepare() lock before calling notify
+            // so we also set mDrmInfoResolved in prepare().
+            synchronized (mp.mDrmLock) {
+                mp.mDrmInfoResolved = true;
+            }
+            break;
+
         }
+
         if (mp.mEventHandler != null) {
             Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
             mp.mEventHandler.sendMessage(m);
@@ -4093,16 +4127,16 @@
      * If the device has not been provisioned before, this call also provisions the device
      * which involves accessing the provisioning server and can take a variable time to
      * complete depending on the network connectivity.
-     * If OnDrmPreparedListener is registered, prepareDrm() runs in non-blocking
+     * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking
      * mode by launching the provisioning in the background and returning. The listener
      * will be called when provisioning and preperation has finished. If a
-     * OnDrmPreparedListener is not registered, prepareDrm() waits till provisioning
+     * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning
      * and preperation has finished, i.e., runs in blocking mode.
      * <p>
-     * If OnDrmPreparedListener is registered, it is called to indicated the DRM session
-     * being ready regardless of blocking or non-blocking mode. The application should
-     * not make any assumption about its call sequence (e.g., before or after prepareDrm
-     * returns) or the thread context that will execute the listener.
+     * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM
+     * session being ready. The application should not make any assumption about its call
+     * sequence (e.g., before or after prepareDrm returns), or the thread context that will
+     * execute the listener (unless the listener is registered with a handler thread).
      * <p>
      *
      * @param uuid The UUID of the crypto scheme.