Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b6d9ba31 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "cas: convert MediaCas to HIDL"

parents accd277c 2659c2f1
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -438,10 +438,6 @@ LOCAL_SRC_FILES += \
	location/java/android/location/INetInitiatedListener.aidl \
	location/java/com/android/internal/location/ILocationProvider.aidl \
	media/java/android/media/IAudioService.aidl \
	../av/drm/libmediadrm/aidl/android/media/ICas.aidl \
	../av/drm/libmediadrm/aidl/android/media/ICasListener.aidl \
	../av/drm/libmediadrm/aidl/android/media/IDescrambler.aidl \
	../av/drm/libmediadrm/aidl/android/media/IMediaCasService.aidl \
	media/java/android/media/IAudioFocusDispatcher.aidl \
	media/java/android/media/IAudioRoutesObserver.aidl \
	media/java/android/media/IMediaHTTPConnection.aidl \
@@ -614,6 +610,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
    android.hardware.vibrator-V1.1-java-constants        \
    android.hardware.wifi-V1.0-java-constants            \

include hardware/interfaces/cas/1.0/CasHal.mk

# Loaded with System.loadLibrary by android.view.textclassifier
LOCAL_REQUIRED_MODULES += libtextclassifier

+78 −130
Original line number Diff line number Diff line
@@ -18,21 +18,20 @@ package android.media;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.cas.V1_0.*;
import android.media.MediaCasException.*;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IHwBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.util.Log;
import android.util.Singleton;

import java.util.ArrayList;

/**
 * MediaCas can be used to obtain keys for descrambling protected media streams, in
 * conjunction with {@link android.media.MediaDescrambler}. The MediaCas APIs are
@@ -95,7 +94,6 @@ import android.util.Singleton;
 */
public final class MediaCas implements AutoCloseable {
    private static final String TAG = "MediaCas";
    private final ParcelableCasData mCasData = new ParcelableCasData();
    private ICas mICas;
    private EventListener mListener;
    private HandlerThread mHandlerThread;
@@ -105,8 +103,10 @@ public final class MediaCas implements AutoCloseable {
            new Singleton<IMediaCasService>() {
        @Override
        protected IMediaCasService create() {
            return IMediaCasService.Stub.asInterface(
                    ServiceManager.getService("media.cas"));
            try {
                return IMediaCasService.getService();
            } catch (RemoteException e) {}
            return null;
        }
    };

@@ -136,65 +136,21 @@ public final class MediaCas implements AutoCloseable {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MSG_CAS_EVENT) {
                mListener.onEvent(MediaCas.this, msg.arg1, msg.arg2, (byte[]) msg.obj);
                mListener.onEvent(MediaCas.this, msg.arg1, msg.arg2,
                        toBytes((ArrayList<Byte>) msg.obj));
            }
        }
    }

    private final ICasListener.Stub mBinder = new ICasListener.Stub() {
        @Override
        public void onEvent(int event, int arg, @Nullable byte[] data)
        public void onEvent(int event, int arg, @Nullable ArrayList<Byte> data)
                throws RemoteException {
            mEventHandler.sendMessage(mEventHandler.obtainMessage(
                    EventHandler.MSG_CAS_EVENT, event, arg, data));
        }
    };

    /**
     * Class for parceling byte array data over ICas binder.
     */
    static class ParcelableCasData implements Parcelable {
        private byte[] mData;
        private int mOffset;
        private int mLength;

        ParcelableCasData() {
            mData = null;
            mOffset = mLength = 0;
        }

        private ParcelableCasData(Parcel in) {
            this();
        }

        void set(@NonNull byte[] data, int offset, int length) {
            mData = data;
            mOffset = offset;
            mLength = length;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeByteArray(mData, mOffset, mLength);
        }

        public static final Parcelable.Creator<ParcelableCasData> CREATOR
                = new Parcelable.Creator<ParcelableCasData>() {
            public ParcelableCasData createFromParcel(Parcel in) {
                return new ParcelableCasData(in);
            }

            public ParcelableCasData[] newArray(int size) {
                return new ParcelableCasData[size];
            }
        };
    }

    /**
     * Describe a CAS plugin with its CA_system_ID and string name.
     *
@@ -210,9 +166,9 @@ public final class MediaCas implements AutoCloseable {
            mName = null;
        }

        PluginDescriptor(int CA_system_id, String name) {
            mCASystemId = CA_system_id;
            mName = name;
        PluginDescriptor(@NonNull HidlCasPluginDescriptor descriptor) {
            mCASystemId = descriptor.caSystemId;
            mName = descriptor.name;
        }

        public int getSystemId() {
@@ -230,13 +186,38 @@ public final class MediaCas implements AutoCloseable {
        }
    }

    private ArrayList<Byte> toByteArray(@NonNull byte[] data, int offset, int length) {
        ArrayList<Byte> byteArray = new ArrayList<Byte>(length);
        for (int i = 0; i < length; i++) {
            byteArray.add(Byte.valueOf(data[offset + i]));
        }
        return byteArray;
    }

    private ArrayList<Byte> toByteArray(@Nullable byte[] data) {
        if (data == null) {
            return new ArrayList<Byte>();
        }
        return toByteArray(data, 0, data.length);
    }

    private byte[] toBytes(@NonNull ArrayList<Byte> byteArray) {
        byte[] data = null;
        if (byteArray != null) {
            data = new byte[byteArray.size()];
            for (int i = 0; i < data.length; i++) {
                data[i] = byteArray.get(i);
            }
        }
        return data;
    }
    /**
     * Class for an open session with the CA system.
     */
    public final class Session implements AutoCloseable {
        final byte[] mSessionId;
        final ArrayList<Byte> mSessionId;

        Session(@NonNull byte[] sessionId) {
        Session(@NonNull ArrayList<Byte> sessionId) {
            mSessionId = sessionId;
        }

@@ -254,9 +235,8 @@ public final class MediaCas implements AutoCloseable {
            validateInternalStates();

            try {
                mICas.setSessionPrivateData(mSessionId, data);
            } catch (ServiceSpecificException e) {
                MediaCasException.throwExceptions(e);
                MediaCasException.throwExceptionIfNeeded(
                        mICas.setSessionPrivateData(mSessionId, toByteArray(data, 0, data.length)));
            } catch (RemoteException e) {
                cleanupAndRethrowIllegalState();
            }
@@ -279,10 +259,8 @@ public final class MediaCas implements AutoCloseable {
            validateInternalStates();

            try {
                mCasData.set(data, offset, length);
                mICas.processEcm(mSessionId, mCasData);
            } catch (ServiceSpecificException e) {
                MediaCasException.throwExceptions(e);
                MediaCasException.throwExceptionIfNeeded(
                        mICas.processEcm(mSessionId, toByteArray(data, offset, length)));
            } catch (RemoteException e) {
                cleanupAndRethrowIllegalState();
            }
@@ -314,56 +292,21 @@ public final class MediaCas implements AutoCloseable {
            validateInternalStates();

            try {
                mICas.closeSession(mSessionId);
            } catch (ServiceSpecificException e) {
                MediaCasStateException.throwExceptions(e);
                MediaCasStateException.throwExceptionIfNeeded(
                        mICas.closeSession(mSessionId));
            } catch (RemoteException e) {
                cleanupAndRethrowIllegalState();
            }
        }
    }

    Session createFromSessionId(byte[] sessionId) {
        if (sessionId == null || sessionId.length == 0) {
    Session createFromSessionId(@NonNull ArrayList<Byte> sessionId) {
        if (sessionId == null || sessionId.size() == 0) {
            return null;
        }
        return new Session(sessionId);
    }

    /**
     * Class for parceling CAS plugin descriptors over IMediaCasService binder.
     */
    static class ParcelableCasPluginDescriptor
        extends PluginDescriptor implements Parcelable {

        private ParcelableCasPluginDescriptor(int CA_system_id, String name) {
            super(CA_system_id, name);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            Log.w(TAG, "ParcelableCasPluginDescriptor.writeToParcel shouldn't be called!");
        }

        public static final Parcelable.Creator<ParcelableCasPluginDescriptor> CREATOR
                = new Parcelable.Creator<ParcelableCasPluginDescriptor>() {
            public ParcelableCasPluginDescriptor createFromParcel(Parcel in) {
                int CA_system_id = in.readInt();
                String name = in.readString();
                return new ParcelableCasPluginDescriptor(CA_system_id, name);
            }

            public ParcelableCasPluginDescriptor[] newArray(int size) {
                return new ParcelableCasPluginDescriptor[size];
            }
        };
    }

    /**
     * Query if a certain CA system is supported on this device.
     *
@@ -393,13 +336,14 @@ public final class MediaCas implements AutoCloseable {

        if (service != null) {
            try {
                ParcelableCasPluginDescriptor[] descriptors = service.enumeratePlugins();
                if (descriptors.length == 0) {
                ArrayList<HidlCasPluginDescriptor> descriptors =
                        service.enumeratePlugins();
                if (descriptors.size() == 0) {
                    return null;
                }
                PluginDescriptor[] results = new PluginDescriptor[descriptors.length];
                PluginDescriptor[] results = new PluginDescriptor[descriptors.size()];
                for (int i = 0; i < results.length; i++) {
                    results[i] = descriptors[i];
                    results[i] = new PluginDescriptor(descriptors.get(i));
                }
                return results;
            } catch (RemoteException e) {
@@ -430,7 +374,7 @@ public final class MediaCas implements AutoCloseable {
        }
    }

    IBinder getBinder() {
    IHwBinder getBinder() {
        validateInternalStates();

        return mICas.asBinder();
@@ -497,14 +441,22 @@ public final class MediaCas implements AutoCloseable {
        validateInternalStates();

        try {
            mICas.setPrivateData(data);
        } catch (ServiceSpecificException e) {
            MediaCasException.throwExceptions(e);
            MediaCasException.throwExceptionIfNeeded(
                    mICas.setPrivateData(toByteArray(data, 0, data.length)));
        } catch (RemoteException e) {
            cleanupAndRethrowIllegalState();
        }
    }

    private class OpenSessionCallback implements ICas.openSessionCallback {
        public Session mSession;
        public int mStatus;
        @Override
        public void onValues(int status, ArrayList<Byte> sessionId) {
            mStatus = status;
            mSession = createFromSessionId(sessionId);
        }
    }
    /**
     * Open a session to descramble one or more streams scrambled by the
     * conditional access system.
@@ -519,9 +471,10 @@ public final class MediaCas implements AutoCloseable {
        validateInternalStates();

        try {
            return createFromSessionId(mICas.openSession());
        } catch (ServiceSpecificException e) {
            MediaCasException.throwExceptions(e);
            OpenSessionCallback cb = new OpenSessionCallback();
            mICas.openSession(cb);
            MediaCasException.throwExceptionIfNeeded(cb.mStatus);
            return cb.mSession;
        } catch (RemoteException e) {
            cleanupAndRethrowIllegalState();
        }
@@ -544,10 +497,8 @@ public final class MediaCas implements AutoCloseable {
        validateInternalStates();

        try {
            mCasData.set(data, offset, length);
            mICas.processEmm(mCasData);
        } catch (ServiceSpecificException e) {
            MediaCasException.throwExceptions(e);
            MediaCasException.throwExceptionIfNeeded(
                    mICas.processEmm(toByteArray(data, offset, length)));
        } catch (RemoteException e) {
            cleanupAndRethrowIllegalState();
        }
@@ -585,9 +536,8 @@ public final class MediaCas implements AutoCloseable {
        validateInternalStates();

        try {
            mICas.sendEvent(event, arg, data);
        } catch (ServiceSpecificException e) {
            MediaCasException.throwExceptions(e);
            MediaCasException.throwExceptionIfNeeded(
                    mICas.sendEvent(event, arg, toByteArray(data)));
        } catch (RemoteException e) {
            cleanupAndRethrowIllegalState();
        }
@@ -608,9 +558,8 @@ public final class MediaCas implements AutoCloseable {
        validateInternalStates();

        try {
            mICas.provision(provisionString);
        } catch (ServiceSpecificException e) {
            MediaCasException.throwExceptions(e);
            MediaCasException.throwExceptionIfNeeded(
                    mICas.provision(provisionString));
        } catch (RemoteException e) {
            cleanupAndRethrowIllegalState();
        }
@@ -631,9 +580,8 @@ public final class MediaCas implements AutoCloseable {
        validateInternalStates();

        try {
            mICas.refreshEntitlements(refreshType, refreshData);
        } catch (ServiceSpecificException e) {
            MediaCasException.throwExceptions(e);
            MediaCasException.throwExceptionIfNeeded(
                    mICas.refreshEntitlements(refreshType, toByteArray(refreshData)));
        } catch (RemoteException e) {
            cleanupAndRethrowIllegalState();
        }
+14 −45
Original line number Diff line number Diff line
@@ -16,60 +16,29 @@

package android.media;

import android.os.ServiceSpecificException;
import android.hardware.cas.V1_0.Status;

/**
 * Base class for MediaCas exceptions
 */
public class MediaCasException extends Exception {

    /** @hide */
    public static final int DRM_ERROR_BASE = -2000;
    /** @hide */
    public static final int ERROR_DRM_UNKNOWN                        = DRM_ERROR_BASE;
    /** @hide */
    public static final int ERROR_DRM_NO_LICENSE                     = DRM_ERROR_BASE - 1;
    /** @hide */
    public static final int ERROR_DRM_LICENSE_EXPIRED                = DRM_ERROR_BASE - 2;
    /** @hide */
    public static final int ERROR_DRM_SESSION_NOT_OPENED             = DRM_ERROR_BASE - 3;
    /** @hide */
    public static final int ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED   = DRM_ERROR_BASE - 4;
    /** @hide */
    public static final int ERROR_DRM_DECRYPT                        = DRM_ERROR_BASE - 5;
    /** @hide */
    public static final int ERROR_DRM_CANNOT_HANDLE                  = DRM_ERROR_BASE - 6;
    /** @hide */
    public static final int ERROR_DRM_TAMPER_DETECTED                = DRM_ERROR_BASE - 7;
    /** @hide */
    public static final int ERROR_DRM_NOT_PROVISIONED                = DRM_ERROR_BASE - 8;
    /** @hide */
    public static final int ERROR_DRM_DEVICE_REVOKED                 = DRM_ERROR_BASE - 9;
    /** @hide */
    public static final int ERROR_DRM_RESOURCE_BUSY                  = DRM_ERROR_BASE - 10;
    /** @hide */
    public static final int ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION = DRM_ERROR_BASE - 11;
    /** @hide */
    public static final int ERROR_DRM_LAST_USED_ERRORCODE            = DRM_ERROR_BASE - 11;
    /** @hide */
    public static final int ERROR_DRM_VENDOR_MAX                     = DRM_ERROR_BASE - 500;
    /** @hide */
    public static final int ERROR_DRM_VENDOR_MIN                     = DRM_ERROR_BASE - 999;

    /** @hide */
    public MediaCasException(String detailMessage) {
    private MediaCasException(String detailMessage) {
        super(detailMessage);
    }

    static void throwExceptions(ServiceSpecificException e) throws MediaCasException {
        if (e.errorCode == ERROR_DRM_NOT_PROVISIONED) {
            throw new NotProvisionedException(e.getMessage());
        } else if (e.errorCode == ERROR_DRM_RESOURCE_BUSY) {
            throw new ResourceBusyException(e.getMessage());
        } else if (e.errorCode == ERROR_DRM_DEVICE_REVOKED) {
            throw new DeniedByServerException(e.getMessage());
    static void throwExceptionIfNeeded(int error) throws MediaCasException {
        if (error == Status.OK) {
            return;
        }

        if (error == Status.ERROR_CAS_NOT_PROVISIONED) {
            throw new NotProvisionedException(null);
        } else if (error == Status.ERROR_CAS_RESOURCE_BUSY) {
            throw new ResourceBusyException(null);
        } else if (error == Status.ERROR_CAS_DEVICE_REVOKED) {
            throw new DeniedByServerException(null);
        } else {
            MediaCasStateException.throwExceptions(e);
            MediaCasStateException.throwExceptionIfNeeded(error);
        }
    }

+34 −19
Original line number Diff line number Diff line
@@ -18,9 +18,8 @@ package android.media;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.ServiceSpecificException;

import static android.media.MediaCasException.*;
import android.hardware.cas.V1_0.Status;

/**
 * Base class for MediaCas runtime exceptions
@@ -29,46 +28,62 @@ public class MediaCasStateException extends IllegalStateException {
    private final int mErrorCode;
    private final String mDiagnosticInfo;

    /** @hide */
    public MediaCasStateException(int err, @Nullable String msg, @Nullable String diagnosticInfo) {
    private MediaCasStateException(int err, @Nullable String msg, @Nullable String diagnosticInfo) {
        super(msg);
        mErrorCode = err;
        mDiagnosticInfo = diagnosticInfo;
    }

    static void throwExceptions(ServiceSpecificException e) {
    static void throwExceptionIfNeeded(int err) {
        throwExceptionIfNeeded(err, null /* msg */);
    }

    static void throwExceptionIfNeeded(int err, @Nullable String msg) {
        if (err == Status.OK) {
            return;
        }
        if (err == Status.BAD_VALUE) {
            throw new IllegalArgumentException();
        }

        String diagnosticInfo = "";
        switch (e.errorCode) {
        case ERROR_DRM_UNKNOWN:
        switch (err) {
        case Status.ERROR_CAS_UNKNOWN:
            diagnosticInfo = "General CAS error";
            break;
        case ERROR_DRM_NO_LICENSE:
        case Status.ERROR_CAS_NO_LICENSE:
            diagnosticInfo = "No license";
            break;
        case ERROR_DRM_LICENSE_EXPIRED:
        case Status.ERROR_CAS_LICENSE_EXPIRED:
            diagnosticInfo = "License expired";
            break;
        case ERROR_DRM_SESSION_NOT_OPENED:
        case Status.ERROR_CAS_SESSION_NOT_OPENED:
            diagnosticInfo = "Session not opened";
            break;
        case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED:
            diagnosticInfo = "Not initialized";
        case Status.ERROR_CAS_CANNOT_HANDLE:
            diagnosticInfo = "Unsupported scheme or data format";
            break;
        case ERROR_DRM_DECRYPT:
            diagnosticInfo = "Decrypt error";
        case Status.ERROR_CAS_INVALID_STATE:
            diagnosticInfo = "Invalid CAS state";
            break;
        case ERROR_DRM_CANNOT_HANDLE:
            diagnosticInfo = "Unsupported scheme or data format";
        case Status.ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION:
            diagnosticInfo = "Insufficient output protection";
            break;
        case ERROR_DRM_TAMPER_DETECTED:
        case Status.ERROR_CAS_TAMPER_DETECTED:
            diagnosticInfo = "Tamper detected";
            break;
        case Status.ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED:
            diagnosticInfo = "Not initialized";
            break;
        case Status.ERROR_CAS_DECRYPT:
            diagnosticInfo = "Decrypt error";
            break;
        default:
            diagnosticInfo = "Unknown CAS state exception";
            break;
        }
        throw new MediaCasStateException(e.errorCode, e.getMessage(),
                String.format("%s (err=%d)", diagnosticInfo, e.errorCode));
        throw new MediaCasStateException(err, msg,
                String.format("%s (err=%d)", diagnosticInfo, err));
    }

    /**
+3 −2
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.media.MediaCodecInfo.CodecCapabilities;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHwBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
@@ -1903,7 +1904,7 @@ final public class MediaCodec {

    private void configure(
            @Nullable MediaFormat format, @Nullable Surface surface,
            @Nullable MediaCrypto crypto, @Nullable IBinder descramblerBinder,
            @Nullable MediaCrypto crypto, @Nullable IHwBinder descramblerBinder,
            @ConfigureFlag int flags) {
        if (crypto != null && descramblerBinder != null) {
            throw new IllegalArgumentException("Can't use crypto and descrambler together!");
@@ -2018,7 +2019,7 @@ final public class MediaCodec {
    private native final void native_configure(
            @Nullable String[] keys, @Nullable Object[] values,
            @Nullable Surface surface, @Nullable MediaCrypto crypto,
            @Nullable IBinder descramblerBinder, @ConfigureFlag int flags);
            @Nullable IHwBinder descramblerBinder, @ConfigureFlag int flags);

    /**
     * Requests a Surface to use as the input to an encoder, in place of input buffers.  This
Loading