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