Freigeben über


PlayReady-Plug-In für Android-Spezifikation

1. Einführung

Diese Spezifikation enthält Anleitungen für OEMs zum Implementieren von PlayReady 4.0-basierten DRM-Plug-Ins (Digital Rights Management) unter Android. Referenz finden Sie unter https://developer.android.com/reference/android/media/MediaDrm.html.

Diese Spezifikation enthält Informationen zur Zuordnung der Plug-In-APIs zu PlayReady-Aufrufen.

1.1. Änderungsverlauf

Version Veränderung
Mai 2016 Ursprüngliche Version

2. Schnittstellen

PlayReadyDrmPlugin stellt die Implementierung für die DRM-Plug-In-Schnittstelle bereit. PlayReadyDrmPlugin ist für das Umschließen der DRM-Manager-APIs verantwortlich und führt die richtige Übersetzung für die Parameter aus, die von der Schnittstelle in ein Format angegeben werden, auf dem PlayReady ausgeführt werden kann.

PlayReadyDRMPlugin-Schnittstelle

PlayReadyCryptoPlugin stellt die Implementierung für die Crypto-Plug-In-Schnittstelle bereit, die für die Entschlüsselung der Beispiele verantwortlich ist. Der OEM muss sicherstellen, dass die entschlüsselten Beispiele niemals die vertrauenswürdige Ausführungsumgebung (TEE) verlassen.

PlayReadyCryptoPlugin-Schnittstelle

3. Vorgang

Die folgenden Schritte beschreiben ein einfaches Wiedergabeszenario:

  1. Die App erstellt das MediaDrm-Objekt , das zur Instanziierung von PlayReadyDrmPlugin führt.

  2. Rufen Sie dann openSession auf, was zur Initialisierung des DRM-Managers führt.

  3. Die App ruft dann getKeyRequest auf und übergibt den aus dem Inhalt extrahierten Content-Header als initData-Parameter. Darüber hinaus kann die App auch die angepassten Daten der Challenge für den Lizenzerwerb im optionalParameters Schlüssel-Wert-Vektor übergeben. Die angepassten Daten der Challenge für den Lizenzerwerb sollten dann als Drm_Content_SetProperty-Aufruf an den DRM Manager weitergegeben werden.

  4. An diesem Punkt kann die App die (getKeyRequest / provideKeyResponse) ausführen, die die entsprechenden Aufrufe (Drm_LicenseAcq_GenerateChallenge) / Drm_LicenseAcq_ProcessResponse) für den DRM-Manager erzeugt.

  5. Die App kann dann ein MediaCrypto-Objekt instanziieren, das eine Instanz einer PlayReadyCryptoPlugin-Schnittstelle (Wrapping DRM_DECRYPT_CONTEXT) erstellt, wenn der Drm_Reader_Bind Aufruf zurückgegeben wird.

  6. Danach verwenden alle Entschlüsselungsaufrufe die PlayReadyCryptoPlugin::d ecrypt-Methode , die ein Handle an die entschlüsselten Beispiele zurückgibt.

Einfaches Wiedergabeflussdiagramm

Einfache Schichten für die Wiedergabe

4. PlayReadyDRMPlugin

setPropertyString

Apps verwenden diese Methode, um Parameter an PlayReady zu übergeben, die aufgrund des aktuellen Designs der Plug-In-APIs nicht andernfalls möglich sind.

  • DeviceStoreName – Die App muss den Speicherort des Gerätespeichers vor dem Öffnen einer Sitzung als Eigenschaft festlegen. Anschließend sucht PlayReady beim Initialisieren des DRM-Managers während der Aufrufe von openSession die DeviceStoreName-Eigenschaft. Der Pfad zum Gerätespeicher muss für die App wie das private Datenverzeichnis der App zugänglich sein. Die Eigenschaft sollte auf einen <Pfad/Dateinamen> zeigen, der den HDS enthalten soll.

  • LicenseChallengeCustomData – Die App kann diese Eigenschaft optional vor Aufrufen von getKeyRequest festlegen, wobei PlayReady die Eigenschaft nachschlagen und eine Lizenzerwerbsaufforderung erstellt und die benutzerdefinierten Daten in die Anforderung einschließt.

  • SecureStopCustomData – Die App kann diese Eigenschaft optional vor einem Aufruf festlegen, um die Secure Stop-Abfrage zu generieren. PlayReady sucht die Eigenschaft und erstellt die Secure Stop-Abfrage und schließt die benutzerdefinierten Daten in die Anforderung ein.

  • SelectKID – In Situationen, in denen die App mehrere In-Memory-Lizenzen in einem einzigen Batch erworben hat, kann sie eine base64-codierte KID für die Bindung angeben.

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

Apps verwenden diese Methode, um Parameter an PlayReady zu übergeben, die aufgrund des aktuellen Designs der Plug-In-APIs nicht andernfalls möglich sind.

  • ContentHeader – Die App sollte den Inhaltsheader für das Plug-In festlegen, bevor Sie versuchen, ein Crypto-Objekt zu erstellen. Der Inhaltsheader kann in einem der folgenden Formate vorliegen:

    • Bytearray mit dem Inhalt des PlayReady-Objekts.

    • Bytearray mit dem Inhalt der Header V2, V2.4, V4, V4.1, V4.2 (Unicode-XML).

    • Breites Zeichenarray, das die 24-stellige KeyID angibt.

  • SecureStopPublisherCert-Die App sollte das Publisher Zertifikat einmal vor allen Aufrufen im Zusammenhang mit Secure Stop festlegen.

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

Dieser Aufruf sollte dazu führen, dass der AppContext initialisiert wird, indem die Drm_Initialize-Funktion des DRM-Managers aufgerufen wird.

Vor diesem Aufruf muss die App PropertyString für das Plug-In festlegen, um den Pfad zum Gerätespeicher bereitzustellen, der zum Speichern des HDS verwendet wird.

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

Das Abschließen einer DRM-Sitzung sollte Drm_Uninitialize aufrufen, um den AppContext freizugeben.

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

Diese Methode generiert eine Lizenzanforderungsanforderung.

ContentHeader—Die App kann entweder den ContentHeader im initData-Parameter übergeben oder, bevor sie diese Funktion aufruft, die ContentHeader-Eigenschaft als Zeichenfolge festlegen.

LicenseChallengeCustomData-Die App kann die angepassten Challenge-Daten im Parameter optionalParameters mit der Zeichenfolge „LicenseChallengeCustomData“ übergeben. Alternativ kann die Methode die LicenseChallengeCustomData abrufen, wenn die App sie bereits als Eigenschaft festgelegt hat.

Die Methode füllt die defaultUrl auf, wenn sie im Inhaltsheader verfügbar ist, sowie die Anforderung, die an den Lizenzserver gesendet werden soll.

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

Sobald ein KeyRequest (LicenseChallenge) von der App an den Lizenzserver gesendet wurde, sollte die App das KeyResponse (LicenseResponse) an das Plug-in übergeben.

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(s)

Apps können die getSecureStop-Methode aufrufen, um eine Abfrage für sicheres Beenden für die angegebene ID für sicheres Beenden zu generieren. Alternativ kann die App getSecureStops aufrufen, um eine Challenge für den Secure Stop zu generieren, der für alle bestehenden Sitzungen gilt.

Bevor die App die Challenge für den Secure Stop generiert, muss sie das Zertifikat des Publishers bereitstellen, indem sie setPropertyByteArray aufruft und SecureStopPublisherCert übergibt.

Optional kann die App auch SecureStopCustomData bereitstellen, die in die Secure-Stop-Herausforderung integriert werden kann.

Nachdem die Abfrage zum sicheren Beenden erstellt wurde, sollte sie von der App an den Server gesendet und die Antwort auf sicheres Beenden an die ReleaseSecureStops-Methode zurückgegeben werden.

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

Sobald die App die Antwort auf sicheres Beenden vom Server empfängt, sollte die Nachricht an das Plug-In übergeben werden, um die Informationen zum sicheren Beenden aus dem Speicher zu entfernen.

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);
}

Nicht unterstützte APIs (kein Vorgang)

Die folgende Liste der APIs verfügt nicht über einen entsprechenden Vorgang in PlayReady und ist für die erfolgreiche Ausführung der unterstützten Szenarien nicht erforderlich.

queryKeyStatus

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

getProvisionRequest

PlayReady unterstützt die lokale Bereitstellung nur auf Android-Geräten.

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 unterstützt die lokale Bereitstellung nur auf Android-Geräten.

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 unterstützt die lokale Bereitstellung nur auf Android-Geräten.

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

setCipherAlgorithm

PlayReady verschlüsselt/entschlüsselt keine beliebigen Daten.

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

setzeMacAlgorithmus

PlayReady signiert/überprüft keine beliebigen Daten.

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

verschlüsseln

PlayReady verschlüsselt/entschlüsselt keine beliebigen Daten.

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); }

entschlüsseln

PlayReady verschlüsselt/entschlüsselt keine beliebigen Daten.

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 signiert/überprüft keine beliebigen Daten.

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); }

überprüfen

PlayReady signiert/überprüft keine beliebigen Daten.

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 signiert/überprüft keine beliebigen Daten.

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

Konstruktor zum Erstellen eines PlayReadyCryptoPlugin-Objekts .

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

Zum Verarbeiten verschlüsselter PlayReady-Inhalte ist eine sichere Decoderkomponente erforderlich.

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

entschlüsseln

Die von MediaCodec aufgerufene Entschlüsselung.

Da die Entschlüsselungsmethode keine eindeutigen Beispiele ausgibt, sondern ein OEM-spezifisches Handle, muss der OEM sicherstellen, dass MediaCodec in der Lage ist, mit diesem Handle korrekt zu arbeiten.

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

Der Krypto-Plug-In-Destruktor muss den Entschlüsselungskontext schließen.

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

6. PlayReadyDrmFactory und PlayReadyCryptoFactory

Die Implementierung der Schnittstellen PlayReadyDrmFactory und PlayReadyCryptoFactory ist erforderlich, um Instanzen von PlayReadyDrmPlugin bzw. PlayReadyCryptoPlugin zu erstellen.

Beide Factoryklassen müssen den Aufrufern anzeigen, dass sie das Erstellen von Objekten unterstützen, die PlayReady-geschützte Inhalte konsumieren können, indem sie die isCryptoSchemeSupported-API ordnungsgemäß implementieren.


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

Es muss ein Singleton Session Manager implementiert werden, um den DRM_APP_CONTEXT zu halten und die Möglichkeit zu bieten, ihn zwischen dem PlayReadyDrmPlugin und PlayReadyCryptoPlugin zu teilen.

Andere Designs, die denselben Zweck erreichen, sind ebenfalls akzeptabel.

SessionManager
static DRM_APP_CONTEXT soAppContext
static Mutex sLock