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 validateInternalStates()44 private final void validateInternalStates() { 45 if (mIDescrambler == null) { 46 throw new IllegalStateException(); 47 } 48 } 49 cleanupAndRethrowIllegalState()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 */ MediaDescrambler(int CA_system_id)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 getBinder()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 */ requiresSecureDecoderComponent(@onNull String mime)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 */ setMediaCasSession(@onNull MediaCas.Session session)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 */ descramble( @onNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf, @NonNull MediaCodec.CryptoInfo cryptoInfo)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 close()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 finalize()240 protected void finalize() { 241 close(); 242 } 243 native_init()244 private static native final void native_init(); native_setup(@onNull IHwBinder decramblerBinder)245 private native final void native_setup(@NonNull IHwBinder decramblerBinder); native_release()246 private native final void native_release(); native_descramble( byte key, byte flags, int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData, @NonNull ByteBuffer srcBuf, int srcOffset, int srcLimit, ByteBuffer dstBuf, int dstOffset, int dstLimit)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"); native_init()255 native_init(); 256 } 257 258 private long mNativeContext; 259 }