2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.content;
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;
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;
41 import dalvik.system.CloseGuard;
42 import dalvik.system.VMRuntime;
44 import java.io.Closeable;
46 import java.io.IOException;
47 import java.util.List;
50 * Native libraries helper.
54 public class NativeLibraryHelper {
55 private static final String TAG = "NativeHelper";
56 private static final boolean DEBUG_NATIVE = false;
58 public static final String LIB_DIR_NAME = "lib";
59 public static final String LIB64_DIR_NAME = "lib64";
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 = "-";
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.
72 public static class Handle implements Closeable {
73 private final CloseGuard mGuard = CloseGuard.get();
74 private volatile boolean mClosed;
76 final long[] apkHandles;
77 final boolean multiArch;
78 final boolean extractNativeLibs;
79 final boolean debuggable;
83 public static Handle create(File packageFile) throws IOException {
85 final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
87 } catch (PackageParserException e) {
88 throw new IOException("Failed to parse package: " + packageFile, e);
92 public static Handle create(Package pkg) throws IOException {
94 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
97 apkdir = pkg.codePath;
99 return create(pkg.getAllCodePaths(),
100 (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
101 (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
102 (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0, pkg.packageName, apkdir);
105 public static Handle create(PackageLite lite) throws IOException {
107 if (lite.codePath == null ||
108 lite.codePath.startsWith("/system/") ||
109 lite.codePath.startsWith("/vendor/") ||
110 lite.codePath.startsWith("/oem/")) {
113 apkdir = lite.codePath;
115 return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
116 lite.debuggable, lite.packageName, apkdir);
119 private static Handle create(List<String> codePaths, boolean multiArch,
120 boolean extractNativeLibs, boolean debuggable, String pkgName, String apkdir) throws IOException {
121 final int size = codePaths.size();
122 final long[] apkHandles = new long[size];
123 for (int i = 0; i < size; i++) {
124 final String path = codePaths.get(i);
125 apkHandles[i] = nativeOpenApk(path);
126 if (apkHandles[i] == 0) {
127 // Unwind everything we've opened so far
128 for (int j = 0; j < i; j++) {
129 nativeClose(apkHandles[j]);
131 throw new IOException("Unable to open APK: " + path);
135 return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable, pkgName, apkdir);
138 Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs,
139 boolean debuggable, String pkgName, String apkdir) {
140 this.apkHandles = apkHandles;
141 this.multiArch = multiArch;
142 this.extractNativeLibs = extractNativeLibs;
143 this.debuggable = debuggable;
144 this.pkgName = pkgName;
145 this.apkDir = apkdir;
146 mGuard.open("close");
150 public void close() {
151 for (long apkHandle : apkHandles) {
152 nativeClose(apkHandle);
159 protected void finalize() throws Throwable {
160 if (mGuard != null) {
173 private static native long nativeOpenApk(String path);
174 private static native void nativeClose(long handle);
176 private static native long nativeSumNativeBinaries(long handle, String cpuAbi,
179 private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
180 String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge,
183 private static long sumNativeBinaries(Handle handle, String abi) {
185 for (long apkHandle : handle.apkHandles) {
186 sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable);
192 * Copies native binaries to a shared library directory.
194 * @param handle APK file to scan for native libraries
195 * @param sharedLibraryDir directory for libraries to be copied to
196 * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
197 * error code from that class if not
199 public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
200 for (long apkHandle : handle.apkHandles) {
201 int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
202 handle.extractNativeLibs, HAS_NATIVE_BRIDGE, handle.debuggable);
203 if (res != INSTALL_SUCCEEDED) {
207 return INSTALL_SUCCEEDED;
211 * Checks if a given APK contains native code for any of the provided
212 * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
213 * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
214 * APK doesn't contain any native code, and
215 * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
217 public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
218 int finalRes = NO_NATIVE_LIBRARIES;
219 for (long apkHandle : handle.apkHandles) {
220 final int res = nativeFindSupportedAbiReplace(apkHandle, supportedAbis,
221 handle.debuggable, handle.pkgName, handle.apkDir);
223 if (res == NO_NATIVE_LIBRARIES) {
224 // No native code, keep looking through all APKs.
225 } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
226 // Found some native code, but no ABI match; update our final
227 // result if we haven't found other valid code.
229 finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
231 } else if (res >= 0) {
232 // Found valid native code, track the best ABI match
233 if (finalRes < 0 || res < finalRes) {
237 // Unexpected error; bail
244 private native static int nativeFindSupportedAbiReplace(long handle, String[] supportedAbis,
245 boolean debuggable, String pkgName, String apkdir);
247 // Convenience method to call removeNativeBinariesFromDirLI(File)
248 public static void removeNativeBinariesLI(String nativeLibraryPath) {
249 if (nativeLibraryPath == null) return;
250 removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
254 * Remove the native binaries of a given package. This deletes the files
256 public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
257 boolean deleteRootDir) {
259 Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
263 * Just remove any file in the directory. Since the directory is owned
264 * by the 'system' UID, the application is not supposed to have written
267 if (nativeLibraryRoot.exists()) {
268 final File[] files = nativeLibraryRoot.listFiles();
270 for (int nn = 0; nn < files.length; nn++) {
272 Slog.d(TAG, " Deleting " + files[nn].getName());
275 if (files[nn].isDirectory()) {
276 removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
277 } else if (!files[nn].delete()) {
278 Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
282 // Do not delete 'lib' directory itself, unless we're specifically
283 // asked to or this will prevent installation of future updates.
285 if (!nativeLibraryRoot.delete()) {
286 Slog.w(TAG, "Could not delete native binary directory: " +
287 nativeLibraryRoot.getPath());
293 private static void createNativeLibrarySubdir(File path) throws IOException {
294 if (!path.isDirectory()) {
298 throw new IOException("Cannot create " + path.getPath());
302 Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
303 } catch (ErrnoException e) {
304 throw new IOException("Cannot chmod native library directory "
305 + path.getPath(), e);
307 } else if (!SELinux.restorecon(path)) {
308 throw new IOException("Cannot set SELinux context for " + path.getPath());
312 private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
313 int abi = findSupportedAbi(handle, abiList);
315 return sumNativeBinaries(handle, abiList[abi]);
321 public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
322 String[] abiList, boolean useIsaSubdir) throws IOException {
323 createNativeLibrarySubdir(libraryRoot);
326 * If this is an internal application or our nativeLibraryPath points to
327 * the app-lib directory, unpack the libraries if necessary.
329 int abi = findSupportedAbi(handle, abiList);
332 * If we have a matching instruction set, construct a subdir under the native
333 * library root that corresponds to this instruction set.
335 final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
338 final File isaSubdir = new File(libraryRoot, instructionSet);
339 createNativeLibrarySubdir(isaSubdir);
342 subDir = libraryRoot;
345 int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
346 if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
354 public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
355 String abiOverride) {
357 if (handle.multiArch) {
358 // Warn if we've set an abiOverride for multi-lib packages..
359 // By definition, we need to copy both 32 and 64 bit libraries for
361 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
362 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
365 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
366 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
367 copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
368 Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
369 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
370 copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
371 Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
376 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
377 copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
378 Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
379 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
380 copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
381 Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
386 String cpuAbiOverride = null;
387 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
388 cpuAbiOverride = null;
389 } else if (abiOverride != null) {
390 cpuAbiOverride = abiOverride;
393 String[] abiList = (cpuAbiOverride != null) ?
394 new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
395 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
396 hasRenderscriptBitcode(handle)) {
397 abiList = Build.SUPPORTED_32_BIT_ABIS;
400 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
401 true /* use isa specific subdirs */);
402 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
403 Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
408 return PackageManager.INSTALL_SUCCEEDED;
409 } catch (IOException e) {
410 Slog.e(TAG, "Copying native libraries failed", e);
411 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
415 public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
418 if (handle.multiArch) {
419 // Warn if we've set an abiOverride for multi-lib packages..
420 // By definition, we need to copy both 32 and 64 bit libraries for
422 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
423 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
426 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
427 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
430 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
431 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
434 String cpuAbiOverride = null;
435 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
436 cpuAbiOverride = null;
437 } else if (abiOverride != null) {
438 cpuAbiOverride = abiOverride;
441 String[] abiList = (cpuAbiOverride != null) ?
442 new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
443 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
444 hasRenderscriptBitcode(handle)) {
445 abiList = Build.SUPPORTED_32_BIT_ABIS;
448 sum += sumNativeBinariesForSupportedAbi(handle, abiList);
453 // We don't care about the other return values for now.
454 private static final int BITCODE_PRESENT = 1;
456 private static final boolean HAS_NATIVE_BRIDGE =
457 !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
459 private static native int hasRenderscriptBitcode(long apkHandle);
461 public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
462 for (long apkHandle : handle.apkHandles) {
463 final int res = hasRenderscriptBitcode(apkHandle);
465 throw new IOException("Error scanning APK, code: " + res);
466 } else if (res == BITCODE_PRESENT) {