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