1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package java.util.jar;
28 
29 import java.io.*;
30 import java.lang.ref.SoftReference;
31 import java.util.*;
32 import java.util.stream.Stream;
33 import java.util.stream.StreamSupport;
34 import java.util.zip.*;
35 import java.security.CodeSigner;
36 import java.security.cert.Certificate;
37 import java.security.AccessController;
38 import sun.misc.IOUtils;
39 import sun.security.action.GetPropertyAction;
40 import sun.security.util.ManifestEntryVerifier;
41 import sun.security.util.SignatureFileVerifier;
42 
43 /**
44  * The <code>JarFile</code> class is used to read the contents of a jar file
45  * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
46  * It extends the class <code>java.util.zip.ZipFile</code> with support
47  * for reading an optional <code>Manifest</code> entry. The
48  * <code>Manifest</code> can be used to specify meta-information about the
49  * jar file and its entries.
50  *
51  * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
52  * or method in this class will cause a {@link NullPointerException} to be
53  * thrown.
54  *
55  * If the verify flag is on when opening a signed jar file, the content of the
56  * file is verified against its signature embedded inside the file. Please note
57  * that the verification process does not include validating the signer's
58  * certificate. A caller should inspect the return value of
59  * {@link JarEntry#getCodeSigners()} to further determine if the signature
60  * can be trusted.
61  *
62  * @author  David Connelly
63  * @see     Manifest
64  * @see     java.util.zip.ZipFile
65  * @see     java.util.jar.JarEntry
66  * @since   1.2
67  */
68 public
69 class JarFile extends ZipFile {
70     // Android-changed: Hold the Manifest via a hard reference. http://b/28692091
71     // private SoftReference<Manifest> manRef;
72     private Manifest manifest;
73     private JarEntry manEntry;
74     private JarVerifier jv;
75     private boolean jvInitialized;
76     private boolean verify;
77 
78     // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true)
79     private boolean hasClassPathAttribute;
80     // true if manifest checked for special attributes
81     private volatile boolean hasCheckedSpecialAttributes;
82 
83     // Android-removed: SharedSecrets.setJavaUtilJarAccess
84     /*
85     // Set up JavaUtilJarAccess in SharedSecrets
86     static {
87         SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
88     }
89     */
90 
91     /**
92      * The JAR manifest file name.
93      */
94     public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
95 
96     /**
97      * Creates a new <code>JarFile</code> to read from the specified
98      * file <code>name</code>. The <code>JarFile</code> will be verified if
99      * it is signed.
100      * @param name the name of the jar file to be opened for reading
101      * @throws IOException if an I/O error has occurred
102      * @throws SecurityException if access to the file is denied
103      *         by the SecurityManager
104      */
JarFile(String name)105     public JarFile(String name) throws IOException {
106         this(new File(name), true, ZipFile.OPEN_READ);
107     }
108 
109     /**
110      * Creates a new <code>JarFile</code> to read from the specified
111      * file <code>name</code>.
112      * @param name the name of the jar file to be opened for reading
113      * @param verify whether or not to verify the jar file if
114      * it is signed.
115      * @throws IOException if an I/O error has occurred
116      * @throws SecurityException if access to the file is denied
117      *         by the SecurityManager
118      */
JarFile(String name, boolean verify)119     public JarFile(String name, boolean verify) throws IOException {
120         this(new File(name), verify, ZipFile.OPEN_READ);
121     }
122 
123     /**
124      * Creates a new <code>JarFile</code> to read from the specified
125      * <code>File</code> object. The <code>JarFile</code> will be verified if
126      * it is signed.
127      * @param file the jar file to be opened for reading
128      * @throws IOException if an I/O error has occurred
129      * @throws SecurityException if access to the file is denied
130      *         by the SecurityManager
131      */
JarFile(File file)132     public JarFile(File file) throws IOException {
133         this(file, true, ZipFile.OPEN_READ);
134     }
135 
136 
137     /**
138      * Creates a new <code>JarFile</code> to read from the specified
139      * <code>File</code> object.
140      * @param file the jar file to be opened for reading
141      * @param verify whether or not to verify the jar file if
142      * it is signed.
143      * @throws IOException if an I/O error has occurred
144      * @throws SecurityException if access to the file is denied
145      *         by the SecurityManager.
146      */
JarFile(File file, boolean verify)147     public JarFile(File file, boolean verify) throws IOException {
148         this(file, verify, ZipFile.OPEN_READ);
149     }
150 
151 
152     // Android-changed: Use of the hidden constructor with a new argument for zip path validation.
153     /**
154      * Creates a new <code>JarFile</code> to read from the specified
155      * <code>File</code> object in the specified mode.  The mode argument
156      * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
157      *
158      * @param file the jar file to be opened for reading
159      * @param verify whether or not to verify the jar file if
160      * it is signed.
161      * @param mode the mode in which the file is to be opened
162      * @throws IOException if an I/O error has occurred
163      * @throws IllegalArgumentException
164      *         if the <tt>mode</tt> argument is invalid
165      * @throws SecurityException if access to the file is denied
166      *         by the SecurityManager
167      * @since 1.3
168      */
JarFile(File file, boolean verify, int mode)169     public JarFile(File file, boolean verify, int mode) throws IOException {
170         this(file, /* enableZipPathValidator */ true, verify, mode);
171     }
172 
173     // Android-added: New hidden constructor with an argument for zip path validation and verify.
174     /** @hide */
JarFile(String name, boolean enableZipPathValidator, boolean verify)175     public JarFile(String name, boolean enableZipPathValidator, boolean verify) throws IOException {
176         this(new File(name), enableZipPathValidator, verify, ZipFile.OPEN_READ);
177     }
178 
179     // Android-added: New hidden constructor with all available arguments.
180     /** @hide */
JarFile(File file, boolean enableZipPathValidator, boolean verify, int mode)181     public JarFile(File file, boolean enableZipPathValidator, boolean verify, int mode) throws
182             IOException {
183         super(file, mode, enableZipPathValidator);
184         this.verify = verify;
185     }
186 
187     /**
188      * Returns the jar file manifest, or <code>null</code> if none.
189      *
190      * @return the jar file manifest, or <code>null</code> if none
191      *
192      * @throws IllegalStateException
193      *         may be thrown if the jar file has been closed
194      * @throws IOException  if an I/O error has occurred
195      */
getManifest()196     public Manifest getManifest() throws IOException {
197         return getManifestFromReference();
198     }
199 
200     // BEGIN Android-changed: Fix JarFile to be thread safe. http://b/27826114
201     // A volatile field might also work instead of synchronized. http://b/81505612
202     // private Manifest getManifestFromReference() throws IOException {
getManifestFromReference()203     private synchronized Manifest getManifestFromReference() throws IOException {
204     // END Android-changed: Fix JarFile to be thread safe. http://b/27826114
205         // Android-changed: Hold the Manifest via a hard reference. http://b/28692091
206         // Manifest man = manRef != null ? manRef.get() : null;
207         Manifest man = manifest;
208         if (man == null) {
209 
210             JarEntry manEntry = getManEntry();
211 
212             // If found then load the manifest
213             if (manEntry != null) {
214                 if (verify) {
215                     byte[] b = getBytes(manEntry);
216                     man = new Manifest(new ByteArrayInputStream(b));
217                     if (!jvInitialized) {
218                         jv = new JarVerifier(manEntry.getName(), b);
219                     }
220                 } else {
221                     man = new Manifest(super.getInputStream(manEntry));
222                 }
223                 // Android-changed: Hold the Manifest via a hard reference. http://b/28692091
224                 // manRef = new SoftReference<>(man);
225                 manifest = man;
226             }
227         }
228         return man;
229     }
230 
getMetaInfEntryNames()231     private native String[] getMetaInfEntryNames();
232 
233     /**
234      * Returns the <code>JarEntry</code> for the given entry name or
235      * <code>null</code> if not found.
236      *
237      * @param name the jar file entry name
238      * @return the <code>JarEntry</code> for the given entry name or
239      *         <code>null</code> if not found.
240      *
241      * @throws IllegalStateException
242      *         may be thrown if the jar file has been closed
243      *
244      * @see java.util.jar.JarEntry
245      */
getJarEntry(String name)246     public JarEntry getJarEntry(String name) {
247         return (JarEntry)getEntry(name);
248     }
249 
250     /**
251      * Returns the <code>ZipEntry</code> for the given entry name or
252      * <code>null</code> if not found.
253      *
254      * @param name the jar file entry name
255      * @return the <code>ZipEntry</code> for the given entry name or
256      *         <code>null</code> if not found
257      *
258      * @throws IllegalStateException
259      *         may be thrown if the jar file has been closed
260      *
261      * @see java.util.zip.ZipEntry
262      */
getEntry(String name)263     public ZipEntry getEntry(String name) {
264         ZipEntry ze = super.getEntry(name);
265         if (ze != null) {
266             return new JarFileEntry(ze);
267         }
268         return null;
269     }
270 
271     private class JarEntryIterator implements Enumeration<JarEntry>,
272             Iterator<JarEntry>
273     {
274         final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
275 
hasNext()276         public boolean hasNext() {
277             return e.hasMoreElements();
278         }
279 
next()280         public JarEntry next() {
281             ZipEntry ze = e.nextElement();
282             return new JarFileEntry(ze);
283         }
284 
hasMoreElements()285         public boolean hasMoreElements() {
286             return hasNext();
287         }
288 
nextElement()289         public JarEntry nextElement() {
290             return next();
291         }
292     }
293 
294     /**
295      * Returns an enumeration of the zip file entries.
296      */
entries()297     public Enumeration<JarEntry> entries() {
298         return new JarEntryIterator();
299     }
300 
301     @Override
stream()302     public Stream<JarEntry> stream() {
303         return StreamSupport.stream(Spliterators.spliterator(
304                 new JarEntryIterator(), size(),
305                 Spliterator.ORDERED | Spliterator.DISTINCT |
306                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
307     }
308 
309     // Android-added: this method is from OpenJDK 17. Made public hidden to access it
310     // in ZipFile w/o SharedSecrets.
311     /**
312      * Creates a ZipEntry suitable for the given ZipFile.
313      * @hide
314      */
entryFor(String name)315     public JarEntry entryFor(String name) {
316         return new JarFileEntry(name);
317     }
318 
319     private class JarFileEntry extends JarEntry {
JarFileEntry(ZipEntry ze)320         JarFileEntry(ZipEntry ze) {
321             super(ze);
322         }
323 
324         // Android-added: imported from OpenJDK 17 for entryFor(String) support.
JarFileEntry(String name)325         JarFileEntry(String name) {
326             super(name);
327         }
328 
getAttributes()329         public Attributes getAttributes() throws IOException {
330             Manifest man = JarFile.this.getManifest();
331             if (man != null) {
332                 return man.getAttributes(getName());
333             } else {
334                 return null;
335             }
336         }
getCertificates()337         public Certificate[] getCertificates() {
338             try {
339                 maybeInstantiateVerifier();
340             } catch (IOException e) {
341                 throw new RuntimeException(e);
342             }
343             if (certs == null && jv != null) {
344                 certs = jv.getCerts(JarFile.this, this);
345             }
346             return certs == null ? null : certs.clone();
347         }
getCodeSigners()348         public CodeSigner[] getCodeSigners() {
349             try {
350                 maybeInstantiateVerifier();
351             } catch (IOException e) {
352                 throw new RuntimeException(e);
353             }
354             if (signers == null && jv != null) {
355                 signers = jv.getCodeSigners(JarFile.this, this);
356             }
357             return signers == null ? null : signers.clone();
358         }
359     }
360 
361     /*
362      * Ensures that the JarVerifier has been created if one is
363      * necessary (i.e., the jar appears to be signed.) This is done as
364      * a quick check to avoid processing of the manifest for unsigned
365      * jars.
366      */
maybeInstantiateVerifier()367     private void maybeInstantiateVerifier() throws IOException {
368         if (jv != null) {
369             return;
370         }
371 
372         if (verify) {
373             // BEGIN Android-changed: use OpenJDK 17 implementation.
374             /*
375             String[] names = getMetaInfEntryNames();
376             if (names != null) {
377                 for (int i = 0; i < names.length; i++) {
378                     String name = names[i].toUpperCase(Locale.ENGLISH);
379                     if (name.endsWith(".DSA") ||
380                         name.endsWith(".RSA") ||
381                         name.endsWith(".EC") ||
382                         name.endsWith(".SF")) {
383                         // Assume since we found a signature-related file
384                         // that the jar is signed and that we therefore
385                         // need a JarVerifier and Manifest
386                         getManifest();
387                         return;
388                     }
389                 }
390             }
391             // No signature-related files; don't instantiate a
392             // verifier
393             verify = false;
394             */
395             // Gets the manifest name, but only if there are
396             // signature-related files. If so we can assume
397             // that the jar is signed and that we therefore
398             // need a JarVerifier and Manifest
399             // String name = JUZFA.getManifestName(this, true);
400             String name = getManifestName(true);
401             if (name != null) {
402                 getManifest();
403                 return;
404             }
405             // No signature-related files; don't instantiate a
406             // verifier
407             verify = false;
408             // END Android-changed: use OpenJDK 17 implementation.
409         }
410     }
411 
412 
413     /*
414      * Initializes the verifier object by reading all the manifest
415      * entries and passing them to the verifier.
416      */
initializeVerifier()417     private void initializeVerifier() {
418         ManifestEntryVerifier mev = null;
419 
420         // Verify "META-INF/" entries...
421         // BEGIN Android-changed: use OpenJDK17 implementation.
422         /*
423         try {
424             String[] names = getMetaInfEntryNames();
425             if (names != null) {
426                 for (int i = 0; i < names.length; i++) {
427                     String uname = names[i].toUpperCase(Locale.ENGLISH);
428                     if (MANIFEST_NAME.equals(uname)
429                             || SignatureFileVerifier.isBlockOrSF(uname)) {
430                         JarEntry e = getJarEntry(names[i]);
431                         if (e == null) {
432                             throw new JarException("corrupted jar file");
433                         }
434                         if (mev == null) {
435                             mev = new ManifestEntryVerifier
436                                 (getManifestFromReference());
437                         }
438                         byte[] b = getBytes(e);
439                         if (b != null && b.length > 0) {
440                             jv.beginEntry(e, mev);
441                             jv.update(b.length, b, 0, b.length, mev);
442                             jv.update(-1, null, 0, 0, mev);
443                         }
444                     }
445                 }
446             }
447         } catch (IOException ex) {
448             // if we had an error parsing any blocks, just
449             // treat the jar file as being unsigned
450             jv = null;
451             verify = false;
452             if (JarVerifier.debug != null) {
453                 JarVerifier.debug.println("jarfile parsing error!");
454                 ex.printStackTrace();
455             }
456         }
457         */
458         try {
459             List<String> names = getManifestAndSignatureRelatedFiles();
460             for (String name : names) {
461                 JarEntry e = getJarEntry(name);
462                 byte[] b;
463                 if (e == null) {
464                     throw new JarException("corrupted jar file");
465                 }
466                 if (mev == null) {
467                     // BEGIN Android-changed: ManifestEntryVerifier(Manifest, String) is not imported yet.
468                     /*
469                     mev = new ManifestEntryVerifier
470                             (getManifestFromReference()), jv.manifestName);
471                     */
472                     mev = new ManifestEntryVerifier(getManifestFromReference());
473                     // END Android-changed: ManifestEntryVerifier(Manifest, String) is not imported yet.
474                 }
475                 if (name.equalsIgnoreCase(MANIFEST_NAME)) {
476                     b = jv.manifestRawBytes;
477                 } else {
478                     b = getBytes(e);
479                 }
480                 if (b != null && b.length > 0) {
481                     jv.beginEntry(e, mev);
482                     jv.update(b.length, b, 0, b.length, mev);
483                     jv.update(-1, null, 0, 0, mev);
484                 }
485             }
486         } catch (IOException | IllegalArgumentException ex) {
487             // if we had an error parsing any blocks, just
488             // treat the jar file as being unsigned
489             jv = null;
490             verify = false;
491             if (JarVerifier.debug != null) {
492                 JarVerifier.debug.println("jarfile parsing error!");
493                 ex.printStackTrace();
494             }
495         }
496         // END Android-changed: use OpenJDK17 implementation.
497 
498         // if after initializing the verifier we have nothing
499         // signed, we null it out.
500 
501         if (jv != null) {
502 
503             jv.doneWithMeta();
504             if (JarVerifier.debug != null) {
505                 JarVerifier.debug.println("done with meta!");
506             }
507 
508             if (jv.nothingToVerify()) {
509                 if (JarVerifier.debug != null) {
510                     JarVerifier.debug.println("nothing to verify!");
511                 }
512                 jv = null;
513                 verify = false;
514             }
515         }
516     }
517 
518     /*
519      * Reads all the bytes for a given entry. Used to process the
520      * META-INF files.
521      */
getBytes(ZipEntry ze)522     private byte[] getBytes(ZipEntry ze) throws IOException {
523         try (InputStream is = super.getInputStream(ze)) {
524             return IOUtils.readFully(is, (int)ze.getSize(), true);
525         }
526     }
527 
528     /**
529      * Returns an input stream for reading the contents of the specified
530      * zip file entry.
531      * @param ze the zip file entry
532      * @return an input stream for reading the contents of the specified
533      *         zip file entry
534      * @throws ZipException if a zip file format error has occurred
535      * @throws IOException if an I/O error has occurred
536      * @throws SecurityException if any of the jar file entries
537      *         are incorrectly signed.
538      * @throws IllegalStateException
539      *         may be thrown if the jar file has been closed
540      */
getInputStream(ZipEntry ze)541     public synchronized InputStream getInputStream(ZipEntry ze)
542         throws IOException
543     {
544         maybeInstantiateVerifier();
545         if (jv == null) {
546             return super.getInputStream(ze);
547         }
548         if (!jvInitialized) {
549             initializeVerifier();
550             jvInitialized = true;
551             // could be set to null after a call to
552             // initializeVerifier if we have nothing to
553             // verify
554             if (jv == null)
555                 return super.getInputStream(ze);
556         }
557 
558         // wrap a verifier stream around the real stream
559         return new JarVerifier.VerifierStream(
560             getManifestFromReference(),
561             ze instanceof JarFileEntry ?
562                     (JarEntry) ze : getJarEntry(ze.getName()),
563             super.getInputStream(ze),
564             jv);
565     }
566 
567     // Statics for hand-coded Boyer-Moore search
568     private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'};
569     // The bad character shift for "class-path"
570     private static final int[] CLASSPATH_LASTOCC;
571     // The good suffix shift for "class-path"
572     private static final int[] CLASSPATH_OPTOSFT;
573 
574     static {
575         CLASSPATH_LASTOCC = new int[128];
576         CLASSPATH_OPTOSFT = new int[10];
577         CLASSPATH_LASTOCC[(int)'c'] = 1;
578         CLASSPATH_LASTOCC[(int)'l'] = 2;
579         CLASSPATH_LASTOCC[(int)'s'] = 5;
580         CLASSPATH_LASTOCC[(int)'-'] = 6;
581         CLASSPATH_LASTOCC[(int)'p'] = 7;
582         CLASSPATH_LASTOCC[(int)'a'] = 8;
583         CLASSPATH_LASTOCC[(int)'t'] = 9;
584         CLASSPATH_LASTOCC[(int)'h'] = 10;
585         for (int i=0; i<9; i++)
586             CLASSPATH_OPTOSFT[i] = 10;
587         CLASSPATH_OPTOSFT[9]=1;
588     }
589 
590     // BEGIN Android-changed: Fix JarFile to be thread safe. http://b/27826114
591     // A volatile field might also work instead of synchronized. http://b/81505612
592     // private JarEntry getManEntry() {
getManEntry()593     private synchronized JarEntry getManEntry() {
594     // END Android-changed: Fix JarFile to be thread safe. http://b/27826114
595         // BEGIN Android-changed: use OpenJDK 17 implementation.
596         /*
597         if (manEntry == null) {
598             // First look up manifest entry using standard name
599             manEntry = getJarEntry(MANIFEST_NAME);
600             if (manEntry == null) {
601                 // If not found, then iterate through all the "META-INF/"
602                 // entries to find a match.
603                 String[] names = getMetaInfEntryNames();
604                 if (names != null) {
605                     for (int i = 0; i < names.length; i++) {
606                         if (MANIFEST_NAME.equals(
607                                                  names[i].toUpperCase(Locale.ENGLISH))) {
608                             manEntry = getJarEntry(names[i]);
609                             break;
610                         }
611                     }
612                 }
613             }
614         }
615         */
616         if (manEntry == null) {
617             // The manifest entry position is resolved during
618             // initialization
619             String name = getManifestName(false);
620             if (name != null) {
621                 this.manEntry = (JarEntry)super.getEntry(name);
622             }
623         }
624         // END Android-changed: use OpenJDK 17 implementation.
625         return manEntry;
626     }
627 
628     /**
629      * Returns {@code true} iff this JAR file has a manifest with the
630      * Class-Path attribute
631      * @hide
632      */
633     // Android-changed: Make hasClassPathAttribute() @hide public, for internal use.
634     // Used by URLClassPath.JarLoader.
635     // boolean hasClassPathAttribute() throws IOException {
hasClassPathAttribute()636     public boolean hasClassPathAttribute() throws IOException {
637         checkForSpecialAttributes();
638         return hasClassPathAttribute;
639     }
640 
641     /**
642      * Returns true if the pattern {@code src} is found in {@code b}.
643      * The {@code lastOcc} and {@code optoSft} arrays are the precomputed
644      * bad character and good suffix shifts.
645      */
match(char[] src, byte[] b, int[] lastOcc, int[] optoSft)646     private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) {
647         int len = src.length;
648         int last = b.length - len;
649         int i = 0;
650         next:
651         while (i<=last) {
652             for (int j=(len-1); j>=0; j--) {
653                 char c = (char) b[i+j];
654                 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
655                 if (c != src[j]) {
656                     i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
657                     continue next;
658                 }
659             }
660             return true;
661         }
662         return false;
663     }
664 
665     /**
666      * On first invocation, check if the JAR file has the Class-Path
667      * attribute. A no-op on subsequent calls.
668      */
checkForSpecialAttributes()669     private void checkForSpecialAttributes() throws IOException {
670         if (hasCheckedSpecialAttributes) return;
671         // Android-changed: Special handling of well-known .jar files specific to OpenJDK.
672         // if (!isKnownNotToHaveSpecialAttributes()) {
673         {
674             JarEntry manEntry = getManEntry();
675             if (manEntry != null) {
676                 byte[] b = getBytes(manEntry);
677                 if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
678                     hasClassPathAttribute = true;
679             }
680         }
681         hasCheckedSpecialAttributes = true;
682     }
683 
684 
685     // Android-removed: Special handling of well-known .jar files specific to OpenJDK.
686     /*
687     private static String javaHome;
688     private static volatile String[] jarNames;
689     private boolean isKnownNotToHaveSpecialAttributes() {
690         // Optimize away even scanning of manifest for jar files we
691         // deliver which don't have a class-path attribute. If one of
692         // these jars is changed to include such an attribute this code
693         // must be changed.
694         if (javaHome == null) {
695             javaHome = AccessController.doPrivileged(
696                 new GetPropertyAction("java.home"));
697         }
698         if (jarNames == null) {
699             String[] names = new String[11];
700             String fileSep = File.separator;
701             int i = 0;
702             names[i++] = fileSep + "rt.jar";
703             names[i++] = fileSep + "jsse.jar";
704             names[i++] = fileSep + "jce.jar";
705             names[i++] = fileSep + "charsets.jar";
706             names[i++] = fileSep + "dnsns.jar";
707             names[i++] = fileSep + "zipfs.jar";
708             names[i++] = fileSep + "localedata.jar";
709             names[i++] = fileSep = "cldrdata.jar";
710             names[i++] = fileSep + "sunjce_provider.jar";
711             names[i++] = fileSep + "sunpkcs11.jar";
712             names[i++] = fileSep + "sunec.jar";
713             jarNames = names;
714         }
715 
716         String name = getName();
717         String localJavaHome = javaHome;
718         if (name.startsWith(localJavaHome)) {
719             String[] names = jarNames;
720             for (int i = 0; i < names.length; i++) {
721                 if (name.endsWith(names[i])) {
722                     return true;
723                 }
724             }
725         }
726         return false;
727     }
728     */
729 
730     // Android-removed: Unused method ensureInitialization().
731     /*
732     private synchronized void ensureInitialization() {
733         try {
734             maybeInstantiateVerifier();
735         } catch (IOException e) {
736             throw new RuntimeException(e);
737         }
738         if (jv != null && !jvInitialized) {
739             initializeVerifier();
740             jvInitialized = true;
741         }
742     }
743     */
744 
newEntry(ZipEntry ze)745     JarEntry newEntry(ZipEntry ze) {
746         return new JarFileEntry(ze);
747     }
748 
749     // Android-removed: Unused methods entryNames(), entries2().
750     /*
751     Enumeration<String> entryNames(CodeSource[] cs) {
752         ensureInitialization();
753         if (jv != null) {
754             return jv.entryNames(this, cs);
755         }
756 
757         /*
758          * JAR file has no signed content. Is there a non-signing
759          * code source?
760          *
761         boolean includeUnsigned = false;
762         for (int i = 0; i < cs.length; i++) {
763             if (cs[i].getCodeSigners() == null) {
764                 includeUnsigned = true;
765                 break;
766             }
767         }
768         if (includeUnsigned) {
769             return unsignedEntryNames();
770         } else {
771             return new Enumeration<String>() {
772 
773                 public boolean hasMoreElements() {
774                     return false;
775                 }
776 
777                 public String nextElement() {
778                     throw new NoSuchElementException();
779                 }
780             };
781         }
782     }
783 
784     /**
785      * Returns an enumeration of the zip file entries
786      * excluding internal JAR mechanism entries and including
787      * signed entries missing from the ZIP directory.
788      *
789     Enumeration<JarEntry> entries2() {
790         ensureInitialization();
791         if (jv != null) {
792             return jv.entries2(this, super.entries());
793         }
794 
795         // screen out entries which are never signed
796         final Enumeration<? extends ZipEntry> enum_ = super.entries();
797         return new Enumeration<JarEntry>() {
798 
799             ZipEntry entry;
800 
801             public boolean hasMoreElements() {
802                 if (entry != null) {
803                     return true;
804                 }
805                 while (enum_.hasMoreElements()) {
806                     ZipEntry ze = enum_.nextElement();
807                     if (JarVerifier.isSigningRelated(ze.getName())) {
808                         continue;
809                     }
810                     entry = ze;
811                     return true;
812                 }
813                 return false;
814             }
815 
816             public JarFileEntry nextElement() {
817                 if (hasMoreElements()) {
818                     ZipEntry ze = entry;
819                     entry = null;
820                     return new JarFileEntry(ze);
821                 }
822                 throw new NoSuchElementException();
823             }
824         };
825     }
826 
827     CodeSource[] getCodeSources(URL url) {
828         ensureInitialization();
829         if (jv != null) {
830             return jv.getCodeSources(this, url);
831         }
832 
833         /*
834          * JAR file has no signed content. Is there a non-signing
835          * code source?
836          *
837         Enumeration<String> unsigned = unsignedEntryNames();
838         if (unsigned.hasMoreElements()) {
839             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
840         } else {
841             return null;
842         }
843     }
844 
845     private Enumeration<String> unsignedEntryNames() {
846         final Enumeration<JarEntry> entries = entries();
847         return new Enumeration<String>() {
848 
849             String name;
850 
851             /*
852              * Grab entries from ZIP directory but screen out
853              * metadata.
854              *
855             public boolean hasMoreElements() {
856                 if (name != null) {
857                     return true;
858                 }
859                 while (entries.hasMoreElements()) {
860                     String value;
861                     ZipEntry e = entries.nextElement();
862                     value = e.getName();
863                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
864                         continue;
865                     }
866                     name = value;
867                     return true;
868                 }
869                 return false;
870             }
871 
872             public String nextElement() {
873                 if (hasMoreElements()) {
874                     String value = name;
875                     name = null;
876                     return value;
877                 }
878                 throw new NoSuchElementException();
879             }
880         };
881     }
882 
883     CodeSource getCodeSource(URL url, String name) {
884         ensureInitialization();
885         if (jv != null) {
886             if (jv.eagerValidation) {
887                 CodeSource cs = null;
888                 JarEntry je = getJarEntry(name);
889                 if (je != null) {
890                     cs = jv.getCodeSource(url, this, je);
891                 } else {
892                     cs = jv.getCodeSource(url, name);
893                 }
894                 return cs;
895             } else {
896                 return jv.getCodeSource(url, name);
897             }
898         }
899 
900         return JarVerifier.getUnsignedCS(url);
901     }
902 
903     void setEagerValidation(boolean eager) {
904         try {
905             maybeInstantiateVerifier();
906         } catch (IOException e) {
907             throw new RuntimeException(e);
908         }
909         if (jv != null) {
910             jv.setEagerValidation(eager);
911         }
912     }
913 
914     List<Object> getManifestDigests() {
915         ensureInitialization();
916         if (jv != null) {
917             return jv.getManifestDigests();
918         }
919         return new ArrayList<Object>();
920     }
921     */
922 }
923