diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 10ab681ef3d1a..bf2e9d9db7fd8 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1095,6 +1095,11 @@ package android.media { method public void forceResourceLost(); } + public final class MediaCodec implements android.media.metrics.PlaybackComponent { + method public String getPlaybackId(); + method public void setPlaybackId(@NonNull String); + } + public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint { ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int, int, @NonNull android.util.Size); ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint, @NonNull android.util.Size); @@ -1103,6 +1108,10 @@ package android.media { method public int getMaxMacroBlocks(); } + public final class MediaDrm implements java.lang.AutoCloseable { + method @Nullable public android.media.metrics.PlaybackComponent getPlaybackComponent(@NonNull byte[]); + } + public final class MediaRoute2Info implements android.os.Parcelable { method @NonNull public String getOriginalId(); } @@ -1165,6 +1174,15 @@ package android.media.audiopolicy { } +package android.media.metrics { + + public interface PlaybackComponent { + method @NonNull public String getPlaybackId(); + method public void setPlaybackId(@NonNull String); + } + +} + package android.media.tv { public final class TvInputManager { diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 2703ee35b0995..49f9d66123418 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -21,8 +21,10 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.TestApi; import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; +import android.media.metrics.PlaybackComponent; import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; @@ -35,8 +37,8 @@ import dalvik.system.CloseGuard; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; +import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -49,7 +51,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Function; - /** * MediaDrm can be used to obtain keys for decrypting protected media streams, in * conjunction with {@link android.media.MediaCrypto}. The MediaDrm APIs @@ -963,14 +964,30 @@ public final class MediaDrm implements AutoCloseable { * a session */ @NonNull - public native byte[] openSession(@SecurityLevel int level) throws + public byte[] openSession(@SecurityLevel int level) throws + NotProvisionedException, ResourceBusyException { + byte[] sessionId = openSessionNative(level); + mPlaybackComponentMap.put(ByteBuffer.wrap(sessionId), new PlaybackComponentImpl(sessionId)); + return sessionId; + } + + @NonNull + private native byte[] openSessionNative(int level) throws NotProvisionedException, ResourceBusyException; /** * Close a session on the MediaDrm object that was previously opened * with {@link #openSession}. */ - public native void closeSession(@NonNull byte[] sessionId); + public void closeSession(@NonNull byte[] sessionId) { + closeSessionNative(sessionId); + mPlaybackComponentMap.remove(ByteBuffer.wrap(sessionId)); + } + + private native void closeSessionNative(@NonNull byte[] sessionId); + + private final Map mPlaybackComponentMap + = new ConcurrentHashMap<>(); /** * This key request type species that the keys will be for online use, they will @@ -2056,6 +2073,7 @@ public final class MediaDrm implements AutoCloseable { mCloseGuard.close(); if (mClosed.compareAndSet(false, true)) { native_release(); + mPlaybackComponentMap.clear(); } } @@ -2430,4 +2448,49 @@ public final class MediaDrm implements AutoCloseable { public static final String EVENT_SESSION_RECLAIMED_COUNT = "drm.mediadrm.event.SESSION_RECLAIMED.count"; } + + /** + * Obtain a {@link PlaybackComponent} associated with a DRM session. + * Call {@link PlaybackComponent#setPlaybackId(String)} on the returned object + * to associate a playback session with the DRM session. + * + * @param sessionId a DRM session ID obtained from {@link #openSession()} + * @return a {@link PlaybackComponent} associated with the session, + * or {@code null} if the session is closed or does not exist. + * @see PlaybackComponent + * @hide + */ + @TestApi + @Nullable + public PlaybackComponent getPlaybackComponent(@NonNull byte[] sessionId) { + if (sessionId == null) { + throw new IllegalArgumentException("sessionId is null"); + } + return mPlaybackComponentMap.get(ByteBuffer.wrap(sessionId)); + } + + private native void setPlaybackId(byte[] sessionId, String playbackId); + + private final class PlaybackComponentImpl implements PlaybackComponent { + private final byte[] mSessionId; + private String mPlaybackId = ""; + + public PlaybackComponentImpl(byte[] sessionId) { + mSessionId = sessionId; + } + + @Override + public void setPlaybackId(@NonNull String playbackId) { + if (playbackId == null) { + throw new IllegalArgumentException("playbackId is null"); + } + MediaDrm.this.setPlaybackId(mSessionId, playbackId); + mPlaybackId = playbackId; + } + + @Override + @NonNull public String getPlaybackId() { + return mPlaybackId; + } + } } diff --git a/media/java/android/media/metrics/PlaybackComponent.java b/media/java/android/media/metrics/PlaybackComponent.java index 625dd0a1870d2..94e55b4843c1f 100644 --- a/media/java/android/media/metrics/PlaybackComponent.java +++ b/media/java/android/media/metrics/PlaybackComponent.java @@ -17,11 +17,13 @@ package android.media.metrics; import android.annotation.NonNull; +import android.annotation.TestApi; /** * Interface for playback related components used by playback metrics. * @hide */ +@TestApi public interface PlaybackComponent { /** diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index babb16b1c8801..0e8719eeb79c2 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -1978,6 +1978,24 @@ static jboolean android_media_MediaDrm_requiresSecureDecoder( return drm->requiresSecureDecoder(mimeType.c_str(), securityLevel); } +static void android_media_MediaDrm_setPlaybackId( + JNIEnv *env, jobject thiz, jbyteArray jsessionId, + jstring jplaybackId) { + sp drm = GetDrm(env, thiz); + if (!CheckSession(env, drm, jsessionId)) { + return; + } + + Vector sessionId(JByteArrayToVector(env, jsessionId)); + + String8 playbackId; + if (jplaybackId != NULL) { + playbackId = JStringToString8(env, jplaybackId); + } + status_t err = drm->setPlaybackId(sessionId, playbackId.c_str()); + throwExceptionAsNecessary(env, err, "Failed to set playbackId"); +} + static const JNINativeMethod gMethods[] = { { "native_release", "()V", (void *)android_media_MediaDrm_native_release }, @@ -1992,10 +2010,10 @@ static const JNINativeMethod gMethods[] = { { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;I)Z", (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative }, - { "openSession", "(I)[B", + { "openSessionNative", "(I)[B", (void *)android_media_MediaDrm_openSession }, - { "closeSession", "([B)V", + { "closeSessionNative", "([B)V", (void *)android_media_MediaDrm_closeSession }, { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)" @@ -2102,6 +2120,9 @@ static const JNINativeMethod gMethods[] = { { "requiresSecureDecoder", "(Ljava/lang/String;I)Z", (void *)android_media_MediaDrm_requiresSecureDecoder }, + + { "setPlaybackId", "([BLjava/lang/String;)V", + (void *)android_media_MediaDrm_setPlaybackId }, }; int register_android_media_Drm(JNIEnv *env) {