Day 3 · 25 min read

MediaDrm API

Sessions, key requests, provisioning, properties.

What MediaDrm is for

android.media.MediaDrm is the Android API that lets applications negotiate licences with a DRM system on the device. For Widevine you instantiate it with the Widevine UUID:

private static final UUID WIDEVINE = UUID.fromString(
    "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed");

MediaDrm drm = new MediaDrm(WIDEVINE);

From here you can:

  • Open / close sessions.
  • Build key requests and accept key responses.
  • Drive provisioning when needed.
  • Inspect device properties (security level, version, etc.).
  • Restore previously persisted keys.

The session lifecycle

MediaDrm online session
Step 1 of 7

AppopenSession()

Allocates a session ID inside the CDM. Sessions are scarce on some devices — close them when done.

Key types

public static final int KEY_TYPE_STREAMING = 1;  // online; in-memory
public static final int KEY_TYPE_OFFLINE   = 2;  // persistent; saved keysetId
public static final int KEY_TYPE_RELEASE   = 3;  // release a persistent keyset

For offline:

byte[] keysetId = drm.provideKeyResponse(sessionId, responseBytes);
// store keysetId locally...

// on later replay:
drm.openSession();
drm.restoreKeys(sessionId, keysetId);   // keys are back; no server roundtrip

To release the offline rights (e.g. user finished the rental):

byte[] req = drm.getKeyRequest(
    /* scope */ null, /* initData */ keysetId,
    /* mimeType */ null, KEY_TYPE_RELEASE, /* params */ null
).getData();
// POST req to server...
drm.provideKeyResponse(/* scope */ null, response);

Provisioning

If the device hasn't been provisioned (or the per-origin certificate is missing), getKeyRequest throws NotProvisionedException. Handle it by running provisioning, then retrying:

try {
    return drm.getKeyRequest(sessionId, initData, "video/mp4",
                             MediaDrm.KEY_TYPE_STREAMING, null);
} catch (NotProvisionedException e) {
    ProvisionRequest pr = drm.getProvisionRequest();
    byte[] resp = httpPost(pr.getDefaultUrl() + "&signedRequest=" +
                           Base64.encode(pr.getData(), Base64.URL_SAFE));
    drm.provideProvisionResponse(resp);
    return drm.getKeyRequest(sessionId, initData, "video/mp4",
                             MediaDrm.KEY_TYPE_STREAMING, null);
}

pr.getDefaultUrl() is the Google provisioning service URL — use it directly unless your application has a reason to proxy provisioning through your own infrastructure.

On-event callbacks

The CDM emits asynchronous events through MediaDrm.OnEventListener:

drm.setOnEventListener((md, sessionId, event, extra, data) -> {
    switch (event) {
        case MediaDrm.EVENT_KEY_REQUIRED:
            // CDM needs new keys (renewal). Build a key request, ship it.
            break;
        case MediaDrm.EVENT_KEY_EXPIRED:
            // Existing keys expired without renewal — playback will stall.
            break;
        case MediaDrm.EVENT_PROVISION_REQUIRED:
            // Device not provisioned for this operation.
            break;
        case MediaDrm.EVENT_VENDOR_DEFINED:
            // Widevine-specific extension event.
            break;
    }
});

Most production apps consume these via ExoPlayer's DefaultDrmSessionManager and never write the listener directly.

Key exceptions

ExceptionMeaningAction
NotProvisionedExceptionDevice cert / per-origin cert missingRun provisioning, retry
DeniedByServerExceptionServer rejected the requestCheck entitlement / token; surface error to user
MediaDrmStateExceptionSession in wrong state (closed, no keys)Open new session, re-request
UnsupportedSchemeExceptionWidevine UUID not supported on this deviceFail gracefully; device cannot play Widevine
ResourceBusyExceptionNo session slots availableFree a session and retry; check for session leaks
Exam tip

Memorise NotProvisionedException and the corrective flow (getProvisionRequest → POST → provideProvisionResponse). It's a frequent exam target.

No questions yet for mediadrm. Add some in content/questions/mediadrm.json.