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