/**
  * Add a key for a session Id.
  *
  * @param sessionId Crypto session Id.
  * @param key Response data from the server.
  */
 @CalledByNative
 private void addKey(String sessionId, byte[] key) {
   if (mSessionId == null || !mSessionId.equals(sessionId)) {
     return;
   }
   try {
     final byte[] session = sessionId.getBytes("UTF-8");
     try {
       mMediaDrm.provideKeyResponse(session, key);
     } catch (java.lang.IllegalStateException e) {
       // This is not really an exception. Some error code are incorrectly
       // reported as an exception.
       // TODO(qinmin): remove this exception catch when b/10495563 is fixed.
       Log.e(
           TAG,
           "Exception intentionally caught when calling provideKeyResponse() " + e.toString());
     }
     mHandler.post(
         new Runnable() {
           public void run() {
             nativeOnKeyAdded(mNativeMediaDrmBridge, mSessionId);
           }
         });
     return;
   } catch (android.media.NotProvisionedException e) {
     Log.e(TAG, "failed to provide key response " + e.toString());
   } catch (android.media.DeniedByServerException e) {
     Log.e(TAG, "failed to provide key response " + e.toString());
   } catch (java.io.UnsupportedEncodingException e) {
     Log.e(TAG, "failed to provide key response " + e.toString());
   }
   onKeyError();
 }
 /**
  * Open a new session and return the sessionId.
  *
  * @return ID of the session.
  */
 private String openSession() {
   String session = null;
   try {
     final byte[] sessionId = mMediaDrm.openSession();
     session = new String(sessionId, "UTF-8");
   } catch (android.media.NotProvisionedException e) {
     Log.e(TAG, "Cannot open a new session " + e.toString());
   } catch (java.io.UnsupportedEncodingException e) {
     Log.e(TAG, "Cannot open a new session " + e.toString());
   }
   return session;
 }
  /**
   * Generate a key request and post an asynchronous task to the native side with the response
   * message.
   *
   * @param initData Data needed to generate the key request.
   * @param mime Mime type.
   */
  @CalledByNative
  private void generateKeyRequest(byte[] initData, String mime) {
    Log.d(TAG, "generateKeyRequest().");

    if (mMimeType == null) {
      mMimeType = mime;
    } else if (!mMimeType.equals(mime)) {
      onKeyError();
      return;
    }

    if (mSessionId == null) {
      mSessionId = openSession();
      if (mSessionId == null) {
        if (mPendingInitData != null) {
          Log.e(TAG, "generateKeyRequest is called when another call is pending.");
          onKeyError();
          return;
        }
        // We assume some event will be fired if openSession() failed.
        // generateKeyRequest() will be resumed after provisioning is finished.
        // TODO(xhwang): Double check if this assumption is true. Otherwise we need
        // to handle the exception in openSession more carefully.
        mPendingInitData = initData;
        return;
      }
    }

    try {
      final byte[] session = mSessionId.getBytes("UTF-8");
      HashMap<String, String> optionalParameters = new HashMap<String, String>();
      final MediaDrm.KeyRequest request =
          mMediaDrm.getKeyRequest(
              session, initData, mime, MediaDrm.KEY_TYPE_STREAMING, optionalParameters);
      mHandler.post(
          new Runnable() {
            public void run() {
              nativeOnKeyMessage(
                  mNativeMediaDrmBridge, mSessionId, request.getData(), request.getDefaultUrl());
            }
          });
      return;
    } catch (android.media.NotProvisionedException e) {
      // MediaDrm.EVENT_PROVISION_REQUIRED is also fired in this case.
      // Provisioning is handled in the handler of that event.
      Log.e(TAG, "Cannot get key request " + e.toString());
      return;
    } catch (java.io.UnsupportedEncodingException e) {
      Log.e(TAG, "Cannot get key request " + e.toString());
    }
    onKeyError();
  }