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