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