1f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey/* 2f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * Copyright (C) 2012 The Android Open Source Project 3f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * 4f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * you may not use this file except in compliance with the License. 6f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * You may obtain a copy of the License at 7f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * 8f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * 10f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * See the License for the specific language governing permissions and 14f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * limitations under the License. 15f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey */ 16f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 17f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkeypackage android.drm; 18f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 19f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkeyimport static android.drm.DrmConvertedStatus.STATUS_OK; 207ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkeyimport static android.drm.DrmManagerClient.INVALID_SESSION; 21c1858228df18528aabe1ff223335f613be2b99d8Elliott Hughesimport static android.system.OsConstants.SEEK_SET; 227ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey 23ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkeyimport android.os.ParcelFileDescriptor; 24f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.ErrnoException; 25f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.Os; 267ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkeyimport android.util.Log; 27f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 28ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkeyimport libcore.io.IoBridge; 29ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkeyimport libcore.io.Streams; 30ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey 31ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkeyimport java.io.FileDescriptor; 32f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkeyimport java.io.FilterOutputStream; 33f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkeyimport java.io.IOException; 34f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkeyimport java.io.OutputStream; 35f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkeyimport java.net.UnknownServiceException; 36f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkeyimport java.util.Arrays; 37f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 38f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey/** 39f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * Stream that applies a {@link DrmManagerClient} transformation to data before 40f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * writing to disk, similar to a {@link FilterOutputStream}. 41f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * 42f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey * @hide 43f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey */ 44f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkeypublic class DrmOutputStream extends OutputStream { 457ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey private static final String TAG = "DrmOutputStream"; 46f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 47f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey private final DrmManagerClient mClient; 48ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey private final ParcelFileDescriptor mPfd; 49ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey private final FileDescriptor mFd; 50f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 517ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey private int mSessionId = INVALID_SESSION; 52f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 537ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey /** 54ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey * @param pfd Opened with "rw" mode. 557ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey */ 56ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey public DrmOutputStream(DrmManagerClient client, ParcelFileDescriptor pfd, String mimeType) 577ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey throws IOException { 58f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey mClient = client; 59ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey mPfd = pfd; 60ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey mFd = pfd.getFileDescriptor(); 617ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey 627ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey mSessionId = mClient.openConvertSession(mimeType); 637ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey if (mSessionId == INVALID_SESSION) { 647ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey throw new UnknownServiceException("Failed to open DRM session for " + mimeType); 657ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey } 667ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey } 677ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey 687ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey public void finish() throws IOException { 697ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey final DrmConvertedStatus status = mClient.closeConvertSession(mSessionId); 707ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey if (status.statusCode == STATUS_OK) { 71ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey try { 72f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughes Os.lseek(mFd, status.offset, SEEK_SET); 73ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey } catch (ErrnoException e) { 74ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey e.rethrowAsIOException(); 75ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey } 76ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey IoBridge.write(mFd, status.convertedData, 0, status.convertedData.length); 777ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey mSessionId = INVALID_SESSION; 787ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey } else { 797ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey throw new IOException("Unexpected DRM status: " + status.statusCode); 80f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } 81f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } 82f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 83f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey @Override 84f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey public void close() throws IOException { 857ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey if (mSessionId == INVALID_SESSION) { 867ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey Log.w(TAG, "Closing stream without finishing"); 87f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } 887ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey 89ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey mPfd.close(); 90f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } 91f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 92f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey @Override 93f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey public void write(byte[] buffer, int offset, int count) throws IOException { 94f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey Arrays.checkOffsetAndCount(buffer.length, offset, count); 95f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 96f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey final byte[] exactBuffer; 97f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey if (count == buffer.length) { 98f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey exactBuffer = buffer; 99f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } else { 100f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey exactBuffer = new byte[count]; 101f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey System.arraycopy(buffer, offset, exactBuffer, 0, count); 102f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } 103f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 104f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey final DrmConvertedStatus status = mClient.convertData(mSessionId, exactBuffer); 105f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey if (status.statusCode == STATUS_OK) { 106ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey IoBridge.write(mFd, status.convertedData, 0, status.convertedData.length); 107f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } else { 108f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey throw new IOException("Unexpected DRM status: " + status.statusCode); 109f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } 110f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } 111f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey 112f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey @Override 113f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey public void write(int oneByte) throws IOException { 1147ccc90955fad0701b6b14c813a2850ee4149c5a2Jeff Sharkey Streams.writeSingleByte(this, oneByte); 115f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey } 116f67c8a9685e0f20d5ffb9de95f6d1ce47f052141Jeff Sharkey} 117