1.はじめに
この仕様では、OEM が Android に PlayReady 4.0 ベースのデジタル著作権管理 (DRM) プラグインを実装するためのガイダンスを確立します。 詳細については、 https://developer.android.com/reference/android/media/MediaDrm.htmlを参照してください。
この仕様では、プラグイン API が PlayReady 呼び出しにどのようにマップされるかについて説明します。
1.1. 変更履歴
バージョン | 変更 |
---|---|
2016 年 5 月 | 初期バージョン |
2. インターフェイス
PlayReadyDrmPlugin は、DRM プラグイン インターフェイスの実装を提供します。 PlayReadyDrmPlugin は、DRM マネージャー API をラップし、インターフェイスで指定されているパラメーターの適切な翻訳を PlayReady が操作できる形式に行う役割を担います。
PlayReadyCryptoPlugin は、サンプルの復号化を担当する Crypto プラグイン インターフェイスの実装を提供します。 OEM は、復号化されたサンプルが信頼された実行環境 (TEE) から離れることがないことを確認する必要があります。
3. 操作
次の手順では、単純な再生シナリオについて説明します。
アプリによって MediaDrm オブジェクトが作成され、 PlayReadyDrmPlugin がインスタンス化されます。
その後、 openSession を呼び出します。これにより、DRM マネージャーが初期化されます。
その後、アプリは getKeyRequest を呼び出し、コンテンツから抽出されたコンテンツ ヘッダーを initData パラメーターとして渡します。 さらに、アプリは 、optionalParameters キー値ベクトルでライセンス取得チャレンジカスタム データを渡すこともできます。 その後、ライセンス取得チャレンジのカスタム データは、Drm_Content_SetProperty呼び出しとして DRM マネージャーに伝達されます。
この時点で、アプリは (getKeyRequest / provideKeyResponse) を実行できるようになります。これにより、DRM マネージャーで同等の (Drm_LicenseAcq_GenerateChallenge) /Drm_LicenseAcq_ProcessResponse) 呼び出しが生成されます。
その後、アプリは、Drm_Reader_Bind呼び出しの返却時にDRM_DECRYPT_CONTEXTをラップするPlayReadyCryptoPluginインターフェイスのインスタンスを作成するために、MediaCryptoオブジェクトをインスタンス化できます。
その後、すべての復号化呼び出しで PlayReadyCryptoPlugin::d ecrypt メソッドが使用され、復号化されたサンプルへのハンドルが返されます。
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 - アプリは、すべてのセキュアストップ関連の呼び出しの前に、パブリッシャー証明書を 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);
}
セッションを開く
この呼び出しでは、DRM マネージャーのDrm_Initialize関数を呼び出して AppContext を初期化します。
この呼び出しの前に、アプリはプラグインに PropertyString を設定して、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);
}
セッションを終了する
DRM セッションを閉じると、Drm_Uninitializeを呼び出して 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
この方法では、ライセンス要求チャレンジが生成されます。
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(s)
アプリは 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); }
プロビジョンリクエストを取得する
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); }
暗号アルゴリズムを設定する
PlayReady では、任意のデータの暗号化と暗号化解除は行われません。
status_t PlayReadyDrmPlugin::setCipherAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
MACアルゴリズム設定
PlayReady は、任意のデータに署名/検証しません。
status_t PlayReadyDrmPlugin::setMacAlgorithm(
Vector<uint8_t> const &sessionId,
String8 const &algorithm) { return _DR_TO_STATUS(DRM_E_NOTIMPL); }
暗号化
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); }
看板
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;
}
セキュアデコーダーコンポーネントが必要です
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を保持し、PlayReadyDrmPlugin と PlayReadyCryptoPlugin の間で共有できるようにするには、シングルトン セッション マネージャーを実装する必要があります。
同じ目的を達成する他の設計も許容されます。
SessionManager |
---|
static DRM_APP_CONTEXT soAppContext // アプリケーションのコンテキストを保持するための静的変数 |
static Mutex sLock |