1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.drm; 17 18 import android.media.DeniedByServerException; 19 import android.media.MediaCryptoException; 20 import android.media.MediaDrm; 21 import android.media.MediaDrmException; 22 import android.media.NotProvisionedException; 23 import android.os.Handler; 24 import android.os.PersistableBundle; 25 import androidx.annotation.Nullable; 26 import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.UUID; 31 32 /** 33 * Used to obtain keys for decrypting protected media streams. See {@link android.media.MediaDrm}. 34 * 35 * <h3>Reference counting</h3> 36 * 37 * <p>Access to an instance is managed by reference counting, where {@link #acquire()} increments 38 * the reference count and {@link #release()} decrements it. When the reference count drops to 0 39 * underlying resources are released, and the instance cannot be re-used. 40 * 41 * <p>Each new instance has an initial reference count of 1. Hence application code that creates a 42 * new instance does not normally need to call {@link #acquire()}, and must call {@link #release()} 43 * when the instance is no longer required. 44 */ 45 public interface ExoMediaDrm { 46 47 /** {@link ExoMediaDrm} instances provider. */ 48 interface Provider { 49 50 /** 51 * Returns an {@link ExoMediaDrm} instance with an incremented reference count. When the caller 52 * no longer needs to use the instance, it must call {@link ExoMediaDrm#release()} to decrement 53 * the reference count. 54 */ acquireExoMediaDrm(UUID uuid)55 ExoMediaDrm acquireExoMediaDrm(UUID uuid); 56 } 57 58 /** 59 * Provides an {@link ExoMediaDrm} instance owned by the app. 60 * 61 * <p>Note that when using this provider the app will have instantiated the {@link ExoMediaDrm} 62 * instance, and remains responsible for calling {@link ExoMediaDrm#release()} on the instance 63 * when it's no longer being used. 64 */ 65 final class AppManagedProvider implements Provider { 66 67 private final ExoMediaDrm exoMediaDrm; 68 69 /** Creates an instance that provides the given {@link ExoMediaDrm}. */ AppManagedProvider(ExoMediaDrm exoMediaDrm)70 public AppManagedProvider(ExoMediaDrm exoMediaDrm) { 71 this.exoMediaDrm = exoMediaDrm; 72 } 73 74 @Override acquireExoMediaDrm(UUID uuid)75 public ExoMediaDrm acquireExoMediaDrm(UUID uuid) { 76 exoMediaDrm.acquire(); 77 return exoMediaDrm; 78 } 79 } 80 81 /** @see MediaDrm#EVENT_KEY_REQUIRED */ 82 @SuppressWarnings("InlinedApi") 83 int EVENT_KEY_REQUIRED = MediaDrm.EVENT_KEY_REQUIRED; 84 /** 85 * @see MediaDrm#EVENT_KEY_EXPIRED 86 */ 87 @SuppressWarnings("InlinedApi") 88 int EVENT_KEY_EXPIRED = MediaDrm.EVENT_KEY_EXPIRED; 89 /** 90 * @see MediaDrm#EVENT_PROVISION_REQUIRED 91 */ 92 @SuppressWarnings("InlinedApi") 93 int EVENT_PROVISION_REQUIRED = MediaDrm.EVENT_PROVISION_REQUIRED; 94 95 /** 96 * @see MediaDrm#KEY_TYPE_STREAMING 97 */ 98 @SuppressWarnings("InlinedApi") 99 int KEY_TYPE_STREAMING = MediaDrm.KEY_TYPE_STREAMING; 100 /** 101 * @see MediaDrm#KEY_TYPE_OFFLINE 102 */ 103 @SuppressWarnings("InlinedApi") 104 int KEY_TYPE_OFFLINE = MediaDrm.KEY_TYPE_OFFLINE; 105 /** 106 * @see MediaDrm#KEY_TYPE_RELEASE 107 */ 108 @SuppressWarnings("InlinedApi") 109 int KEY_TYPE_RELEASE = MediaDrm.KEY_TYPE_RELEASE; 110 111 /** @see android.media.MediaDrm.OnEventListener */ 112 interface OnEventListener { 113 /** 114 * Called when an event occurs that requires the app to be notified 115 * 116 * @param mediaDrm The {@link ExoMediaDrm} object on which the event occurred. 117 * @param sessionId The DRM session ID on which the event occurred. 118 * @param event Indicates the event type. 119 * @param extra A secondary error code. 120 * @param data Optional byte array of data that may be associated with the event. 121 */ onEvent( ExoMediaDrm mediaDrm, @Nullable byte[] sessionId, int event, int extra, @Nullable byte[] data)122 void onEvent( 123 ExoMediaDrm mediaDrm, 124 @Nullable byte[] sessionId, 125 int event, 126 int extra, 127 @Nullable byte[] data); 128 } 129 130 /** @see android.media.MediaDrm.OnKeyStatusChangeListener */ 131 interface OnKeyStatusChangeListener { 132 /** 133 * Called when the keys in a session change status, such as when the license is renewed or 134 * expires. 135 * 136 * @param mediaDrm The {@link ExoMediaDrm} object on which the event occurred. 137 * @param sessionId The DRM session ID on which the event occurred. 138 * @param exoKeyInformation A list of {@link KeyStatus} that contains key ID and status. 139 * @param hasNewUsableKey Whether a new key became usable. 140 */ onKeyStatusChange( ExoMediaDrm mediaDrm, byte[] sessionId, List<KeyStatus> exoKeyInformation, boolean hasNewUsableKey)141 void onKeyStatusChange( 142 ExoMediaDrm mediaDrm, 143 byte[] sessionId, 144 List<KeyStatus> exoKeyInformation, 145 boolean hasNewUsableKey); 146 } 147 148 /** @see android.media.MediaDrm.OnExpirationUpdateListener */ 149 interface OnExpirationUpdateListener { 150 151 /** 152 * Called when a session expiration update occurs, to inform the app about the change in 153 * expiration time 154 * 155 * @param mediaDrm The {@link ExoMediaDrm} object on which the event occurred. 156 * @param sessionId The DRM session ID on which the event occurred 157 * @param expirationTimeMs The new expiration time for the keys in the session. The time is in 158 * milliseconds, relative to the Unix epoch. A time of 0 indicates that the keys never 159 * expire. 160 */ onExpirationUpdate(ExoMediaDrm mediaDrm, byte[] sessionId, long expirationTimeMs)161 void onExpirationUpdate(ExoMediaDrm mediaDrm, byte[] sessionId, long expirationTimeMs); 162 } 163 164 /** @see android.media.MediaDrm.KeyStatus */ 165 final class KeyStatus { 166 167 private final int statusCode; 168 private final byte[] keyId; 169 KeyStatus(int statusCode, byte[] keyId)170 public KeyStatus(int statusCode, byte[] keyId) { 171 this.statusCode = statusCode; 172 this.keyId = keyId; 173 } 174 getStatusCode()175 public int getStatusCode() { 176 return statusCode; 177 } 178 getKeyId()179 public byte[] getKeyId() { 180 return keyId; 181 } 182 183 } 184 185 /** @see android.media.MediaDrm.KeyRequest */ 186 final class KeyRequest { 187 188 private final byte[] data; 189 private final String licenseServerUrl; 190 KeyRequest(byte[] data, String licenseServerUrl)191 public KeyRequest(byte[] data, String licenseServerUrl) { 192 this.data = data; 193 this.licenseServerUrl = licenseServerUrl; 194 } 195 getData()196 public byte[] getData() { 197 return data; 198 } 199 getLicenseServerUrl()200 public String getLicenseServerUrl() { 201 return licenseServerUrl; 202 } 203 204 } 205 206 /** @see android.media.MediaDrm.ProvisionRequest */ 207 final class ProvisionRequest { 208 209 private final byte[] data; 210 private final String defaultUrl; 211 ProvisionRequest(byte[] data, String defaultUrl)212 public ProvisionRequest(byte[] data, String defaultUrl) { 213 this.data = data; 214 this.defaultUrl = defaultUrl; 215 } 216 getData()217 public byte[] getData() { 218 return data; 219 } 220 getDefaultUrl()221 public String getDefaultUrl() { 222 return defaultUrl; 223 } 224 225 } 226 227 /** 228 * Sets the listener for DRM events. 229 * 230 * <p>This is an optional method, and some implementations may only support it on certain Android 231 * API levels. 232 * 233 * @param listener The listener to receive events, or {@code null} to stop receiving events. 234 * @throws UnsupportedOperationException if the implementation doesn't support this method. 235 * @see MediaDrm#setOnEventListener(MediaDrm.OnEventListener) 236 */ setOnEventListener(@ullable OnEventListener listener)237 void setOnEventListener(@Nullable OnEventListener listener); 238 239 /** 240 * Sets the listener for key status change events. 241 * 242 * <p>This is an optional method, and some implementations may only support it on certain Android 243 * API levels. 244 * 245 * @param listener The listener to receive events, or {@code null} to stop receiving events. 246 * @throws UnsupportedOperationException if the implementation doesn't support this method. 247 * @see MediaDrm#setOnKeyStatusChangeListener(MediaDrm.OnKeyStatusChangeListener, Handler) 248 */ setOnKeyStatusChangeListener(@ullable OnKeyStatusChangeListener listener)249 void setOnKeyStatusChangeListener(@Nullable OnKeyStatusChangeListener listener); 250 251 /** 252 * Sets the listener for session expiration events. 253 * 254 * <p>This is an optional method, and some implementations may only support it on certain Android 255 * API levels. 256 * 257 * @param listener The listener to receive events, or {@code null} to stop receiving events. 258 * @throws UnsupportedOperationException if the implementation doesn't support this method. 259 * @see MediaDrm#setOnExpirationUpdateListener(MediaDrm.OnExpirationUpdateListener, Handler) 260 */ setOnExpirationUpdateListener(@ullable OnExpirationUpdateListener listener)261 void setOnExpirationUpdateListener(@Nullable OnExpirationUpdateListener listener); 262 263 /** @see MediaDrm#openSession() */ openSession()264 byte[] openSession() throws MediaDrmException; 265 266 /** 267 * @see MediaDrm#closeSession(byte[]) 268 */ closeSession(byte[] sessionId)269 void closeSession(byte[] sessionId); 270 271 /** 272 * Generates a key request. 273 * 274 * @param scope If {@code keyType} is {@link #KEY_TYPE_STREAMING} or {@link #KEY_TYPE_OFFLINE}, 275 * the session id that the keys will be provided to. If {@code keyType} is {@link 276 * #KEY_TYPE_RELEASE}, the keySetId of the keys to release. 277 * @param schemeDatas If key type is {@link #KEY_TYPE_STREAMING} or {@link #KEY_TYPE_OFFLINE}, a 278 * list of {@link SchemeData} instances extracted from the media. Null otherwise. 279 * @param keyType The type of the request. Either {@link #KEY_TYPE_STREAMING} to acquire keys for 280 * streaming, {@link #KEY_TYPE_OFFLINE} to acquire keys for offline usage, or {@link 281 * #KEY_TYPE_RELEASE} to release acquired keys. Releasing keys invalidates them for all 282 * sessions. 283 * @param optionalParameters Are included in the key request message to allow a client application 284 * to provide additional message parameters to the server. This may be {@code null} if no 285 * additional parameters are to be sent. 286 * @return The generated key request. 287 * @see MediaDrm#getKeyRequest(byte[], byte[], String, int, HashMap) 288 */ getKeyRequest( byte[] scope, @Nullable List<SchemeData> schemeDatas, int keyType, @Nullable HashMap<String, String> optionalParameters)289 KeyRequest getKeyRequest( 290 byte[] scope, 291 @Nullable List<SchemeData> schemeDatas, 292 int keyType, 293 @Nullable HashMap<String, String> optionalParameters) 294 throws NotProvisionedException; 295 296 /** @see MediaDrm#provideKeyResponse(byte[], byte[]) */ 297 @Nullable provideKeyResponse(byte[] scope, byte[] response)298 byte[] provideKeyResponse(byte[] scope, byte[] response) 299 throws NotProvisionedException, DeniedByServerException; 300 301 /** 302 * @see MediaDrm#getProvisionRequest() 303 */ getProvisionRequest()304 ProvisionRequest getProvisionRequest(); 305 306 /** 307 * @see MediaDrm#provideProvisionResponse(byte[]) 308 */ provideProvisionResponse(byte[] response)309 void provideProvisionResponse(byte[] response) throws DeniedByServerException; 310 311 /** 312 * @see MediaDrm#queryKeyStatus(byte[]) 313 */ queryKeyStatus(byte[] sessionId)314 Map<String, String> queryKeyStatus(byte[] sessionId); 315 316 /** 317 * Increments the reference count. When the caller no longer needs to use the instance, it must 318 * call {@link #release()} to decrement the reference count. 319 * 320 * <p>A new instance will have an initial reference count of 1, and therefore it is not normally 321 * necessary for application code to call this method. 322 */ acquire()323 void acquire(); 324 325 /** 326 * Decrements the reference count. If the reference count drops to 0 underlying resources are 327 * released, and the instance cannot be re-used. 328 */ release()329 void release(); 330 331 /** 332 * @see MediaDrm#restoreKeys(byte[], byte[]) 333 */ restoreKeys(byte[] sessionId, byte[] keySetId)334 void restoreKeys(byte[] sessionId, byte[] keySetId); 335 336 /** 337 * Returns drm metrics. May be null if unavailable. 338 * 339 * @see MediaDrm#getMetrics() 340 */ 341 @Nullable getMetrics()342 PersistableBundle getMetrics(); 343 344 /** 345 * @see MediaDrm#getPropertyString(String) 346 */ getPropertyString(String propertyName)347 String getPropertyString(String propertyName); 348 349 /** 350 * @see MediaDrm#getPropertyByteArray(String) 351 */ getPropertyByteArray(String propertyName)352 byte[] getPropertyByteArray(String propertyName); 353 354 /** 355 * @see MediaDrm#setPropertyString(String, String) 356 */ setPropertyString(String propertyName, String value)357 void setPropertyString(String propertyName, String value); 358 359 /** 360 * @see MediaDrm#setPropertyByteArray(String, byte[]) 361 */ setPropertyByteArray(String propertyName, byte[] value)362 void setPropertyByteArray(String propertyName, byte[] value); 363 364 /** 365 * @see android.media.MediaCrypto#MediaCrypto(UUID, byte[]) 366 * @param sessionId The DRM session ID. 367 * @return An object extends {@link ExoMediaCrypto}, using opaque crypto scheme specific data. 368 * @throws MediaCryptoException If the instance can't be created. 369 */ createMediaCrypto(byte[] sessionId)370 ExoMediaCrypto createMediaCrypto(byte[] sessionId) throws MediaCryptoException; 371 372 /** 373 * Returns the {@link ExoMediaCrypto} type created by {@link #createMediaCrypto(byte[])}, or null 374 * if this instance cannot create any {@link ExoMediaCrypto} instances. 375 */ 376 @Nullable getExoMediaCryptoType()377 Class<? extends ExoMediaCrypto> getExoMediaCryptoType(); 378 } 379