15ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski/*
25ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * Copyright (C) 2016 The Android Open Source Project
35ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski *
45ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * Licensed under the Apache License, Version 2.0 (the "License");
55ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * you may not use this file except in compliance with the License.
65ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * You may obtain a copy of the License at
75ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski *
85ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski *      http://www.apache.org/licenses/LICENSE-2.0
95ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski *
105ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * Unless required by applicable law or agreed to in writing, software
115ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * distributed under the License is distributed on an "AS IS" BASIS,
125ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * See the License for the specific language governing permissions and
145ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * limitations under the License.
155ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski */
165ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
175ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskipackage com.android.documentsui.archives;
185ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
195ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport android.os.ProxyFileDescriptorCallback;
205ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport android.system.ErrnoException;
215ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport android.system.OsConstants;
225ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport android.util.Log;
235ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport android.util.jar.StrictJarFile;
245ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
255ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport java.io.IOException;
265ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport java.io.InputStream;
275ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport java.util.zip.ZipEntry;
285ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
295ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskiimport libcore.io.IoUtils;
305ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
315ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski/**
325ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski * Provides a backend for a seekable file descriptors for files in archives.
335ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski */
345ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewskipublic class Proxy extends ProxyFileDescriptorCallback {
355ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    private final StrictJarFile mFile;
365ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    private final ZipEntry mEntry;
375ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    private InputStream mInputStream = null;
385ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    private long mOffset = 0;
395ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
405ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    Proxy(StrictJarFile file, ZipEntry entry) throws IOException {
415ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        mFile = file;
425ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        mEntry = entry;
435ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        recreateInputStream();
445ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    }
455ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
465ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    @Override
475ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    public long onGetSize() throws ErrnoException {
485ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        return mEntry.getSize();
495ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    }
505ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
515ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    @Override
525ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    public int onRead(long offset, int size, byte[] data) throws ErrnoException {
535ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        // TODO: Add a ring buffer to prevent expensive seeks.
545ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        if (offset < mOffset) {
555ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            try {
565ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                recreateInputStream();
575ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            } catch (IOException e) {
585ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                throw new ErrnoException("onRead", OsConstants.EIO);
595ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            }
605ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        }
615ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
625ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        while (mOffset < offset) {
635ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            try {
645ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                mOffset +=  mInputStream.skip(offset - mOffset);
655ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            } catch (IOException e) {
665ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                throw new ErrnoException("onRead", OsConstants.EIO);
675ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            }
685ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        }
695ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
705ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        int remainingSize = size;
715ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        while (remainingSize > 0) {
725ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            try {
735ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                int bytes = mInputStream.read(data, size - remainingSize, remainingSize);
745ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                if (bytes <= 0) {
755ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                    return size - remainingSize;
765ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                }
775ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                remainingSize -= bytes;
785ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                mOffset += bytes;
795ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            } catch (IOException e) {
805ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski                throw new ErrnoException("onRead", OsConstants.EIO);
815ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski            }
825ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        }
835ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
845ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        return size - remainingSize;
855ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski   }
865ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
875ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    @Override public void onRelease() {
885ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        IoUtils.closeQuietly(mInputStream);
895ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    }
905ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski
915ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    private void recreateInputStream() throws IOException {
925ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        IoUtils.closeQuietly(mInputStream);
935ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        mInputStream = mFile.getInputStream(mEntry);
945ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski        mOffset = 0;
955ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski    }
965ed698344f36d5ce7c342033e7c8fe370d1c9c69Tomasz Mikolajewski}
97