blob: fcb29e3327cdb0770f8860b7874bad58f7d256d0 [file] [log] [blame]
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +09001/*
2 * Copyright (C) 2016 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
17package com.android.documentsui.archives;
18
felkachang1556c362018-08-30 16:21:59 +080019import android.os.FileUtils;
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090020import android.os.ProxyFileDescriptorCallback;
21import android.system.ErrnoException;
22import android.system.OsConstants;
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090023
24import java.io.IOException;
25import java.io.InputStream;
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090026
felkachang1556c362018-08-30 16:21:59 +080027import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
28import org.apache.commons.compress.archivers.zip.ZipFile;
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090029
30/**
31 * Provides a backend for a seekable file descriptors for files in archives.
32 */
33public class Proxy extends ProxyFileDescriptorCallback {
felkachang1556c362018-08-30 16:21:59 +080034 private final ZipFile mFile;
35 private final ZipArchiveEntry mEntry;
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090036 private InputStream mInputStream = null;
37 private long mOffset = 0;
38
felkachang1556c362018-08-30 16:21:59 +080039 Proxy(ZipFile file, ZipArchiveEntry entry) throws IOException {
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090040 mFile = file;
41 mEntry = entry;
42 recreateInputStream();
43 }
44
45 @Override
46 public long onGetSize() throws ErrnoException {
47 return mEntry.getSize();
48 }
49
50 @Override
51 public int onRead(long offset, int size, byte[] data) throws ErrnoException {
52 // TODO: Add a ring buffer to prevent expensive seeks.
53 if (offset < mOffset) {
54 try {
55 recreateInputStream();
56 } catch (IOException e) {
57 throw new ErrnoException("onRead", OsConstants.EIO);
58 }
59 }
60
61 while (mOffset < offset) {
62 try {
63 mOffset += mInputStream.skip(offset - mOffset);
64 } catch (IOException e) {
65 throw new ErrnoException("onRead", OsConstants.EIO);
66 }
67 }
68
69 int remainingSize = size;
70 while (remainingSize > 0) {
71 try {
72 int bytes = mInputStream.read(data, size - remainingSize, remainingSize);
73 if (bytes <= 0) {
74 return size - remainingSize;
75 }
76 remainingSize -= bytes;
77 mOffset += bytes;
78 } catch (IOException e) {
79 throw new ErrnoException("onRead", OsConstants.EIO);
80 }
81 }
82
83 return size - remainingSize;
felkachang1556c362018-08-30 16:21:59 +080084 }
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090085
86 @Override public void onRelease() {
Jeff Sharkey94785ef2018-07-09 16:37:41 -060087 FileUtils.closeQuietly(mInputStream);
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090088 }
89
90 private void recreateInputStream() throws IOException {
Jeff Sharkey94785ef2018-07-09 16:37:41 -060091 FileUtils.closeQuietly(mInputStream);
Tomasz Mikolajewski5ed69832016-11-30 17:43:57 +090092 mInputStream = mFile.getInputStream(mEntry);
93 mOffset = 0;
94 }
95}