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.FileDescriptor;
47 import java.io.IOException;
48 import java.util.List;
51 * Native libraries helper.
55 public class NativeLibraryHelper {
56 private static final String TAG = "NativeHelper";
57 private static final boolean DEBUG_NATIVE = false;
59 public static final String LIB_DIR_NAME = "lib";
60 public static final String LIB64_DIR_NAME = "lib64";
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 = "-";
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.
73 public static class Handle implements Closeable {
74 private final CloseGuard mGuard = CloseGuard.get();
75 private volatile boolean mClosed;
77 final long[] apkHandles;
78 final boolean multiArch;
79 final boolean extractNativeLibs;
80 final boolean debuggable;
82 public static Handle create(File packageFile) throws IOException {
84 final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
86 } catch (PackageParserException e) {
87 throw new IOException("Failed to parse package: " + packageFile, e);
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);
98 public static Handle create(PackageLite lite) throws IOException {
99 return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
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]);
115 throw new IOException("Unable to open APK: " + path);
119 return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable);
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);
130 return new Handle(apkHandles, lite.multiArch, lite.extractNativeLibs, lite.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");
143 public void close() {
144 for (long apkHandle : apkHandles) {
145 nativeClose(apkHandle);
152 protected void finalize() throws Throwable {
153 if (mGuard != null) {
166 private static native long nativeOpenApk(String path);
167 private static native long nativeOpenApkFd(FileDescriptor fd, String debugPath);
168 private static native void nativeClose(long handle);
170 private static native long nativeSumNativeBinaries(long handle, String cpuAbi,
173 private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
174 String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge,
177 private static long sumNativeBinaries(Handle handle, String abi) {
179 for (long apkHandle : handle.apkHandles) {
180 sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable);
186 * Copies native binaries to a shared library directory.
188 * @param handle APK file to scan for native libraries
189 * @param sharedLibraryDir directory for libraries to be copied to
190 * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
191 * error code from that class if not
193 public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
194 for (long apkHandle : handle.apkHandles) {
195 int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
196 handle.extractNativeLibs, HAS_NATIVE_BRIDGE, handle.debuggable);
197 if (res != INSTALL_SUCCEEDED) {
201 return INSTALL_SUCCEEDED;
205 * Checks if a given APK contains native code for any of the provided
206 * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
207 * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
208 * APK doesn't contain any native code, and
209 * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
211 public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
212 int finalRes = NO_NATIVE_LIBRARIES;
213 for (long apkHandle : handle.apkHandles) {
214 final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable);
215 if (res == NO_NATIVE_LIBRARIES) {
216 // No native code, keep looking through all APKs.
217 } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
218 // Found some native code, but no ABI match; update our final
219 // result if we haven't found other valid code.
221 finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
223 } else if (res >= 0) {
224 // Found valid native code, track the best ABI match
225 if (finalRes < 0 || res < finalRes) {
229 // Unexpected error; bail
236 private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
239 // Convenience method to call removeNativeBinariesFromDirLI(File)
240 public static void removeNativeBinariesLI(String nativeLibraryPath) {
241 if (nativeLibraryPath == null) return;
242 removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
246 * Remove the native binaries of a given package. This deletes the files
248 public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
249 boolean deleteRootDir) {
251 Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
255 * Just remove any file in the directory. Since the directory is owned
256 * by the 'system' UID, the application is not supposed to have written
259 if (nativeLibraryRoot.exists()) {
260 final File[] files = nativeLibraryRoot.listFiles();
262 for (int nn = 0; nn < files.length; nn++) {
264 Slog.d(TAG, " Deleting " + files[nn].getName());
267 if (files[nn].isDirectory()) {
268 removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
269 } else if (!files[nn].delete()) {
270 Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
274 // Do not delete 'lib' directory itself, unless we're specifically
275 // asked to or this will prevent installation of future updates.
277 if (!nativeLibraryRoot.delete()) {
278 Slog.w(TAG, "Could not delete native binary directory: " +
279 nativeLibraryRoot.getPath());
288 public static void createNativeLibrarySubdir(File path) throws IOException {
289 if (!path.isDirectory()) {
293 throw new IOException("Cannot create " + path.getPath());
297 Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
298 } catch (ErrnoException e) {
299 throw new IOException("Cannot chmod native library directory "
300 + path.getPath(), e);
302 } else if (!SELinux.restorecon(path)) {
303 throw new IOException("Cannot set SELinux context for " + path.getPath());
307 private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
308 int abi = findSupportedAbi(handle, abiList);
310 return sumNativeBinaries(handle, abiList[abi]);
316 public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
317 String[] abiList, boolean useIsaSubdir) throws IOException {
318 createNativeLibrarySubdir(libraryRoot);
321 * If this is an internal application or our nativeLibraryPath points to
322 * the app-lib directory, unpack the libraries if necessary.
324 int abi = findSupportedAbi(handle, abiList);
327 * If we have a matching instruction set, construct a subdir under the native
328 * library root that corresponds to this instruction set.
330 final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
333 final File isaSubdir = new File(libraryRoot, instructionSet);
334 createNativeLibrarySubdir(isaSubdir);
337 subDir = libraryRoot;
340 int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
341 if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
349 public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
350 String abiOverride) {
352 if (handle.multiArch) {
353 // Warn if we've set an abiOverride for multi-lib packages..
354 // By definition, we need to copy both 32 and 64 bit libraries for
356 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
357 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
360 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
361 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
362 copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
363 Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
364 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
365 copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
366 Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
371 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
372 copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
373 Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
374 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
375 copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
376 Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
381 String cpuAbiOverride = null;
382 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
383 cpuAbiOverride = null;
384 } else if (abiOverride != null) {
385 cpuAbiOverride = abiOverride;
388 String[] abiList = (cpuAbiOverride != null) ?
389 new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
390 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
391 hasRenderscriptBitcode(handle)) {
392 abiList = Build.SUPPORTED_32_BIT_ABIS;
395 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
396 true /* use isa specific subdirs */);
397 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
398 Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
403 return PackageManager.INSTALL_SUCCEEDED;
404 } catch (IOException e) {
405 Slog.e(TAG, "Copying native libraries failed", e);
406 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
410 public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
413 if (handle.multiArch) {
414 // Warn if we've set an abiOverride for multi-lib packages..
415 // By definition, we need to copy both 32 and 64 bit libraries for
417 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
418 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
421 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
422 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
425 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
426 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
429 String cpuAbiOverride = null;
430 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
431 cpuAbiOverride = null;
432 } else if (abiOverride != null) {
433 cpuAbiOverride = abiOverride;
436 String[] abiList = (cpuAbiOverride != null) ?
437 new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
438 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
439 hasRenderscriptBitcode(handle)) {
440 abiList = Build.SUPPORTED_32_BIT_ABIS;
443 sum += sumNativeBinariesForSupportedAbi(handle, abiList);
448 // We don't care about the other return values for now.
449 private static final int BITCODE_PRESENT = 1;
451 private static final boolean HAS_NATIVE_BRIDGE =
452 !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
454 private static native int hasRenderscriptBitcode(long apkHandle);
456 public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
457 for (long apkHandle : handle.apkHandles) {
458 final int res = hasRenderscriptBitcode(apkHandle);
460 throw new IOException("Error scanning APK, code: " + res);
461 } else if (res == BITCODE_PRESENT) {