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 17package android.media; 18 19import android.annotation.NonNull; 20import android.media.MediaCasException.UnsupportedCasException; 21import android.os.IBinder; 22import android.os.Parcel; 23import android.os.Parcelable; 24import android.os.RemoteException; 25import android.os.ServiceSpecificException; 26import android.util.Log; 27 28import java.nio.ByteBuffer; 29 30/** 31 * MediaDescrambler class can be used in conjunction with {@link android.media.MediaCodec} 32 * and {@link android.media.MediaExtractor} to decode media data scrambled by conditional 33 * access (CA) systems such as those in the ISO/IEC13818-1. 34 * 35 * A MediaDescrambler object is initialized from a session opened by a MediaCas object, 36 * and can be used to descramble media streams scrambled with that session's keys. 37 * 38 * Scrambling schemes are identified by 16-bit unsigned integer as in CA_system_id. 39 * 40 */ 41public final class MediaDescrambler implements AutoCloseable { 42 private static final String TAG = "MediaDescrambler"; 43 private IDescrambler mIDescrambler; 44 45 private final void validateInternalStates() { 46 if (mIDescrambler == null) { 47 throw new IllegalStateException(); 48 } 49 } 50 51 private final void cleanupAndRethrowIllegalState() { 52 mIDescrambler = null; 53 throw new IllegalStateException(); 54 } 55 56 /** 57 * Class for parceling descrambling parameters over IDescrambler binder. 58 */ 59 // This class currently is not used by Java binder. descramble() goes through 60 // jni to use shared memory. However, the parcelable is still required for AIDL. 61 static class DescrambleInfo implements Parcelable { 62 private DescrambleInfo() { 63 } 64 65 private DescrambleInfo(Parcel in) { 66 } 67 68 @Override 69 public int describeContents() { 70 return 0; 71 } 72 73 @Override 74 public void writeToParcel(Parcel dest, int flags) { 75 } 76 77 public static final Parcelable.Creator<DescrambleInfo> CREATOR 78 = new Parcelable.Creator<DescrambleInfo>() { 79 public DescrambleInfo createFromParcel(Parcel in) { 80 return new DescrambleInfo(in); 81 } 82 83 public DescrambleInfo[] newArray(int size) { 84 return new DescrambleInfo[size]; 85 } 86 }; 87 } 88 89 /** 90 * Instantiate a MediaDescrambler. 91 * 92 * @param CA_system_id The system id of the scrambling scheme. 93 * 94 * @throws UnsupportedCasException if the scrambling scheme is not supported. 95 */ 96 public MediaDescrambler(int CA_system_id) throws UnsupportedCasException { 97 try { 98 mIDescrambler = MediaCas.getService().createDescrambler(CA_system_id); 99 } catch(Exception e) { 100 Log.e(TAG, "Failed to create descrambler: " + e); 101 mIDescrambler = null; 102 } finally { 103 if (mIDescrambler == null) { 104 throw new UnsupportedCasException("Unsupported CA_system_id " + CA_system_id); 105 } 106 } 107 native_setup(mIDescrambler.asBinder()); 108 } 109 110 IBinder getBinder() { 111 validateInternalStates(); 112 113 return mIDescrambler.asBinder(); 114 } 115 116 /** 117 * Query if the scrambling scheme requires the use of a secure decoder 118 * to decode data of the given mime type. 119 * 120 * @param mime The mime type of the media data 121 * 122 * @throws IllegalStateException if the descrambler instance is not valid. 123 */ 124 public final boolean requiresSecureDecoderComponent(@NonNull String mime) { 125 validateInternalStates(); 126 127 try { 128 return mIDescrambler.requiresSecureDecoderComponent(mime); 129 } catch (RemoteException e) { 130 cleanupAndRethrowIllegalState(); 131 } 132 return true; 133 } 134 135 /** 136 * Associate a MediaCas session with this MediaDescrambler instance. 137 * The MediaCas session is used to securely load decryption keys for 138 * the descrambler. The crypto keys loaded through the MediaCas session 139 * may be selected for use during the descrambling operation performed 140 * by {@link android.media.MediaExtractor or @link 141 * android.media.MediaCodec#queueSecureInputBuffer} by specifying even 142 * or odd key in the {@link android.media.MediaCodec.CryptoInfo#key} field. 143 * 144 * @param session the MediaCas session to associate with this 145 * MediaDescrambler instance. 146 * 147 * @throws IllegalStateException if the descrambler instance is not valid. 148 * @throws MediaCasStateException for CAS-specific state exceptions. 149 */ 150 public final void setMediaCasSession(@NonNull MediaCas.Session session) { 151 validateInternalStates(); 152 153 try { 154 mIDescrambler.setMediaCasSession(session.mSessionId); 155 } catch (ServiceSpecificException e) { 156 MediaCasStateException.throwExceptions(e); 157 } catch (RemoteException e) { 158 cleanupAndRethrowIllegalState(); 159 } 160 } 161 162 /** 163 * Descramble a ByteBuffer of data described by a 164 * {@link android.media.MediaCodec.CryptoInfo} structure. 165 * 166 * @param srcBuf ByteBuffer containing the scrambled data, which starts at 167 * srcBuf.position(). 168 * @param dstBuf ByteBuffer to hold the descrambled data, which starts at 169 * dstBuf.position(). 170 * @param cryptoInfo a {@link android.media.MediaCodec.CryptoInfo} structure 171 * describing the subsamples contained in src. 172 * 173 * @return number of bytes that have been successfully descrambled, with negative 174 * values indicating errors. 175 * 176 * @throws IllegalStateException if the descrambler instance is not valid. 177 * @throws MediaCasStateException for CAS-specific state exceptions. 178 */ 179 public final int descramble( 180 @NonNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf, 181 @NonNull MediaCodec.CryptoInfo cryptoInfo) { 182 validateInternalStates(); 183 184 if (cryptoInfo.numSubSamples <= 0) { 185 throw new IllegalArgumentException( 186 "Invalid CryptoInfo: invalid numSubSamples=" + cryptoInfo.numSubSamples); 187 } else if (cryptoInfo.numBytesOfClearData == null 188 && cryptoInfo.numBytesOfEncryptedData == null) { 189 throw new IllegalArgumentException( 190 "Invalid CryptoInfo: clearData and encryptedData size arrays are both null!"); 191 } else if (cryptoInfo.numBytesOfClearData != null 192 && cryptoInfo.numBytesOfClearData.length < cryptoInfo.numSubSamples) { 193 throw new IllegalArgumentException( 194 "Invalid CryptoInfo: numBytesOfClearData is too small!"); 195 } else if (cryptoInfo.numBytesOfEncryptedData != null 196 && cryptoInfo.numBytesOfEncryptedData.length < cryptoInfo.numSubSamples) { 197 throw new IllegalArgumentException( 198 "Invalid CryptoInfo: numBytesOfEncryptedData is too small!"); 199 } else if (cryptoInfo.key == null || cryptoInfo.key.length != 16) { 200 throw new IllegalArgumentException( 201 "Invalid CryptoInfo: key array is invalid!"); 202 } 203 204 try { 205 return native_descramble( 206 cryptoInfo.key[0], 207 cryptoInfo.numSubSamples, 208 cryptoInfo.numBytesOfClearData, 209 cryptoInfo.numBytesOfEncryptedData, 210 srcBuf, srcBuf.position(), srcBuf.limit(), 211 dstBuf, dstBuf.position(), dstBuf.limit()); 212 } catch (ServiceSpecificException e) { 213 MediaCasStateException.throwExceptions(e); 214 } 215 return -1; 216 } 217 218 @Override 219 public void close() { 220 if (mIDescrambler != null) { 221 try { 222 mIDescrambler.release(); 223 } catch (RemoteException e) { 224 } finally { 225 mIDescrambler = null; 226 } 227 } 228 native_release(); 229 } 230 231 @Override 232 protected void finalize() { 233 close(); 234 } 235 236 private static native final void native_init(); 237 private native final void native_setup(@NonNull IBinder decramblerBinder); 238 private native final void native_release(); 239 private native final int native_descramble( 240 byte key, int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData, 241 @NonNull ByteBuffer srcBuf, int srcOffset, int srcLimit, 242 ByteBuffer dstBuf, int dstOffset, int dstLimit); 243 244 static { 245 System.loadLibrary("media_jni"); 246 native_init(); 247 } 248 249 private long mNativeContext; 250}