Share via


Plug-in PlayReady per Android specifica

1. Introduzione

Questa specifica stabilisce indicazioni per gli OEMS per implementare plug-in digital rights management (DRM) basati su PlayReady 4.0 in Android. Per informazioni di riferimento, vedere https://developer.android.com/reference/android/media/MediaDrm.html.

Questa specifica fornisce informazioni sul mapping delle API plug-in alle chiamate PlayReady.

1.1. Cronologia modifiche

Versione Modifica
Maggio 2016 Versione iniziale

2. Interfacce

PlayReadyDrmPlugin fornisce l'implementazione per l'interfaccia plug-in DRM. PlayReadyDrmPlugin è responsabile del wrapping delle API di GESTIONE DRM e dell'esecuzione della traduzione appropriata per i parametri come specificato dall'interfaccia in un formato su cui è possibile operare PlayReady.

PlayReadyDRMPlugin interface

PlayReadyCryptoPlugin fornisce l'implementazione dell'interfaccia plug-in Crypto, responsabile della decrittografia degli esempi. L'OEM deve assicurarsi che gli esempi decrittografati non lascino mai l'ambiente di esecuzione attendibile (TEE).

PlayReadyCryptoPlugin interface

3. Operazione

I passaggi seguenti descrivono uno scenario di riproduzione semplice:

  1. L'app crea l'oggetto MediaDrm , che comporterà l'istanza di PlayReadyDrmPlugin.

  2. Chiamare quindi openSession, che comporterà l'inizializzazione di DRM Manager.

  3. L'app chiamerà quindi getKeyRequest e passerà l'intestazione del contenuto estratta dal contenuto come parametro initData . Inoltre, l'app può anche superare i dati personalizzati di acquisizione delle licenze nel vettore key-value facoltativoParameters . I dati personalizzati per l'acquisizione delle licenze devono quindi propagarsi a DRM Manager come chiamata Drm_Content_SetProperty.

  4. A questo punto l'app sarà in grado di eseguire le chiamate (getKeyRequest / provideKeyResponse) che produrranno le chiamate equivalenti (Drm_LicenseAcq_GenerateChallenge) / Drm_LicenseAcq_ProcessResponse) su DRM Manager.

  5. L'app può quindi creare un'istanza di un oggetto MediaCrypto che creerà un'istanza di un'interfaccia PlayReadyCryptoPlugin (wrapping DRM_DECRYPT_CONTEXT) quando viene restituita la chiamata Drm_Reader_Bind.

  6. In seguito, tutte le chiamate di decrittografia utilizzeranno il metodo PlayReadyCryptoPlugin::d ecrypt , che restituirà un handle agli esempi decrittografati.

Simple Playback Flowchart

Simple Playback Layers

4. PlayReadyDRMPlugin

setPropertyString

Le app useranno questo metodo per passare i parametri a PlayReady che non sono altrimenti possibili a causa della progettazione corrente delle API plug-in.

  • DeviceStoreName : l'app deve impostare il percorso dell'archivio dispositivi come proprietà prima di aprire una sessione. PlayReady cercherà quindi la proprietà DeviceStoreName durante l'inizializzazione di DRM Manager durante le chiamate a openSession. Il percorso dell'archivio dispositivi deve essere accessibile all'app, ad esempio la directory dei dati privati dell'app. La proprietà deve puntare a un <percorso/nome file> che deve contenere HDS.

  • LicenseChallengeCustomData : l'app può facoltativamente impostare questa proprietà prima delle chiamate a getKeyRequest, dove PlayReady cercherà la proprietà e comporre una richiesta di acquisizione delle licenze e includere i dati personalizzati nella richiesta.

  • SecureStopCustomData : l'app può impostare facoltativamente questa proprietà prima di una chiamata per generare la richiesta di arresto sicuro. PlayReady cercherà la proprietà e componirà la sfida Secure Stop e includerà i dati personalizzati nella richiesta.

  • SelectKID : in situazioni in cui l'app ha acquisito più licenze in memoria in un singolo batch, può specificare un KID con codifica base64 per l'associazione.

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

Le app useranno questo metodo per passare i parametri a PlayReady che non sono altrimenti possibili a causa della progettazione corrente delle API plug-in.

  • ContentHeader: l'app deve impostare l'intestazione del contenuto nel plug-in prima di tentare di creare un oggetto Crypto. L'intestazione del contenuto può essere in uno dei formati seguenti:

    • Matrice di byte con il contenuto dell'oggetto PlayReady.

    • Matrice di byte con il contenuto delle intestazioni V2, V2.4, V4, V4.1, V4.2 (UNICODE XML).

    • Matrice di caratteri wide che specifica il keyID a 24 caratteri.

  • SecureStopPublisherCert: l'app deve impostare il certificato di Publisher una sola volta prima di tutte le chiamate correlate all'arresto sicuro.

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

Questa chiamata dovrebbe comportare l'inizializzazione dell'appContext chiamando la funzione Drm_Initialize di GESTIONE DRM.

Prima di questa chiamata, l'app deve impostare PropertyString nel plug-in per fornire il percorso all'archivio dispositivi che verrà usato per archiviare hdS.

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

La chiusura di una sessione DRM deve chiamare Drm_Uninitialize per rilasciare AppContext.

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

Questo metodo genererà una richiesta di licenza.

ContentHeader: l'app può passare l'intestazione del contenuto nel parametro initData o, prima di chiamare questa funzione, l'app deve impostare la proprietà stringa ContentHeader .

LicenseChallengeCustomData: l'app può superare i dati personalizzati del parametro facoltativoParameters con la chiave di stringa "LicenseChallengeCustomData". In alternativa, il metodo può selezionare LicenseChallengeCustomData se l'app lo ha già impostato come proprietà.

Il metodo popola il valore defaultUrl se disponibile nell'intestazione del contenuto e la richiesta di invio al server licenze.

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

Dopo aver inviato un'app KeyRequest (LicenseChallenge) al server licenze, l'app deve passare KeyResponse (LicenseResponse) al plug-in.

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)

Le app possono chiamare il metodo getSecureStop per generare una richiesta di arresto sicura per l'ID di arresto sicuro specificato. In alternativa, l'app può chiamare getSecureStops per generare una sfida per tutte le sessioni di arresto sicure esistenti.

Prima di generare la sfida di arresto sicuro, l'app deve fornire il certificato dell'editore chiamando setPropertyByteArray e passando SecureStopPublisherCert.

Facoltativamente, l'app può anche fornire SecureStopCustomData da includere come parte della sfida di arresto sicura.

Dopo aver creato la richiesta di arresto sicuro, l'app deve inviarla al server e fornire la risposta di arresto sicuro al metodo 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

Dopo che l'app riceve la risposta di arresto sicura dal server, il messaggio deve essere passato al plug-in per rimuovere le informazioni di arresto sicure dall'archiviazione.

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 non supportate (Nessuna operazione)

L'elenco seguente di API non dispone di un'operazione corrispondente in PlayReady e non è necessaria per l'esecuzione corretta degli scenari supportati.

queryKeyStatus

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

getProvisionRequest

PlayReady supporta il provisioning locale solo nei dispositivi 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 supporta il provisioning locale solo nei dispositivi 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 supporta il provisioning locale solo nei dispositivi Android.

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

setCipherAlgorithm

PlayReady non crittografa/decrittografa dati arbitrari.

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

setMacAlgorithm

PlayReady non firma/verifica i dati arbitrari.

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

encrypt

PlayReady non crittografa/decrittografa dati arbitrari.

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

decrypt

PlayReady non crittografa/decrittografa dati arbitrari.

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

segno

PlayReady non firma/verifica i dati arbitrari.

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

verify

PlayReady non firma/verifica i dati arbitrari.

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 non firma/verifica i dati arbitrari.

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

Costruttore per creare un oggetto 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;
}

richiedeSecureDecoderComponent

È necessario un componente decodificatore sicuro per elaborare il contenuto crittografato PlayReady.

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

decrypt

Decrittografia chiamata da MediaCodec.

Poiché il metodo di decrittografia non darà esempi chiari, ma piuttosto un handle specifico dell'OEM, l'OEM deve assicurarsi che MediaCodec sia in grado di funzionare correttamente su tale handle.

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

Il distruttore del plug-in di crittografia deve chiudere il contesto del decrittografia.

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

6. PlayReadyDrmFactory e PlayReadyCryptoFactory

L'implementazione delle interfacce PlayReadyDrmFactory e PlayReadyCryptoFactory è necessaria rispettivamente per la creazione di istanze di PlayReadyDrmPlugin e PlayReadyCryptoPlugin .

Entrambe le classi factory devono indicare ai chiamanti che supportano la creazione di oggetti che possono utilizzare contenuto protetto PlayReady implementando correttamente l'API isCryptoSchemeSupported .


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

È necessario implementare un gestore di sessioni singleton per contenere il DRM_APP_CONTEXT e consentire la condivisione tra PlayReadyDrmPlugin e PlayReadyCryptoPlugin.

Anche altri progetti che raggiungono lo stesso scopo sono accettabili.

SessionManager
static DRM_APP_CONTEXT soAppContext
static Mutex sLock