1 package com.android.internal.content; 2 3 import android.content.pm.PackageManager; 4 import android.os.Build; 5 import android.os.FileUtils; 6 import android.os.SystemProperties; 7 import android.util.Config; 8 import android.util.Log; 9 import android.util.Pair; 10 import android.util.Slog; 11 12 import java.io.File; 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.util.Enumeration; 16 import java.util.LinkedList; 17 import java.util.List; 18 import java.util.zip.ZipEntry; 19 import java.util.zip.ZipException; 20 import java.util.zip.ZipFile; 21 22 /** 23 * Native libraries helper. 24 * 25 * @hide 26 */ 27 public 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 183 final String installGdbServerPath = GDBSERVER; 184 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 } 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