Home | History | Annotate | Download | only in content
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.content;
     18 
     19 import static android.content.pm.PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS;
     20 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
     21 import static android.content.pm.PackageManager.NO_NATIVE_LIBRARIES;
     22 import static android.system.OsConstants.S_IRGRP;
     23 import static android.system.OsConstants.S_IROTH;
     24 import static android.system.OsConstants.S_IRWXU;
     25 import static android.system.OsConstants.S_IXGRP;
     26 import static android.system.OsConstants.S_IXOTH;
     27 
     28 import android.content.pm.ApplicationInfo;
     29 import android.content.pm.PackageManager;
     30 import android.content.pm.PackageParser;
     31 import android.content.pm.PackageParser.Package;
     32 import android.content.pm.PackageParser.PackageLite;
     33 import android.content.pm.PackageParser.PackageParserException;
     34 import android.os.Build;
     35 import android.os.SELinux;
     36 import android.os.SystemProperties;
     37 import android.system.ErrnoException;
     38 import android.system.Os;
     39 import android.util.Slog;
     40 
     41 import dalvik.system.CloseGuard;
     42 import dalvik.system.VMRuntime;
     43 
     44 import java.io.Closeable;
     45 import java.io.File;
     46 import java.io.FileDescriptor;
     47 import java.io.IOException;
     48 import java.util.List;
     49 
     50 /**
     51  * Native libraries helper.
     52  *
     53  * @hide
     54  */
     55 public class NativeLibraryHelper {
     56     private static final String TAG = "NativeHelper";
     57     private static final boolean DEBUG_NATIVE = false;
     58 
     59     public static final String LIB_DIR_NAME = "lib";
     60     public static final String LIB64_DIR_NAME = "lib64";
     61 
     62     // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
     63     // that the cpuAbiOverride must be clear.
     64     public static final String CLEAR_ABI_OVERRIDE = "-";
     65 
     66     /**
     67      * A handle to an opened package, consisting of one or more APKs. Used as
     68      * input to the various NativeLibraryHelper methods. Allows us to scan and
     69      * parse the APKs exactly once instead of doing it multiple times.
     70      *
     71      * @hide
     72      */
     73     public static class Handle implements Closeable {
     74         private final CloseGuard mGuard = CloseGuard.get();
     75         private volatile boolean mClosed;
     76 
     77         final long[] apkHandles;
     78         final boolean multiArch;
     79         final boolean extractNativeLibs;
     80         final boolean debuggable;
     81 
     82         public static Handle create(File packageFile) throws IOException {
     83             try {
     84                 final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
     85                 return create(lite);
     86             } catch (PackageParserException e) {
     87                 throw new IOException("Failed to parse package: " + packageFile, e);
     88             }
     89         }
     90 
     91         public static Handle create(Package pkg) throws IOException {
     92             return create(pkg.getAllCodePaths(),
     93                     (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
     94                     (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
     95                     (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
     96         }
     97 
     98         public static Handle create(PackageLite lite) throws IOException {
     99             return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
    100                     lite.debuggable);
    101         }
    102 
    103         private static Handle create(List<String> codePaths, boolean multiArch,
    104                 boolean extractNativeLibs, boolean debuggable) throws IOException {
    105             final int size = codePaths.size();
    106             final long[] apkHandles = new long[size];
    107             for (int i = 0; i < size; i++) {
    108                 final String path = codePaths.get(i);
    109                 apkHandles[i] = nativeOpenApk(path);
    110                 if (apkHandles[i] == 0) {
    111                     // Unwind everything we've opened so far
    112                     for (int j = 0; j < i; j++) {
    113                         nativeClose(apkHandles[j]);
    114                     }
    115                     throw new IOException("Unable to open APK: " + path);
    116                 }
    117             }
    118 
    119             return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable);
    120         }
    121 
    122         public static Handle createFd(PackageLite lite, FileDescriptor fd) throws IOException {
    123             final long[] apkHandles = new long[1];
    124             final String path = lite.baseCodePath;
    125             apkHandles[0] = nativeOpenApkFd(fd, path);
    126             if (apkHandles[0] == 0) {
    127                 throw new IOException("Unable to open APK " + path + " from fd " + fd);
    128             }
    129 
    130             return new Handle(apkHandles, lite.multiArch, lite.extractNativeLibs, lite.debuggable);
    131         }
    132 
    133         Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs,
    134                 boolean debuggable) {
    135             this.apkHandles = apkHandles;
    136             this.multiArch = multiArch;
    137             this.extractNativeLibs = extractNativeLibs;
    138             this.debuggable = debuggable;
    139             mGuard.open("close");
    140         }
    141 
    142         @Override
    143         public void close() {
    144             for (long apkHandle : apkHandles) {
    145                 nativeClose(apkHandle);
    146             }
    147             mGuard.close();
    148             mClosed = true;
    149         }
    150 
    151         @Override
    152         protected void finalize() throws Throwable {
    153             if (mGuard != null) {
    154                 mGuard.warnIfOpen();
    155             }
    156             try {
    157                 if (!mClosed) {
    158                     close();
    159                 }
    160             } finally {
    161                 super.finalize();
    162             }
    163         }
    164     }
    165 
    166     private static native long nativeOpenApk(String path);
    167     private static native long nativeOpenApkFd(FileDescriptor fd, String debugPath);
    168     private static native void nativeClose(long handle);
    169 
    170     private static native long nativeSumNativeBinaries(long handle, String cpuAbi,
    171             boolean debuggable);
    172 
    173     private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
    174             String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge,
    175             boolean debuggable);
    176 
    177     private static long sumNativeBinaries(Handle handle, String abi) {
    178         long sum = 0;
    179         for (long apkHandle : handle.apkHandles) {
    180             sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable);
    181         }
    182         return sum;
    183     }
    184 
    185     /**
    186      * Copies native binaries to a shared library directory.
    187      *
    188      * @param handle APK file to scan for native libraries
    189      * @param sharedLibraryDir directory for libraries to be copied to
    190      * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
    191      *         error code from that class if not
    192      */
    193     public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
    194         for (long apkHandle : handle.apkHandles) {
    195             int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
    196                     handle.extractNativeLibs, HAS_NATIVE_BRIDGE, handle.debuggable);
    197             if (res != INSTALL_SUCCEEDED) {
    198                 return res;
    199             }
    200         }
    201         return INSTALL_SUCCEEDED;
    202     }
    203 
    204     /**
    205      * Checks if a given APK contains native code for any of the provided
    206      * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
    207      * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
    208      * APK doesn't contain any native code, and
    209      * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
    210      */
    211     public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
    212         int finalRes = NO_NATIVE_LIBRARIES;
    213         for (long apkHandle : handle.apkHandles) {
    214             final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable);
    215             if (res == NO_NATIVE_LIBRARIES) {
    216                 // No native code, keep looking through all APKs.
    217             } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
    218                 // Found some native code, but no ABI match; update our final
    219                 // result if we haven't found other valid code.
    220                 if (finalRes < 0) {
    221                     finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
    222                 }
    223             } else if (res >= 0) {
    224                 // Found valid native code, track the best ABI match
    225                 if (finalRes < 0 || res < finalRes) {
    226                     finalRes = res;
    227                 }
    228             } else {
    229                 // Unexpected error; bail
    230                 return res;
    231             }
    232         }
    233         return finalRes;
    234     }
    235 
    236     private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
    237             boolean debuggable);
    238 
    239     // Convenience method to call removeNativeBinariesFromDirLI(File)
    240     public static void removeNativeBinariesLI(String nativeLibraryPath) {
    241         if (nativeLibraryPath == null) return;
    242         removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
    243     }
    244 
    245     /**
    246      * Remove the native binaries of a given package. This deletes the files
    247      */
    248     public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
    249             boolean deleteRootDir) {
    250         if (DEBUG_NATIVE) {
    251             Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
    252         }
    253 
    254         /*
    255          * Just remove any file in the directory. Since the directory is owned
    256          * by the 'system' UID, the application is not supposed to have written
    257          * anything there.
    258          */
    259         if (nativeLibraryRoot.exists()) {
    260             final File[] files = nativeLibraryRoot.listFiles();
    261             if (files != null) {
    262                 for (int nn = 0; nn < files.length; nn++) {
    263                     if (DEBUG_NATIVE) {
    264                         Slog.d(TAG, "    Deleting " + files[nn].getName());
    265                     }
    266 
    267                     if (files[nn].isDirectory()) {
    268                         removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
    269                     } else if (!files[nn].delete()) {
    270                         Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
    271                     }
    272                 }
    273             }
    274             // Do not delete 'lib' directory itself, unless we're specifically
    275             // asked to or this will prevent installation of future updates.
    276             if (deleteRootDir) {
    277                 if (!nativeLibraryRoot.delete()) {
    278                     Slog.w(TAG, "Could not delete native binary directory: " +
    279                             nativeLibraryRoot.getPath());
    280                 }
    281             }
    282         }
    283     }
    284 
    285     /**
    286      * @hide
    287      */
    288     public static void createNativeLibrarySubdir(File path) throws IOException {
    289         if (!path.isDirectory()) {
    290             path.delete();
    291 
    292             if (!path.mkdir()) {
    293                 throw new IOException("Cannot create " + path.getPath());
    294             }
    295 
    296             try {
    297                 Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
    298             } catch (ErrnoException e) {
    299                 throw new IOException("Cannot chmod native library directory "
    300                         + path.getPath(), e);
    301             }
    302         } else if (!SELinux.restorecon(path)) {
    303             throw new IOException("Cannot set SELinux context for " + path.getPath());
    304         }
    305     }
    306 
    307     private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
    308         int abi = findSupportedAbi(handle, abiList);
    309         if (abi >= 0) {
    310             return sumNativeBinaries(handle, abiList[abi]);
    311         } else {
    312             return 0;
    313         }
    314     }
    315 
    316     public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
    317             String[] abiList, boolean useIsaSubdir) throws IOException {
    318         createNativeLibrarySubdir(libraryRoot);
    319 
    320         /*
    321          * If this is an internal application or our nativeLibraryPath points to
    322          * the app-lib directory, unpack the libraries if necessary.
    323          */
    324         int abi = findSupportedAbi(handle, abiList);
    325         if (abi >= 0) {
    326             /*
    327              * If we have a matching instruction set, construct a subdir under the native
    328              * library root that corresponds to this instruction set.
    329              */
    330             final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
    331             final File subDir;
    332             if (useIsaSubdir) {
    333                 final File isaSubdir = new File(libraryRoot, instructionSet);
    334                 createNativeLibrarySubdir(isaSubdir);
    335                 subDir = isaSubdir;
    336             } else {
    337                 subDir = libraryRoot;
    338             }
    339 
    340             int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
    341             if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
    342                 return copyRet;
    343             }
    344         }
    345 
    346         return abi;
    347     }
    348 
    349     public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
    350             String abiOverride) {
    351         try {
    352             if (handle.multiArch) {
    353                 // Warn if we've set an abiOverride for multi-lib packages..
    354                 // By definition, we need to copy both 32 and 64 bit libraries for
    355                 // such packages.
    356                 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
    357                     Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
    358                 }
    359 
    360                 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
    361                 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
    362                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
    363                             Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
    364                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
    365                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
    366                         Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
    367                         return copyRet;
    368                     }
    369                 }
    370 
    371                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
    372                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
    373                             Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
    374                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
    375                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
    376                         Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
    377                         return copyRet;
    378                     }
    379                 }
    380             } else {
    381                 String cpuAbiOverride = null;
    382                 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
    383                     cpuAbiOverride = null;
    384                 } else if (abiOverride != null) {
    385                     cpuAbiOverride = abiOverride;
    386                 }
    387 
    388                 String[] abiList = (cpuAbiOverride != null) ?
    389                         new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
    390                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
    391                         hasRenderscriptBitcode(handle)) {
    392                     abiList = Build.SUPPORTED_32_BIT_ABIS;
    393                 }
    394 
    395                 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
    396                         true /* use isa specific subdirs */);
    397                 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
    398                     Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
    399                     return copyRet;
    400                 }
    401             }
    402 
    403             return PackageManager.INSTALL_SUCCEEDED;
    404         } catch (IOException e) {
    405             Slog.e(TAG, "Copying native libraries failed", e);
    406             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    407         }
    408     }
    409 
    410     public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
    411             throws IOException {
    412         long sum = 0;
    413         if (handle.multiArch) {
    414             // Warn if we've set an abiOverride for multi-lib packages..
    415             // By definition, we need to copy both 32 and 64 bit libraries for
    416             // such packages.
    417             if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
    418                 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
    419             }
    420 
    421             if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
    422                 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
    423             }
    424 
    425             if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
    426                 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
    427             }
    428         } else {
    429             String cpuAbiOverride = null;
    430             if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
    431                 cpuAbiOverride = null;
    432             } else if (abiOverride != null) {
    433                 cpuAbiOverride = abiOverride;
    434             }
    435 
    436             String[] abiList = (cpuAbiOverride != null) ?
    437                     new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
    438             if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
    439                     hasRenderscriptBitcode(handle)) {
    440                 abiList = Build.SUPPORTED_32_BIT_ABIS;
    441             }
    442 
    443             sum += sumNativeBinariesForSupportedAbi(handle, abiList);
    444         }
    445         return sum;
    446     }
    447 
    448     // We don't care about the other return values for now.
    449     private static final int BITCODE_PRESENT = 1;
    450 
    451     private static final boolean HAS_NATIVE_BRIDGE =
    452             !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
    453 
    454     private static native int hasRenderscriptBitcode(long apkHandle);
    455 
    456     public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
    457         for (long apkHandle : handle.apkHandles) {
    458             final int res = hasRenderscriptBitcode(apkHandle);
    459             if (res < 0) {
    460                 throw new IOException("Error scanning APK, code: " + res);
    461             } else if (res == BITCODE_PRESENT) {
    462                 return true;
    463             }
    464         }
    465         return false;
    466     }
    467 }
    468