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
App — openSession()
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
| Exception | Meaning | Action |
|---|---|---|
NotProvisionedException | Device cert / per-origin cert missing | Run provisioning, retry |
DeniedByServerException | Server rejected the request | Check entitlement / token; surface error to user |
MediaDrmStateException | Session in wrong state (closed, no keys) | Open new session, re-request |
UnsupportedSchemeException | Widevine UUID not supported on this device | Fail gracefully; device cannot play Widevine |
ResourceBusyException | No session slots available | Free a session and retry; check for session leaks |
Memorise NotProvisionedException and the corrective flow (getProvisionRequest → POST → provideProvisionResponse). It's a frequent exam target.