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 debuggable);
176 private static long sumNativeBinaries(Handle handle, String abi) {
178 for (long apkHandle : handle.apkHandles) {
179 sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable);
185 * Copies native binaries to a shared library directory.
187 * @param handle APK file to scan for native libraries
188 * @param sharedLibraryDir directory for libraries to be copied to
189 * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
190 * error code from that class if not
192 public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
193 for (long apkHandle : handle.apkHandles) {
194 int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
195 handle.extractNativeLibs, handle.debuggable);
196 if (res != INSTALL_SUCCEEDED) {
200 return INSTALL_SUCCEEDED;
204 * Checks if a given APK contains native code for any of the provided
205 * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
206 * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
207 * APK doesn't contain any native code, and
208 * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
210 public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
211 int finalRes = NO_NATIVE_LIBRARIES;
212 for (long apkHandle : handle.apkHandles) {
213 final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable);
214 if (res == NO_NATIVE_LIBRARIES) {
215 // No native code, keep looking through all APKs.
216 } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
217 // Found some native code, but no ABI match; update our final
218 // result if we haven't found other valid code.
220 finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
222 } else if (res >= 0) {
223 // Found valid native code, track the best ABI match
224 if (finalRes < 0 || res < finalRes) {
228 // Unexpected error; bail
235 private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
238 // Convenience method to call removeNativeBinariesFromDirLI(File)
239 public static void removeNativeBinariesLI(String nativeLibraryPath) {
240 if (nativeLibraryPath == null) return;
241 removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
245 * Remove the native binaries of a given package. This deletes the files
247 public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
248 boolean deleteRootDir) {
250 Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
254 * Just remove any file in the directory. Since the directory is owned
255 * by the 'system' UID, the application is not supposed to have written
258 if (nativeLibraryRoot.exists()) {
259 final File[] files = nativeLibraryRoot.listFiles();
261 for (int nn = 0; nn < files.length; nn++) {
263 Slog.d(TAG, " Deleting " + files[nn].getName());
266 if (files[nn].isDirectory()) {
267 removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
268 } else if (!files[nn].delete()) {
269 Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
273 // Do not delete 'lib' directory itself, unless we're specifically
274 // asked to or this will prevent installation of future updates.
276 if (!nativeLibraryRoot.delete()) {
277 Slog.w(TAG, "Could not delete native binary directory: " +
278 nativeLibraryRoot.getPath());
287 public static void createNativeLibrarySubdir(File path) throws IOException {
288 if (!path.isDirectory()) {
292 throw new IOException("Cannot create " + path.getPath());
296 Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
297 } catch (ErrnoException e) {
298 throw new IOException("Cannot chmod native library directory "
299 + path.getPath(), e);
301 } else if (!SELinux.restorecon(path)) {
302 throw new IOException("Cannot set SELinux context for " + path.getPath());
306 private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
307 int abi = findSupportedAbi(handle, abiList);
309 return sumNativeBinaries(handle, abiList[abi]);
315 public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
316 String[] abiList, boolean useIsaSubdir) throws IOException {
317 createNativeLibrarySubdir(libraryRoot);
320 * If this is an internal application or our nativeLibraryPath points to
321 * the app-lib directory, unpack the libraries if necessary.
323 int abi = findSupportedAbi(handle, abiList);
326 * If we have a matching instruction set, construct a subdir under the native
327 * library root that corresponds to this instruction set.
329 final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
332 final File isaSubdir = new File(libraryRoot, instructionSet);
333 createNativeLibrarySubdir(isaSubdir);
336 subDir = libraryRoot;
339 int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
340 if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
348 public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
349 String abiOverride) {
351 if (handle.multiArch) {
352 // Warn if we've set an abiOverride for multi-lib packages..
353 // By definition, we need to copy both 32 and 64 bit libraries for
355 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
356 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
359 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
360 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
361 copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
362 Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
363 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
364 copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
365 Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
370 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
371 copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
372 Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
373 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
374 copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
375 Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
380 String cpuAbiOverride = null;
381 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
382 cpuAbiOverride = null;
383 } else if (abiOverride != null) {
384 cpuAbiOverride = abiOverride;
387 String[] abiList = (cpuAbiOverride != null) ?
388 new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
389 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
390 hasRenderscriptBitcode(handle)) {
391 abiList = Build.SUPPORTED_32_BIT_ABIS;
394 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
395 true /* use isa specific subdirs */);
396 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
397 Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
402 return PackageManager.INSTALL_SUCCEEDED;
403 } catch (IOException e) {
404 Slog.e(TAG, "Copying native libraries failed", e);
405 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
409 public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
412 if (handle.multiArch) {
413 // Warn if we've set an abiOverride for multi-lib packages..
414 // By definition, we need to copy both 32 and 64 bit libraries for
416 if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
417 Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
420 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
421 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
424 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
425 sum += sumNativeBinariesForSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
428 String cpuAbiOverride = null;
429 if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
430 cpuAbiOverride = null;
431 } else if (abiOverride != null) {
432 cpuAbiOverride = abiOverride;
435 String[] abiList = (cpuAbiOverride != null) ?
436 new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
437 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
438 hasRenderscriptBitcode(handle)) {
439 abiList = Build.SUPPORTED_32_BIT_ABIS;
442 sum += sumNativeBinariesForSupportedAbi(handle, abiList);
447 // We don't care about the other return values for now.
448 private static final int BITCODE_PRESENT = 1;
450 private static native int hasRenderscriptBitcode(long apkHandle);
452 public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
453 for (long apkHandle : handle.apkHandles) {
454 final int res = hasRenderscriptBitcode(apkHandle);
456 throw new IOException("Error scanning APK, code: " + res);
457 } else if (res == BITCODE_PRESENT) {