次の方法で共有


Android仕様用 PlayReady プラグイン

1.はじめに

この仕様では、OEM が PlayReady 4.0 ベースのデジタル著作権管理 (DRM) プラグインをAndroidに実装するためのガイダンスを確立します。 リファレンスについては、次を参照してください https://developer.android.com/reference/android/media/MediaDrm.html

この仕様では、プラグイン API が PlayReady 呼び出しにどのようにマップされるかについて説明します。

1.1. 変更履歴

Version Change
2016 年 5 月 初期バージョン

2. インターフェイス

PlayReadyDrmPlugin は、DRM プラグイン インターフェイスの実装を提供します。 PlayReadyDrmPlugin は、DRM マネージャー API をラップし、インターフェイスで指定されたパラメーターの適切な翻訳を PlayReady が操作できる形式に行います。

PlayReadyDRMPlugin interface

PlayReadyCryptoPlugin は、サンプルの復号化を担当する Crypto プラグイン インターフェイスの実装を提供します。 OEM は、復号化されたサンプルが信頼された実行環境 (TEE) から離れることがないことを確認する必要があります。

PlayReadyCryptoPlugin interface

3. 操作

次の手順では、単純な再生シナリオについて説明します。

  1. アプリによって MediaDrm オブジェクトが作成され、 PlayReadyDrmPlugin がインスタンス化されます。

  2. その後 、openSession を呼び出すと、DRM マネージャーが初期化されます。

  3. その後、アプリは getKeyRequest を呼び出し、コンテンツから抽出されたコンテンツ ヘッダーを initData パラメーターとして渡します。 さらに、アプリは 、optionalParameters キー値ベクトルでライセンス取得チャレンジ カスタム データを渡すこともできます。 ライセンス取得チャレンジカスタム データは、Drm_Content_SetProperty呼び出しとして DRM マネージャーに伝達する必要があります。

  4. この時点で、アプリは (getKeyRequest / provideKeyResponse) を実行でき、DRM マネージャーで同等の (Drm_LicenseAcq_GenerateChallenge) /Drm_LicenseAcq_ProcessResponse) 呼び出しが生成されます。

  5. アプリは、Drm_Reader_Bind呼び出しが返されたときに PlayReadyCryptoPlugin インターフェイスのインスタンスを作成する MediaCrypto オブジェクトをインスタンス化できます (DRM_DECRYPT_CONTEXTラップ)。

  6. その後、すべての復号化呼び出しで PlayReadyCryptoPlugin::d ecrypt メソッドが使用され、復号化されたサンプルへのハンドルが返されます。

Simple Playback Flowchart

Simple Playback Layers

4. PlayReadyDRMPlugin

setPropertyString

アプリはこのメソッドを使用して、プラグイン API の現在の設計により不可能なパラメーターを PlayReady に渡します。

  • DeviceStoreName — アプリは、セッションを開く前に、デバイス ストアの場所をプロパティとして設定する必要があります。 次に、PlayReady は、openSession の呼び出し中に DRM マネージャーを初期化するときに DeviceStoreName プロパティを検索します。 デバイス ストアへのパスには、アプリのプライベート データ ディレクトリのようにアプリからアクセスできる必要があります。 このプロパティは、HDS を <保持する必要があるパス/ファイル名> を指す必要があります。

  • LicenseChallengeCustomData - アプリは 、getKeyRequest の呼び出しの前にこのプロパティをオプションで設定できます。PlayReady はプロパティを検索し、ライセンス取得チャレンジを作成し、要求にカスタム データを含めます。

  • SecureStopCustomData - アプリは、Secure Stop チャレンジを生成する呼び出しの前に、必要に応じてこのプロパティを設定できます。 PlayReady はプロパティを検索し、Secure Stop チャレンジを作成し、要求にカスタム データを含めます。

  • SelectKID - アプリが 1 つのバッチで複数のインメモリ ライセンスを取得した場合、バインド用に base64 でエンコードされた KID を指定できます。

status_t PlayReadyDrmPlugin::setPropertyString(
String8 const &name,
String8 const &value)
{
  DRM_RESULT       dr = DRM_SUCCESS;
  Mutex::Autolock lock(&SessionManager::sLock);

  //
  // Store the Property in the name/value KeyedVector
  // for later usage.
  //
  ChkDR( mStringProperties.add(name, value));

  if (name == "SelectKID")
  {
    DRM_SUBSTRING dasstrKID = DRM_EMPTY_DRM_SUBSTRING;
    DRM_CONST_STRING dstrKID = DRM_EMPTY_DRM_STRING;
    DRM_BYTE rgbKID[CCH_BASE64_EQUIV(CCH_BASE64_EQUIV(DRM_ID_SIZE)] = {0};

    const char *pszhKID = value.string();

    // Convert the ASCII KID to UNICODE
    DRM_DSTR_FROM_PB(&dstrKID, rgbKID, sizeof(rgbKID));
    DRM_UTL_PromoteASCIItoUNICODE(pszhKID, &dasstrKID, (DRM_STRING *)&dstrKID);

    ChkDR(Drm_Content_SetProperty(
    &SessionManager::soAppContext, // DRM_APP_CONTEXT pointer
    DRM_CSP_SELECT_KID,
    dstrKID.pwszString,
    dstrKID.cchString * sizeof(DRM_WCHAR)));
  }

ErrorExit:
  return _DR_TO_STATUS(dr);
}

setPropertyByteArray

アプリはこのメソッドを使用して、プラグイン API の現在の設計により不可能なパラメーターを PlayReady に渡します。

  • ContentHeader - アプリは、Crypto オブジェクトを作成する前に、プラグインにコンテンツ ヘッダーを設定する必要があります。 コンテンツ ヘッダーは、次のいずれかの形式になります。

    • PlayReady オブジェクトの内容を含むバイト配列。

    • V2、V2.4、V4、V4.1、V4.2 ヘッダー (Unicode XML) の内容を含むバイト配列。

    • 24 文字の KeyID を指定するワイド文字配列。

  • SecureStopPublisherCert - アプリは、すべてのセキュリティで保護された停止関連の呼び出しの前に、Publisher証明書を 1 回設定する必要があります。

status_t PlayReadyDrmPlugin::setPropertyByteArray(
String8         const &name,
Vector<uint8_t> const &value)
{
  DRM_RESULT      dr = DRM_SUCCESS;
  Mutex::Autolock lock(&SessionManager::sLock);

  // Add the name/value pair to a KeyedVector
  mByteArrayProperties.add(name, value);

  // If the provided property is a content header
  // go ahead and set the property.
  if (name == "ContentHeader")
  {
    ChkDR(Drm_Content_SetProperty(
      &SessionManager::soAppContext, // DRM_APP_CONTEXT pointer
      DRM_CSP_AUTODETECT_HEADER,
      value.data(),
      value.size()));
  }

ErrorExit:
  return _DR_TO_STATUS(dr);
}

openSession

この呼び出しでは、DRM マネージャーのDrm_Initialize関数を呼び出して AppContext を初期化する必要があります。

この呼び出しの前に、アプリは、HDS の格納に使用されるデバイス ストアへのパスを提供するために、プラグインで PropertyString を設定する必要があります。

status_t PlayReadyDrmPlugin::openSession(Vector<luint8_t> &sessionId)
{
  DRM_RESULT          dr = DRM_SUCCESS;
  DRM_CONST_STRING    dstrDeviceStoreName = { 0 };
  String8             deviceStoreName;

  Mutex::Autolock lock(&SessionManager::sLock);

  ChkMem(mpbOpaqueBuffer =
    (DRM_BYTE*)Oem_MemAlloc(MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE));

  //
  // Application must call setPropertyString to set the DeviceStoreName
  // before opening the session.
  // So the call to getPropertyString with DeviceStoreName must return a value.
  //
  ChkDR(getPropertyString(String8("DeviceStoreName"), deviceStoreName));

  // Convert the deviceStoreName to a DRM_CONST_STRING */
  ChkDR(util_String8ToDrmConstString(deviceStoreName, &dstrDeviceStoreName));

  // Initialize AppContext
  ChkDR(Drm_Initialize(
    &SessionManager::soAppContext,
    NULL,             // pOEMContext : OEM can initialize and pass if needed.
    mpbOpaqueBuffer,
    MINIMUM_APPCONTEXT_OPAQUE_BUFFER_SIZE,
    &dstrDeviceStoreName));

ErrorExit:
  return _DR_TO_STATUS(dr);
}

closeSession

DRM セッションを閉じるには、AppContext を解放するためにDrm_Uninitializeを呼び出す必要があります。

status_t PlayReadyDrmPlugin::closeSession(Vector<uint8_t> const &sessionId)
{
  Mutex::Autolock lock(&SessionManager::sLock);

  // Clear the App Context
  Drm_Uninitialize(&SessionManager::soAppContext);
  SAFE_FREE(mpbOpaqueBuffer);
  return OK;
}

getKeyRequest

この方法では、ライセンス要求チャレンジが生成されます。

ContentHeader - アプリは initData パラメーターでコンテンツ ヘッダーを渡すか、この関数を呼び出す前に、 ContentHeader 文字列プロパティを設定する必要があります。

LicenseChallengeCustomData - アプリは、文字列キー "LicenseChallengeCustomData" を使用して 、optionalParameters パラメーターでチャレンジ カスタム データを渡すことができます。 または、アプリが既にプロパティとして設定している場合は、このメソッドで LicenseChallengeCustomData を取得することもできます。

このメソッドは、コンテンツ ヘッダーで使用可能な場合は defaultUrl と、ライセンス サーバーに送信する要求を設定します。

status_t PlayReadyDrmPlugin::getKeyRequest(
        Vector<uint8_t>                   const &sessionId,
        Vector<uint8_t>                   const &initData, s// ContentHeader
        String8                           const &mimeType,
        KeyType                                 keyType,
        KeyedVector<String8, String8="">  const &optionalParameters, // ChallengeCustomData
        Vector<uint8_t>                         &request,            // Output Challenge
        String8                                 &defaultUrl,         // Output URL
        KeyRequestType                         *keyRequestType)
{
  DRM_RESULT      dr = DRM_SUCCESS;
  DRM_BYTE       *pbContentProperty = NULL;
  DRM_DWORD       cbContentProperty = 0;
  DRM_CHAR       *pchCustomData = NULL;
  DRM_DWORD       cchCustomData = 0;
  DRM_CHAR       *pchSilentURL = NULL;
  DRM_DWORD       cchSilentURL = 0;
  DRM_BYTE       *pbChallenge = NULL;
  DRM_DWORD       cbChallenge = 0;
  DRM_BOOL        fHasUrl = FALSE;
  Vector<uint8_t> contentHeader;
  String8         customData;

  Mutex::Autolock lock(&SessionManager::sLock);

  if (getValue(optionalParameters, String8("LicenseChallengeCustomData"), customData) == OK)
  {
    //
    // The Application can pass the custom data as a part of the optionalParameters.
    // The key string would be "LicenseChallengeCustomData".
    // If it is provided. The plug-in should use it when generating the license challenge.
    //
    pchCustomData = customData.data();
    cchCustomData = customData.size();
  }
  else if (getPropertyString(String8("LicenseChallengeCustomData"), customData) == OK)
  {
    //
    // Alternatively the Application could have provided customData for this operation
    // via a previous call to setPropertyString.
    // Try to retrieve it if available. Otherwise, skip without failing.
    //
    pchCustomData = customData.data();
    cchCustomData = customData.size();
  }

  //
  // The Application could choose to pass the content header as the initData
  // If the initData is passed, use it to call Drm_Content_SetProperty
  //
  if (value.size() != 0)
  {
    ChkDR(Drm_Content_SetProperty(
      &SessionManager::soAppContext, // DRM_APP_CONTEXT pointer
      DRM_CSP_AUTODETECT_HEADER,
      value.data(),
      value.size()));
  }

  //
  // First, try to retrieve the URL.
  //
  dr = Drm_LicenseAcq_GenerateChallenge(
    &SessionManager::soAppContext,
    NULL,
    0,
    NULL,
    pchCustomData,
    cchCustomData,
    pchSilentURL,
    &cchSilentURL,    // Attempt to get the URL size.
    NULL,
    NULL,
    pbChallenge,
    &cbChallenge);
  if (dr == DRM_E_BUFFERTOOSMALL)oi
  {
    fHasUrl = TRUE;

    // ContentHeader has a URL. Allocate buffer for it.
    ChkMem(pchSilentURL = (DRM_CHAR*)Oem_MemAlloc(cchSilentURL));
  }
  else if (dr == DRM_E_NO_URL)
  {
    // No Url in the content header, no need to fail.
    // The Application can get the URL independently.
    dr = DRM_SUCCESS;
  }
  else
  {
    ChkDR(dr);
  }

  //
  // Second, get the challenge size.
  //
  dr = Drm_LicenseAcq_GenerateChallenge(
    poAppContext,
    NULL,
    0,
    NULL,
    pchCustomData,
    cchCustomData,
    pchSilentURL,
    fHasUrl ? &cchSilentURL : NULL,
    NULL,
    NULL,
    pbChallenge,
    &cbChallenge);
  if (dr == DRM_E_BUFFERTOOSMALL)
  {
    // Allocate buffer that is sufficient
    // to store the license acquisition challenge.
    ChkMem(pbChallenge = (DRM_BYTE*)Oem_MemAlloc(cbChallenge));
  }
  else
  {
    ChkDR(dr);
  }

  //
  // Finally, generate challenge.
  //
  ChkDR(Drm_LicenseAcq_GenerateChallenge(
    &SessionManager::soAppContext,
    NULL,
    0,
    NULL,
    pchCustomData,
    cchCustomData,
    pchSilentURL,
    fHasUrl ? &cchSilentURL : NULL,
    NULL,
    NULL,
    pbChallenge,
    &cbChallenge));

  //
  // Write the License Challenge to the request buffer.
  //
  request.appendArray(pbChallenge, cbChallenge);

  if (fHasUrl)
  {
    defaultUrl.appendArray(pchSilentURL, cchSilentURL);
  }

ErrorExit:
  SAFE_OEM_FREE(pbChallenge);
  SAFE_OEM_FREE(pchSilentURL);
  return _DR_TO_STATUS(dr);
}

provideKeyResponse

KeyRequest (LicenseChallenge)アプリによってライセンス サーバーに送信されると、アプリはプラグインに渡すKeyResponse (LicenseResponse)必要があります。

status_t PlayReadyDrmPlugin::provideKeyResponse(
      Vector<uint8_t>        const &sessionId,
      Vector<uint8_t>        const &response,
      Vector<uint8_t>        &keySetId)
{
  DRM_RESULT           dr = DRM_SUCCESS;
  DRM_LICENSE_RESPONSE oLicenseResponse = { 0 };
  DRM_DWORD            idx = 0;
  DRM_BOOL             fExceedMaxLic = FALSE;
  Mutex::Autolock lock(&SessionManager::sLock);

  //
  // Process the response.
  //
  dr = Drm_LicenseAcq_ProcessResponse(
            &SessionManager::soAppContext,
            0,
            NULL,
            NULL,
            response.data(),
            response.size(),
            &oLicenseResponse);
  if(dr ==  DRM_E_LICACQ_TOO_MANY_LICENSES)
  {
    //
    // Received more licenses than DRM_MAX_LICENSE_ACK.
    // Allocate space for that.
    //

    oLicenseResponse.m_cMaxAcks = oLicenseResponse.m_cAcks;
    ChkMem(oLicenseResponse.m_pAcks=
    (DRM_BYTE*)Oem_MemAlloc(sizeof(DRM_LICENSE_ACK)
    * oLicenseResponse.m_cAcks ));
    ChkDR(Drm_LicenseAcq_ProcessResponse(
            &SessionManager::soAppContext,
            0,
            NULL,
            NULL,
            response.data(),
            response.size(),
            &oLicenseResponse);

    fExceedMaxLic = TRUE;
  }
  ChkDR(dr);

  //
  // Ensure that all licenses were processed successfully.
  //

  if(fExceedMaxLic)
  {
    for (idx = 0; idx < oLicenseResponse.m_cAcks; idx++)
    {
      ChkDR(oLicenseResponse.m_pAcks[idx].m_dwResult);
    }
  }
  else
  {
    for (idx = 0; idx < oLicenseResponse.m_cAcks; idx++)
    {
      ChkDR(oLicenseResponse.m_rgoAcks[idx].m_dwResult);
    }
  }

ErrorExit:
  SAFE_FREE(oLicenseResponse.m_pAcks);
  return _DR_TO_STATUS(dr);
}

getSecureStop

アプリは 、getSecureStop メソッドを呼び出して、指定されたセキュリティで保護された停止 ID に対してセキュリティで保護された停止チャレンジを生成できます。 または、アプリで getSecureStops を呼び出して、既存のすべてのセキュリティで保護された停止セッションに対するチャレンジを生成することもできます。

セキュリティで保護された停止チャレンジを生成する前に、アプリは setPropertyByteArray を呼び出し、 SecureStopPublisherCert を渡すことによって発行元証明書を提供する必要があります。

必要に応じて、アプリは SecureStopCustomData を提供して、セキュリティで保護された停止チャレンジの一部として含めることもできます。

セキュリティで保護された停止チャレンジが作成された後、アプリはサーバーに送信し、 releaseSecureStops メソッドに対してセキュリティで保護された停止応答を返す必要があります。

DRM_RESULT PlayReadyDrmPlugin::_getSecureStop(
DRM_ID       *pIdSession,
DRM_BYTE    **ppbChallenge,
DRM_DWORD    *pcbChallenge)
{
  DRM_RESULT       dr = DRM_SUCCESS;
  DRM_CHAR        *pchCustomData = NULL;
  DRM_DWORD        cchCustomData = 0;
  String8          customData;
  Vector<uint8_t>  publisherCert;

  if (getPropertyString("SecureStopCustomData", customData) == OK)
  {
    // SecureStop CustomData is optional
    pchCustomData = customData.data();
    cchCustomData = customData.size();
  }

  // Application must set SecureStopPublisherCert before calling getSecureStop(s)
  ChkDR(getPropertyByteArray("SecureStopPublisherCert", publisherCert));

  ChkDR(Drm_SecureStop_GenerateChallenge(
        &SessionManager::soAppContext,
        pIdSession,
        publisherCert.size(),
        publisherCert.data(),
        cchCustomData,
        pchCustomData,
        pcbChallenge,
        ppbChallenge));
ErrorExit:
  return dr;
}

status_t PlayReadyDrmPlugin::getSecureStop(
        Vector<uint8_t>          const &ssid,           // In
        Vector<uint8_t>                &secureStop)     // Out
{
  DRM_RESULT dr = DRM_SUCCESS;
  DRM_ID     idSecureStop = { 0 };
  DRM_BYTE  *pbChallenge = NULL;
  DRM_DWORD  cbChallenge = 0;
  Mutex::Autolock lock(&SessionManager::sLock);

  ChkArg(ssid.size() == sizeof(DRM_ID));
  memcpy(&idSecureStop, ssid.data(), sizeof(DRM_ID));

  ChkDR(_getSecureStop(
            &idSecureStop,
            &pbChallenge,
            &cbChallenge));

  secureStop.appendArray(pbChallenge, cbChallenge);
ErrorExit:
  SAFE_FREE(pbChallenge);
  return _DR_TO_STATUS(dr);
}

status_t PlayReadyDrmPlugin::getSecureStops(
            List<Vector<uint8_t> > &secureStops)
{
  DRM_RESULT dr = DRM_SUCCESS;
  DRM_BYTE  *pbChallenge = NULL;
  DRM_DWORD  cbChallenge = 0;
  Vector<uint8_t> secureStopChallenge;
  Mutex::Autolock lock(&SessionManager::sLock);

  ChkDR(_getSecureStop(
    NULL,
    &pbChallenge,
    &cbChallenge));

  secureStopChallenge.appendArray(pbChallenge, cbChallenge);
  secureStops.push_back(secureStopChallenge);
ErrorExit:
  SAFE_FREE(pbChallenge);
  return _DR_TO_STATUS(dr);
}

releaseSecureStops

アプリがサーバーから安全な停止応答を受信したら、メッセージをプラグインに渡して、ストレージから安全な停止情報を削除する必要があります。

status_t PlayReadyDrmPlugin::releaseSecureStops(
    Vector<uint8_t>      const &ssRelease)
{
  DRM_RESULT dr = DRM_SUCCESS;
  DRM_CHAR    *pchCustomData = NULL;
  DRM_DWORD    cchCustomData = 0;
  Vector<uint8_t> publisherCert;
  Mutex::Autolock lock(&SessionManager::sLock);

  // Application must set SecureStopPublisherCert before calling
  // releaseSecureStops
  ChkDR(getPropertyByteArray("SecureStopPublisherCert", publisherCert));

  ChkDR(Drm_SecureStop_ProcessResponse(
    &SessionManager::soAppContext,
    NULL,
    publisherCert.size(),
    publisherCert.data(),
    ssRelease.size(),
    ssRelease.data(),
    &cchCustomData,
    &pchCustomData));
ErrorExit:
  SAFE_FREE(pchCustomData);
  return _DR_TO_STATUS(dr);
}

サポートされていない API (操作なし)

次の API の一覧には PlayReady で対応する操作がなく、サポートされているシナリオの正常な実行には必要ありません。

queryKeyStatus

status_t PlayReadyDrmPlugin::queryKeyStatus(
        Vector<uint8_t>                  const &sessionId,
        KeyedVector<String8, String8="">       &infoMap) const
{ return _DR_TO_STATUS(DRM_E_NOTIMPL); }

getProvisionRequest

PlayReady では、Android デバイスでのみローカル プロビジョニングがサポートされます。

status_t PlayReadyDrmPlugin::getProvisionRequest(
        String8 const         &certType,
        String8 const         &certAuthority,
        Vector<uint8_t>       &request,
        String8               &defaultUrl) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

provideProvisionResponse

PlayReady では、Android デバイスでのみローカル プロビジョニングがサポートされます。

status_t PlayReadyDrmPlugin::provideProvisionResponse(
        Vector<uint8_t>          const &response,
        Vector<uint8_t>                &certificate,
        Vector<uint8_t>                &wrappedKey) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

unprovisionDevice

PlayReady では、Android デバイスでのみローカル プロビジョニングがサポートされます。

status_t PlayReadyDrmPlugin::unprovisionDevice() { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

setCipherAlgorithm

PlayReady では、任意のデータの暗号化と暗号化解除は行われません。

status_t PlayReadyDrmPlugin::setCipherAlgorithm(
        Vector<uint8_t>       const &sessionId,
        String8               const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

setMacAlgorithm

PlayReady は、任意のデータに署名/検証しません。

status_t PlayReadyDrmPlugin::setMacAlgorithm(
        Vector<uint8_t>       const &sessionId,
        String8               const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

encrypt

PlayReady では、任意のデータの暗号化と暗号化解除は行われません。

status_t PlayReadyDrmPlugin::encrypt(
        Vector<uint8_t> const &sessionId,
        Vector<uint8_t> const &keyId,
        Vector<uint8_t> const &input,
        Vector<uint8_t> const &iv,
        Vector<uint8_t>       &output) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

復号化

PlayReady では、任意のデータの暗号化と暗号化解除は行われません。

status_t PlayReadyDrmPlugin::decrypt(
        Vector<uint8_t> const &sessionId,
        Vector<uint8_t> const &keyId,
        Vector<uint8_t> const &input,
        Vector<uint8_t> const &iv,
        Vector<uint8_t>       &output) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

sign

PlayReady は、任意のデータに署名/検証しません。

status_t PlayReadyDrmPlugin::sign(
        Vector<uint8_t> const &sessionId,
        Vector<uint8_t> const &keyId,
        Vector<uint8_t> const &message,
        Vector<uint8_t>       &signature) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

確認

PlayReady は、任意のデータに署名/検証しません。

status_t PlayReadyDrmPlugin::verify(
        Vector<uint8_t> const &sessionId,
        Vector<uint8_t> const &keyId,
        Vector<uint8_t> const &message,
        Vector<uint8_t> const &signature,
        bool                  &match) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

signRSA

PlayReady は、任意のデータに署名/検証しません。

status_t PlayReadyDrmPlugin::signRSA(
        Vector<uint8_t> const &sessionId,
        String8         const &algorithm,
        Vector<uint8_t> const &message,
        Vector<uint8_t> const &wrappedKey,
        Vector<uint8_t>       &signature) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }

5. PlayReadyCryptoPlugin

PlayReadyCryptoPlugin

PlayReadyCryptoPlugin オブジェクトを作成するコンストラクター。

PlayReadyCryptoPlugin::PlayReadyCryptoPlugin(
      const uint8_t sessionId[16],
      const void *  data,
      size_t        size);
{
  DRM_RESULT         dr = DRM_SUCCESS;
  Mutex::Autolock lock(&SessionManager::sLock);

  ChkDR(Drm_Reader_Bind(
         &SessionManager::soAppContext,
         NULL,
         0,
         NULL,
         NULL,
         &moDecryptContext));

ErrorExit:
  // Cache the Bind Result and check for it on the first call to decrypt.
  mdrBindResult = dr;
}

requiresSecureDecoderComponent

PlayReady で暗号化されたコンテンツを処理するには、セキュリティで保護されたデコーダー コンポーネントが必要です。

bool PlayReadyCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
{
  // Must always return true for PlayReady Content.
  return true;
}

復号化

MediaCodec によって呼び出される復号化。

復号化方法では明確なサンプルではなく、OEM 固有のハンドルが提供されるため、OEM はそのハンドルで MediaCodec が正しく動作できることを確認する必要があります。

ssize_t PlayReadyCryptoPlugin::decrypt(
      bool                 secure,
      const uint8_t        key[16],
      const uint8_t        iv[16],
      Mode                 mode,
      const void          *srcPtr,
      const SubSample     *subSamples,
      size_t               numSubSamples,
      void                *dstPtr,
      AString             *errorDetailMsg)
{
  DRM_RESULT        dr = DRM_SUCCESS;
  DRM_DWORD         idx = 0;
  DRM_DWORD         idxSubSample = 0;
  DRM_DWORD         cbEncryptedContent = 0;
  DRM_UINT64        ui64InitializationVector;
  DRM_DWORD        *pdwEncryptedRegionMappings = NULL;
  DRM_DWORD         cbOpaqueClearContentHandle = 0;
  Mutex::Autolock lock(mLock);

  // Only AES_CTR is supported
  ChkBOOL(mode == kMode_AES_CTR, DRM_E_UNSUPPORTED_ALGORITHM);
  ChkArg(secure == TRUE);

  // Ensure that bind returned successfully in the constructor.
  ChkDR( mdrBindResult );

  // Allocate a list for the region mapping.
  ChkMem(pdwEncryptedRegionMappings
        = Oem_MemAlloc(sizeof(DRM_DWORD)* numSubSamples * 2));

  // Fill the region mappings list with the information
  // from the subSamples.
  for (idxSubSample = 0; idxSubSample < numSubSamples; idxSubSample++)
  {
    pdwEncryptedRegionMappings[idx++]
          = subSamples[idxSubSample].mNumBytesOfClearData;
    pdwEncryptedRegionMappings[idx++]
          = subSamples[idxSubSample].mNumBytesOfEncryptedData;

    // Calculate the total number of bytes
    cbEncryptedContent +=
          (subSamples[idxSubSample].mNumBytesOfClearData
          + subSamples[idxSubSample].mNumBytesOfEncryptedData);
  }

  // Convert the IV from a uint8 array to a DRM_UINT64
  // Endianess should be taken into consideration.
  ChkStatus(initializeIV(iv, &ui64InitializationVector));

  // Decrypt
  ChkDR(Drm_Reader_DecryptOpaque(
      &moDecryptContext,
      numSubSamples * 2,
      pdwEncryptedRegionMappings,
      ui64InitializationVector,
      cbEncryptedContent,
      srcPtr,
      &cbOpaqueClearContentHandle,
      &dstPtr);

ErrorExit:
  // Free the Region mappings list.
  SAFE_FREE(pdwEncryptedRegionMappings);

  if (DRM_FAILED(dr))
  {
    // If an error occurs, fill the errorMessage
    // then return the DRM_RESULT
    SetErrorDetails(errorDetailMsg, dr);
    return _DR_TO_STATUS(dr);
  }
  // In case of success, return the size of the handle pointing
  // to the clear content.
  return cbOpaqueClearContentHandle;
}

~PlayReadyCryptoPlugin

暗号化プラグインデストラクターは、復号化コンテキストを閉じる必要があります。

~PlayReadyCryptoPlugin::PlayReadyCryptoPlugin()
{
  Mutex::Autolock lock(mLock);
  Drm_Reader_Close(&moDecryptContext);
}

6. PlayReadyDrmFactory と PlayReadyCryptoFactory

PlayReadyDrmFactory インターフェイスと PlayReadyCryptoFactory インターフェイスの実装は、それぞれ PlayReadyDrmPlugin とPlayReadyCryptoPlugin の両方のインスタンスを作成するために必要です。

どちらのファクトリ クラスも、 isCryptoSchemeSupported API を適切に実装することで PlayReady で保護されたコンテンツを使用できるオブジェクトの作成をサポートしていることを呼び出し元に示す必要があります。


const uint8_t playready_uuid[16] =
    {0x79, 0xf0, 0x04, 0x9a, 0x40, 0x98, 0x86, 0x42, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95};

bool isCryptoSchemeSupported(const uint8_t uuid[16])
{
  return (!memcmp(uuid, playready_uuid, sizeof(playready_uuid)));
}

7. SessionManager

DRM_APP_CONTEXTを保持し、PlayReadyDrmPluginPlayReadyCryptoPlugin の間で共有できるようにするには、シングルトン セッション マネージャーを実装する必要があります。

同じ目的を達成する他の設計も許容されます。

SessionManager
static DRM_APP_CONTEXT soAppContext
static Mutex sLock