Skip to content

Commit c43041e

Browse files
committed
improve preview loading speed
1 parent 8853adc commit c43041e

File tree

8 files changed

+532
-95
lines changed

8 files changed

+532
-95
lines changed
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
package com.katcom.androidFileVault.CustomedCrypto;
2+
3+
/*
4+
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
5+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6+
*
7+
* This code is free software; you can redistribute it and/or modify it
8+
* under the terms of the GNU General Public License version 2 only, as
9+
* published by the Free Software Foundation. Oracle designates this
10+
* particular file as subject to the "Classpath" exception as provided
11+
* by Oracle in the LICENSE file that accompanied this code.
12+
*
13+
* This code is distributed in the hope that it will be useful, but WITHOUT
14+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16+
* version 2 for more details (a copy is included in the LICENSE file that
17+
* accompanied this code).
18+
*
19+
* You should have received a copy of the GNU General Public License version
20+
* 2 along with this work; if not, write to the Free Software Foundation,
21+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22+
*
23+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
24+
* or visit www.oracle.com if you need additional information or have any
25+
* questions.
26+
*/
27+
import java.io.InputStream;
28+
import java.io.FilterInputStream;
29+
import java.io.IOException;
30+
31+
import javax.crypto.AEADBadTagException;
32+
import javax.crypto.BadPaddingException;
33+
import javax.crypto.Cipher;
34+
import javax.crypto.IllegalBlockSizeException;
35+
import javax.crypto.NullCipher;
36+
import javax.crypto.ShortBufferException;
37+
38+
/**
39+
* A CipherInputStream is composed of an InputStream and a Cipher so
40+
* that read() methods return data that are read in from the
41+
* underlying InputStream but have been additionally processed by the
42+
* Cipher. The Cipher must be fully initialized before being used by
43+
* a CipherInputStream.
44+
*
45+
* <p> For example, if the Cipher is initialized for decryption, the
46+
* CipherInputStream will attempt to read in data and decrypt them,
47+
* before returning the decrypted data.
48+
*
49+
* <p> This class adheres strictly to the semantics, especially the
50+
* failure semantics, of its ancestor classes
51+
* java.io.FilterInputStream and java.io.InputStream. This class has
52+
* exactly those methods specified in its ancestor classes, and
53+
* overrides them all. Moreover, this class catches all exceptions
54+
* that are not thrown by its ancestor classes. In particular, the
55+
* <code>skip</code> method skips, and the <code>available</code>
56+
* method counts only data that have been processed by the encapsulated Cipher.
57+
*
58+
* <p> It is crucial for a programmer using this class not to use
59+
* methods that are not defined or overriden in this class (such as a
60+
* new method or constructor that is later added to one of the super
61+
* classes), because the design and implementation of those methods
62+
* are unlikely to have considered security impact with regard to
63+
* CipherInputStream.
64+
*
65+
* @author Li Gong
66+
* @see java.io.InputStream
67+
* @see java.io.FilterInputStream
68+
* @see javax.crypto.Cipher
69+
* @see javax.crypto.CipherOutputStream
70+
*
71+
* @since 1.4
72+
*/
73+
74+
public class CustomCipherInputStream extends FilterInputStream {
75+
76+
// the cipher engine to use to process stream data
77+
private Cipher cipher;
78+
79+
// the underlying input stream
80+
private InputStream input;
81+
82+
/* the buffer holding data that have been read in from the
83+
underlying stream, but have not been processed by the cipher
84+
engine. This class change the buffer size from 512 of the original CipherInputStream to 8192 */
85+
private byte[] ibuffer = new byte[8192];
86+
87+
// having reached the end of the underlying input stream
88+
private boolean done = false;
89+
90+
/* the buffer holding data that have been processed by the cipher
91+
engine, but have not been read out */
92+
private byte[] obuffer;
93+
// the offset pointing to the next "new" byte
94+
private int ostart = 0;
95+
// the offset pointing to the last "new" byte
96+
private int ofinish = 0;
97+
// stream status
98+
private boolean closed = false;
99+
100+
/**
101+
* private convenience function.
102+
*
103+
* Entry condition: ostart = ofinish
104+
*
105+
* Exit condition: ostart <= ofinish
106+
*
107+
* return (ofinish-ostart) (we have this many bytes for you)
108+
* return 0 (no data now, but could have more later)
109+
* return -1 (absolutely no more data)
110+
*
111+
* Note: Exceptions are only thrown after the stream is completely read.
112+
* For AEAD ciphers a read() of any length will internally cause the
113+
* whole stream to be read fully and verify the authentication tag before
114+
* returning decrypted data or exceptions.
115+
*/
116+
private int getMoreData() throws IOException {
117+
// Android-changed: The method was creating a new object every time update(byte[], int, int)
118+
// or doFinal() was called resulting in the old object being GCed. With do(byte[], int) and
119+
// update(byte[], int, int, byte[], int), we use already initialized obuffer.
120+
if (done) return -1;
121+
ofinish = 0;
122+
ostart = 0;
123+
int expectedOutputSize = cipher.getOutputSize(ibuffer.length);
124+
if (obuffer == null || expectedOutputSize > obuffer.length) {
125+
obuffer = new byte[expectedOutputSize];
126+
}
127+
int readin = input.read(ibuffer);
128+
if (readin == -1) {
129+
done = true;
130+
try {
131+
// doFinal resets the cipher and it is the final call that is made. If there isn't
132+
// any more byte available, it returns 0. In case of any exception is raised,
133+
// obuffer will get reset and therefore, it is equivalent to no bytes returned.
134+
ofinish = cipher.doFinal(obuffer, 0);
135+
} catch (IllegalBlockSizeException | BadPaddingException e) {
136+
obuffer = null;
137+
throw new IOException(e);
138+
} catch (ShortBufferException e) {
139+
obuffer = null;
140+
throw new IllegalStateException("ShortBufferException is not expected", e);
141+
}
142+
} else {
143+
// update returns number of bytes stored in obuffer.
144+
try {
145+
ofinish = cipher.update(ibuffer, 0, readin, obuffer, 0);
146+
} catch (IllegalStateException e) {
147+
obuffer = null;
148+
throw e;
149+
} catch (ShortBufferException e) {
150+
// Should not reset the value of ofinish as the cipher is still not invalidated.
151+
obuffer = null;
152+
throw new IllegalStateException("ShortBufferException is not expected", e);
153+
}
154+
}
155+
return ofinish;
156+
}
157+
158+
/**
159+
* Constructs a CipherInputStream from an InputStream and a
160+
* Cipher.
161+
* <br>Note: if the specified input stream or cipher is
162+
* null, a NullPointerException may be thrown later when
163+
* they are used.
164+
* @param is the to-be-processed input stream
165+
* @param c an initialized Cipher object
166+
*/
167+
public CustomCipherInputStream(InputStream is, Cipher c) {
168+
super(is);
169+
input = is;
170+
cipher = c;
171+
}
172+
173+
/**
174+
* Constructs a CipherInputStream from an InputStream without
175+
* specifying a Cipher. This has the effect of constructing a
176+
* CipherInputStream using a NullCipher.
177+
* <br>Note: if the specified input stream is null, a
178+
* NullPointerException may be thrown later when it is used.
179+
* @param is the to-be-processed input stream
180+
*/
181+
protected CustomCipherInputStream(InputStream is) {
182+
super(is);
183+
input = is;
184+
cipher = new NullCipher();
185+
}
186+
187+
/**
188+
* Reads the next byte of data from this input stream. The value
189+
* byte is returned as an <code>int</code> in the range
190+
* <code>0</code> to <code>255</code>. If no byte is available
191+
* because the end of the stream has been reached, the value
192+
* <code>-1</code> is returned. This method blocks until input data
193+
* is available, the end of the stream is detected, or an exception
194+
* is thrown.
195+
* <p>
196+
*
197+
* @return the next byte of data, or <code>-1</code> if the end of the
198+
* stream is reached.
199+
* @exception IOException if an I/O error occurs.
200+
* @since JCE1.2
201+
*/
202+
public int read() throws IOException {
203+
if (ostart >= ofinish) {
204+
// we loop for new data as the spec says we are blocking
205+
int i = 0;
206+
while (i == 0) i = getMoreData();
207+
if (i == -1) return -1;
208+
}
209+
return ((int) obuffer[ostart++] & 0xff);
210+
};
211+
212+
/**
213+
* Reads up to <code>b.length</code> bytes of data from this input
214+
* stream into an array of bytes.
215+
* <p>
216+
* The <code>read</code> method of <code>InputStream</code> calls
217+
* the <code>read</code> method of three arguments with the arguments
218+
* <code>b</code>, <code>0</code>, and <code>b.length</code>.
219+
*
220+
* @param b the buffer into which the data is read.
221+
* @return the total number of bytes read into the buffer, or
222+
* <code>-1</code> is there is no more data because the end of
223+
* the stream has been reached.
224+
* @exception IOException if an I/O error occurs.
225+
* @see java.io.InputStream#read(byte[], int, int)
226+
* @since JCE1.2
227+
*/
228+
public int read(byte b[]) throws IOException {
229+
return read(b, 0, b.length);
230+
}
231+
232+
/**
233+
* Reads up to <code>len</code> bytes of data from this input stream
234+
* into an array of bytes. This method blocks until some input is
235+
* available. If the first argument is <code>null,</code> up to
236+
* <code>len</code> bytes are read and discarded.
237+
*
238+
* @param b the buffer into which the data is read.
239+
* @param off the start offset in the destination array
240+
* <code>buf</code>
241+
* @param len the maximum number of bytes read.
242+
* @return the total number of bytes read into the buffer, or
243+
* <code>-1</code> if there is no more data because the end of
244+
* the stream has been reached.
245+
* @exception IOException if an I/O error occurs.
246+
* @see java.io.InputStream#read()
247+
* @since JCE1.2
248+
*/
249+
public int read(byte b[], int off, int len) throws IOException {
250+
if (ostart >= ofinish) {
251+
// we loop for new data as the spec says we are blocking
252+
int i = 0;
253+
while (i == 0) i = getMoreData();
254+
if (i == -1) return -1;
255+
}
256+
if (len <= 0) {
257+
return 0;
258+
}
259+
int available = ofinish - ostart;
260+
if (len < available) available = len;
261+
if (b != null) {
262+
System.arraycopy(obuffer, ostart, b, off, available);
263+
}
264+
ostart = ostart + available;
265+
return available;
266+
}
267+
268+
/**
269+
* Skips <code>n</code> bytes of input from the bytes that can be read
270+
* from this input stream without blocking.
271+
*
272+
* <p>Fewer bytes than requested might be skipped.
273+
* The actual number of bytes skipped is equal to <code>n</code> or
274+
* the result of a call to
275+
* {@link #available() available},
276+
* whichever is smaller.
277+
* If <code>n</code> is less than zero, no bytes are skipped.
278+
*
279+
* <p>The actual number of bytes skipped is returned.
280+
*
281+
* @param n the number of bytes to be skipped.
282+
* @return the actual number of bytes skipped.
283+
* @exception IOException if an I/O error occurs.
284+
* @since JCE1.2
285+
*/
286+
public long skip(long n) throws IOException {
287+
int available = ofinish - ostart;
288+
if (n > available) {
289+
n = available;
290+
}
291+
if (n < 0) {
292+
return 0;
293+
}
294+
ostart += n;
295+
return n;
296+
}
297+
298+
/**
299+
* Returns the number of bytes that can be read from this input
300+
* stream without blocking. The <code>available</code> method of
301+
* <code>InputStream</code> returns <code>0</code>. This method
302+
* <B>should</B> be overridden by subclasses.
303+
*
304+
* @return the number of bytes that can be read from this input stream
305+
* without blocking.
306+
* @exception IOException if an I/O error occurs.
307+
* @since JCE1.2
308+
*/
309+
public int available() throws IOException {
310+
return (ofinish - ostart);
311+
}
312+
313+
/**
314+
* Closes this input stream and releases any system resources
315+
* associated with the stream.
316+
* <p>
317+
* The <code>close</code> method of <code>CipherInputStream</code>
318+
* calls the <code>close</code> method of its underlying input
319+
* stream.
320+
*
321+
* @exception IOException if an I/O error occurs.
322+
* @since JCE1.2
323+
*/
324+
public void close() throws IOException {
325+
if (closed) {
326+
return;
327+
}
328+
329+
closed = true;
330+
input.close();
331+
332+
// Android-removed: Removed a now-inaccurate comment
333+
if (!done) {
334+
try {
335+
cipher.doFinal();
336+
}
337+
catch (BadPaddingException | IllegalBlockSizeException ex) {
338+
// Android-changed: Added throw if bad tag is seen. See b/31590622.
339+
if (ex instanceof AEADBadTagException) {
340+
throw new IOException(ex);
341+
}
342+
}
343+
}
344+
ostart = 0;
345+
ofinish = 0;
346+
}
347+
348+
/**
349+
* Tests if this input stream supports the <code>mark</code>
350+
* and <code>reset</code> methods, which it does not.
351+
*
352+
* @return <code>false</code>, since this class does not support the
353+
* <code>mark</code> and <code>reset</code> methods.
354+
* @see java.io.InputStream#mark(int)
355+
* @see java.io.InputStream#reset()
356+
* @since JCE1.2
357+
*/
358+
public boolean markSupported() {
359+
return false;
360+
}
361+
}

0 commit comments

Comments
 (0)