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