From ca47469aeb86fbcfcccbb30c49ca0c94cda2c63f Mon Sep 17 00:00:00 2001 From: Hong Ngoc Nguyen Date: Tue, 6 Feb 2024 15:53:00 -0500 Subject: [PATCH] [APT-9577] Add DRM for downloads for offline usage : release and remove the downloaded license and license key ID when the corresponding download is removed --- .../armadillo/download/DownloadEngine.kt | 5 +++- .../download/drm/DashDrmLicenseDownloader.kt | 24 +++++++++++++++++++ .../download/drm/DrmLicenseDownloader.kt | 1 + .../download/drm/OfflineDrmManager.kt | 7 ++++++ .../armadillo/encryption/SecureStorage.kt | 9 +++++++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Armadillo/src/main/java/com/scribd/armadillo/download/DownloadEngine.kt b/Armadillo/src/main/java/com/scribd/armadillo/download/DownloadEngine.kt index 2b4b3e5..4cd6beb 100644 --- a/Armadillo/src/main/java/com/scribd/armadillo/download/DownloadEngine.kt +++ b/Armadillo/src/main/java/com/scribd/armadillo/download/DownloadEngine.kt @@ -76,7 +76,10 @@ internal class ExoplayerDownloadEngine @Inject constructor( }) } - override fun removeDownload(audiobook: AudioPlayable) = downloadManager.removeDownload(audiobook.request.url) + override fun removeDownload(audiobook: AudioPlayable) { + downloadManager.removeDownload(audiobook.request.url) + offlineDrmManager.removeDownloadedDrm(audiobook) + } override fun removeAllDownloads() = downloadManager.removeAllDownloads() diff --git a/Armadillo/src/main/java/com/scribd/armadillo/download/drm/DashDrmLicenseDownloader.kt b/Armadillo/src/main/java/com/scribd/armadillo/download/drm/DashDrmLicenseDownloader.kt index 37b8c7c..e65e323 100644 --- a/Armadillo/src/main/java/com/scribd/armadillo/download/drm/DashDrmLicenseDownloader.kt +++ b/Armadillo/src/main/java/com/scribd/armadillo/download/drm/DashDrmLicenseDownloader.kt @@ -2,6 +2,7 @@ package com.scribd.armadillo.download.drm import android.content.Context import android.net.Uri +import com.google.android.exoplayer2.drm.DrmSession import com.google.android.exoplayer2.drm.DrmSessionEventListener import com.google.android.exoplayer2.drm.OfflineLicenseHelper import com.google.android.exoplayer2.source.dash.DashUtil @@ -61,6 +62,29 @@ internal class DashDrmLicenseDownloader @Inject constructor(private val context: } } + override fun removeDrmLicense(audiobook: AudioPlayable) { + globalScope.launch(Dispatchers.IO) { + audiobook.request.drmInfo?.let { drmInfo -> + secureStorage.getDrmKeyId(context, audiobook.request.url, drmInfo.drmType.name)?.let { drmLicenseKeyId -> + // Remove the key ID from storage immediately so we won't use it anymore + secureStorage.removeDrmKeyId(context, audiobook.request.url, drmInfo.drmType.name) + + // Release the license with the server + drmDataSourceFactory.addCustomHeaders(audiobook.request.drmInfo.drmHeaders) + val offlineHelper = when (drmInfo.drmType) { + DrmType.WIDEVINE -> OfflineLicenseHelper.newWidevineInstance(drmInfo.licenseServer, drmDataSourceFactory, drmEventDispatcher) + } + try { + offlineHelper.releaseLicense(drmLicenseKeyId) + } catch (e: DrmSession.DrmSessionException) { + Log.e(DrmLicenseDownloader.TAG, "Failure to release DRM license") + throw DrmDownloadException(e) + } + } + } + } + } + private fun DefaultHttpDataSource.Factory.addCustomHeaders(customHeaders: Map) { customHeaders.takeIf { it.isNotEmpty() }?.let { headers -> setDefaultRequestProperties(headers) diff --git a/Armadillo/src/main/java/com/scribd/armadillo/download/drm/DrmLicenseDownloader.kt b/Armadillo/src/main/java/com/scribd/armadillo/download/drm/DrmLicenseDownloader.kt index 29beadb..292d7e5 100644 --- a/Armadillo/src/main/java/com/scribd/armadillo/download/drm/DrmLicenseDownloader.kt +++ b/Armadillo/src/main/java/com/scribd/armadillo/download/drm/DrmLicenseDownloader.kt @@ -13,4 +13,5 @@ internal interface DrmLicenseDownloader { } fun downloadDrmLicense(audiobook: AudioPlayable) + fun removeDrmLicense(audiobook: AudioPlayable) } \ No newline at end of file diff --git a/Armadillo/src/main/java/com/scribd/armadillo/download/drm/OfflineDrmManager.kt b/Armadillo/src/main/java/com/scribd/armadillo/download/drm/OfflineDrmManager.kt index 5303383..d5b62ea 100644 --- a/Armadillo/src/main/java/com/scribd/armadillo/download/drm/OfflineDrmManager.kt +++ b/Armadillo/src/main/java/com/scribd/armadillo/download/drm/OfflineDrmManager.kt @@ -18,4 +18,11 @@ internal class OfflineDrmManager @Inject constructor( else -> throw DrmContentTypeUnsupportedException(type) }.downloadDrmLicense(audiobook) } + + fun removeDownloadedDrm(audiobook: AudioPlayable) { + when (@C.ContentType val type = Util.inferContentType(audiobook.request.url.toUri(), null)) { + C.TYPE_DASH -> dashDrmLicenseDownloader + else -> throw DrmContentTypeUnsupportedException(type) + }.removeDrmLicense(audiobook) + } } \ No newline at end of file diff --git a/Armadillo/src/main/java/com/scribd/armadillo/encryption/SecureStorage.kt b/Armadillo/src/main/java/com/scribd/armadillo/encryption/SecureStorage.kt index ff4eef8..70d4665 100644 --- a/Armadillo/src/main/java/com/scribd/armadillo/encryption/SecureStorage.kt +++ b/Armadillo/src/main/java/com/scribd/armadillo/encryption/SecureStorage.kt @@ -12,6 +12,7 @@ internal interface SecureStorage { fun downloadSecretKey(context: Context): ByteArray fun saveDrmKeyId(context: Context, audioUrl: String, drmType: String, drmKeyId: ByteArray) fun getDrmKeyId(context: Context, audioUrl: String, drmType: String): ByteArray? + fun removeDrmKeyId(context: Context, audioUrl: String, drmType: String) } @Singleton @@ -64,6 +65,14 @@ internal class ArmadilloSecureStorage @Inject constructor() : SecureStorage { } } + override fun removeDrmKeyId(context: Context, audioUrl: String, drmType: String) { + context.getSharedPreferences(LOCATION, Context.MODE_PRIVATE).also { sharedPrefs -> + val key = Base64.encodeToString(audioUrl.toByteArray(StandardCharsets.UTF_8) + drmType.toByteArray(StandardCharsets.UTF_8), + Base64.NO_WRAP) + sharedPrefs.edit().remove(key).apply() + } + } + private val String.toSecretByteArray: ByteArray get() { val keyBytes = ByteArray(16)