summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--include/utils/ZipFileRO.h15
-rw-r--r--libs/utils/tests/Android.mk3
-rw-r--r--libs/utils/tests/ZipFileRO_test.cpp64
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java29
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java32
9 files changed, 498 insertions, 310 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));
+}
+
+};
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 3a99979..547e36a 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -38,6 +38,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <time.h>
namespace android {
@@ -174,6 +175,20 @@ public:
size_t uncompLen, size_t compLen);
/*
+ * Utility function to convert ZIP's time format to a timespec struct.
+ */
+ static inline void zipTimeToTimespec(long when, struct tm* timespec) {
+ const long date = when >> 16;
+ timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980
+ timespec->tm_mon = (date >> 5) & 0x0F;
+ timespec->tm_mday = date & 0x1F;
+
+ timespec->tm_hour = (when >> 11) & 0x1F;
+ timespec->tm_min = (when >> 5) & 0x3F;
+ timespec->tm_sec = (when & 0x1F) << 1;
+ }
+
+ /*
* Some basic functions for raw data manipulation. "LE" means
* Little Endian.
*/
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index 8726a53..b97f52f 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -8,7 +8,8 @@ test_src_files := \
ObbFile_test.cpp \
Looper_test.cpp \
String8_test.cpp \
- Unicode_test.cpp
+ Unicode_test.cpp \
+ ZipFileRO_test.cpp \
shared_libraries := \
libz \
diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/utils/tests/ZipFileRO_test.cpp
new file mode 100644
index 0000000..7a1d0bd
--- /dev/null
+++ b/libs/utils/tests/ZipFileRO_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "ZipFileRO_test"
+#include <utils/Log.h>
+#include <utils/ZipFileRO.h>
+
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <string.h>
+
+namespace android {
+
+class ZipFileROTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(ZipFileROTest, ZipTimeConvertSuccess) {
+ struct tm t;
+
+ // 2011-06-29 14:40:40
+ long when = 0x3EDD7514;
+
+ ZipFileRO::zipTimeToTimespec(when, &t);
+
+ EXPECT_EQ(2011, t.tm_year + 1900)
+ << "Year was improperly converted.";
+
+ EXPECT_EQ(6, t.tm_mon)
+ << "Month was improperly converted.";
+
+ EXPECT_EQ(29, t.tm_mday)
+ << "Day was improperly converted.";
+
+ EXPECT_EQ(14, t.tm_hour)
+ << "Hour was improperly converted.";
+
+ EXPECT_EQ(40, t.tm_min)
+ << "Minute was improperly converted.";
+
+ EXPECT_EQ(40, t.tm_sec)
+ << "Second was improperly converted.";
+}
+
+}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 15c1653..626cc86 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -515,34 +515,7 @@ public class DefaultContainerService extends IntentService {
// Check all the native files that need to be copied and add that to the
// container size.
- ZipFile zipFile;
- final List<Pair<ZipEntry, String>> nativeFiles;
- try {
- zipFile = new ZipFile(apkFile);
-
- if (outFiles != null) {
- nativeFiles = outFiles;
- } else {
- nativeFiles = new ArrayList<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);
-
- /*
- * Note a 1MB padding is added to the claimed size, so we don't
- * have to worry about block alignment here.
- */
- sizeBytes += entry.first.getSize();
- }
- } catch (ZipException e) {
- Log.w(TAG, "Failed to extract data from package file", e);
- } catch (IOException e) {
- Log.w(TAG, "Failed to cache package shared libs", e);
- }
+ sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkFile);
int sizeMb = (int) (sizeBytes >> 20);
if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 9eb1179..88e0fa8 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static libcore.io.OsConstants.S_ISLNK;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
@@ -76,6 +77,7 @@ import android.os.Environment;
import android.os.FileObserver;
import android.os.FileUtils;
import android.os.FileUtils.FileStatus;
+import android.os.Debug;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -128,6 +130,9 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
/**
* Keep track of all those .apks everywhere.
*
@@ -3338,16 +3343,27 @@ public class PackageManagerService extends IPackageManager.Stub {
} else if (nativeLibraryDir.getCanonicalFile().getParent()
.equals(dataPathString)) {
/*
+ * Make sure the native library dir isn't a symlink to
+ * something. If it is, ask installd to remove it and create
+ * a directory so we can copy to it afterwards.
+ */
+ boolean isSymLink;
+ try {
+ isSymLink = S_ISLNK(Libcore.os.lstat(nativeLibraryDir.getPath()).st_mode);
+ } catch (ErrnoException e) {
+ // This shouldn't happen, but we'll fail-safe.
+ isSymLink = true;
+ }
+ if (isSymLink) {
+ mInstaller.unlinkNativeLibraryDirectory(dataPathString);
+ }
+
+ /*
* If this is an internal application or our
* nativeLibraryPath points to our data directory, unpack
- * the libraries. The native library path pointing to the
- * data directory for an application in an ASEC container
- * can happen for older apps that existed before an OTA to
- * Gingerbread.
+ * the libraries if necessary.
*/
- Slog.i(TAG, "Unpacking native libraries for " + path);
- mInstaller.unlinkNativeLibraryDirectory(dataPathString);
- NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
+ NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir);
} else {
Slog.i(TAG, "Linking native library dir for " + path);
mInstaller.linkNativeLibraryDirectory(dataPathString,
@@ -7930,7 +7946,7 @@ public class PackageManagerService extends IPackageManager.Stub {
.unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) {
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
} else {
- NativeLibraryHelper.copyNativeBinariesLI(new File(
+ NativeLibraryHelper.copyNativeBinariesIfNeededLI(new File(
newCodePath), new File(newNativePath));
}
} else {