Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | package org.mozilla.gecko.util; |
michael@0 | 6 | |
michael@0 | 7 | import org.mozilla.gecko.mozglue.NativeZip; |
michael@0 | 8 | |
michael@0 | 9 | import android.content.res.Resources; |
michael@0 | 10 | import android.graphics.Bitmap; |
michael@0 | 11 | import android.graphics.drawable.BitmapDrawable; |
michael@0 | 12 | import android.util.Log; |
michael@0 | 13 | import org.mozilla.gecko.mozglue.RobocopTarget; |
michael@0 | 14 | |
michael@0 | 15 | import java.io.BufferedReader; |
michael@0 | 16 | import java.io.IOException; |
michael@0 | 17 | import java.io.InputStream; |
michael@0 | 18 | import java.io.InputStreamReader; |
michael@0 | 19 | import java.net.URI; |
michael@0 | 20 | import java.net.URISyntaxException; |
michael@0 | 21 | import java.util.Stack; |
michael@0 | 22 | |
michael@0 | 23 | /* Reads out of a multiple level deep jar file such as |
michael@0 | 24 | * jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png |
michael@0 | 25 | */ |
michael@0 | 26 | public final class GeckoJarReader { |
michael@0 | 27 | private static final String LOGTAG = "GeckoJarReader"; |
michael@0 | 28 | |
michael@0 | 29 | private GeckoJarReader() {} |
michael@0 | 30 | |
michael@0 | 31 | public static Bitmap getBitmap(Resources resources, String url) { |
michael@0 | 32 | BitmapDrawable drawable = getBitmapDrawable(resources, url); |
michael@0 | 33 | return (drawable != null) ? drawable.getBitmap() : null; |
michael@0 | 34 | } |
michael@0 | 35 | |
michael@0 | 36 | public static BitmapDrawable getBitmapDrawable(Resources resources, String url) { |
michael@0 | 37 | Stack<String> jarUrls = parseUrl(url); |
michael@0 | 38 | InputStream inputStream = null; |
michael@0 | 39 | BitmapDrawable bitmap = null; |
michael@0 | 40 | |
michael@0 | 41 | NativeZip zip = null; |
michael@0 | 42 | try { |
michael@0 | 43 | // Load the initial jar file as a zip |
michael@0 | 44 | zip = getZipFile(jarUrls.pop()); |
michael@0 | 45 | inputStream = getStream(zip, jarUrls, url); |
michael@0 | 46 | if (inputStream != null) { |
michael@0 | 47 | bitmap = new BitmapDrawable(resources, inputStream); |
michael@0 | 48 | } |
michael@0 | 49 | } catch (IOException ex) { |
michael@0 | 50 | Log.e(LOGTAG, "Exception ", ex); |
michael@0 | 51 | } catch (URISyntaxException ex) { |
michael@0 | 52 | Log.e(LOGTAG, "Exception ", ex); |
michael@0 | 53 | } finally { |
michael@0 | 54 | if (inputStream != null) { |
michael@0 | 55 | try { |
michael@0 | 56 | inputStream.close(); |
michael@0 | 57 | } catch(IOException ex) { |
michael@0 | 58 | Log.e(LOGTAG, "Error closing stream", ex); |
michael@0 | 59 | } |
michael@0 | 60 | } |
michael@0 | 61 | if (zip != null) { |
michael@0 | 62 | zip.close(); |
michael@0 | 63 | } |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | return bitmap; |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | public static String getText(String url) { |
michael@0 | 70 | Stack<String> jarUrls = parseUrl(url); |
michael@0 | 71 | |
michael@0 | 72 | NativeZip zip = null; |
michael@0 | 73 | BufferedReader reader = null; |
michael@0 | 74 | String text = null; |
michael@0 | 75 | try { |
michael@0 | 76 | zip = getZipFile(jarUrls.pop()); |
michael@0 | 77 | InputStream input = getStream(zip, jarUrls, url); |
michael@0 | 78 | if (input != null) { |
michael@0 | 79 | reader = new BufferedReader(new InputStreamReader(input)); |
michael@0 | 80 | text = reader.readLine(); |
michael@0 | 81 | } |
michael@0 | 82 | } catch (IOException ex) { |
michael@0 | 83 | Log.e(LOGTAG, "Exception ", ex); |
michael@0 | 84 | } catch (URISyntaxException ex) { |
michael@0 | 85 | Log.e(LOGTAG, "Exception ", ex); |
michael@0 | 86 | } finally { |
michael@0 | 87 | if (reader != null) { |
michael@0 | 88 | try { |
michael@0 | 89 | reader.close(); |
michael@0 | 90 | } catch(IOException ex) { |
michael@0 | 91 | Log.e(LOGTAG, "Error closing reader", ex); |
michael@0 | 92 | } |
michael@0 | 93 | } |
michael@0 | 94 | if (zip != null) { |
michael@0 | 95 | zip.close(); |
michael@0 | 96 | } |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | return text; |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | private static NativeZip getZipFile(String url) throws IOException, URISyntaxException { |
michael@0 | 103 | URI fileUrl = new URI(url); |
michael@0 | 104 | return new NativeZip(fileUrl.getPath()); |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | @RobocopTarget |
michael@0 | 108 | public static InputStream getStream(String url) { |
michael@0 | 109 | Stack<String> jarUrls = parseUrl(url); |
michael@0 | 110 | try { |
michael@0 | 111 | NativeZip zip = getZipFile(jarUrls.pop()); |
michael@0 | 112 | return getStream(zip, jarUrls, url); |
michael@0 | 113 | } catch (Exception ex) { |
michael@0 | 114 | // Some JNI code throws IllegalArgumentException on a bad file name; |
michael@0 | 115 | // swallow the error and return null. We could also see legitimate |
michael@0 | 116 | // IOExceptions here. |
michael@0 | 117 | return null; |
michael@0 | 118 | } |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | private static InputStream getStream(NativeZip zip, Stack<String> jarUrls, String origUrl) { |
michael@0 | 122 | InputStream inputStream = null; |
michael@0 | 123 | |
michael@0 | 124 | // loop through children jar files until we reach the innermost one |
michael@0 | 125 | while (!jarUrls.empty()) { |
michael@0 | 126 | String fileName = jarUrls.pop(); |
michael@0 | 127 | |
michael@0 | 128 | if (inputStream != null) { |
michael@0 | 129 | // intermediate NativeZips and InputStreams will be garbage collected. |
michael@0 | 130 | try { |
michael@0 | 131 | zip = new NativeZip(inputStream); |
michael@0 | 132 | } catch (IllegalArgumentException e) { |
michael@0 | 133 | String description = "!!! BUG 849589 !!! origUrl=" + origUrl; |
michael@0 | 134 | Log.e(LOGTAG, description, e); |
michael@0 | 135 | throw new IllegalArgumentException(description); |
michael@0 | 136 | } |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | inputStream = zip.getInputStream(fileName); |
michael@0 | 140 | if (inputStream == null) { |
michael@0 | 141 | Log.d(LOGTAG, "No Entry for " + fileName); |
michael@0 | 142 | return null; |
michael@0 | 143 | } |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | return inputStream; |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | /* Returns a stack of strings breaking the url up into pieces. Each piece |
michael@0 | 150 | * is assumed to point to a jar file except for the final one. Callers should |
michael@0 | 151 | * pass in the url to parse, and null for the parent parameter (used for recursion) |
michael@0 | 152 | * For example, jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png |
michael@0 | 153 | * will return: |
michael@0 | 154 | * file:///data/app/org.mozilla.fennec.apk |
michael@0 | 155 | * omni.ja |
michael@0 | 156 | * chrome/chrome/content/branding/favicon32.png |
michael@0 | 157 | */ |
michael@0 | 158 | private static Stack<String> parseUrl(String url) { |
michael@0 | 159 | return parseUrl(url, null); |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | private static Stack<String> parseUrl(String url, Stack<String> results) { |
michael@0 | 163 | if (results == null) { |
michael@0 | 164 | results = new Stack<String>(); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | if (url.startsWith("jar:")) { |
michael@0 | 168 | int jarEnd = url.lastIndexOf("!"); |
michael@0 | 169 | String subStr = url.substring(4, jarEnd); |
michael@0 | 170 | results.push(url.substring(jarEnd+2)); // remove the !/ characters |
michael@0 | 171 | return parseUrl(subStr, results); |
michael@0 | 172 | } else { |
michael@0 | 173 | results.push(url); |
michael@0 | 174 | return results; |
michael@0 | 175 | } |
michael@0 | 176 | } |
michael@0 | 177 | } |