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 が操作できる形式に行います。
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呼び出しが返されたときに PlayReadyCryptoPlugin インターフェイスのインスタンスを作成する MediaCrypto オブジェクトをインスタンス化できます (DRM_DECRYPT_CONTEXTラップ)。
その後、すべての復号化呼び出しで 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 - アプリは、すべてのセキュリティで保護された停止関連の呼び出しの前に、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を保持し、PlayReadyDrmPlugin と PlayReadyCryptoPlugin の間で共有できるようにするには、シングルトン セッション マネージャーを実装する必要があります。
同じ目的を達成する他の設計も許容されます。
SessionManager |
---|
static DRM_APP_CONTEXT soAppContext |
static Mutex sLock |