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