blob: 4ae55fc4e3eef708a90dd82f6c6ea3a6b309962b [file] [log] [blame]
Kenny Root85387d72010-08-26 10:13:11 -07001package com.android.internal.content;
2
3import android.content.pm.PackageManager;
4import android.os.Build;
5import android.os.FileUtils;
6import android.os.SystemProperties;
7import android.util.Config;
8import android.util.Log;
9import android.util.Pair;
10import android.util.Slog;
11
12import java.io.File;
13import java.io.IOException;
14import java.io.InputStream;
15import java.util.Enumeration;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.zip.ZipEntry;
19import java.util.zip.ZipException;
20import java.util.zip.ZipFile;
21
22/**
23 * Native libraries helper.
24 *
25 * @hide
26 */
27public class NativeLibraryHelper {
28 private static final String TAG = "NativeHelper";
29
30 private static final boolean DEBUG_NATIVE = false;
31
32 /*
33 * The following constants are returned by listPackageSharedLibsForAbiLI
34 * to indicate if native shared libraries were found in the package.
35 * Values are:
36 * PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES => native libraries found and installed
37 * PACKAGE_INSTALL_NATIVE_NO_LIBRARIES => no native libraries in package
38 * PACKAGE_INSTALL_NATIVE_ABI_MISMATCH => native libraries for another ABI found
39 * in package (and not installed)
40 *
41 */
42 private static final int PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES = 0;
43 private static final int PACKAGE_INSTALL_NATIVE_NO_LIBRARIES = 1;
44 private static final int PACKAGE_INSTALL_NATIVE_ABI_MISMATCH = 2;
45
46 // Directory in the APK that holds all the native shared libraries.
47 private static final String APK_LIB = "lib/";
48 private static final int APK_LIB_LENGTH = APK_LIB.length();
49
50 // Prefix that native shared libraries must have.
51 private static final String LIB_PREFIX = "lib";
52 private static final int LIB_PREFIX_LENGTH = LIB_PREFIX.length();
53
54 // Suffix that the native shared libraries must have.
55 private static final String LIB_SUFFIX = ".so";
56 private static final int LIB_SUFFIX_LENGTH = LIB_SUFFIX.length();
57
58 // Name of the GDB binary.
59 private static final String GDBSERVER = "gdbserver";
60
61 // the minimum length of a valid native shared library of the form
62 // lib/<something>/lib<name>.so.
63 private static final int MIN_ENTRY_LENGTH = APK_LIB_LENGTH + 2 + LIB_PREFIX_LENGTH + 1
64 + LIB_SUFFIX_LENGTH;
65
66 /*
67 * Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk
68 * and add them to a list to be installed later.
69 *
70 * NOTE: this method may throw an IOException if the library cannot
71 * be copied to its final destination, e.g. if there isn't enough
72 * room left on the data partition, or a ZipException if the package
73 * file is malformed.
74 */
75 private static int listPackageSharedLibsForAbiLI(ZipFile zipFile,
76 String cpuAbi, List<Pair<ZipEntry, String>> libEntries) throws IOException,
77 ZipException {
78 final int cpuAbiLen = cpuAbi.length();
79 boolean hasNativeLibraries = false;
80 boolean installedNativeLibraries = false;
81
82 if (DEBUG_NATIVE) {
83 Slog.d(TAG, "Checking " + zipFile.getName() + " for shared libraries of CPU ABI type "
84 + cpuAbi);
85 }
86
87 Enumeration<? extends ZipEntry> entries = zipFile.entries();
88
89 while (entries.hasMoreElements()) {
90 ZipEntry entry = entries.nextElement();
91
92 // skip directories
93 if (entry.isDirectory()) {
94 continue;
95 }
96 String entryName = entry.getName();
97
98 /*
99 * Check that the entry looks like lib/<something>/lib<name>.so
100 * here, but don't check the ABI just yet.
101 *
102 * - must be sufficiently long
103 * - must end with LIB_SUFFIX, i.e. ".so"
104 * - must start with APK_LIB, i.e. "lib/"
105 */
106 if (entryName.length() < MIN_ENTRY_LENGTH || !entryName.endsWith(LIB_SUFFIX)
107 || !entryName.startsWith(APK_LIB)) {
108 continue;
109 }
110
111 // file name must start with LIB_PREFIX, i.e. "lib"
112 int lastSlash = entryName.lastIndexOf('/');
113
114 if (lastSlash < 0
115 || !entryName.regionMatches(lastSlash + 1, LIB_PREFIX, 0, LIB_PREFIX_LENGTH)) {
116 continue;
117 }
118
119 hasNativeLibraries = true;
120
121 // check the cpuAbi now, between lib/ and /lib<name>.so
122 if (lastSlash != APK_LIB_LENGTH + cpuAbiLen
123 || !entryName.regionMatches(APK_LIB_LENGTH, cpuAbi, 0, cpuAbiLen))
124 continue;
125
126 /*
127 * Extract the library file name, ensure it doesn't contain
128 * weird characters. we're guaranteed here that it doesn't contain
129 * a directory separator though.
130 */
131 String libFileName = entryName.substring(lastSlash+1);
132 if (!FileUtils.isFilenameSafe(new File(libFileName))) {
133 continue;
134 }
135
136 installedNativeLibraries = true;
137
138 if (DEBUG_NATIVE) {
139 Log.d(TAG, "Caching shared lib " + entry.getName());
140 }
141
142 libEntries.add(Pair.create(entry, libFileName));
143 }
144 if (!hasNativeLibraries)
145 return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
146
147 if (!installedNativeLibraries)
148 return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH;
149
150 return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
151 }
152
153 /*
154 * Find the gdbserver executable program in a package at
155 * lib/<cpuAbi>/gdbserver and add it to the list of binaries
156 * to be copied out later.
157 *
158 * Returns PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES on success,
159 * or PACKAGE_INSTALL_NATIVE_NO_LIBRARIES otherwise.
160 */
161 private static int listPackageGdbServerLI(ZipFile zipFile, String cpuAbi,
162 List<Pair<ZipEntry, String>> nativeFiles) throws IOException, ZipException {
163 final String apkGdbServerPath = "lib/" + cpuAbi + "/" + GDBSERVER;
164
165 Enumeration<? extends ZipEntry> entries = zipFile.entries();
166
167 while (entries.hasMoreElements()) {
168 ZipEntry entry = entries.nextElement();
169 // skip directories
170 if (entry.isDirectory()) {
171 continue;
172 }
173 String entryName = entry.getName();
174
175 if (!entryName.equals(apkGdbServerPath)) {
176 continue;
177 }
178
179 if (Config.LOGD) {
180 Log.d(TAG, "Found gdbserver: " + entry.getName());
181 }
182
Kenny Roote6da1182010-09-30 14:13:59 -0700183 final String installGdbServerPath = GDBSERVER;
Kenny Root85387d72010-08-26 10:13:11 -0700184 nativeFiles.add(Pair.create(entry, installGdbServerPath));
185
186 return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
187 }
188 return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
189 }
190
191 /*
192 * Examine shared libraries stored in the APK as
193 * lib/<cpuAbi>/lib<name>.so and add them to a list to be copied
194 * later.
195 *
196 * This function will first try the main CPU ABI defined by Build.CPU_ABI
197 * (which corresponds to ro.product.cpu.abi), and also try an alternate
198 * one if ro.product.cpu.abi2 is defined.
199 */
200 public static int listPackageNativeBinariesLI(ZipFile zipFile,
201 List<Pair<ZipEntry, String>> nativeFiles) throws ZipException, IOException {
202 String cpuAbi = Build.CPU_ABI;
203
204 int result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi, nativeFiles);
205
206 /*
207 * Some architectures are capable of supporting several CPU ABIs
208 * for example, 'armeabi-v7a' also supports 'armeabi' native code
209 * this is indicated by the definition of the ro.product.cpu.abi2
210 * system property.
211 *
212 * only scan the package twice in case of ABI mismatch
213 */
214 if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
215 final String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2", null);
216 if (cpuAbi2 != null) {
217 result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi2, nativeFiles);
218 }
219
220 if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
221 Slog.w(TAG, "Native ABI mismatch from package file");
222 return PackageManager.INSTALL_FAILED_INVALID_APK;
223 }
224
225 if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) {
226 cpuAbi = cpuAbi2;
227 }
228 }
229
230 /*
231 * Debuggable packages may have gdbserver embedded, so add it to
232 * the list to the list of items to be extracted (as lib/gdbserver)
233 * into the application's native library directory later.
234 */
235 if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) {
236 listPackageGdbServerLI(zipFile, cpuAbi, nativeFiles);
237 }
238 return PackageManager.INSTALL_SUCCEEDED;
239 }
240
241 public static int copyNativeBinariesLI(File scanFile, File sharedLibraryDir) {
242 /*
243 * Check all the native files that need to be copied and add
244 * that to the container size.
245 */
246 ZipFile zipFile;
247 try {
248 zipFile = new ZipFile(scanFile);
249
250 List<Pair<ZipEntry, String>> nativeFiles = new LinkedList<Pair<ZipEntry, String>>();
251
252 NativeLibraryHelper.listPackageNativeBinariesLI(zipFile, nativeFiles);
253
254 final int N = nativeFiles.size();
255
256 for (int i = 0; i < N; i++) {
257 final Pair<ZipEntry, String> entry = nativeFiles.get(i);
258
259 File destFile = new File(sharedLibraryDir, entry.second);
260 copyNativeBinaryLI(zipFile, entry.first, sharedLibraryDir, destFile);
261 }
Brian Carlstromfd9ddd12010-11-04 11:24:58 -0700262 zipFile.close();
Kenny Root85387d72010-08-26 10:13:11 -0700263 } catch (ZipException e) {
264 Slog.w(TAG, "Failed to extract data from package file", e);
265 return PackageManager.INSTALL_FAILED_INVALID_APK;
266 } catch (IOException e) {
267 Slog.w(TAG, "Failed to cache package shared libs", e);
268 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
269 }
270
271 return PackageManager.INSTALL_SUCCEEDED;
272 }
273
274 private static void copyNativeBinaryLI(ZipFile zipFile, ZipEntry entry,
275 File binaryDir, File binaryFile) throws IOException {
276 InputStream inputStream = zipFile.getInputStream(entry);
277 try {
278 File tempFile = File.createTempFile("tmp", "tmp", binaryDir);
279 String tempFilePath = tempFile.getPath();
280 // XXX package manager can't change owner, so the executable files for
281 // now need to be left as world readable and owned by the system.
282 if (!FileUtils.copyToFile(inputStream, tempFile)
283 || !tempFile.setLastModified(entry.getTime())
284 || FileUtils.setPermissions(tempFilePath, FileUtils.S_IRUSR | FileUtils.S_IWUSR
285 | FileUtils.S_IRGRP | FileUtils.S_IXUSR | FileUtils.S_IXGRP
286 | FileUtils.S_IXOTH | FileUtils.S_IROTH, -1, -1) != 0
287 || !tempFile.renameTo(binaryFile)) {
288 // Failed to properly write file.
289 tempFile.delete();
290 throw new IOException("Couldn't create cached binary " + binaryFile + " in "
291 + binaryDir);
292 }
293 } finally {
294 inputStream.close();
295 }
296 }
Kenny Root8f7cc022010-09-12 09:04:56 -0700297
Kenny Root831baa22010-10-05 12:29:25 -0700298 // Convenience method to call removeNativeBinariesFromDirLI(File)
299 public static boolean removeNativeBinariesLI(String nativeLibraryPath) {
300 return removeNativeBinariesFromDirLI(new File(nativeLibraryPath));
301 }
302
Kenny Root8f7cc022010-09-12 09:04:56 -0700303 // Remove the native binaries of a given package. This simply
304 // gets rid of the files in the 'lib' sub-directory.
Kenny Root831baa22010-10-05 12:29:25 -0700305 public static boolean removeNativeBinariesFromDirLI(File nativeLibraryDir) {
Kenny Root8f7cc022010-09-12 09:04:56 -0700306 if (DEBUG_NATIVE) {
Kenny Root831baa22010-10-05 12:29:25 -0700307 Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryDir.getPath());
Kenny Root8f7cc022010-09-12 09:04:56 -0700308 }
309
Kenny Root831baa22010-10-05 12:29:25 -0700310 boolean deletedFiles = false;
311
Kenny Root8f7cc022010-09-12 09:04:56 -0700312 /*
313 * Just remove any file in the directory. Since the directory is owned
314 * by the 'system' UID, the application is not supposed to have written
315 * anything there.
316 */
Kenny Root831baa22010-10-05 12:29:25 -0700317 if (nativeLibraryDir.exists()) {
318 final File[] binaries = nativeLibraryDir.listFiles();
Kenny Root8f7cc022010-09-12 09:04:56 -0700319 if (binaries != null) {
320 for (int nn = 0; nn < binaries.length; nn++) {
321 if (DEBUG_NATIVE) {
322 Slog.d(TAG, " Deleting " + binaries[nn].getName());
323 }
Kenny Root831baa22010-10-05 12:29:25 -0700324
Kenny Root8f7cc022010-09-12 09:04:56 -0700325 if (!binaries[nn].delete()) {
326 Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
Kenny Root831baa22010-10-05 12:29:25 -0700327 } else {
328 deletedFiles = true;
Kenny Root8f7cc022010-09-12 09:04:56 -0700329 }
330 }
331 }
332 // Do not delete 'lib' directory itself, or this will prevent
333 // installation of future updates.
334 }
Kenny Root831baa22010-10-05 12:29:25 -0700335
336 return deletedFiles;
Kenny Root8f7cc022010-09-12 09:04:56 -0700337 }
Kenny Root85387d72010-08-26 10:13:11 -0700338}