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.annotation.TestApi; 21 import android.hardware.cas.IDescrambler; 22 import android.hardware.cas.ScramblingControl; 23 import android.hardware.cas.V1_0.IDescramblerBase; 24 import android.media.MediaCasException.UnsupportedCasException; 25 import android.os.IHwBinder; 26 import android.os.RemoteException; 27 import android.os.ServiceSpecificException; 28 import android.util.Log; 29 30 import java.nio.ByteBuffer; 31 import java.util.ArrayList; 32 33 /** 34 * MediaDescrambler class can be used in conjunction with {@link android.media.MediaCodec} 35 * and {@link android.media.MediaExtractor} to decode media data scrambled by conditional 36 * access (CA) systems such as those in the ISO/IEC13818-1. 37 * 38 * A MediaDescrambler object is initialized from a session opened by a MediaCas object, 39 * and can be used to descramble media streams scrambled with that session's keys. 40 * 41 * Scrambling schemes are identified by 16-bit unsigned integer as in CA_system_id. 42 * 43 */ 44 public final class MediaDescrambler implements AutoCloseable { 45 private static final String TAG = "MediaDescrambler"; 46 private DescramblerWrapper mIDescrambler; 47 private boolean mIsAidlHal; 48 49 private interface DescramblerWrapper { 50 asBinder()51 IHwBinder asBinder(); 52 descramble( @onNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf, @NonNull MediaCodec.CryptoInfo cryptoInfo)53 int descramble( 54 @NonNull ByteBuffer srcBuf, 55 @NonNull ByteBuffer dstBuf, 56 @NonNull MediaCodec.CryptoInfo cryptoInfo) 57 throws RemoteException; 58 requiresSecureDecoderComponent(@onNull String mime)59 boolean requiresSecureDecoderComponent(@NonNull String mime) throws RemoteException; 60 setMediaCasSession(byte[] sessionId)61 void setMediaCasSession(byte[] sessionId) throws RemoteException; 62 release()63 void release() throws RemoteException; 64 }; 65 66 private class AidlDescrambler implements DescramblerWrapper { 67 68 IDescrambler mAidlDescrambler; 69 AidlDescrambler(IDescrambler aidlDescrambler)70 AidlDescrambler(IDescrambler aidlDescrambler) throws Exception { 71 if (aidlDescrambler != null) { 72 mAidlDescrambler = aidlDescrambler; 73 } else { 74 throw new Exception("Descrambler could not be created"); 75 } 76 } 77 78 @Override asBinder()79 public IHwBinder asBinder() { 80 return null; 81 } 82 83 @Override descramble( @onNull ByteBuffer src, @NonNull ByteBuffer dst, @NonNull MediaCodec.CryptoInfo cryptoInfo)84 public int descramble( 85 @NonNull ByteBuffer src, 86 @NonNull ByteBuffer dst, 87 @NonNull MediaCodec.CryptoInfo cryptoInfo) 88 throws RemoteException { 89 throw new RemoteException("Not supported"); 90 } 91 92 @Override requiresSecureDecoderComponent(@onNull String mime)93 public boolean requiresSecureDecoderComponent(@NonNull String mime) throws RemoteException { 94 throw new RemoteException("Not supported"); 95 } 96 97 @Override setMediaCasSession(byte[] sessionId)98 public void setMediaCasSession(byte[] sessionId) throws RemoteException { 99 throw new RemoteException("Not supported"); 100 } 101 102 @Override release()103 public void release() throws RemoteException { 104 mAidlDescrambler.release(); 105 } 106 } 107 108 private class HidlDescrambler implements DescramblerWrapper { 109 110 IDescramblerBase mHidlDescrambler; 111 HidlDescrambler(IDescramblerBase hidlDescrambler)112 HidlDescrambler(IDescramblerBase hidlDescrambler) throws Exception { 113 if (hidlDescrambler != null) { 114 mHidlDescrambler = hidlDescrambler; 115 native_setup(hidlDescrambler.asBinder()); 116 } else { 117 throw new Exception("Descrambler could not be created"); 118 } 119 } 120 121 @Override asBinder()122 public IHwBinder asBinder() { 123 return mHidlDescrambler.asBinder(); 124 } 125 126 @Override descramble( @onNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf, @NonNull MediaCodec.CryptoInfo cryptoInfo)127 public int descramble( 128 @NonNull ByteBuffer srcBuf, 129 @NonNull ByteBuffer dstBuf, 130 @NonNull MediaCodec.CryptoInfo cryptoInfo) 131 throws RemoteException { 132 133 try { 134 return native_descramble( 135 cryptoInfo.key[0], 136 cryptoInfo.key[1], 137 cryptoInfo.numSubSamples, 138 cryptoInfo.numBytesOfClearData, 139 cryptoInfo.numBytesOfEncryptedData, 140 srcBuf, 141 srcBuf.position(), 142 srcBuf.limit(), 143 dstBuf, 144 dstBuf.position(), 145 dstBuf.limit()); 146 } catch (ServiceSpecificException e) { 147 MediaCasStateException.throwExceptionIfNeeded(e.errorCode, e.getMessage()); 148 } catch (RemoteException e) { 149 cleanupAndRethrowIllegalState(); 150 } 151 return -1; 152 } 153 154 @Override requiresSecureDecoderComponent(@onNull String mime)155 public boolean requiresSecureDecoderComponent(@NonNull String mime) throws RemoteException { 156 return mHidlDescrambler.requiresSecureDecoderComponent(mime); 157 } 158 159 @Override setMediaCasSession(byte[] sessionId)160 public void setMediaCasSession(byte[] sessionId) throws RemoteException { 161 ArrayList<Byte> byteArray = new ArrayList<>(); 162 163 if (sessionId != null) { 164 int length = sessionId.length; 165 byteArray = new ArrayList<Byte>(length); 166 for (int i = 0; i < length; i++) { 167 byteArray.add(Byte.valueOf(sessionId[i])); 168 } 169 } 170 171 MediaCasStateException.throwExceptionIfNeeded( 172 mHidlDescrambler.setMediaCasSession(byteArray)); 173 } 174 175 @Override release()176 public void release() throws RemoteException { 177 mHidlDescrambler.release(); 178 native_release(); 179 } 180 } 181 validateInternalStates()182 private final void validateInternalStates() { 183 if (mIDescrambler == null) { 184 throw new IllegalStateException(); 185 } 186 } 187 cleanupAndRethrowIllegalState()188 private final void cleanupAndRethrowIllegalState() { 189 mIDescrambler = null; 190 throw new IllegalStateException(); 191 } 192 193 /** 194 * Instantiate a MediaDescrambler. 195 * 196 * @param CA_system_id The system id of the scrambling scheme. 197 * 198 * @throws UnsupportedCasException if the scrambling scheme is not supported. 199 */ MediaDescrambler(int CA_system_id)200 public MediaDescrambler(int CA_system_id) throws UnsupportedCasException { 201 try { 202 if (MediaCas.getService() != null) { 203 mIDescrambler = 204 new AidlDescrambler(MediaCas.getService().createDescrambler(CA_system_id)); 205 mIsAidlHal = true; 206 } else if (MediaCas.getServiceHidl() != null) { 207 mIDescrambler = 208 new HidlDescrambler( 209 MediaCas.getServiceHidl().createDescrambler(CA_system_id)); 210 mIsAidlHal = false; 211 } else { 212 throw new Exception("No CAS service found!"); 213 } 214 } catch(Exception e) { 215 Log.e(TAG, "Failed to create descrambler: " + e); 216 mIDescrambler = null; 217 } finally { 218 if (mIDescrambler == null) { 219 throw new UnsupportedCasException("Unsupported CA_system_id " + CA_system_id); 220 } 221 } 222 } 223 224 /** 225 * Check if the underlying HAL is AIDL. For CTS testing purpose. 226 * 227 * @hide 228 */ 229 @TestApi isAidlHal()230 public boolean isAidlHal() { 231 return mIsAidlHal; 232 } 233 getBinder()234 IHwBinder getBinder() { 235 validateInternalStates(); 236 237 return mIDescrambler.asBinder(); 238 } 239 240 /** 241 * Query if the scrambling scheme requires the use of a secure decoder 242 * to decode data of the given mime type. 243 * 244 * @param mime The mime type of the media data 245 * 246 * @throws IllegalStateException if the descrambler instance is not valid. 247 */ requiresSecureDecoderComponent(@onNull String mime)248 public final boolean requiresSecureDecoderComponent(@NonNull String mime) { 249 validateInternalStates(); 250 251 try { 252 return mIDescrambler.requiresSecureDecoderComponent(mime); 253 } catch (RemoteException e) { 254 cleanupAndRethrowIllegalState(); 255 } 256 return true; 257 } 258 259 /** 260 * Associate a MediaCas session with this MediaDescrambler instance. 261 * The MediaCas session is used to securely load decryption keys for 262 * the descrambler. The crypto keys loaded through the MediaCas session 263 * may be selected for use during the descrambling operation performed 264 * by {@link android.media.MediaExtractor or @link 265 * android.media.MediaCodec#queueSecureInputBuffer} by specifying even 266 * or odd key in the {@link android.media.MediaCodec.CryptoInfo#key} field. 267 * 268 * @param session the MediaCas session to associate with this 269 * MediaDescrambler instance. 270 * 271 * @throws IllegalStateException if the descrambler instance is not valid. 272 * @throws MediaCasStateException for CAS-specific state exceptions. 273 */ setMediaCasSession(@onNull MediaCas.Session session)274 public final void setMediaCasSession(@NonNull MediaCas.Session session) { 275 validateInternalStates(); 276 277 try { 278 mIDescrambler.setMediaCasSession(session.mSessionId); 279 } catch (RemoteException e) { 280 cleanupAndRethrowIllegalState(); 281 } 282 } 283 284 /** 285 * Scramble control value indicating that the samples are not scrambled. 286 * 287 * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) 288 */ 289 public static final byte SCRAMBLE_CONTROL_UNSCRAMBLED = (byte) ScramblingControl.UNSCRAMBLED; 290 291 /** 292 * Scramble control value reserved and shouldn't be used currently. 293 * 294 * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) 295 */ 296 public static final byte SCRAMBLE_CONTROL_RESERVED = (byte) ScramblingControl.RESERVED; 297 298 /** 299 * Scramble control value indicating that the even key is used. 300 * 301 * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) 302 */ 303 public static final byte SCRAMBLE_CONTROL_EVEN_KEY = (byte) ScramblingControl.EVENKEY; 304 305 /** 306 * Scramble control value indicating that the odd key is used. 307 * 308 * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) 309 */ 310 public static final byte SCRAMBLE_CONTROL_ODD_KEY = (byte) ScramblingControl.ODDKEY; 311 312 /** 313 * Scramble flag for a hint indicating that the descrambling request is for 314 * retrieving the PES header info only. 315 * 316 * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo) 317 */ 318 public static final byte SCRAMBLE_FLAG_PES_HEADER = (1 << 0); 319 320 /** 321 * Descramble a ByteBuffer of data described by a 322 * {@link android.media.MediaCodec.CryptoInfo} structure. 323 * 324 * @param srcBuf ByteBuffer containing the scrambled data, which starts at 325 * srcBuf.position(). 326 * @param dstBuf ByteBuffer to hold the descrambled data, which starts at 327 * dstBuf.position(). 328 * @param cryptoInfo a {@link android.media.MediaCodec.CryptoInfo} structure 329 * describing the subsamples contained in srcBuf. The iv and mode fields in 330 * CryptoInfo are not used. key[0] contains the MPEG2TS scrambling control bits 331 * (as defined in ETSI TS 100 289 (2011): "Digital Video Broadcasting (DVB); 332 * Support for use of the DVB Scrambling Algorithm version 3 within digital 333 * broadcasting systems"), and the value must be one of {@link #SCRAMBLE_CONTROL_UNSCRAMBLED}, 334 * {@link #SCRAMBLE_CONTROL_RESERVED}, {@link #SCRAMBLE_CONTROL_EVEN_KEY} or 335 * {@link #SCRAMBLE_CONTROL_ODD_KEY}. key[1] is a set of bit flags, with the 336 * only possible bit being {@link #SCRAMBLE_FLAG_PES_HEADER} currently. 337 * key[2~15] are not used. 338 * 339 * @return number of bytes that have been successfully descrambled, with negative 340 * values indicating errors. 341 * 342 * @throws IllegalStateException if the descrambler instance is not valid. 343 * @throws MediaCasStateException for CAS-specific state exceptions. 344 */ descramble( @onNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf, @NonNull MediaCodec.CryptoInfo cryptoInfo)345 public final int descramble( 346 @NonNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf, 347 @NonNull MediaCodec.CryptoInfo cryptoInfo) { 348 validateInternalStates(); 349 350 if (cryptoInfo.numSubSamples <= 0) { 351 throw new IllegalArgumentException( 352 "Invalid CryptoInfo: invalid numSubSamples=" + cryptoInfo.numSubSamples); 353 } else if (cryptoInfo.numBytesOfClearData == null 354 && cryptoInfo.numBytesOfEncryptedData == null) { 355 throw new IllegalArgumentException( 356 "Invalid CryptoInfo: clearData and encryptedData size arrays are both null!"); 357 } else if (cryptoInfo.numBytesOfClearData != null 358 && cryptoInfo.numBytesOfClearData.length < cryptoInfo.numSubSamples) { 359 throw new IllegalArgumentException( 360 "Invalid CryptoInfo: numBytesOfClearData is too small!"); 361 } else if (cryptoInfo.numBytesOfEncryptedData != null 362 && cryptoInfo.numBytesOfEncryptedData.length < cryptoInfo.numSubSamples) { 363 throw new IllegalArgumentException( 364 "Invalid CryptoInfo: numBytesOfEncryptedData is too small!"); 365 } else if (cryptoInfo.key == null || cryptoInfo.key.length != 16) { 366 throw new IllegalArgumentException( 367 "Invalid CryptoInfo: key array is invalid!"); 368 } 369 370 try { 371 return mIDescrambler.descramble(srcBuf, dstBuf, cryptoInfo); 372 } catch (ServiceSpecificException e) { 373 MediaCasStateException.throwExceptionIfNeeded(e.errorCode, e.getMessage()); 374 } catch (RemoteException e) { 375 cleanupAndRethrowIllegalState(); 376 } 377 return -1; 378 } 379 380 @Override close()381 public void close() { 382 if (mIDescrambler != null) { 383 try { 384 mIDescrambler.release(); 385 } catch (RemoteException e) { 386 } finally { 387 mIDescrambler = null; 388 } 389 } 390 } 391 392 @Override finalize()393 protected void finalize() { 394 close(); 395 } 396 native_init()397 private static native final void native_init(); native_setup(@onNull IHwBinder decramblerBinder)398 private native final void native_setup(@NonNull IHwBinder decramblerBinder); native_release()399 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)400 private native final int native_descramble( 401 byte key, byte flags, int numSubSamples, 402 int[] numBytesOfClearData, int[] numBytesOfEncryptedData, 403 @NonNull ByteBuffer srcBuf, int srcOffset, int srcLimit, 404 ByteBuffer dstBuf, int dstOffset, int dstLimit) throws RemoteException; 405 406 static { 407 System.loadLibrary("media_jni"); native_init()408 native_init(); 409 } 410 411 private long mNativeContext; 412 } 413