diff --git a/core/api/current.txt b/core/api/current.txt index b0a7a0e60dcf3..10b69716dc4cf 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -21634,6 +21634,7 @@ package android.media { method @android.media.MediaDrm.HdcpLevel public int getConnectedHdcpLevel(); method public android.media.MediaDrm.CryptoSession getCryptoSession(@NonNull byte[], @NonNull String, @NonNull String); method @NonNull public android.media.MediaDrm.KeyRequest getKeyRequest(@NonNull byte[], @Nullable byte[], @Nullable String, int, @Nullable java.util.HashMap) throws android.media.NotProvisionedException; + method @NonNull public java.util.List getLogMessages(); method @android.media.MediaDrm.HdcpLevel public int getMaxHdcpLevel(); method public static int getMaxSecurityLevel(); method public int getMaxSessionCount(); @@ -21742,6 +21743,12 @@ package android.media { field public static final int STATUS_USABLE_IN_FUTURE = 5; // 0x5 } + public static class MediaDrm.LogMessage { + field @NonNull public final String message; + field public final int priority; + field public final long timestampMillis; + } + public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException { method @NonNull public String getDiagnosticInfo(); } diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 49f9d66123418..adb8a54c01673 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -38,6 +38,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; +import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; @@ -2493,4 +2494,80 @@ public final class MediaDrm implements AutoCloseable { return mPlaybackId; } } + + /** + * Returns recent {@link LogMessage LogMessages} associated with this {@link MediaDrm} + * instance. + */ + @NonNull + public native List getLogMessages(); + + /** + * A {@link LogMessage} records an event in the {@link MediaDrm} framework + * or vendor plugin. + */ + public static class LogMessage { + + /** + * Timing of the recorded event measured in milliseconds since the Epoch, + * 1970-01-01 00:00:00 +0000 (UTC). + */ + public final long timestampMillis; + + /** + * Priority of the recorded event. + *

+ * Possible priority constants are defined in {@link Log}, e.g.: + *

    + *
  • {@link Log#ASSERT}
  • + *
  • {@link Log#ERROR}
  • + *
  • {@link Log#WARN}
  • + *
  • {@link Log#INFO}
  • + *
  • {@link Log#DEBUG}
  • + *
  • {@link Log#VERBOSE}
  • + *
+ */ + @Log.Level + public final int priority; + + /** + * Description of the recorded event. + */ + @NonNull + public final String message; + + private LogMessage(long timestampMillis, int priority, String message) { + this.timestampMillis = timestampMillis; + if (priority < Log.VERBOSE || priority > Log.ASSERT) { + throw new IllegalArgumentException("invalid log priority " + priority); + } + this.priority = priority; + this.message = message; + } + + private char logPriorityChar() { + switch (priority) { + case Log.VERBOSE: + return 'V'; + case Log.DEBUG: + return 'D'; + case Log.INFO: + return 'I'; + case Log.WARN: + return 'W'; + case Log.ERROR: + return 'E'; + case Log.ASSERT: + return 'F'; + default: + } + return 'U'; + } + + @Override + public String toString() { + return String.format("LogMessage{%s %c %s}", + Instant.ofEpochMilli(timestampMillis), logPriorityChar(), message); + } + } } diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 4972529eb705b..6160b81522954 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -64,6 +64,7 @@ cc_library_shared { "android.hardware.cas@1.0", "android.hardware.cas.native@1.0", "android.hardware.drm@1.3", + "android.hardware.drm@1.4", "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 0e8719eeb79c2..22c3572e963b0 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -37,6 +37,7 @@ #include #include #include +#include using ::android::os::PersistableBundle; namespace drm = ::android::hardware::drm; @@ -187,6 +188,11 @@ struct KeyStatusFields { jclass classId; }; +struct LogMessageFields { + jmethodID init; + jclass classId; +}; + struct fields_t { jfieldID context; jmethodID post_event; @@ -208,6 +214,7 @@ struct fields_t { jmethodID createFromParcelId; jclass parcelCreatorClassId; KeyStatusFields keyStatus; + LogMessageFields logMessage; }; static fields_t gFields; @@ -224,6 +231,19 @@ jbyteArray hidlVectorToJByteArray(const hardware::hidl_vec &vector) { return result; } +jobject hidlLogMessagesToJavaList(JNIEnv *env, const Vector &logs) { + jclass clazz = gFields.arraylistClassId; + jobject arrayList = env->NewObject(clazz, gFields.arraylist.init); + clazz = gFields.logMessage.classId; + for (auto log: logs) { + jobject jLog = env->NewObject(clazz, gFields.logMessage.init, + static_cast(log.timeMs), + static_cast(log.priority), + env->NewStringUTF(log.message.c_str())); + env->CallBooleanMethod(arrayList, gFields.arraylist.add, jLog); + } + return arrayList; +} } // namespace anonymous // ---------------------------------------------------------------------------- @@ -907,6 +927,10 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { FIND_CLASS(clazz, "android/media/MediaDrm$KeyStatus"); gFields.keyStatus.classId = static_cast(env->NewGlobalRef(clazz)); GET_METHOD_ID(gFields.keyStatus.init, clazz, "", "([BI)V"); + + FIND_CLASS(clazz, "android/media/MediaDrm$LogMessage"); + gFields.logMessage.classId = static_cast(env->NewGlobalRef(clazz)); + GET_METHOD_ID(gFields.logMessage.init, clazz, "", "(JILjava/lang/String;)V"); } static void android_media_MediaDrm_native_setup( @@ -1996,6 +2020,22 @@ static void android_media_MediaDrm_setPlaybackId( throwExceptionAsNecessary(env, err, "Failed to set playbackId"); } +static jobject android_media_MediaDrm_getLogMessages( + JNIEnv *env, jobject thiz) { + sp drm = GetDrm(env, thiz); + if (!CheckDrm(env, drm)) { + return NULL; + } + + Vector logs; + status_t err = drm->getLogMessages(logs); + ALOGI("drm->getLogMessages %zu logs", logs.size()); + if (throwExceptionAsNecessary(env, err, "Failed to get log messages")) { + return NULL; + } + return hidlLogMessagesToJavaList(env, logs); +} + static const JNINativeMethod gMethods[] = { { "native_release", "()V", (void *)android_media_MediaDrm_native_release }, @@ -2123,6 +2163,9 @@ static const JNINativeMethod gMethods[] = { { "setPlaybackId", "([BLjava/lang/String;)V", (void *)android_media_MediaDrm_setPlaybackId }, + + { "getLogMessages", "()Ljava/util/List;", + (void *)android_media_MediaDrm_getLogMessages }, }; int register_android_media_Drm(JNIEnv *env) {