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 debuggable)173 private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath, 174 String abiToCopy, boolean extractNativeLibs, boolean debuggable); 175 sumNativeBinaries(Handle handle, String abi)176 private static long sumNativeBinaries(Handle handle, String abi) { 177 long sum = 0; 178 for (long apkHandle : handle.apkHandles) { 179 sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable); 180 } 181 return sum; 182 } 183 184 /** 185 * Copies native binaries to a shared library directory. 186 * 187 * @param handle APK file to scan for native libraries 188 * @param sharedLibraryDir directory for libraries to be copied to 189 * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another 190 * error code from that class if not 191 */ copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi)192 public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) { 193 for (long apkHandle : handle.apkHandles) { 194 int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi, 195 handle.extractNativeLibs, handle.debuggable); 196 if (res != INSTALL_SUCCEEDED) { 197 return res; 198 } 199 } 200 return INSTALL_SUCCEEDED; 201 } 202 203 /** 204 * Checks if a given APK contains native code for any of the provided 205 * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching 206 * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the 207 * APK doesn't contain any native code, and 208 * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match. 209 */ findSupportedAbi(Handle handle, String[] supportedAbis)210 public static int findSupportedAbi(Handle handle, String[] supportedAbis) { 211 int finalRes = NO_NATIVE_LIBRARIES; 212 for (long apkHandle : handle.apkHandles) { 213 final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable); 214 if (res == NO_NATIVE_LIBRARIES) { 215 // No native code, keep looking through all APKs. 216 } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) { 217 // Found some native code, but no ABI match; update our final 218 // result if we haven't found other valid code. 219 if (finalRes < 0) { 220 finalRes = INSTALL_FAILED_NO_MATCHING_ABIS; 221 } 222 } else if (res >= 0) { 223 // Found valid native code, track the best ABI match 224 if (finalRes < 0 || res < finalRes) { 225 finalRes = res; 226 } 227 } else { 228 // Unexpected error; bail 229 return res; 230 } 231 } 232 return finalRes; 233 } 234 nativeFindSupportedAbi(long handle, String[] supportedAbis, boolean debuggable)235 private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis, 236 boolean debuggable); 237 238 // Convenience method to call removeNativeBinariesFromDirLI(File) removeNativeBinariesLI(String nativeLibraryPath)239 public static void removeNativeBinariesLI(String nativeLibraryPath) { 240 if (nativeLibraryPath == null) return; 241 removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */); 242 } 243 244 /** 245 * Remove the native binaries of a given package. This deletes the files 246 */ removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir)247 public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, 248 boolean deleteRootDir) { 249 if (DEBUG_NATIVE) { 250 Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath()); 251 } 252 253 /* 254 * Just remove any file in the directory. Since the directory is owned 255 * by the 'system' UID, the application is not supposed to have written 256 * anything there. 257 */ 258 if (nativeLibraryRoot.exists()) { 259 final File[] files = nativeLibraryRoot.listFiles(); 260 if (files != null) { 261 for (int nn = 0; nn < files.length; nn++) { 262 if (DEBUG_NATIVE) { 263 Slog.d(TAG, " Deleting " + files[nn].getName()); 264 } 265 266 if (files[nn].isDirectory()) { 267 removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */); 268 } else if (!files[nn].delete()) { 269 Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath()); 270 } 271 } 272 } 273 // Do not delete 'lib' directory itself, unless we're specifically 274 // asked to or this will prevent installation of future updates. 275 if (deleteRootDir) { 276 if (!nativeLibraryRoot.delete()) { 277 Slog.w(TAG, "Could not delete native binary directory: " + 278 nativeLibraryRoot.getPath()); 279 } 280 } 281 } 282 } 283 284 /** 285 * @hide 286 */ createNativeLibrarySubdir(File path)287 public static void createNativeLibrarySubdir(File path) throws IOException { 288 if (!path.isDirectory()) { 289 path.delete(); 290 291 if (!path.mkdir()) { 292 throw new IOException("Cannot create " + path.getPath()); 293 } 294 295 try { 296 Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 297 } catch (ErrnoException e) { 298 throw new IOException("Cannot chmod native library directory " 299 + path.getPath(), e); 300 } 301 } else if (!SELinux.restorecon(path)) { 302 throw new IOException("Cannot set SELinux context for " + path.getPath()); 303 } 304 } 305 sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList)306 private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) { 307 int abi = findSupportedAbi(handle, abiList); 308 if (abi >= 0) { 309 return sumNativeBinaries(handle, abiList[abi]); 310 } else { 311 return 0; 312 } 313 } 314 copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot, String[] abiList, boolean useIsaSubdir)315 public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot, 316 String[] abiList, boolean useIsaSubdir) throws IOException { 317 createNativeLibrarySubdir(libraryRoot); 318 319 /* 320 * If this is an internal application or our nativeLibraryPath points to 321 * the app-lib directory, unpack the libraries if necessary. 322 */ 323 int abi = findSupportedAbi(handle, abiList); 324 if (abi >= 0) { 325 /* 326 * If we have a matching instruction set, construct a subdir under the native 327 * library root that corresponds to this instruction set. 328 */ 329 final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]); 330 final File subDir; 331 if (useIsaSubdir) { 332 final File isaSubdir = new File(libraryRoot, instructionSet); 333 createNativeLibrarySubdir(isaSubdir); 334 subDir = isaSubdir; 335 } else { 336 subDir = libraryRoot; 337 } 338 339 int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]); 340 if (copyRet != PackageManager.INSTALL_SUCCEEDED) { 341 return copyRet; 342 } 343 } 344 345 return abi; 346 } 347 copyNativeBinariesWithOverride(Handle handle, File libraryRoot, String abiOverride)348 public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot, 349 String abiOverride) { 350 try { 351 if (handle.multiArch) { 352 // Warn if we've set an abiOverride for multi-lib packages.. 353 // By definition, we need to copy both 32 and 64 bit libraries for 354 // such packages. 355 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) { 356 Slog.w(TAG, "Ignoring abiOverride for multi arch application."); 357 } 358 359 int copyRet = PackageManager.NO_NATIVE_LIBRARIES; 360 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { 361 copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, 362 Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */); 363 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES && 364 copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { 365 Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet); 366 return copyRet; 367 } 368 } 369 370 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { 371 copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, 372 Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */); 373 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES && 374 copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { 375 Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet); 376 return copyRet; 377 } 378 } 379 } else { 380 String cpuAbiOverride = null; 381 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) { 382 cpuAbiOverride = null; 383 } else if (abiOverride != null) { 384 cpuAbiOverride = abiOverride; 385 } 386 387 String[] abiList = (cpuAbiOverride != null) ? 388 new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS; 389 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null && 390 hasRenderscriptBitcode(handle)) { 391 abiList = Build.SUPPORTED_32_BIT_ABIS; 392 } 393 394 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList, 395 true /* use isa specific subdirs */); 396 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { 397 Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]"); 398 return copyRet; 399 } 400 } 401 402 return PackageManager.INSTALL_SUCCEEDED; 403 } catch (IOException e) { 404 Slog.e(TAG, "Copying native libraries failed", e); 405 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 406 } 407 } 408 sumNativeBinariesWithOverride(Handle handle, String abiOverride)409 public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride) 410 throws IOException { 411 long sum = 0; 412 if (handle.multiArch) { 413 // Warn if we've set an abiOverride for multi-lib packages.. 414 // By definition, we need to copy both 32 and 64 bit libraries for 415 // such packages. 416 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) { 417 Slog.w(TAG, "Ignoring abiOverride for multi arch application."); 418 } 419 420 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { 421 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); 422 } 423 424 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { 425 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); 426 } 427 } else { 428 String cpuAbiOverride = null; 429 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) { 430 cpuAbiOverride = null; 431 } else if (abiOverride != null) { 432 cpuAbiOverride = abiOverride; 433 } 434 435 String[] abiList = (cpuAbiOverride != null) ? 436 new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS; 437 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null && 438 hasRenderscriptBitcode(handle)) { 439 abiList = Build.SUPPORTED_32_BIT_ABIS; 440 } 441 442 sum += sumNativeBinariesForSupportedAbi(handle, abiList); 443 } 444 return sum; 445 } 446 447 // We don't care about the other return values for now. 448 private static final int BITCODE_PRESENT = 1; 449 hasRenderscriptBitcode(long apkHandle)450 private static native int hasRenderscriptBitcode(long apkHandle); 451 hasRenderscriptBitcode(Handle handle)452 public static boolean hasRenderscriptBitcode(Handle handle) throws IOException { 453 for (long apkHandle : handle.apkHandles) { 454 final int res = hasRenderscriptBitcode(apkHandle); 455 if (res < 0) { 456 throw new IOException("Error scanning APK, code: " + res); 457 } else if (res == BITCODE_PRESENT) { 458 return true; 459 } 460 } 461 return false; 462 } 463 } 464