Skip to content

Commit

Permalink
[APT-9577] Add DRM for downloads for offline usage : release and remo…
Browse files Browse the repository at this point in the history
…ve the downloaded license and license key ID when the corresponding download is removed
  • Loading branch information
rubeus90 committed Feb 6, 2024
1 parent 67b63a1 commit d531ce3
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(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<String, String>) {
customHeaders.takeIf { it.isNotEmpty() }?.let { headers ->
setDefaultRequestProperties(headers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ internal interface DrmLicenseDownloader {
}

fun downloadDrmLicense(audiobook: AudioPlayable)
fun removeDrmLicense(audiobook: AudioPlayable)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit d531ce3

Please sign in to comment.