Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Side by Side Diff: base/android/java/src/org/chromium/base/library_loader/LibraryLoaderHelper.java

Issue 1641513004: Update //base to chromium 9659b08ea5a34f889dc4166217f438095ddc10d2 (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5
6 package org.chromium.base.library_loader;
7
8 import android.content.Context;
9 import android.util.Log;
10
11 import java.io.BufferedOutputStream;
12 import java.io.Closeable;
13 import java.io.File;
14 import java.io.FileNotFoundException;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStream;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashSet;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25 import java.util.zip.ZipEntry;
26 import java.util.zip.ZipException;
27 import java.util.zip.ZipFile;
28
29 /**
30 * Class representing an exception which occured during the unpacking process.
31 */
32 class UnpackingException extends Exception {
33 public UnpackingException(String message, Throwable cause) {
34 super(message, cause);
35 }
36
37 public UnpackingException(String message) {
38 super(message);
39 }
40 }
41
42 /**
43 * The class provides helper functions to extract native libraries from APK,
44 * and load libraries from there.
45 */
46 class LibraryLoaderHelper {
47 private static final String TAG = "LibraryLoaderHelper";
48
49 // Fallback directories.
50 static final String LOAD_FROM_APK_FALLBACK_DIR = "fallback";
51
52 private static final int BUFFER_SIZE = 16384;
53
54 /**
55 * Returns the directory for holding extracted native libraries.
56 * It may create the directory if it doesn't exist.
57 *
58 * @param context The context the code is running.
59 * @param dirName The name of the directory containing the libraries.
60 * @return The directory file object.
61 */
62 static File getLibDir(Context context, String dirName) {
63 return context.getDir(dirName, Context.MODE_PRIVATE);
64 }
65
66 /**
67 * Delete libraries and their directory synchronously.
68 */
69 private static void deleteLibrariesSynchronously(Context context, String dir Name) {
70 File libDir = getLibDir(context, dirName);
71 deleteObsoleteLibraries(libDir, Collections.<File>emptyList());
72 }
73
74 /**
75 * Delete libraries and their directory asynchronously.
76 * The actual deletion is done in a background thread.
77 */
78 static void deleteLibrariesAsynchronously(
79 final Context context, final String dirName) {
80 // Child process should not reach here.
81 new Thread() {
82 @Override
83 public void run() {
84 deleteLibrariesSynchronously(context, dirName);
85 }
86 }.start();
87 }
88
89 /**
90 * Copy a library from a zip file to the application's private directory.
91 * This is used as a fallback when we are unable to load the library
92 * directly from the APK file (crbug.com/390618).
93 *
94 * @param context The context the code is running in.
95 * @param library Library name.
96 * @return name of the fallback copy of the library.
97 */
98 static String buildFallbackLibrary(Context context, String library) {
99 try {
100 String libName = System.mapLibraryName(library);
101 File fallbackLibDir = getLibDir(context, LOAD_FROM_APK_FALLBACK_DIR) ;
102 File fallbackLibFile = new File(fallbackLibDir, libName);
103 String pathInZipFile = Linker.getLibraryFilePathInZipFile(libName);
104 Map<String, File> dstFiles = Collections.singletonMap(pathInZipFile, fallbackLibFile);
105
106 deleteObsoleteLibraries(fallbackLibDir, dstFiles.values());
107 unpackLibraries(context, dstFiles);
108
109 return fallbackLibFile.getAbsolutePath();
110 } catch (Exception e) {
111 String errorMessage = "Unable to load fallback for library " + libra ry
112 + " (" + (e.getMessage() == null ? e.toString() : e.getMessa ge()) + ")";
113 Log.e(TAG, errorMessage, e);
114 throw new UnsatisfiedLinkError(errorMessage);
115 }
116 }
117
118 // Delete obsolete libraries from a library folder.
119 private static void deleteObsoleteLibraries(File libDir, Collection<File> ke ptFiles) {
120 try {
121 // Build a list of libraries that should NOT be deleted.
122 Set<String> keptFileNames = new HashSet<String>();
123 for (File k : keptFiles) {
124 keptFileNames.add(k.getName());
125 }
126
127 // Delete the obsolete libraries.
128 Log.i(TAG, "Deleting obsolete libraries in " + libDir.getPath());
129 File[] files = libDir.listFiles();
130 if (files != null) {
131 for (File f : files) {
132 if (!keptFileNames.contains(f.getName())) {
133 delete(f);
134 }
135 }
136 } else {
137 Log.e(TAG, "Failed to list files in " + libDir.getPath());
138 }
139
140 // Delete the folder if no libraries were kept.
141 if (keptFileNames.isEmpty()) {
142 delete(libDir);
143 }
144 } catch (Exception e) {
145 Log.e(TAG, "Failed to remove obsolete libraries from " + libDir.getP ath());
146 }
147 }
148
149 // Unpack libraries from a zip file to the file system.
150 private static void unpackLibraries(Context context,
151 Map<String, File> dstFiles) throws UnpackingException {
152 String zipFilePath = context.getApplicationInfo().sourceDir;
153 Log.i(TAG, "Opening zip file " + zipFilePath);
154 File zipFile = new File(zipFilePath);
155 ZipFile zipArchive = openZipFile(zipFile);
156
157 try {
158 for (Entry<String, File> d : dstFiles.entrySet()) {
159 String pathInZipFile = d.getKey();
160 File dstFile = d.getValue();
161 Log.i(TAG, "Unpacking " + pathInZipFile
162 + " to " + dstFile.getAbsolutePath());
163 ZipEntry packedLib = zipArchive.getEntry(pathInZipFile);
164
165 if (needToUnpackLibrary(zipFile, packedLib, dstFile)) {
166 unpackLibraryFromZipFile(zipArchive, packedLib, dstFile);
167 setLibraryFilePermissions(dstFile);
168 }
169 }
170 } finally {
171 closeZipFile(zipArchive);
172 }
173 }
174
175 // Open a zip file.
176 private static ZipFile openZipFile(File zipFile) throws UnpackingException {
177 try {
178 return new ZipFile(zipFile);
179 } catch (ZipException e) {
180 throw new UnpackingException("Failed to open zip file " + zipFile.ge tPath());
181 } catch (IOException e) {
182 throw new UnpackingException("Failed to open zip file " + zipFile.ge tPath());
183 }
184 }
185
186 // Determine whether it is necessary to unpack a library from a zip file.
187 private static boolean needToUnpackLibrary(
188 File zipFile, ZipEntry packedLib, File dstFile) {
189 // Check if the fallback library already exists.
190 if (!dstFile.exists()) {
191 Log.i(TAG, "File " + dstFile.getPath() + " does not exist yet");
192 return true;
193 }
194
195 // Check last modification dates.
196 long zipTime = zipFile.lastModified();
197 long fallbackLibTime = dstFile.lastModified();
198 if (zipTime > fallbackLibTime) {
199 Log.i(TAG, "Not using existing fallback file because "
200 + "the APK file " + zipFile.getPath()
201 + " (timestamp=" + zipTime + ") is newer than "
202 + "the fallback library " + dstFile.getPath()
203 + "(timestamp=" + fallbackLibTime + ")");
204 return true;
205 }
206
207 // Check file sizes.
208 long packedLibSize = packedLib.getSize();
209 long fallbackLibSize = dstFile.length();
210 if (fallbackLibSize != packedLibSize) {
211 Log.i(TAG, "Not using existing fallback file because "
212 + "the library in the APK " + zipFile.getPath()
213 + " (" + packedLibSize + "B) has a different size than "
214 + "the fallback library " + dstFile.getPath()
215 + "(" + fallbackLibSize + "B)");
216 return true;
217 }
218
219 Log.i(TAG, "Reusing existing file " + dstFile.getPath());
220 return false;
221 }
222
223 // Unpack a library from a zip file to the filesystem.
224 private static void unpackLibraryFromZipFile(ZipFile zipArchive, ZipEntry pa ckedLib,
225 File dstFile) throws UnpackingException {
226 // Open input stream for the library file inside the zip file.
227 InputStream in;
228 try {
229 in = zipArchive.getInputStream(packedLib);
230 } catch (IOException e) {
231 throw new UnpackingException(
232 "IO exception when locating library in the zip file", e);
233 }
234
235 // Ensure that the input stream is closed at the end.
236 try {
237 // Delete existing file if it exists.
238 if (dstFile.exists()) {
239 Log.i(TAG, "Deleting existing unpacked library file " + dstFile. getPath());
240 if (!dstFile.delete()) {
241 throw new UnpackingException(
242 "Failed to delete existing unpacked library file " + dstFile.getPath());
243 }
244 }
245
246 // Ensure that the library folder exists. Since this is added
247 // for increased robustness, we log errors and carry on.
248 try {
249 dstFile.getParentFile().mkdirs();
250 } catch (Exception e) {
251 Log.e(TAG, "Failed to make library folder", e);
252 }
253
254 // Create the destination file.
255 try {
256 if (!dstFile.createNewFile()) {
257 throw new UnpackingException("existing unpacked library file was not deleted");
258 }
259 } catch (IOException e) {
260 throw new UnpackingException("failed to create unpacked library file", e);
261 }
262
263 // Open the output stream for the destination file.
264 OutputStream out;
265 try {
266 out = new BufferedOutputStream(new FileOutputStream(dstFile));
267 } catch (FileNotFoundException e) {
268 throw new UnpackingException(
269 "failed to open output stream for unpacked library file" , e);
270 }
271
272 // Ensure that the output stream is closed at the end.
273 try {
274 // Copy the library from the zip file to the destination file.
275 Log.i(TAG, "Copying " + packedLib.getName() + " from " + zipArch ive.getName()
276 + " to " + dstFile.getPath());
277 byte[] buffer = new byte[BUFFER_SIZE];
278 int len;
279 while ((len = in.read(buffer)) != -1) {
280 out.write(buffer, 0, len);
281 }
282 } catch (IOException e) {
283 throw new UnpackingException(
284 "failed to copy the library from the zip file", e);
285 } finally {
286 close(out, "output stream");
287 }
288 } finally {
289 close(in, "input stream");
290 }
291 }
292
293 // Set up library file permissions.
294 private static void setLibraryFilePermissions(File libFile) {
295 // Change permission to rwxr-xr-x
296 Log.i(TAG, "Setting file permissions for " + libFile.getPath());
297 if (!libFile.setReadable(/* readable */ true, /* ownerOnly */ false)) {
298 Log.e(TAG, "failed to chmod a+r the temporary file");
299 }
300 if (!libFile.setExecutable(/* executable */ true, /* ownerOnly */ false) ) {
301 Log.e(TAG, "failed to chmod a+x the temporary file");
302 }
303 if (!libFile.setWritable(/* writable */ true)) {
304 Log.e(TAG, "failed to chmod +w the temporary file");
305 }
306 }
307
308 // Close a closable and log a warning if it fails.
309 private static void close(Closeable closeable, String name) {
310 try {
311 closeable.close();
312 } catch (IOException e) {
313 // Warn and ignore.
314 Log.w(TAG, "IO exception when closing " + name, e);
315 }
316 }
317
318 // Close a zip file and log a warning if it fails.
319 // This needs to be a separate method because ZipFile is not Closeable in
320 // Java 6 (used on some older devices).
321 private static void closeZipFile(ZipFile file) {
322 try {
323 file.close();
324 } catch (IOException e) {
325 // Warn and ignore.
326 Log.w(TAG, "IO exception when closing zip file", e);
327 }
328 }
329
330 // Delete a file and log it.
331 private static void delete(File file) {
332 if (file.delete()) {
333 Log.i(TAG, "Deleted " + file.getPath());
334 } else {
335 Log.w(TAG, "Failed to delete " + file.getPath());
336 }
337 }
338 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698