summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java195
1 files changed, 195 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
new file mode 100644
index 0000000000..baa5bf0916
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mozilla.thirdparty.com.google.android.exoplayer2.drm;
+
+import android.annotation.TargetApi;
+import android.net.Uri;
+import android.text.TextUtils;
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.upstream.DataSourceInputStream;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.upstream.DataSpec;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.upstream.HttpDataSource;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Util;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A {@link MediaDrmCallback} that makes requests using {@link HttpDataSource} instances.
+ */
+@TargetApi(18)
+public final class HttpMediaDrmCallback implements MediaDrmCallback {
+
+ private static final int MAX_MANUAL_REDIRECTS = 5;
+
+ private final HttpDataSource.Factory dataSourceFactory;
+ private final String defaultLicenseUrl;
+ private final boolean forceDefaultLicenseUrl;
+ private final Map<String, String> keyRequestProperties;
+
+ /**
+ * @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
+ * their own license URL.
+ * @param dataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
+ */
+ public HttpMediaDrmCallback(String defaultLicenseUrl, HttpDataSource.Factory dataSourceFactory) {
+ this(defaultLicenseUrl, false, dataSourceFactory);
+ }
+
+ /**
+ * @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
+ * their own license URL, or for all key requests if {@code forceDefaultLicenseUrl} is
+ * set to true.
+ * @param forceDefaultLicenseUrl Whether to use {@code defaultLicenseUrl} for key requests that
+ * include their own license URL.
+ * @param dataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
+ */
+ public HttpMediaDrmCallback(String defaultLicenseUrl, boolean forceDefaultLicenseUrl,
+ HttpDataSource.Factory dataSourceFactory) {
+ this.dataSourceFactory = dataSourceFactory;
+ this.defaultLicenseUrl = defaultLicenseUrl;
+ this.forceDefaultLicenseUrl = forceDefaultLicenseUrl;
+ this.keyRequestProperties = new HashMap<>();
+ }
+
+ /**
+ * Sets a header for key requests made by the callback.
+ *
+ * @param name The name of the header field.
+ * @param value The value of the field.
+ */
+ public void setKeyRequestProperty(String name, String value) {
+ Assertions.checkNotNull(name);
+ Assertions.checkNotNull(value);
+ synchronized (keyRequestProperties) {
+ keyRequestProperties.put(name, value);
+ }
+ }
+
+ /**
+ * Clears a header for key requests made by the callback.
+ *
+ * @param name The name of the header field.
+ */
+ public void clearKeyRequestProperty(String name) {
+ Assertions.checkNotNull(name);
+ synchronized (keyRequestProperties) {
+ keyRequestProperties.remove(name);
+ }
+ }
+
+ /**
+ * Clears all headers for key requests made by the callback.
+ */
+ public void clearAllKeyRequestProperties() {
+ synchronized (keyRequestProperties) {
+ keyRequestProperties.clear();
+ }
+ }
+
+ @Override
+ public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException {
+ String url =
+ request.getDefaultUrl() + "&signedRequest=" + Util.fromUtf8Bytes(request.getData());
+ return executePost(dataSourceFactory, url, /* httpBody= */ null, /* requestProperties= */ null);
+ }
+
+ @Override
+ public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws Exception {
+ String url = request.getLicenseServerUrl();
+ if (forceDefaultLicenseUrl || TextUtils.isEmpty(url)) {
+ url = defaultLicenseUrl;
+ }
+ Map<String, String> requestProperties = new HashMap<>();
+ // Add standard request properties for supported schemes.
+ String contentType = C.PLAYREADY_UUID.equals(uuid) ? "text/xml"
+ : (C.CLEARKEY_UUID.equals(uuid) ? "application/json" : "application/octet-stream");
+ requestProperties.put("Content-Type", contentType);
+ if (C.PLAYREADY_UUID.equals(uuid)) {
+ requestProperties.put("SOAPAction",
+ "http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense");
+ }
+ // Add additional request properties.
+ synchronized (keyRequestProperties) {
+ requestProperties.putAll(keyRequestProperties);
+ }
+ return executePost(dataSourceFactory, url, request.getData(), requestProperties);
+ }
+
+ private static byte[] executePost(
+ HttpDataSource.Factory dataSourceFactory,
+ String url,
+ @Nullable byte[] httpBody,
+ @Nullable Map<String, String> requestProperties)
+ throws IOException {
+ HttpDataSource dataSource = dataSourceFactory.createDataSource();
+ if (requestProperties != null) {
+ for (Map.Entry<String, String> requestProperty : requestProperties.entrySet()) {
+ dataSource.setRequestProperty(requestProperty.getKey(), requestProperty.getValue());
+ }
+ }
+
+ int manualRedirectCount = 0;
+ while (true) {
+ DataSpec dataSpec =
+ new DataSpec(
+ Uri.parse(url),
+ DataSpec.HTTP_METHOD_POST,
+ httpBody,
+ /* absoluteStreamPosition= */ 0,
+ /* position= */ 0,
+ /* length= */ C.LENGTH_UNSET,
+ /* key= */ null,
+ DataSpec.FLAG_ALLOW_GZIP);
+ DataSourceInputStream inputStream = new DataSourceInputStream(dataSource, dataSpec);
+ try {
+ return Util.toByteArray(inputStream);
+ } catch (InvalidResponseCodeException e) {
+ // For POST requests, the underlying network stack will not normally follow 307 or 308
+ // redirects automatically. Do so manually here.
+ boolean manuallyRedirect =
+ (e.responseCode == 307 || e.responseCode == 308)
+ && manualRedirectCount++ < MAX_MANUAL_REDIRECTS;
+ String redirectUrl = manuallyRedirect ? getRedirectUrl(e) : null;
+ if (redirectUrl == null) {
+ throw e;
+ }
+ url = redirectUrl;
+ } finally {
+ Util.closeQuietly(inputStream);
+ }
+ }
+ }
+
+ private static @Nullable String getRedirectUrl(InvalidResponseCodeException exception) {
+ Map<String, List<String>> headerFields = exception.headerFields;
+ if (headerFields != null) {
+ List<String> locationHeaders = headerFields.get("Location");
+ if (locationHeaders != null && !locationHeaders.isEmpty()) {
+ return locationHeaders.get(0);
+ }
+ }
+ return null;
+ }
+
+}