MediaDrm: enumerate supported schemes

Bug: 139134043
Test: MediaDrmTest
Change-Id: If6f8c8afb7f96a424556638d6909ff0396f0612e
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 63657a6..6523e30 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -36,9 +36,12 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
@@ -203,6 +206,16 @@
                 securityLevel);
     }
 
+    /**
+     * @return list of crypto schemes (as {@link UUID}s) for which
+     * {@link #isCryptoSchemeSupported(UUID)} returns true; each {@link UUID}
+     * can be used as input to create {@link MediaDrm} objects via {@link #MediaDrm(UUID)}.
+     */
+    public static final @NonNull List<UUID> getSupportedCryptoSchemes(){
+        byte[] uuidBytes = getSupportedCryptoSchemesNative();
+        return getUUIDsFromByteArray(uuidBytes);
+    }
+
     private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
         long msb = uuid.getMostSignificantBits();
         long lsb = uuid.getLeastSignificantBits();
@@ -216,6 +229,28 @@
         return uuidBytes;
     }
 
+    private static final UUID getUUIDFromByteArray(@NonNull byte[] uuidBytes, int off) {
+        long msb = 0;
+        long lsb = 0;
+
+        for (int i = 0; i < 8; ++i) {
+            msb = (msb << 8) | (0xffl & uuidBytes[off + i]);
+            lsb = (lsb << 8) | (0xffl & uuidBytes[off + i + 8]);
+        }
+
+        return new UUID(msb, lsb);
+    }
+
+    private static final List<UUID> getUUIDsFromByteArray(@NonNull byte[] uuidBytes) {
+        Set<UUID> uuids = new LinkedHashSet<>();
+        for (int off = 0; off < uuidBytes.length; off+=16) {
+            uuids.add(getUUIDFromByteArray(uuidBytes, off));
+        }
+        return new ArrayList<>(uuids);
+    }
+
+    private static final native byte[] getSupportedCryptoSchemesNative();
+
     private static final native boolean isCryptoSchemeSupportedNative(
             @NonNull byte[] uuid, @Nullable String mimeType, @SecurityLevel int securityLevel);
 
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 2f53cbb..3ba8688 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -58,6 +58,7 @@
         "libsonivox",
         "android.hardware.cas@1.0",
         "android.hardware.cas.native@1.0",
+        "android.hardware.drm@1.3",
         "android.hidl.memory@1.0",
         "android.hidl.token@1.0-utils",
     ],
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 3833c6b..ee6bbf4 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -27,6 +27,7 @@
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 
+#include <android/hardware/drm/1.3/IDrmFactory.h>
 #include <binder/Parcel.h>
 #include <binder/PersistableBundle.h>
 #include <cutils/properties.h>
@@ -36,7 +37,7 @@
 #include <mediadrm/IDrm.h>
 
 using ::android::os::PersistableBundle;
-
+namespace drm = ::android::hardware::drm;
 
 namespace android {
 
@@ -969,6 +970,26 @@
     return level;
 }
 
+static jbyteArray android_media_MediaDrm_getSupportedCryptoSchemesNative(JNIEnv *env) {
+    std::vector<uint8_t> bv;
+    for (auto &factory : DrmUtils::MakeDrmFactories()) {
+        sp<drm::V1_3::IDrmFactory> factoryV1_3 = drm::V1_3::IDrmFactory::castFrom(factory);
+        if (factoryV1_3 == nullptr) {
+            continue;
+        }
+        factoryV1_3->getSupportedCryptoSchemes(
+            [&](const hardware::hidl_vec<hardware::hidl_array<uint8_t, 16>>& schemes) {
+                for (const auto &scheme : schemes) {
+                    bv.insert(bv.end(), scheme.data(), scheme.data() + scheme.size());
+                }
+            });
+    }
+
+    jbyteArray jUuidBytes = env->NewByteArray(bv.size());
+    env->SetByteArrayRegion(jUuidBytes, 0, bv.size(), reinterpret_cast<const jbyte *>(bv.data()));
+    return jUuidBytes;
+}
+
 static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
         JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType,
         jint jSecurityLevel) {
@@ -1938,6 +1959,9 @@
     { "native_setup", "(Ljava/lang/Object;[BLjava/lang/String;)V",
       (void *)android_media_MediaDrm_native_setup },
 
+    { "getSupportedCryptoSchemesNative", "()[B",
+      (void *)android_media_MediaDrm_getSupportedCryptoSchemesNative },
+
     { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;I)Z",
       (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },