1487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk/* 2487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * Copyright (C) 2012 The Android Open Source Project 3487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * 4487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * Licensed under the Apache License, Version 2.0 (the "License"); 5487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * you may not use this file except in compliance with the License. 6487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * You may obtain a copy of the License at 7487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * 8487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * http://www.apache.org/licenses/LICENSE-2.0 9487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * 10487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * Unless required by applicable law or agreed to in writing, software 11487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * distributed under the License is distributed on an "AS IS" BASIS, 12487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * See the License for the specific language governing permissions and 14487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * limitations under the License. 15487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk */ 16487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 17487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukpackage android.drm; 18487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 19487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport static android.drm.DrmConvertedStatus.STATUS_OK; 20487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport static android.drm.DrmManagerClient.INVALID_SESSION; 21487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport static android.system.OsConstants.SEEK_SET; 22487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 23487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport android.os.ParcelFileDescriptor; 24487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport android.system.ErrnoException; 25487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport android.system.Os; 26487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport android.util.Log; 27487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 28487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport libcore.io.IoBridge; 29487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport libcore.io.Streams; 30487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 31487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport java.io.FileDescriptor; 32487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport java.io.FilterOutputStream; 33487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport java.io.IOException; 34487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport java.io.OutputStream; 35487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport java.net.UnknownServiceException; 36487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukimport java.util.Arrays; 37487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 38487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk/** 39487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * Stream that applies a {@link DrmManagerClient} transformation to data before 40487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * writing to disk, similar to a {@link FilterOutputStream}. 41487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * 42487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * @hide 43487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk */ 44487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsukpublic class DrmOutputStream extends OutputStream { 45487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk private static final String TAG = "DrmOutputStream"; 46487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 47487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk private final DrmManagerClient mClient; 48487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk private final ParcelFileDescriptor mPfd; 49487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk private final FileDescriptor mFd; 50487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 51487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk private int mSessionId = INVALID_SESSION; 52487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 53487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk /** 54487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk * @param pfd Opened with "rw" mode. 55487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk */ 56487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk public DrmOutputStream(DrmManagerClient client, ParcelFileDescriptor pfd, String mimeType) 57487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk throws IOException { 58487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk mClient = client; 59487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk mPfd = pfd; 60487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk mFd = pfd.getFileDescriptor(); 61487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 62487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk mSessionId = mClient.openConvertSession(mimeType); 63487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk if (mSessionId == INVALID_SESSION) { 64487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk throw new UnknownServiceException("Failed to open DRM session for " + mimeType); 65487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 66487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 67487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 68487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk public void finish() throws IOException { 69487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk final DrmConvertedStatus status = mClient.closeConvertSession(mSessionId); 70487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk if (status.statusCode == STATUS_OK) { 71487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk try { 72487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk Os.lseek(mFd, status.offset, SEEK_SET); 73487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } catch (ErrnoException e) { 74487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk e.rethrowAsIOException(); 75487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 76487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk IoBridge.write(mFd, status.convertedData, 0, status.convertedData.length); 77487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk mSessionId = INVALID_SESSION; 78487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } else { 79487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk throw new IOException("Unexpected DRM status: " + status.statusCode); 80487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 81487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 82487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 83487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk @Override 84487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk public void close() throws IOException { 85487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk if (mSessionId == INVALID_SESSION) { 86487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk Log.w(TAG, "Closing stream without finishing"); 87487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 88487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 89487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk mPfd.close(); 90487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 91487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 92487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk @Override 93487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk public void write(byte[] buffer, int offset, int count) throws IOException { 94487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk Arrays.checkOffsetAndCount(buffer.length, offset, count); 95487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 96487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk final byte[] exactBuffer; 97487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk if (count == buffer.length) { 98487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk exactBuffer = buffer; 99487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } else { 100487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk exactBuffer = new byte[count]; 101487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk System.arraycopy(buffer, offset, exactBuffer, 0, count); 102487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 103487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 104487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk final DrmConvertedStatus status = mClient.convertData(mSessionId, exactBuffer); 105487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk if (status.statusCode == STATUS_OK) { 106487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk IoBridge.write(mFd, status.convertedData, 0, status.convertedData.length); 107487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } else { 108487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk throw new IOException("Unexpected DRM status: " + status.statusCode); 109487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 110487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 111487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk 112487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk @Override 113487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk public void write(int oneByte) throws IOException { 114487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk Streams.writeSingleByte(this, oneByte); 115487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk } 116487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk} 117487a92caef2eb90a62e8f8d7a6fe6315f1c1d8d8Rob Tsuk