Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media;
     18 
     19 import android.annotation.NonNull;
     20 import android.hardware.cas.V1_0.*;
     21 import android.media.MediaCasException.UnsupportedCasException;
     22 import android.os.IHwBinder;
     23 import android.os.RemoteException;
     24 import android.os.ServiceSpecificException;
     25 import android.util.Log;
     26 
     27 import java.nio.ByteBuffer;
     28 
     29 /**
     30  * MediaDescrambler class can be used in conjunction with {@link android.media.MediaCodec}
     31  * and {@link android.media.MediaExtractor} to decode media data scrambled by conditional
     32  * access (CA) systems such as those in the ISO/IEC13818-1.
     33  *
     34  * A MediaDescrambler object is initialized from a session opened by a MediaCas object,
     35  * and can be used to descramble media streams scrambled with that session's keys.
     36  *
     37  * Scrambling schemes are identified by 16-bit unsigned integer as in CA_system_id.
     38  *
     39  */
     40 public final class MediaDescrambler implements AutoCloseable {
     41     private static final String TAG = "MediaDescrambler";
     42     private IDescramblerBase mIDescrambler;
     43 
     44     private final void validateInternalStates() {
     45         if (mIDescrambler == null) {
     46             throw new IllegalStateException();
     47         }
     48     }
     49 
     50     private final void cleanupAndRethrowIllegalState() {
     51         mIDescrambler = null;
     52         throw new IllegalStateException();
     53     }
     54 
     55     /**
     56      * Instantiate a MediaDescrambler.
     57      *
     58      * @param CA_system_id The system id of the scrambling scheme.
     59      *
     60      * @throws UnsupportedCasException if the scrambling scheme is not supported.
     61      */
     62     public MediaDescrambler(int CA_system_id) throws UnsupportedCasException {
     63         try {
     64             mIDescrambler = MediaCas.getService().createDescrambler(CA_system_id);
     65         } catch(Exception e) {
     66             Log.e(TAG, "Failed to create descrambler: " + e);
     67             mIDescrambler = null;
     68         } finally {
     69             if (mIDescrambler == null) {
     70                 throw new UnsupportedCasException("Unsupported CA_system_id " + CA_system_id);
     71             }
     72         }
     73         native_setup(mIDescrambler.asBinder());
     74     }
     75 
     76     IHwBinder getBinder() {
     77         validateInternalStates();
     78 
     79         return mIDescrambler.asBinder();
     80     }
     81 
     82     /**
     83      * Query if the scrambling scheme requires the use of a secure decoder
     84      * to decode data of the given mime type.
     85      *
     86      * @param mime The mime type of the media data
     87      *
     88      * @throws IllegalStateException if the descrambler instance is not valid.
     89      */
     90     public final boolean requiresSecureDecoderComponent(@NonNull String mime) {
     91         validateInternalStates();
     92 
     93         try {
     94             return mIDescrambler.requiresSecureDecoderComponent(mime);
     95         } catch (RemoteException e) {
     96             cleanupAndRethrowIllegalState();
     97         }
     98         return true;
     99     }
    100 
    101     /**
    102      * Associate a MediaCas session with this MediaDescrambler instance.
    103      * The MediaCas session is used to securely load decryption keys for
    104      * the descrambler. The crypto keys loaded through the MediaCas session
    105      * may be selected for use during the descrambling operation performed
    106      * by {@link android.media.MediaExtractor or @link
    107      * android.media.MediaCodec#queueSecureInputBuffer} by specifying even
    108      * or odd key in the {@link android.media.MediaCodec.CryptoInfo#key} field.
    109      *
    110      * @param session the MediaCas session to associate with this
    111      * MediaDescrambler instance.
    112      *
    113      * @throws IllegalStateException if the descrambler instance is not valid.
    114      * @throws MediaCasStateException for CAS-specific state exceptions.
    115      */
    116     public final void setMediaCasSession(@NonNull MediaCas.Session session) {
    117         validateInternalStates();
    118 
    119         try {
    120             MediaCasStateException.throwExceptionIfNeeded(
    121                     mIDescrambler.setMediaCasSession(session.mSessionId));
    122         } catch (RemoteException e) {
    123             cleanupAndRethrowIllegalState();
    124         }
    125     }
    126 
    127     /**
    128      * Scramble control value indicating that the samples are not scrambled.
    129      * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
    130      */
    131     public static final byte SCRAMBLE_CONTROL_UNSCRAMBLED = 0;
    132 
    133     /**
    134      * Scramble control value reserved and shouldn't be used currently.
    135      * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
    136      */
    137     public static final byte SCRAMBLE_CONTROL_RESERVED    = 1;
    138 
    139     /**
    140      * Scramble control value indicating that the even key is used.
    141      * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
    142      */
    143     public static final byte SCRAMBLE_CONTROL_EVEN_KEY     = 2;
    144 
    145     /**
    146      * Scramble control value indicating that the odd key is used.
    147      * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
    148      */
    149     public static final byte SCRAMBLE_CONTROL_ODD_KEY      = 3;
    150 
    151     /**
    152      * Scramble flag for a hint indicating that the descrambling request is for
    153      * retrieving the PES header info only.
    154      *
    155      * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
    156      */
    157     public static final byte SCRAMBLE_FLAG_PES_HEADER = (1 << 0);
    158 
    159     /**
    160      * Descramble a ByteBuffer of data described by a
    161      * {@link android.media.MediaCodec.CryptoInfo} structure.
    162      *
    163      * @param srcBuf ByteBuffer containing the scrambled data, which starts at
    164      * srcBuf.position().
    165      * @param dstBuf ByteBuffer to hold the descrambled data, which starts at
    166      * dstBuf.position().
    167      * @param cryptoInfo a {@link android.media.MediaCodec.CryptoInfo} structure
    168      * describing the subsamples contained in srcBuf. The iv and mode fields in
    169      * CryptoInfo are not used. key[0] contains the MPEG2TS scrambling control bits
    170      * (as defined in ETSI TS 100 289 (2011): "Digital Video Broadcasting (DVB);
    171      * Support for use of the DVB Scrambling Algorithm version 3 within digital
    172      * broadcasting systems"), and the value must be one of {@link #SCRAMBLE_CONTROL_UNSCRAMBLED},
    173      * {@link #SCRAMBLE_CONTROL_RESERVED}, {@link #SCRAMBLE_CONTROL_EVEN_KEY} or
    174      * {@link #SCRAMBLE_CONTROL_ODD_KEY}. key[1] is a set of bit flags, with the
    175      * only possible bit being {@link #SCRAMBLE_FLAG_PES_HEADER} currently.
    176      * key[2~15] are not used.
    177      *
    178      * @return number of bytes that have been successfully descrambled, with negative
    179      * values indicating errors.
    180      *
    181      * @throws IllegalStateException if the descrambler instance is not valid.
    182      * @throws MediaCasStateException for CAS-specific state exceptions.
    183      */
    184     public final int descramble(
    185             @NonNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf,
    186             @NonNull MediaCodec.CryptoInfo cryptoInfo) {
    187         validateInternalStates();
    188 
    189         if (cryptoInfo.numSubSamples <= 0) {
    190             throw new IllegalArgumentException(
    191                     "Invalid CryptoInfo: invalid numSubSamples=" + cryptoInfo.numSubSamples);
    192         } else if (cryptoInfo.numBytesOfClearData == null
    193                 && cryptoInfo.numBytesOfEncryptedData == null) {
    194             throw new IllegalArgumentException(
    195                     "Invalid CryptoInfo: clearData and encryptedData size arrays are both null!");
    196         } else if (cryptoInfo.numBytesOfClearData != null
    197                 && cryptoInfo.numBytesOfClearData.length < cryptoInfo.numSubSamples) {
    198             throw new IllegalArgumentException(
    199                     "Invalid CryptoInfo: numBytesOfClearData is too small!");
    200         } else if (cryptoInfo.numBytesOfEncryptedData != null
    201                 && cryptoInfo.numBytesOfEncryptedData.length < cryptoInfo.numSubSamples) {
    202             throw new IllegalArgumentException(
    203                     "Invalid CryptoInfo: numBytesOfEncryptedData is too small!");
    204         } else if (cryptoInfo.key == null || cryptoInfo.key.length != 16) {
    205             throw new IllegalArgumentException(
    206                     "Invalid CryptoInfo: key array is invalid!");
    207         }
    208 
    209         try {
    210             return native_descramble(
    211                     cryptoInfo.key[0],
    212                     cryptoInfo.key[1],
    213                     cryptoInfo.numSubSamples,
    214                     cryptoInfo.numBytesOfClearData,
    215                     cryptoInfo.numBytesOfEncryptedData,
    216                     srcBuf, srcBuf.position(), srcBuf.limit(),
    217                     dstBuf, dstBuf.position(), dstBuf.limit());
    218         } catch (ServiceSpecificException e) {
    219             MediaCasStateException.throwExceptionIfNeeded(e.errorCode, e.getMessage());
    220         } catch (RemoteException e) {
    221             cleanupAndRethrowIllegalState();
    222         }
    223         return -1;
    224     }
    225 
    226     @Override
    227     public void close() {
    228         if (mIDescrambler != null) {
    229             try {
    230                 mIDescrambler.release();
    231             } catch (RemoteException e) {
    232             } finally {
    233                 mIDescrambler = null;
    234             }
    235         }
    236         native_release();
    237     }
    238 
    239     @Override
    240     protected void finalize() {
    241         close();
    242     }
    243 
    244     private static native final void native_init();
    245     private native final void native_setup(@NonNull IHwBinder decramblerBinder);
    246     private native final void native_release();
    247     private native final int native_descramble(
    248             byte key, byte flags, int numSubSamples,
    249             int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
    250             @NonNull ByteBuffer srcBuf, int srcOffset, int srcLimit,
    251             ByteBuffer dstBuf, int dstOffset, int dstLimit) throws RemoteException;
    252 
    253     static {
    254         System.loadLibrary("media_jni");
    255         native_init();
    256     }
    257 
    258     private long mNativeContext;
    259 }