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