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