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
This commit is contained in:
Brian Lindahl 2022-02-01 11:10:30 +01:00
parent daa918b287
commit 6ceeed4fdd
5 changed files with 66 additions and 19 deletions

View File

@ -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);
}

View File

@ -502,6 +502,8 @@ applications that come with the platform
<permission name="android.permission.MANAGE_APP_HIBERNATION"/>
<!-- Permission required for CTS test - ResourceObserverNativeTest -->
<permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" />
<!-- Permission required for CTS test - MediaCodecResourceTest -->
<permission name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" />
<!-- Permission required for CTS test - CtsAlarmManagerTestCases -->
<permission name="android.permission.SCHEDULE_PRIORITIZED_ALARM" />
<!-- Permission required for CTS test - SystemMediaRouter2Test -->

View File

@ -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();

View File

@ -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<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
sp<JMediaCodec> 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",

View File

@ -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;