Home | History | Annotate | Download | only in content
      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