Merge "MediaPlayer2: make prepareDrm asynchronous only"
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index e94413c..54b5ab1 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1361,7 +1361,7 @@
     }
 
     /**
-     * Sets the callback to be invoked when the media source is ready for playback.
+     * Registers the callback to be invoked for various events covered by {@link EventCallback}.
      *
      * @param executor the executor through which the callback should be invoked
      * @param eventCallback the callback that will be run
@@ -1378,7 +1378,6 @@
     // This is a synchronous call.
     public abstract void unregisterEventCallback(EventCallback eventCallback);
 
-
     /* Do not change these values without updating their counterparts
      * in include/media/mediaplayer2.h!
      */
@@ -1387,7 +1386,8 @@
      */
     public static final int MEDIA_ERROR_UNKNOWN = 1;
 
-    /** The video is streamed and its container is not valid for progressive
+    /**
+     * The video is streamed and its container is not valid for progressive
      * playback i.e the video's index (e.g moov atom) is not at the start of the
      * file.
      * @see EventCallback#onError
@@ -1698,19 +1698,33 @@
      * @see EventCallback#onCallCompleted
      * @hide
      */
-    public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 1001;
+    public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 31;
 
-    /** The player just completed a call {@code setVideoScalingMode}.
+    /** The player just completed a call {@link #setVideoScalingMode}.
      * @see EventCallback#onCallCompleted
      * @hide
      */
-    public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 1002;
+    public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 32;
 
-    /** The player just completed a call {@code notifyWhenCommandLabelReached}.
+    /**
+     * The start of the methods which have separate call complete callback.
+     * @hide
+     */
+    public static final int SEPARATE_CALL_COMPLETED_CALLBACK_START = 1000;
+
+    /** The player just completed a call {@link #notifyWhenCommandLabelReached}.
      * @see EventCallback#onCommandLabelReached
      * @hide
      */
-    public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED = 1003;
+    public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED =
+            SEPARATE_CALL_COMPLETED_CALLBACK_START;
+
+    /** The player just completed a call {@link #prepareDrm}.
+     * @see DrmEventCallback#onDrmPrepared
+     * @hide
+     */
+    public static final int CALL_COMPLETED_PREPARE_DRM =
+            SEPARATE_CALL_COMPLETED_CALLBACK_START + 1;
 
     /**
      * @hide
@@ -1740,7 +1754,8 @@
             CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES,
             CALL_COMPLETED_SET_BUFFERING_PARAMS,
             CALL_COMPLETED_SET_VIDEO_SCALING_MODE,
-            CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
+            CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED,
+            CALL_COMPLETED_PREPARE_DRM,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CallCompleted {}
@@ -1864,20 +1879,22 @@
     }
 
     /**
-     * Sets the callback to be invoked when the media source is ready for playback.
+     * Registers the callback to be invoked for various DRM events.
      *
      * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
      */
     // This is a synchronous call.
-    public abstract void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public abstract void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback);
 
     /**
-     * Clears the {@link DrmEventCallback}.
+     * Unregisters the {@link DrmEventCallback}.
+     *
+     * @param eventCallback the callback to be unregistered
      */
     // This is a synchronous call.
-    public abstract void clearDrmEventCallback();
+    public abstract void unregisterDrmEventCallback(DrmEventCallback eventCallback);
 
     /**
      * The status codes for {@link DrmEventCallback#onDrmPrepared} listener.
@@ -1902,6 +1919,15 @@
      */
     public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
 
+    /**
+     * The crypto scheme UUID is not supported by the device.
+     */
+    public static final int PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME = 4;
+
+    /**
+     * The hardware resources are not available, due to being in use.
+     */
+    public static final int PREPARE_DRM_STATUS_RESOURCE_BUSY = 5;
 
     /** @hide */
     @IntDef(flag = false, prefix = "PREPARE_DRM_STATUS", value = {
@@ -1909,6 +1935,8 @@
         PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
         PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,
         PREPARE_DRM_STATUS_PREPARATION_ERROR,
+        PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME,
+        PREPARE_DRM_STATUS_RESOURCE_BUSY,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PrepareDrmStatusCode {}
@@ -1925,41 +1953,28 @@
      * <p>
      * If {@link OnDrmConfigHelper} is registered, it will be called during
      * preparation to allow configuration of the DRM properties before opening the
-     * DRM session. Note that the callback is called synchronously in the thread that called
-     * {@link #prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
-     * and {@code setDrmPropertyString} calls and refrain from any lengthy operation.
+     * DRM session. It should be used only for a series of {@link #getDrmPropertyString}
+     * and {@link #setDrmPropertyString} calls and refrain from any lengthy operation.
      * <p>
      * 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 {@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 preparation has finished. If a
-     * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning
-     * and preparation has finished, i.e., runs in blocking mode.
+     * When needed, the provisioning will be launched  in the background.
+     * The listener {@link DrmEventCallback#onDrmPrepared}
+     * will be called when provisioning and preparation are finished. The application should
+     * check the status code returned with {@link DrmEventCallback#onDrmPrepared} to proceed.
      * <p>
-     * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM
+     * The registered {@link DrmEventCallback#onDrmPrepared} 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).
+     * sequence (e.g., before or after prepareDrm returns).
      * <p>
      *
      * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
-     * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}.
-     *
-     * @throws IllegalStateException              if called before being prepared or the DRM was
-     *                                            prepared already
-     * @throws UnsupportedSchemeException         if the crypto scheme is not supported
-     * @throws ResourceBusyException              if required DRM resources are in use
-     * @throws ProvisioningNetworkErrorException  if provisioning is required but failed due to a
-     *                                            network error
-     * @throws ProvisioningServerErrorException   if provisioning is required but failed due to
-     *                                            the request denied by the provisioning server
+     * from the source through {@code getDrmInfo} or registering a
+     * {@link DrmEventCallback#onDrmInfo}.
      */
-    // This is a synchronous call.
-    public abstract void prepareDrm(@NonNull UUID uuid)
-            throws UnsupportedSchemeException, ResourceBusyException,
-                   ProvisioningNetworkErrorException, ProvisioningServerErrorException;
+    // This is an asynchronous call.
+    public abstract void prepareDrm(@NonNull UUID uuid);
 
     /**
      * Releases the DRM session
@@ -2107,28 +2122,6 @@
           }
     }
 
-    /**
-     * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed due to a network error (Internet reachability, timeout, etc.).
-     * Extends MediaDrm.MediaDrmException
-     */
-    public abstract static class ProvisioningNetworkErrorException extends MediaDrmException {
-          protected ProvisioningNetworkErrorException(String detailMessage) {
-              super(detailMessage);
-          }
-    }
-
-    /**
-     * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed due to the provisioning server denying the request.
-     * Extends MediaDrm.MediaDrmException
-     */
-    public abstract static class ProvisioningServerErrorException extends MediaDrmException {
-          protected ProvisioningServerErrorException(String detailMessage) {
-              super(detailMessage);
-          }
-    }
-
     public static final class MetricsConstants {
         private MetricsConstants() {}
 
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 6263e5d..a5eb1fb 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -2409,7 +2409,7 @@
         }
     }
 
-    public static void checkArgument(boolean expression, String errorMessage) {
+    private static void checkArgument(boolean expression, String errorMessage) {
         if (!expression) {
             throw new IllegalArgumentException(errorMessage);
         }
@@ -2473,15 +2473,8 @@
     private ArrayList<Pair<Executor, DrmEventCallback> > mDrmEventCallbackRecords
         = new ArrayList<Pair<Executor, DrmEventCallback> >();
 
-    /**
-     * Register a callback to be invoked when the media source is ready
-     * for playback.
-     *
-     * @param eventCallback the callback that will be run
-     * @param executor the executor through which the callback should be invoked
-     */
     @Override
-    public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback) {
         if (eventCallback == null) {
             throw new IllegalArgumentException("Illegal null EventCallback");
@@ -2495,17 +2488,17 @@
         }
     }
 
-    /**
-     * Clears the {@link DrmEventCallback}.
-     */
     @Override
-    public void clearDrmEventCallback() {
+    public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
         synchronized (mDrmEventCbLock) {
-            mDrmEventCallbackRecords.clear();
+            for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                if (cb.second == eventCallback) {
+                    mDrmEventCallbackRecords.remove(cb);
+                }
+            }
         }
     }
 
-
     /**
      * Retrieves the DRM Info associated with the current source
      *
@@ -2532,54 +2525,80 @@
         return drmInfo;
     }
 
-
-    /**
-     * Prepares the DRM for the current source
-     * <p>
-     * If {@code OnDrmConfigHelper} is registered, it will be called during
-     * preparation to allow configuration of the DRM properties before opening the
-     * DRM session. Note that the callback is called synchronously in the thread that called
-     * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
-     * and {@code setDrmPropertyString} calls and refrain from any lengthy operation.
-     * <p>
-     * 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 {@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 preparation has finished. If a
-     * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning
-     * and preparation has finished, i.e., runs in blocking mode.
-     * <p>
-     * 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. If not known beforehand, it can be retrieved
-     * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}.
-     *
-     * @throws IllegalStateException              if called before prepare(), or the DRM was
-     *                                            prepared already
-     * @throws UnsupportedSchemeException         if the crypto scheme is not supported
-     * @throws ResourceBusyException              if required DRM resources are in use
-     * @throws ProvisioningNetworkErrorException  if provisioning is required but failed due to a
-     *                                            network error
-     * @throws ProvisioningServerErrorException   if provisioning is required but failed due to
-     *                                            the request denied by the provisioning server
-     */
     @Override
-    public void prepareDrm(@NonNull UUID uuid)
+    public void prepareDrm(@NonNull UUID uuid) {
+        addTask(new Task(CALL_COMPLETED_PREPARE_DRM, true) {
+            @Override
+            void process() {
+                int status = PREPARE_DRM_STATUS_SUCCESS;
+                boolean sendEvent = true;
+
+                try {
+                    doPrepareDrm(uuid);
+                } catch (ResourceBusyException e) {
+                    status = PREPARE_DRM_STATUS_RESOURCE_BUSY;
+                } catch (UnsupportedSchemeException e) {
+                    status = PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME;
+                } catch (NotProvisionedException e) {
+                    Log.w(TAG, "prepareDrm: NotProvisionedException");
+
+                    // handle provisioning internally; it'll reset mPrepareDrmInProgress
+                    status = HandleProvisioninig(uuid);
+
+                    if (status == PREPARE_DRM_STATUS_SUCCESS) {
+                        // DrmEventCallback will be fired in provisioning
+                        sendEvent = false;
+                    } else {
+                        synchronized (mDrmLock) {
+                            cleanDrmObj();
+                        }
+
+                        switch (status) {
+                        case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:
+                            Log.e(TAG, "prepareDrm: Provisioning was required but failed " +
+                                    "due to a network error.");
+                            break;
+
+                        case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:
+                            Log.e(TAG, "prepareDrm: Provisioning was required but the request " +
+                                    "was denied by the server.");
+                            break;
+
+                        case PREPARE_DRM_STATUS_PREPARATION_ERROR:
+                        default:
+                            Log.e(TAG, "prepareDrm: Post-provisioning preparation failed.");
+                            break;
+                        }
+                    }
+                } catch (Exception e) {
+                    status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
+                }
+
+                if (sendEvent) {
+                    final int prepareDrmStatus = status;
+                    sendDrmEvent(new DrmEventNotifier() {
+                        @Override
+                        public void notify(DrmEventCallback callback) {
+                            callback.onDrmPrepared(
+                                    MediaPlayer2Impl.this, mCurrentDSD, prepareDrmStatus);
+                        }
+                    });
+
+                    synchronized (mTaskLock) {
+                        mCurrentTask = null;
+                        processPendingTask_l();
+                    }
+                }
+            }
+        });
+    }
+
+    private void doPrepareDrm(@NonNull UUID uuid)
             throws UnsupportedSchemeException, ResourceBusyException,
-                   ProvisioningNetworkErrorException, ProvisioningServerErrorException
-    {
+                   NotProvisionedException {
         Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
 
-        boolean allDoneWithoutProvisioning = false;
-
         synchronized (mDrmLock) {
-
             // only allowing if tied to a protected source; might relax for releasing offline keys
             if (mDrmInfoImpl == null) {
                 final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " +
@@ -2623,8 +2642,7 @@
             }
 
             mDrmConfigAllowed = true;
-        }   // synchronized
-
+        }  // synchronized
 
         // call the callback outside the lock
         if (mOnDrmConfigHelper != null)  {
@@ -2640,74 +2658,28 @@
 
                 mDrmUUID = uuid;
                 mActiveDrmScheme = true;
-
-                allDoneWithoutProvisioning = true;
+                mPrepareDrmInProgress = false;
             } catch (IllegalStateException e) {
                 final String msg = "prepareDrm(): Wrong usage: The player must be " +
                         "in the prepared state to call prepareDrm().";
                 Log.e(TAG, msg);
                 earlyExit = true;
+                mPrepareDrmInProgress = false;
                 throw new IllegalStateException(msg);
             } catch (NotProvisionedException e) {
-                Log.w(TAG, "prepareDrm: NotProvisionedException");
-
-                // handle provisioning internally; it'll reset mPrepareDrmInProgress
-                int result = HandleProvisioninig(uuid);
-
-                // if blocking mode, we're already done;
-                // if non-blocking mode, we attempted to launch background provisioning
-                if (result != PREPARE_DRM_STATUS_SUCCESS) {
-                    earlyExit = true;
-                    String msg;
-
-                    switch (result) {
-                    case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:
-                        msg = "prepareDrm: Provisioning was required but failed " +
-                                "due to a network error.";
-                        Log.e(TAG, msg);
-                        throw new ProvisioningNetworkErrorExceptionImpl(msg);
-
-                    case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:
-                        msg = "prepareDrm: Provisioning was required but the request " +
-                                "was denied by the server.";
-                        Log.e(TAG, msg);
-                        throw new ProvisioningServerErrorExceptionImpl(msg);
-
-                    case PREPARE_DRM_STATUS_PREPARATION_ERROR:
-                    default: // default for safeguard
-                        msg = "prepareDrm: Post-provisioning preparation failed.";
-                        Log.e(TAG, msg);
-                        throw new IllegalStateException(msg);
-                    }
-                }
-                // nothing else to do;
-                // if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup
+                Log.w(TAG, "prepareDrm: NotProvisionedException", e);
+                throw e;
             } catch (Exception e) {
                 Log.e(TAG, "prepareDrm: Exception " + e);
                 earlyExit = true;
+                mPrepareDrmInProgress = false;
                 throw e;
             } finally {
-                if (!mDrmProvisioningInProgress) {// if early exit other than provisioning exception
-                    mPrepareDrmInProgress = false;
-                }
-                if (earlyExit) {    // cleaning up object if didn't succeed
+                if (earlyExit) {  // clean up object if didn't succeed
                     cleanDrmObj();
                 }
-            } // finally
-        }   // synchronized
-
-
-        // if finished successfully without provisioning, call the callback outside the lock
-        if (allDoneWithoutProvisioning) {
-            sendDrmEvent(new DrmEventNotifier() {
-                @Override
-                public void notify(DrmEventCallback callback) {
-                    callback.onDrmPrepared(
-                            MediaPlayer2Impl.this, mCurrentDSD, PREPARE_DRM_STATUS_SUCCESS);
-                }
-            });
-        }
-
+            }  // finally
+        }  // synchronized
     }
 
 
@@ -3164,31 +3136,6 @@
         }
     }
 
-    /**
-     * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed due to a network error (Internet reachability, timeout, etc.).
-     * Extends MediaDrm.MediaDrmException
-     */
-    public static final class ProvisioningNetworkErrorExceptionImpl
-            extends ProvisioningNetworkErrorException {
-        public ProvisioningNetworkErrorExceptionImpl(String detailMessage) {
-            super(detailMessage);
-        }
-    }
-
-    /**
-     * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed due to the provisioning server denying the request.
-     * Extends MediaDrm.MediaDrmException
-     */
-    public static final class ProvisioningServerErrorExceptionImpl
-            extends ProvisioningServerErrorException {
-        public ProvisioningServerErrorExceptionImpl(String detailMessage) {
-            super(detailMessage);
-        }
-    }
-
-
     private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
 
     // Modular DRM helpers
@@ -3296,7 +3243,6 @@
         private Object drmLock;
         private MediaPlayer2Impl mediaPlayer;
         private int status;
-        private boolean finished;
         public  int status() {
             return status;
         }
@@ -3362,38 +3308,7 @@
 
             boolean succeeded = false;
 
-            boolean hasCallback = false;
-            synchronized (mDrmEventCbLock) {
-                hasCallback = !mDrmEventCallbackRecords.isEmpty();
-            }
-            // non-blocking mode needs the lock
-            if (hasCallback) {
-
-                synchronized (drmLock) {
-                    // continuing with prepareDrm
-                    if (provisioningSucceeded) {
-                        succeeded = mediaPlayer.resumePrepareDrm(uuid);
-                        status = (succeeded) ?
-                                PREPARE_DRM_STATUS_SUCCESS :
-                                PREPARE_DRM_STATUS_PREPARATION_ERROR;
-                    }
-                    mediaPlayer.mDrmProvisioningInProgress = false;
-                    mediaPlayer.mPrepareDrmInProgress = false;
-                    if (!succeeded) {
-                        cleanDrmObj();  // cleaning up if it hasn't gone through while in the lock
-                    }
-                } // synchronized
-
-                // calling the callback outside the lock
-                sendDrmEvent(new DrmEventNotifier() {
-                    @Override
-                    public void notify(DrmEventCallback callback) {
-                        callback.onDrmPrepared(
-                                mediaPlayer, mCurrentDSD, status);
-                    }
-                });
-            } else {   // blocking mode already has the lock
-
+            synchronized (drmLock) {
                 // continuing with prepareDrm
                 if (provisioningSucceeded) {
                     succeeded = mediaPlayer.resumePrepareDrm(uuid);
@@ -3404,12 +3319,28 @@
                 mediaPlayer.mDrmProvisioningInProgress = false;
                 mediaPlayer.mPrepareDrmInProgress = false;
                 if (!succeeded) {
-                    cleanDrmObj();  // cleaning up if it hasn't gone through
+                    cleanDrmObj();  // cleaning up if it hasn't gone through while in the lock
+                }
+            }  // synchronized
+
+            // calling the callback outside the lock
+            sendDrmEvent(new DrmEventNotifier() {
+                @Override
+                public void notify(DrmEventCallback callback) {
+                    callback.onDrmPrepared(
+                            mediaPlayer, mCurrentDSD, status);
+                }
+            });
+
+            synchronized (mTaskLock) {
+                if (mCurrentTask != null
+                        && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
+                        && mCurrentTask.mNeedToWaitForEventToComplete) {
+                    mCurrentTask = null;
+                    processPendingTask_l();
                 }
             }
-
-            finished = true;
-        }   // run()
+        }
 
         /**
          * Returns a byte[] containing the remainder of 'in', closing it when done.
@@ -3437,50 +3368,29 @@
     }   // ProvisioningThread
 
     private int HandleProvisioninig(UUID uuid) {
-        // the lock is already held by the caller
-
-        if (mDrmProvisioningInProgress) {
-            Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress");
-            return PREPARE_DRM_STATUS_PREPARATION_ERROR;
-        }
-
-        MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
-        if (provReq == null) {
-            Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null.");
-            return PREPARE_DRM_STATUS_PREPARATION_ERROR;
-        }
-
-        Log.v(TAG, "HandleProvisioninig provReq " +
-                " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
-
-        // networking in a background thread
-        mDrmProvisioningInProgress = true;
-
-        mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this);
-        mDrmProvisioningThread.start();
-
-        int result;
-
-        // non-blocking: this is not the final result
-        boolean hasCallback = false;
-        synchronized (mDrmEventCbLock) {
-            hasCallback = !mDrmEventCallbackRecords.isEmpty();
-        }
-        if (hasCallback) {
-            result = PREPARE_DRM_STATUS_SUCCESS;
-        } else {
-            // if blocking mode, wait till provisioning is done
-            try {
-                mDrmProvisioningThread.join();
-            } catch (Exception e) {
-                Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e);
+        synchronized (mDrmLock) {
+            if (mDrmProvisioningInProgress) {
+                Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress");
+                return PREPARE_DRM_STATUS_PREPARATION_ERROR;
             }
-            result = mDrmProvisioningThread.status();
-            // no longer need the thread
-            mDrmProvisioningThread = null;
-        }
 
-        return result;
+            MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+            if (provReq == null) {
+                Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null.");
+                return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+            }
+
+            Log.v(TAG, "HandleProvisioninig provReq " +
+                    " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
+
+            // networking in a background thread
+            mDrmProvisioningInProgress = true;
+
+            mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this);
+            mDrmProvisioningThread.start();
+
+            return PREPARE_DRM_STATUS_SUCCESS;
+        }
     }
 
     private boolean resumePrepareDrm(UUID uuid) {
@@ -3691,7 +3601,9 @@
         private void sendCompleteNotification(int status) {
             // In {@link #notifyWhenCommandLabelReached} case, a separate callback
             // {@link #onCommandLabelReached} is already called in {@code process()}.
-            if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED) {
+            // CALL_COMPLETED_PREPARE_DRM is sent via DrmEventCallback
+            if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
+                    || mMediaCallType == CALL_COMPLETED_PREPARE_DRM) {
                 return;
             }
             sendEvent(new EventNotifier() {