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 create(File packageFile)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 create(Package pkg)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 create(PackageLite lite)98 public static Handle create(PackageLite lite) throws IOException { 99 return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs, 100 lite.debuggable); 101 } 102 create(List<String> codePaths, boolean multiArch, boolean extractNativeLibs, boolean debuggable)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 createFd(PackageLite lite, FileDescriptor fd)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 Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs, boolean debuggable)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 close()143 public void close() { 144 for (long apkHandle : apkHandles) { 145 nativeClose(apkHandle); 146 } 147 mGuard.close(); 148 mClosed = true; 149 } 150 151 @Override finalize()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 nativeOpenApk(String path)166 private static native long nativeOpenApk(String path); nativeOpenApkFd(FileDescriptor fd, String debugPath)167 private static native long nativeOpenApkFd(FileDescriptor fd, String debugPath); nativeClose(long handle)168 private static native void nativeClose(long handle); 169 nativeSumNativeBinaries(long handle, String cpuAbi, boolean debuggable)170 private static native long nativeSumNativeBinaries(long handle, String cpuAbi, 171 boolean debuggable); 172 nativeCopyNativeBinaries(long handle, String sharedLibraryPath, String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge, boolean debuggable)173 private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath, 174 String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge, 175 boolean debuggable); 176 sumNativeBinaries(Handle handle, String abi)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 */ copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi)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 */ findSupportedAbi(Handle handle, String[] supportedAbis)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 nativeFindSupportedAbi(long handle, String[] supportedAbis, boolean debuggable)236 private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis, 237 boolean debuggable); 238 239 // Convenience method to call removeNativeBinariesFromDirLI(File) removeNativeBinariesLI(String nativeLibraryPath)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 */ removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir)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 */ createNativeLibrarySubdir(File path)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 sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList)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 copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot, String[] abiList, boolean useIsaSubdir)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 copyNativeBinariesWithOverride(Handle handle, File libraryRoot, String abiOverride)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 sumNativeBinariesWithOverride(Handle handle, String abiOverride)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 hasRenderscriptBitcode(long apkHandle)454 private static native int hasRenderscriptBitcode(long apkHandle); 455 hasRenderscriptBitcode(Handle handle)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