summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2011-07-12 14:14:01 -0700
committerKenny Root <kroot@google.com>2011-07-26 10:14:58 -0700
commit66269ea6f68f2f25888ce1080c94ac782742fafc (patch)
tree0ecbefcdbecf98cbacd428a422e6a2a846ac726e /core
parente432a0005180ba9ac2c1d7822c4761b475fddc51 (diff)
downloadframeworks_base-66269ea6f68f2f25888ce1080c94ac782742fafc.zip
frameworks_base-66269ea6f68f2f25888ce1080c94ac782742fafc.tar.gz
frameworks_base-66269ea6f68f2f25888ce1080c94ac782742fafc.tar.bz2
Move extract native libraries to JNI code
The built-in ZipFile class was quite a long time to find an unpack libraries. Move everything to using the libutils ZipFileRO class that goes quite a bit faster. Initial measurements are 6 times faster than the Java code. Also, read files off the disk and compare their CRC against the APK's CRC to see if we need to write the new file to disk. This also cuts down the bootup time by up to a second per APK that has native files. Change-Id: Ic464a7969a17368fb6a6b81d026888c4136c7603
Diffstat (limited to 'core')
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java285
-rw-r--r--core/jni/Android.mk3
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp375
4 files changed, 392 insertions, 273 deletions
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 9ae7def..1531946 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -1,22 +1,9 @@
package com.android.internal.content;
-import android.content.pm.PackageManager;
import android.os.Build;
-import android.os.FileUtils;
-import android.os.SystemProperties;
-import android.util.Log;
-import android.util.Pair;
import android.util.Slog;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Enumeration;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
/**
* Native libraries helper.
@@ -28,270 +15,22 @@ public class NativeLibraryHelper {
private static final boolean DEBUG_NATIVE = false;
- /*
- * The following constants are returned by listPackageSharedLibsForAbiLI
- * to indicate if native shared libraries were found in the package.
- * Values are:
- * PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES => native libraries found and installed
- * PACKAGE_INSTALL_NATIVE_NO_LIBRARIES => no native libraries in package
- * PACKAGE_INSTALL_NATIVE_ABI_MISMATCH => native libraries for another ABI found
- * in package (and not installed)
- *
- */
- private static final int PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES = 0;
- private static final int PACKAGE_INSTALL_NATIVE_NO_LIBRARIES = 1;
- private static final int PACKAGE_INSTALL_NATIVE_ABI_MISMATCH = 2;
+ private static native long nativeSumNativeBinaries(String file, String cpuAbi, String cpuAbi2);
- // Directory in the APK that holds all the native shared libraries.
- private static final String APK_LIB = "lib/";
- private static final int APK_LIB_LENGTH = APK_LIB.length();
-
- // Prefix that native shared libraries must have.
- private static final String LIB_PREFIX = "lib";
- private static final int LIB_PREFIX_LENGTH = LIB_PREFIX.length();
-
- // Suffix that the native shared libraries must have.
- private static final String LIB_SUFFIX = ".so";
- private static final int LIB_SUFFIX_LENGTH = LIB_SUFFIX.length();
-
- // Name of the GDB binary.
- private static final String GDBSERVER = "gdbserver";
-
- // the minimum length of a valid native shared library of the form
- // lib/<something>/lib<name>.so.
- private static final int MIN_ENTRY_LENGTH = APK_LIB_LENGTH + 2 + LIB_PREFIX_LENGTH + 1
- + LIB_SUFFIX_LENGTH;
-
- /*
- * Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk
- * and add them to a list to be installed later.
- *
- * NOTE: this method may throw an IOException if the library cannot
- * be copied to its final destination, e.g. if there isn't enough
- * room left on the data partition, or a ZipException if the package
- * file is malformed.
- */
- private static int listPackageSharedLibsForAbiLI(ZipFile zipFile,
- String cpuAbi, List<Pair<ZipEntry, String>> libEntries) throws IOException,
- ZipException {
- final int cpuAbiLen = cpuAbi.length();
- boolean hasNativeLibraries = false;
- boolean installedNativeLibraries = false;
-
- if (DEBUG_NATIVE) {
- Slog.d(TAG, "Checking " + zipFile.getName() + " for shared libraries of CPU ABI type "
- + cpuAbi);
- }
-
- Enumeration<? extends ZipEntry> entries = zipFile.entries();
-
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
-
- // skip directories
- if (entry.isDirectory()) {
- continue;
- }
- String entryName = entry.getName();
-
- /*
- * Check that the entry looks like lib/<something>/lib<name>.so
- * here, but don't check the ABI just yet.
- *
- * - must be sufficiently long
- * - must end with LIB_SUFFIX, i.e. ".so"
- * - must start with APK_LIB, i.e. "lib/"
- */
- if (entryName.length() < MIN_ENTRY_LENGTH || !entryName.endsWith(LIB_SUFFIX)
- || !entryName.startsWith(APK_LIB)) {
- continue;
- }
-
- // file name must start with LIB_PREFIX, i.e. "lib"
- int lastSlash = entryName.lastIndexOf('/');
-
- if (lastSlash < 0
- || !entryName.regionMatches(lastSlash + 1, LIB_PREFIX, 0, LIB_PREFIX_LENGTH)) {
- continue;
- }
-
- hasNativeLibraries = true;
-
- // check the cpuAbi now, between lib/ and /lib<name>.so
- if (lastSlash != APK_LIB_LENGTH + cpuAbiLen
- || !entryName.regionMatches(APK_LIB_LENGTH, cpuAbi, 0, cpuAbiLen))
- continue;
-
- /*
- * Extract the library file name, ensure it doesn't contain
- * weird characters. we're guaranteed here that it doesn't contain
- * a directory separator though.
- */
- String libFileName = entryName.substring(lastSlash+1);
- if (!FileUtils.isFilenameSafe(new File(libFileName))) {
- continue;
- }
-
- installedNativeLibraries = true;
-
- if (DEBUG_NATIVE) {
- Log.d(TAG, "Caching shared lib " + entry.getName());
- }
-
- libEntries.add(Pair.create(entry, libFileName));
- }
- if (!hasNativeLibraries)
- return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
-
- if (!installedNativeLibraries)
- return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH;
-
- return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
+ public static long sumNativeBinariesLI(File apkFile) {
+ final String cpuAbi = Build.CPU_ABI;
+ final String cpuAbi2 = Build.CPU_ABI2;
+ return nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2);
}
- /*
- * Find the gdbserver executable program in a package at
- * lib/<cpuAbi>/gdbserver and add it to the list of binaries
- * to be copied out later.
- *
- * Returns PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES on success,
- * or PACKAGE_INSTALL_NATIVE_NO_LIBRARIES otherwise.
- */
- private static int listPackageGdbServerLI(ZipFile zipFile, String cpuAbi,
- List<Pair<ZipEntry, String>> nativeFiles) throws IOException, ZipException {
- final String apkGdbServerPath = "lib/" + cpuAbi + "/" + GDBSERVER;
-
- Enumeration<? extends ZipEntry> entries = zipFile.entries();
-
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- // skip directories
- if (entry.isDirectory()) {
- continue;
- }
- String entryName = entry.getName();
-
- if (!entryName.equals(apkGdbServerPath)) {
- continue;
- }
-
- if (false) {
- Log.d(TAG, "Found gdbserver: " + entry.getName());
- }
+ private native static int nativeCopyNativeBinaries(String filePath, String sharedLibraryPath,
+ String cpuAbi, String cpuAbi2);
- final String installGdbServerPath = GDBSERVER;
- nativeFiles.add(Pair.create(entry, installGdbServerPath));
-
- return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
- }
- return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
- }
-
- /*
- * Examine shared libraries stored in the APK as
- * lib/<cpuAbi>/lib<name>.so and add them to a list to be copied
- * later.
- *
- * This function will first try the main CPU ABI defined by Build.CPU_ABI
- * (which corresponds to ro.product.cpu.abi), and also try an alternate
- * one if ro.product.cpu.abi2 is defined.
- */
- public static int listPackageNativeBinariesLI(ZipFile zipFile,
- List<Pair<ZipEntry, String>> nativeFiles) throws ZipException, IOException {
- String cpuAbi = Build.CPU_ABI;
-
- int result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi, nativeFiles);
-
- /*
- * Some architectures are capable of supporting several CPU ABIs
- * for example, 'armeabi-v7a' also supports 'armeabi' native code
- * this is indicated by the definition of the ro.product.cpu.abi2
- * system property.
- *
- * only scan the package twice in case of ABI mismatch
- */
- if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
- final String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2", null);
- if (cpuAbi2 != null) {
- result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi2, nativeFiles);
- }
-
- if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
- Slog.w(TAG, "Native ABI mismatch from package file");
- return PackageManager.INSTALL_FAILED_INVALID_APK;
- }
-
- if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) {
- cpuAbi = cpuAbi2;
- }
- }
-
- /*
- * Debuggable packages may have gdbserver embedded, so add it to
- * the list to the list of items to be extracted (as lib/gdbserver)
- * into the application's native library directory later.
- */
- if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) {
- listPackageGdbServerLI(zipFile, cpuAbi, nativeFiles);
- }
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- public static int copyNativeBinariesLI(File scanFile, File sharedLibraryDir) {
- /*
- * Check all the native files that need to be copied and add
- * that to the container size.
- */
- ZipFile zipFile;
- try {
- zipFile = new ZipFile(scanFile);
-
- List<Pair<ZipEntry, String>> nativeFiles = new LinkedList<Pair<ZipEntry, String>>();
-
- NativeLibraryHelper.listPackageNativeBinariesLI(zipFile, nativeFiles);
-
- final int N = nativeFiles.size();
-
- for (int i = 0; i < N; i++) {
- final Pair<ZipEntry, String> entry = nativeFiles.get(i);
-
- File destFile = new File(sharedLibraryDir, entry.second);
- copyNativeBinaryLI(zipFile, entry.first, sharedLibraryDir, destFile);
- }
- zipFile.close();
- } catch (ZipException e) {
- Slog.w(TAG, "Failed to extract data from package file", e);
- return PackageManager.INSTALL_FAILED_INVALID_APK;
- } catch (IOException e) {
- Slog.w(TAG, "Failed to cache package shared libs", e);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
-
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- private static void copyNativeBinaryLI(ZipFile zipFile, ZipEntry entry,
- File binaryDir, File binaryFile) throws IOException {
- InputStream inputStream = zipFile.getInputStream(entry);
- try {
- File tempFile = File.createTempFile("tmp", "tmp", binaryDir);
- String tempFilePath = tempFile.getPath();
- // XXX package manager can't change owner, so the executable files for
- // now need to be left as world readable and owned by the system.
- if (!FileUtils.copyToFile(inputStream, tempFile)
- || !tempFile.setLastModified(entry.getTime())
- || FileUtils.setPermissions(tempFilePath, FileUtils.S_IRUSR | FileUtils.S_IWUSR
- | FileUtils.S_IRGRP | FileUtils.S_IXUSR | FileUtils.S_IXGRP
- | FileUtils.S_IXOTH | FileUtils.S_IROTH, -1, -1) != 0
- || !tempFile.renameTo(binaryFile)) {
- // Failed to properly write file.
- tempFile.delete();
- throw new IOException("Couldn't create cached binary " + binaryFile + " in "
- + binaryDir);
- }
- } finally {
- inputStream.close();
- }
+ public static int copyNativeBinariesIfNeededLI(File apkFile, File sharedLibraryDir) {
+ final String cpuAbi = Build.CPU_ABI;
+ final String cpuAbi2 = Build.CPU_ABI2;
+ return nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi,
+ cpuAbi2);
}
// Convenience method to call removeNativeBinariesFromDirLI(File)
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 514e59d..6e73889 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -29,6 +29,7 @@ LOCAL_SRC_FILES:= \
ActivityManager.cpp \
AndroidRuntime.cpp \
Time.cpp \
+ com_android_internal_content_NativeLibraryHelper.cpp \
com_google_android_gles_jni_EGLImpl.cpp \
com_google_android_gles_jni_GLImpl.cpp.arm \
android_app_NativeActivity.cpp \
@@ -178,6 +179,7 @@ LOCAL_C_INCLUDES += \
external/icu4c/common \
external/jpeg \
external/harfbuzz/src \
+ external/zlib \
frameworks/opt/emoji \
libcore/include
@@ -211,6 +213,7 @@ LOCAL_SHARED_LIBRARIES := \
libnfc_ndef \
libusbhost \
libharfbuzz \
+ libz \
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6ab8fe3..9e46d80 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -175,6 +175,7 @@ extern int register_android_view_VelocityTracker(JNIEnv* env);
extern int register_android_content_res_ObbScanner(JNIEnv* env);
extern int register_android_content_res_Configuration(JNIEnv* env);
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
+extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1199,6 +1200,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_content_res_Configuration),
REG_JNI(register_android_animation_PropertyValuesHolder),
+ REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
};
/*
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
new file mode 100644
index 0000000..830f70e
--- /dev/null
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NativeLibraryHelper"
+//#define LOG_NDEBUG 0
+
+#include <android_runtime/AndroidRuntime.h>
+
+#include <utils/Log.h>
+#include <ScopedUtfChars.h>
+#include <utils/ZipFileRO.h>
+
+#include <zlib.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+#define APK_LIB "lib/"
+#define APK_LIB_LEN (sizeof(APK_LIB) - 1)
+
+#define LIB_PREFIX "/lib"
+#define LIB_PREFIX_LEN (sizeof(LIB_PREFIX) - 1)
+
+#define LIB_SUFFIX ".so"
+#define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1)
+
+#define GDBSERVER "gdbserver"
+#define GDBSERVER_LEN (sizeof(GDBSERVER) - 1)
+
+#define TMP_FILE_PATTERN "/tmp.XXXXXX"
+#define TMP_FILE_PATTERN_LEN (sizeof(TMP_FILE_PATTERN) - 1)
+
+namespace android {
+
+typedef void (*iterFunc)(JNIEnv*, void*, ZipFileRO*, ZipEntryRO, const char*);
+
+// These match PackageManager.java install codes
+typedef enum {
+ INSTALL_SUCCEEDED = 0,
+ INSTALL_FAILED_INVALID_APK = -2,
+ INSTALL_FAILED_INSUFFICIENT_STORAGE = -4,
+} install_status_t;
+
+// Equivalent to isFilenameSafe
+static bool
+isFilenameSafe(const char* filename)
+{
+ off_t offset = 0;
+ for (;;) {
+ switch (*(filename + offset)) {
+ case 0:
+ // Null.
+ // If we've reached the end, all the other characters are good.
+ return true;
+
+ case 'A' ... 'Z':
+ case 'a' ... 'z':
+ case '0' ... '9':
+ case '+':
+ case ',':
+ case '-':
+ case '.':
+ case '/':
+ case '=':
+ case '_':
+ offset++;
+ break;
+
+ default:
+ // We found something that is not good.
+ return false;
+ }
+ }
+ // Should not reach here.
+}
+
+static bool
+isFileDifferent(const char* filePath, size_t fileSize, time_t modifiedTime,
+ long zipCrc, struct stat64* st)
+{
+ if (lstat64(filePath, st) < 0) {
+ // File is not found or cannot be read.
+ LOGV("Couldn't stat %s, copying: %s\n", filePath, strerror(errno));
+ return true;
+ }
+
+ if (!S_ISREG(st->st_mode)) {
+ return true;
+ }
+
+ if (st->st_size != fileSize) {
+ return true;
+ }
+
+ // For some reason, bionic doesn't define st_mtime as time_t
+ if (time_t(st->st_mtime) != modifiedTime) {
+ LOGV("mod time doesn't match: %ld vs. %ld\n", st->st_mtime, modifiedTime);
+ return true;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open(filePath, O_RDONLY));
+ if (fd < 0) {
+ LOGV("Couldn't open file %s: %s", filePath, strerror(errno));
+ return true;
+ }
+
+ long crc = crc32(0L, Z_NULL, 0);
+ unsigned char crcBuffer[16384];
+ ssize_t numBytes;
+ while ((numBytes = TEMP_FAILURE_RETRY(read(fd, crcBuffer, sizeof(crcBuffer)))) > 0) {
+ crc = crc32(crc, crcBuffer, numBytes);
+ }
+ close(fd);
+
+ LOGV("%s: crc = %lx, zipCrc = %lx\n", filePath, crc, zipCrc);
+
+ if (crc != zipCrc) {
+ return true;
+ }
+
+ return false;
+}
+
+static void
+sumFiles(JNIEnv* env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
+{
+ size_t* total = (size_t*) arg;
+ size_t uncompLen;
+
+ if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, NULL, NULL)) {
+ return;
+ }
+
+ *total += uncompLen;
+}
+
+/*
+ * Copy the native library if needed.
+ *
+ * This function assumes the library and path names passed in are considered safe.
+ */
+static void
+copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
+{
+ jstring* javaNativeLibPath = (jstring*) arg;
+ ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
+
+ size_t uncompLen;
+ long when;
+ long crc;
+ time_t modTime;
+
+ if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, &when, &crc)) {
+ return;
+ } else {
+ struct tm t;
+ ZipFileRO::zipTimeToTimespec(when, &t);
+ modTime = mktime(&t);
+ }
+
+ // Build local file path
+ const size_t fileNameLen = strlen(fileName);
+ char localFileName[nativeLibPath.size() + fileNameLen + 2];
+
+ if (strlcpy(localFileName, nativeLibPath.c_str(), sizeof(localFileName)) != nativeLibPath.size()) {
+ LOGD("Couldn't allocate local file name for library: %s", strerror(errno));
+ return;
+ }
+
+ *(localFileName + nativeLibPath.size()) = '/';
+
+ if (strlcpy(localFileName + nativeLibPath.size() + 1, fileName, sizeof(localFileName)
+ - nativeLibPath.size() - 1) != fileNameLen) {
+ LOGD("Couldn't allocate local file name for library: %s", strerror(errno));
+ return;
+ }
+
+ // Only copy out the native file if it's different.
+ struct stat st;
+ if (!isFileDifferent(localFileName, uncompLen, modTime, crc, &st)) {
+ return;
+ }
+
+ char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 2];
+ if (strlcpy(localTmpFileName, nativeLibPath.c_str(), sizeof(localTmpFileName))
+ != nativeLibPath.size()) {
+ LOGD("Couldn't allocate local file name for library: %s", strerror(errno));
+ return;
+ }
+
+ *(localFileName + nativeLibPath.size()) = '/';
+
+ if (strlcpy(localTmpFileName + nativeLibPath.size(), TMP_FILE_PATTERN,
+ TMP_FILE_PATTERN_LEN - nativeLibPath.size()) != TMP_FILE_PATTERN_LEN) {
+ LOGI("Couldn't allocate temporary file name for library: %s", strerror(errno));
+ return;
+ }
+
+ int fd = mkstemp(localTmpFileName);
+ if (fd < 0) {
+ LOGI("Couldn't open temporary file name: %s: %s\n", localTmpFileName, strerror(errno));
+ return;
+ }
+
+ if (!zipFile->uncompressEntry(zipEntry, fd)) {
+ LOGI("Failed uncompressing %s to %s: %s", fileName, localTmpFileName, strerror(errno));
+ close(fd);
+ unlink(localTmpFileName);
+ return;
+ }
+
+ close(fd);
+
+ // Set the modification time for this file to the ZIP's mod time.
+ struct timeval times[2];
+ times[0].tv_sec = st.st_atime;
+ times[1].tv_sec = modTime;
+ times[0].tv_usec = times[1].tv_usec = 0;
+ if (utimes(localTmpFileName, times) < 0) {
+ LOGI("Couldn't change modification time on %s: %s\n", localTmpFileName, strerror(errno));
+ unlink(localTmpFileName);
+ return;
+ }
+
+ // Set the mode to 755
+ static const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ if (chmod(localTmpFileName, mode) < 0) {
+ LOGI("Couldn't change permissions on %s: %s\n", localTmpFileName, strerror(errno));
+ unlink(localTmpFileName);
+ return;
+ }
+
+ // Finally, rename it to the final name.
+ if (rename(localTmpFileName, localFileName) < 0) {
+ LOGI("Couldn't rename %s to %s: %s\n", localTmpFileName, localFileName, strerror(errno));
+ unlink(localTmpFileName);
+ return;
+ }
+
+ LOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName);
+}
+
+static install_status_t
+iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2,
+ iterFunc callFunc, void* callArg) {
+ ScopedUtfChars filePath(env, javaFilePath);
+ ScopedUtfChars cpuAbi(env, javaCpuAbi);
+ ScopedUtfChars cpuAbi2(env, javaCpuAbi2);
+
+ ZipFileRO zipFile;
+
+ if (zipFile.open(filePath.c_str()) != NO_ERROR) {
+ LOGI("Couldn't open APK %s\n", filePath.c_str());
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ const int N = zipFile.getNumEntries();
+
+ char fileName[PATH_MAX];
+
+ for (int i = 0; i < N; i++) {
+ const ZipEntryRO entry = zipFile.findEntryByIndex(i);
+ if (entry == NULL) {
+ continue;
+ }
+
+ // Make sure this entry has a filename.
+ if (zipFile.getEntryFileName(entry, fileName, sizeof(fileName))) {
+ continue;
+ }
+
+ // Make sure we're in the lib directory of the ZIP.
+ if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) {
+ continue;
+ }
+
+ // Make sure the filename is at least to the minimum library name size.
+ const size_t fileNameLen = strlen(fileName);
+ static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
+ if (fileNameLen < minLength) {
+ continue;
+ }
+
+ const char* lastSlash = strrchr(fileName, '/');
+ if (lastSlash == NULL) {
+ LOG_ASSERT("last slash was null somehow for %s\n", fileName);
+ continue;
+ }
+
+ // Check to make sure the CPU ABI of this file is one we support.
+ const char* cpuAbiOffset = fileName + APK_LIB_LEN;
+ const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset;
+
+ LOGV("Comparing ABIs %s and %s versus %s\n", cpuAbi.c_str(), cpuAbi2.c_str(), cpuAbiOffset);
+ if (cpuAbi.size() == cpuAbiRegionSize
+ && *(cpuAbiOffset + cpuAbi.size()) == '/'
+ && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
+ LOGV("Using ABI %s\n", cpuAbi.c_str());
+ } else if (cpuAbi2.size() == cpuAbiRegionSize
+ && *(cpuAbiOffset + cpuAbi2.size()) == '/'
+ && !strncmp(cpuAbiOffset, cpuAbi2.c_str(), cpuAbiRegionSize)) {
+ LOGV("Using ABI %s\n", cpuAbi2.c_str());
+ } else {
+ LOGV("abi didn't match anything: %s (end at %zd)\n", cpuAbiOffset, cpuAbiRegionSize);
+ continue;
+ }
+
+ // If this is a .so file, check to see if we need to copy it.
+ if (!strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
+ && !strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)
+ && isFilenameSafe(lastSlash + 1)) {
+ callFunc(env, callArg, &zipFile, entry, lastSlash + 1);
+ } else if (!strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
+ callFunc(env, callArg, &zipFile, entry, lastSlash + 1);
+ }
+ }
+
+ return INSTALL_SUCCEEDED;
+}
+
+static jint
+com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
+ jstring javaFilePath, jstring javaNativeLibPath, jstring javaCpuAbi, jstring javaCpuAbi2)
+{
+ return iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2,
+ copyFileIfChanged, &javaNativeLibPath);
+}
+
+static jlong
+com_android_internal_content_NativeLibraryHelper_sumNativeBinaries(JNIEnv *env, jclass clazz,
+ jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2)
+{
+ size_t totalSize = 0;
+
+ iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2, sumFiles, &totalSize);
+
+ return totalSize;
+}
+
+static JNINativeMethod gMethods[] = {
+ {"nativeCopyNativeBinaries",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
+ {"nativeSumNativeBinaries",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
+ (void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
+};
+
+
+int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env)
+{
+ return AndroidRuntime::registerNativeMethods(env,
+ "com/android/internal/content/NativeLibraryHelper", gMethods, NELEM(gMethods));
+}
+
+};