0
# DRM Support
1
2
ExoPlayer provides comprehensive Digital Rights Management (DRM) support for protected content playback. The DRM system handles key acquisition, session management, and secure decoding for various DRM schemes including Widevine, PlayReady, and ClearKey.
3
4
## DrmSessionManager Interface
5
6
The core interface for managing DRM sessions and cryptographic operations.
7
8
```java { .api }
9
public interface DrmSessionManager {
10
/**
11
* Prepares the DRM session manager.
12
*/
13
void prepare();
14
15
/**
16
* Returns the crypto type for the given format.
17
*
18
* @param format The format
19
* @return The crypto type, or C.CRYPTO_TYPE_NONE if not encrypted
20
*/
21
@C.CryptoType int getCryptoType(Format format);
22
23
/**
24
* Acquires a DRM session for the given DRM init data.
25
*
26
* @param playbackLooper The playback looper
27
* @param drmInitData The DRM initialization data
28
* @return The DRM session
29
*/
30
@Nullable DrmSession acquireSession(@Nullable Looper playbackLooper, @Nullable DrmInitData drmInitData);
31
32
/**
33
* Releases a DRM session.
34
*
35
* @param drmSession The session to release
36
*/
37
void releaseSession(@Nullable DrmSession drmSession);
38
39
/**
40
* Sets the DRM mode and offline license key set ID.
41
*
42
* @param mode The DRM mode
43
* @param offlineLicenseKeySetId The offline license key set ID, or null
44
*/
45
void setMode(@C.CryptoMode int mode, @Nullable byte[] offlineLicenseKeySetId);
46
47
/**
48
* Sets the player using this DRM session manager.
49
*
50
* @param playbackLooper The playback looper
51
* @param playerId The player ID
52
*/
53
void setPlayer(@Nullable Looper playbackLooper, @Nullable PlayerId playerId);
54
}
55
```
56
57
## DefaultDrmSessionManager
58
59
The default implementation of DrmSessionManager supporting common DRM schemes.
60
61
```java { .api }
62
public final class DefaultDrmSessionManager implements DrmSessionManager {
63
/**
64
* Builder for DefaultDrmSessionManager.
65
*/
66
public static final class Builder {
67
/**
68
* Creates a builder.
69
*/
70
public Builder();
71
72
/**
73
* Sets the UUID of the DRM scheme.
74
*
75
* @param uuid The DRM scheme UUID
76
* @return This builder
77
*/
78
public Builder setUuidAndExoMediaDrmProvider(UUID uuid, ExoMediaDrmProvider exoMediaDrmProvider);
79
80
/**
81
* Sets whether to use multiple sessions.
82
*
83
* @param multiSession Whether to use multiple sessions
84
* @return This builder
85
*/
86
public Builder setMultiSession(boolean multiSession);
87
88
/**
89
* Sets whether to play clear samples without keys.
90
*
91
* @param playClearSamplesWithoutKeys Whether to play clear samples without keys
92
* @return This builder
93
*/
94
public Builder setPlayClearSamplesWithoutKeys(boolean playClearSamplesWithoutKeys);
95
96
/**
97
* Sets the session keepalive timeout.
98
*
99
* @param useDrmSessionsForClearContentTrackTypes The track types
100
* @return This builder
101
*/
102
public Builder setUseDrmSessionsForClearContent(int... useDrmSessionsForClearContentTrackTypes);
103
104
/**
105
* Sets the key request parameters.
106
*
107
* @param keyRequestParameters The key request parameters
108
* @return This builder
109
*/
110
public Builder setKeyRequestParameters(@Nullable Map<String, String> keyRequestParameters);
111
112
/**
113
* Sets the load error handling policy.
114
*
115
* @param loadErrorHandlingPolicy The load error handling policy
116
* @return This builder
117
*/
118
public Builder setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy loadErrorHandlingPolicy);
119
120
/**
121
* Builds the DefaultDrmSessionManager.
122
*
123
* @param httpDataSource The HTTP data source for license requests
124
* @return The built DefaultDrmSessionManager
125
*/
126
public DefaultDrmSessionManager build(HttpDataSource.Factory httpDataSource);
127
}
128
129
/**
130
* Sets the mode and offline license key set ID.
131
*
132
* @param mode The DRM mode
133
* @param offlineLicenseKeySetId The offline license key set ID
134
*/
135
@Override
136
public void setMode(@C.CryptoMode int mode, @Nullable byte[] offlineLicenseKeySetId);
137
138
/**
139
* Sets the player.
140
*
141
* @param playbackLooper The playback looper
142
* @param playerId The player ID
143
*/
144
@Override
145
public void setPlayer(@Nullable Looper playbackLooper, @Nullable PlayerId playerId);
146
147
/**
148
* Prepares the DRM session manager.
149
*/
150
@Override
151
public void prepare();
152
153
/**
154
* Releases the DRM session manager.
155
*/
156
public void release();
157
}
158
```
159
160
## DrmSession Interface
161
162
Represents a DRM session that provides cryptographic keys for decryption.
163
164
```java { .api }
165
public interface DrmSession {
166
/**
167
* DRM session states.
168
*/
169
@IntDef({STATE_RELEASED, STATE_ERROR, STATE_OPENING, STATE_OPENED, STATE_OPENED_WITH_KEYS})
170
@interface State {}
171
172
int STATE_RELEASED = 0;
173
int STATE_ERROR = 1;
174
int STATE_OPENING = 2;
175
int STATE_OPENED = 3;
176
int STATE_OPENED_WITH_KEYS = 4;
177
178
/**
179
* Returns the current state of the session.
180
*
181
* @return The session state
182
*/
183
@State int getState();
184
185
/**
186
* Returns whether clear samples can be played.
187
*
188
* @return Whether clear samples can be played
189
*/
190
boolean playClearSamples();
191
192
/**
193
* Returns the session error, if any.
194
*
195
* @return The error, or null if none
196
*/
197
@Nullable DrmSessionException getError();
198
199
/**
200
* Returns the crypto configuration.
201
*
202
* @return The crypto configuration, or null if not available
203
*/
204
@Nullable CryptoConfig getCryptoConfig();
205
206
/**
207
* Returns whether secure decoding is required.
208
*
209
* @param mimeType The MIME type
210
* @return Whether secure decoding is required
211
*/
212
boolean requiresSecureDecoder(String mimeType);
213
214
/**
215
* Acquires a reference to this session.
216
*
217
* @param eventDispatcher The event dispatcher
218
* @return This session
219
*/
220
DrmSession acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher);
221
222
/**
223
* Releases a reference to this session.
224
*
225
* @param eventDispatcher The event dispatcher
226
*/
227
void release(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher);
228
}
229
```
230
231
## DrmSessionEventListener
232
233
Listener for DRM session events.
234
235
```java { .api }
236
public interface DrmSessionEventListener {
237
/**
238
* Called when a DRM session manager error occurs.
239
*
240
* @param error The error
241
*/
242
default void onDrmSessionManagerError(Exception error) {}
243
244
/**
245
* Called when DRM keys are loaded.
246
*/
247
default void onDrmKeysLoaded() {}
248
249
/**
250
* Called when a DRM session is acquired.
251
*/
252
default void onDrmSessionAcquired() {}
253
254
/**
255
* Called when DRM keys are removed.
256
*/
257
default void onDrmKeysRemoved() {}
258
259
/**
260
* Called when DRM keys are restored.
261
*/
262
default void onDrmKeysRestored() {}
263
264
/**
265
* Called when a DRM session is released.
266
*/
267
default void onDrmSessionReleased() {}
268
269
/**
270
* Event dispatcher for DRM session events.
271
*/
272
final class EventDispatcher {
273
/**
274
* Creates an event dispatcher.
275
*
276
* @param eventHandler The event handler
277
* @param eventListener The event listener
278
*/
279
public EventDispatcher(@Nullable Handler eventHandler,
280
@Nullable DrmSessionEventListener eventListener);
281
282
/**
283
* Dispatches a DRM session manager error event.
284
*
285
* @param error The error
286
*/
287
public void drmSessionManagerError(Exception error);
288
289
/**
290
* Dispatches a DRM keys loaded event.
291
*/
292
public void drmKeysLoaded();
293
294
/**
295
* Dispatches a DRM session acquired event.
296
*/
297
public void drmSessionAcquired();
298
299
/**
300
* Dispatches a DRM keys removed event.
301
*/
302
public void drmKeysRemoved();
303
304
/**
305
* Dispatches a DRM keys restored event.
306
*/
307
public void drmKeysRestored();
308
309
/**
310
* Dispatches a DRM session released event.
311
*/
312
public void drmSessionReleased();
313
}
314
}
315
```
316
317
## MediaDrm and ExoMediaDrm
318
319
Interfaces for interacting with the Android MediaDrm system.
320
321
```java { .api }
322
public interface ExoMediaDrm {
323
/**
324
* Exception thrown by ExoMediaDrm operations.
325
*/
326
final class KeyRequest {
327
public final byte[] data;
328
public final String licenseServerUrl;
329
@KeyRequestType public final int requestType;
330
331
public KeyRequest(byte[] data, String licenseServerUrl, int requestType);
332
}
333
334
/**
335
* Exception thrown by ExoMediaDrm operations.
336
*/
337
final class ProvisionRequest {
338
public final byte[] data;
339
public final String defaultUrl;
340
341
public ProvisionRequest(byte[] data, String defaultUrl);
342
}
343
344
/**
345
* Sets the property value for the given property name.
346
*
347
* @param propertyName The property name
348
* @param value The property value
349
*/
350
void setPropertyString(String propertyName, String value);
351
352
/**
353
* Sets the property value for the given property name.
354
*
355
* @param propertyName The property name
356
* @param value The property value
357
*/
358
void setPropertyByteArray(String propertyName, byte[] value);
359
360
/**
361
* Gets the property value for the given property name.
362
*
363
* @param propertyName The property name
364
* @return The property value
365
*/
366
String getPropertyString(String propertyName);
367
368
/**
369
* Gets the property value for the given property name.
370
*
371
* @param propertyName The property name
372
* @return The property value
373
*/
374
byte[] getPropertyByteArray(String propertyName);
375
376
/**
377
* Opens a new DRM session.
378
*
379
* @return The session ID
380
* @throws MediaDrmException If opening the session fails
381
*/
382
byte[] openSession() throws MediaDrmException;
383
384
/**
385
* Closes a DRM session.
386
*
387
* @param sessionId The session ID
388
*/
389
void closeSession(byte[] sessionId);
390
391
/**
392
* Generates a key request.
393
*
394
* @param scope The scope (session ID or key set ID)
395
* @param init The initialization data
396
* @param mimeType The MIME type
397
* @param keyType The key type
398
* @param optionalParameters Optional parameters
399
* @return The key request
400
* @throws MediaDrmException If generating the request fails
401
*/
402
KeyRequest generateKeyRequest(byte[] scope, @Nullable byte[] init, @Nullable String mimeType,
403
@KeyType int keyType, @Nullable HashMap<String, String> optionalParameters)
404
throws MediaDrmException;
405
406
/**
407
* Provides a key response to the DRM session.
408
*
409
* @param scope The scope (session ID or key set ID)
410
* @param response The key response
411
* @return The key set ID for offline keys, or null for streaming keys
412
* @throws MediaDrmException If providing the response fails
413
*/
414
@Nullable byte[] provideKeyResponse(byte[] scope, byte[] response) throws MediaDrmException;
415
}
416
```
417
418
## Usage Examples
419
420
### Basic Widevine DRM Setup
421
422
```java
423
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager;
424
import androidx.media3.exoplayer.drm.FrameworkMediaDrm;
425
import androidx.media3.exoplayer.drm.HttpMediaDrmCallback;
426
import androidx.media3.datasource.DefaultHttpDataSource;
427
import androidx.media3.common.C;
428
import java.util.UUID;
429
430
// Widevine UUID
431
UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
432
433
// Create HTTP data source for license requests
434
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory()
435
.setUserAgent("MyApp/1.0");
436
437
// Create DRM callback for license server communication
438
String licenseUrl = "https://your-license-server.com/license";
439
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, httpDataSourceFactory);
440
441
// Create DRM session manager
442
DefaultDrmSessionManager drmSessionManager = new DefaultDrmSessionManager.Builder()
443
.setUuidAndExoMediaDrmProvider(WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)
444
.build(drmCallback);
445
446
// Use with media source
447
MediaSource mediaSource = new DashMediaSource.Factory(dataSourceFactory)
448
.setDrmSessionManagerProvider(unusedMediaItem -> drmSessionManager)
449
.createMediaSource(MediaItem.fromUri(manifestUri));
450
451
player.setMediaSource(mediaSource);
452
```
453
454
### Custom License Headers and Parameters
455
456
```java
457
// Create DRM callback with custom headers
458
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, httpDataSourceFactory);
459
460
// Add custom headers
461
Map<String, String> licenseHeaders = new HashMap<>();
462
licenseHeaders.put("Authorization", "Bearer " + authToken);
463
licenseHeaders.put("Content-Type", "application/octet-stream");
464
drmCallback.setKeyRequestHeaders(licenseHeaders);
465
466
// Create DRM session manager with custom parameters
467
Map<String, String> keyRequestParameters = new HashMap<>();
468
keyRequestParameters.put("userId", "user123");
469
keyRequestParameters.put("deviceId", "device456");
470
471
DefaultDrmSessionManager drmSessionManager = new DefaultDrmSessionManager.Builder()
472
.setUuidAndExoMediaDrmProvider(WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)
473
.setKeyRequestParameters(keyRequestParameters)
474
.build(drmCallback);
475
```
476
477
### Multi-Session DRM
478
479
```java
480
// Enable multi-session for different DRM keys per track
481
DefaultDrmSessionManager drmSessionManager = new DefaultDrmSessionManager.Builder()
482
.setUuidAndExoMediaDrmProvider(WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)
483
.setMultiSession(true) // Enable multiple sessions
484
.build(drmCallback);
485
```
486
487
### Offline License Management
488
489
```java
490
public class OfflineLicenseHelper {
491
private final DefaultDrmSessionManager drmSessionManager;
492
private final MediaDrmCallback mediaDrmCallback;
493
494
public OfflineLicenseHelper(UUID drmSchemeUuid, ExoMediaDrm.Provider mediaDrmProvider,
495
MediaDrmCallback mediaDrmCallback) {
496
this.mediaDrmCallback = mediaDrmCallback;
497
this.drmSessionManager = new DefaultDrmSessionManager.Builder()
498
.setUuidAndExoMediaDrmProvider(drmSchemeUuid, mediaDrmProvider)
499
.build(mediaDrmCallback);
500
}
501
502
/**
503
* Downloads an offline license.
504
*
505
* @param drmInitData The DRM initialization data
506
* @return The offline license key set ID
507
*/
508
public byte[] downloadLicense(DrmInitData drmInitData) throws DrmSessionException {
509
drmSessionManager.setMode(C.CRYPTO_MODE_AES_CTR, null);
510
drmSessionManager.prepare();
511
512
try {
513
DrmSession drmSession = drmSessionManager.acquireSession(Looper.myLooper(), drmInitData);
514
515
// Wait for keys to be loaded
516
while (drmSession.getState() == DrmSession.STATE_OPENING) {
517
try {
518
Thread.sleep(10);
519
} catch (InterruptedException e) {
520
Thread.currentThread().interrupt();
521
throw new DrmSessionException(e);
522
}
523
}
524
525
if (drmSession.getState() == DrmSession.STATE_ERROR) {
526
throw drmSession.getError();
527
}
528
529
// Get offline license key set ID
530
return getOfflineLicenseKeySetId(drmSession);
531
532
} finally {
533
drmSessionManager.release();
534
}
535
}
536
537
/**
538
* Renews an offline license.
539
*
540
* @param offlineLicenseKeySetId The offline license key set ID
541
* @return The renewed key set ID
542
*/
543
public byte[] renewLicense(byte[] offlineLicenseKeySetId) throws DrmSessionException {
544
drmSessionManager.setMode(C.CRYPTO_MODE_AES_CTR, offlineLicenseKeySetId);
545
drmSessionManager.prepare();
546
547
try {
548
// Renewal logic similar to download
549
return performLicenseRenewal(offlineLicenseKeySetId);
550
} finally {
551
drmSessionManager.release();
552
}
553
}
554
555
/**
556
* Releases an offline license.
557
*
558
* @param offlineLicenseKeySetId The offline license key set ID
559
*/
560
public void releaseLicense(byte[] offlineLicenseKeySetId) {
561
// Implementation to release offline license
562
}
563
564
private byte[] getOfflineLicenseKeySetId(DrmSession drmSession) {
565
// Extract key set ID from DRM session
566
return null; // Implementation specific
567
}
568
569
private byte[] performLicenseRenewal(byte[] keySetId) throws DrmSessionException {
570
// Implementation for license renewal
571
return keySetId;
572
}
573
}
574
```
575
576
### DRM Event Handling
577
578
```java
579
public class DrmEventHandler implements DrmSessionEventListener {
580
private static final String TAG = "DrmEventHandler";
581
582
@Override
583
public void onDrmSessionAcquired() {
584
Log.d(TAG, "DRM session acquired");
585
// Update UI to show DRM is ready
586
showDrmStatus("Protected content ready");
587
}
588
589
@Override
590
public void onDrmKeysLoaded() {
591
Log.d(TAG, "DRM keys loaded successfully");
592
// Start playback now that keys are available
593
startPlayback();
594
}
595
596
@Override
597
public void onDrmSessionManagerError(Exception error) {
598
Log.e(TAG, "DRM session manager error", error);
599
600
// Handle specific DRM errors
601
if (error instanceof UnsupportedDrmException) {
602
showError("DRM scheme not supported on this device");
603
} else if (error instanceof DrmSessionException) {
604
DrmSessionException drmError = (DrmSessionException) error;
605
handleDrmSessionError(drmError);
606
} else {
607
showError("DRM error: " + error.getMessage());
608
}
609
}
610
611
@Override
612
public void onDrmKeysRemoved() {
613
Log.d(TAG, "DRM keys removed");
614
// Handle key removal (e.g., license expired)
615
showError("License expired. Please renew.");
616
}
617
618
@Override
619
public void onDrmKeysRestored() {
620
Log.d(TAG, "DRM keys restored");
621
// Resume playback after key restoration
622
resumePlayback();
623
}
624
625
@Override
626
public void onDrmSessionReleased() {
627
Log.d(TAG, "DRM session released");
628
// Clean up DRM-related resources
629
cleanupDrmResources();
630
}
631
632
private void handleDrmSessionError(DrmSessionException error) {
633
// Handle specific DRM session errors
634
switch (error.reason) {
635
case DrmSessionException.ERROR_PROVISIONING_FAILED:
636
showError("Device provisioning failed");
637
break;
638
case DrmSessionException.ERROR_LICENSE_ACQUISITION_FAILED:
639
showError("Failed to acquire license");
640
break;
641
case DrmSessionException.ERROR_PROVISIONING_CONFIG_CHANGED:
642
showError("Provisioning configuration changed");
643
break;
644
default:
645
showError("DRM session error: " + error.getMessage());
646
}
647
}
648
649
private void showDrmStatus(String message) {
650
// Update UI with DRM status
651
}
652
653
private void showError(String message) {
654
// Display error to user
655
}
656
657
private void startPlaybook() {
658
// Start media playback
659
}
660
661
private void resumePlayback() {
662
// Resume media playback
663
}
664
665
private void cleanupDrmResources() {
666
// Clean up DRM-related resources
667
}
668
}
669
670
// Use the event handler
671
DrmEventHandler drmEventHandler = new DrmEventHandler();
672
DrmSessionEventListener.EventDispatcher eventDispatcher =
673
new DrmSessionEventListener.EventDispatcher(handler, drmEventHandler);
674
```
675
676
### ClearKey DRM (for testing)
677
678
```java
679
// ClearKey UUID (for testing purposes)
680
UUID CLEARKEY_UUID = new UUID(0x1077EFECC0B24D02L, 0xACE33C1E52E2FB4BL);
681
682
// Create ClearKey DRM session manager (no license server needed)
683
DefaultDrmSessionManager clearKeyDrmManager = new DefaultDrmSessionManager.Builder()
684
.setUuidAndExoMediaDrmProvider(CLEARKEY_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)
685
.build(new LocalMediaDrmCallback("{\"keys\":[{\"kty\":\"oct\",\"k\":\"base64-key\",\"kid\":\"base64-key-id\"}]}".getBytes()));
686
687
// Use for testing DRM functionality without a license server
688
```
689
690
### Custom DRM Configuration
691
692
```java
693
public class CustomDrmConfiguration {
694
695
public static DefaultDrmSessionManager createCustomDrmManager(Context context, String licenseUrl) {
696
// Create custom HTTP data source with timeout and retry configuration
697
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory()
698
.setUserAgent("MyApp/1.0")
699
.setConnectTimeoutMs(10000) // 10 second connect timeout
700
.setReadTimeoutMs(30000); // 30 second read timeout
701
702
// Create DRM callback with custom configuration
703
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, httpDataSourceFactory);
704
705
// Set custom request properties
706
Map<String, String> requestProperties = new HashMap<>();
707
requestProperties.put("Custom-Header", "CustomValue");
708
drmCallback.setKeyRequestHeaders(requestProperties);
709
710
// Create load error handling policy for DRM requests
711
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() {
712
@Override
713
public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
714
// Custom retry delay for DRM errors
715
return 2000; // 2 second delay between retries
716
}
717
718
@Override
719
public int getMinimumLoadableRetryCount(int dataType) {
720
// More retries for DRM requests
721
return dataType == C.DATA_TYPE_DRM ? 5 : super.getMinimumLoadableRetryCount(dataType);
722
}
723
};
724
725
return new DefaultDrmSessionManager.Builder()
726
.setUuidAndExoMediaDrmProvider(WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)
727
.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
728
.setPlayClearSamplesWithoutKeys(false) // Require keys for all samples
729
.setMultiSession(true) // Support multiple DRM sessions
730
.build(drmCallback);
731
}
732
}
733
```