From 6ceeed4fdd225669ca9bde1c95d580dc1a0245b8 Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Tue, 1 Feb 2022 11:10:30 +0100 Subject: [PATCH] Allow MediaCodecs to be created on behalf of other client processes. Use case is for TV Input Service which creates MediaCodecs when tuning to TV channels on behalf of operator and OEM apps. Requires the MEDIA_RESOURCE_PID_OVERRIDE permission. Bug: 217746837 Test: atest MediaCodecResourceTest Change-Id: I191ff13c5730970664a818a9333c160e6e773c7d --- core/api/system-current.txt | 4 +++ data/etc/privapp-permissions-platform.xml | 2 ++ media/java/android/media/MediaCodec.java | 44 +++++++++++++++++++---- media/jni/android_media_MediaCodec.cpp | 33 ++++++++++------- media/jni/android_media_MediaCodec.h | 2 +- 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d6e48157063c..919832bd0bfb 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5990,6 +5990,10 @@ package android.media { method @NonNull public android.media.HwAudioSource.Builder setAudioDeviceInfo(@NonNull android.media.AudioDeviceInfo); } + public final class MediaCodec { + method @NonNull @RequiresPermission("android.permission.MEDIA_RESOURCE_OVERRIDE_PID") public static android.media.MediaCodec createByCodecNameForClient(@NonNull String, int, int) throws java.io.IOException; + } + public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index b3dcc34d6e93..58a4d3568e17 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -502,6 +502,8 @@ applications that come with the platform + + diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 939b679676aa..4563259c31f2 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -16,9 +16,12 @@ package android.media; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.ImageFormat; import android.graphics.Rect; @@ -1934,12 +1937,41 @@ final public class MediaCodec { @NonNull public static MediaCodec createByCodecName(@NonNull String name) throws IOException { - return new MediaCodec( - name, false /* nameIsType */, false /* unused */); + return new MediaCodec(name, false /* nameIsType */, false /* encoder */); } - private MediaCodec( - @NonNull String name, boolean nameIsType, boolean encoder) { + /** + * This is the same as createByCodecName, but allows for instantiating a codec on behalf of a + * client process. This is used for system apps or system services that create MediaCodecs on + * behalf of other processes and will reclaim resources as necessary from processes with lower + * priority than the client process, rather than processes with lower priority than the system + * app or system service. Likely to be used with information obtained from + * {@link android.media.MediaCodecList}. + * @param name + * @param clientPid + * @param clientUid + * @throws IOException if the codec cannot be created. + * @throws IllegalArgumentException if name is not valid. + * @throws NullPointerException if name is null. + * @throws SecurityException if the MEDIA_RESOURCE_OVERRIDE_PID permission is not granted. + * + * @hide + */ + @NonNull + @SystemApi + @RequiresPermission(Manifest.permission.MEDIA_RESOURCE_OVERRIDE_PID) + public static MediaCodec createByCodecNameForClient(@NonNull String name, int clientPid, + int clientUid) throws IOException { + return new MediaCodec(name, false /* nameIsType */, false /* encoder */, clientPid, + clientUid); + } + + private MediaCodec(@NonNull String name, boolean nameIsType, boolean encoder) { + this(name, nameIsType, encoder, -1 /* pid */, -1 /* uid */); + } + + private MediaCodec(@NonNull String name, boolean nameIsType, boolean encoder, int pid, + int uid) { Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); @@ -1957,7 +1989,7 @@ final public class MediaCodec { // save name used at creation mNameAtCreation = nameIsType ? null : name; - native_setup(name, nameIsType, encoder); + native_setup(name, nameIsType, encoder, pid, uid); } private String mNameAtCreation; @@ -4991,7 +5023,7 @@ final public class MediaCodec { private static native final void native_init(); private native final void native_setup( - @NonNull String name, boolean nameIsType, boolean encoder); + @NonNull String name, boolean nameIsType, boolean encoder, int pid, int uid); private native final void native_finalize(); diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index f6944823feb5..c8d2d1ee621f 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -202,7 +202,7 @@ static const void *sRefBaseOwner; JMediaCodec::JMediaCodec( JNIEnv *env, jobject thiz, - const char *name, bool nameIsType, bool encoder) + const char *name, bool nameIsType, bool encoder, int pid, int uid) : mClass(NULL), mObject(NULL) { jclass clazz = env->GetObjectClass(thiz); @@ -220,12 +220,12 @@ JMediaCodec::JMediaCodec( ANDROID_PRIORITY_VIDEO); if (nameIsType) { - mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus); + mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus, pid, uid); if (mCodec == nullptr || mCodec->getName(&mNameAtCreation) != OK) { mNameAtCreation = "(null)"; } } else { - mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus); + mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus, pid, uid); mNameAtCreation = name; } CHECK((mCodec != NULL) != (mInitStatus != OK)); @@ -3136,7 +3136,7 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { static void android_media_MediaCodec_native_setup( JNIEnv *env, jobject thiz, - jstring name, jboolean nameIsType, jboolean encoder) { + jstring name, jboolean nameIsType, jboolean encoder, int pid, int uid) { if (name == NULL) { jniThrowException(env, "java/lang/NullPointerException", NULL); return; @@ -3148,24 +3148,33 @@ static void android_media_MediaCodec_native_setup( return; } - sp codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder); + sp codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder, pid, uid); const status_t err = codec->initCheck(); if (err == NAME_NOT_FOUND) { // fail and do not try again. jniThrowException(env, "java/lang/IllegalArgumentException", - String8::format("Failed to initialize %s, error %#x", tmp, err)); + String8::format("Failed to initialize %s, error %#x (NAME_NOT_FOUND)", tmp, err)); env->ReleaseStringUTFChars(name, tmp); return; - } if (err == NO_MEMORY) { + } + if (err == NO_MEMORY) { throwCodecException(env, err, ACTION_CODE_TRANSIENT, - String8::format("Failed to initialize %s, error %#x", tmp, err)); + String8::format("Failed to initialize %s, error %#x (NO_MEMORY)", tmp, err)); env->ReleaseStringUTFChars(name, tmp); return; - } else if (err != OK) { + } + if (err == PERMISSION_DENIED) { + jniThrowException(env, "java/lang/SecurityException", + String8::format("Failed to initialize %s, error %#x (PERMISSION_DENIED)", tmp, + err)); + env->ReleaseStringUTFChars(name, tmp); + return; + } + if (err != OK) { // believed possible to try again jniThrowException(env, "java/io/IOException", - String8::format("Failed to find matching codec %s, error %#x", tmp, err)); + String8::format("Failed to find matching codec %s, error %#x (?)", tmp, err)); env->ReleaseStringUTFChars(name, tmp); return; } @@ -3174,7 +3183,7 @@ static void android_media_MediaCodec_native_setup( codec->registerSelf(); - setMediaCodec(env,thiz, codec); + setMediaCodec(env, thiz, codec); } static void android_media_MediaCodec_native_finalize( @@ -3478,7 +3487,7 @@ static const JNINativeMethod gMethods[] = { { "native_init", "()V", (void *)android_media_MediaCodec_native_init }, - { "native_setup", "(Ljava/lang/String;ZZ)V", + { "native_setup", "(Ljava/lang/String;ZZII)V", (void *)android_media_MediaCodec_native_setup }, { "native_finalize", "()V", diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index ee456c9ba82d..616c31b29157 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -55,7 +55,7 @@ using hardware::cas::native::V1_0::IDescrambler; struct JMediaCodec : public AHandler { JMediaCodec( JNIEnv *env, jobject thiz, - const char *name, bool nameIsType, bool encoder); + const char *name, bool nameIsType, bool encoder, int pid, int uid); status_t initCheck() const;