diff options
author | Kenny Root <kroot@google.com> | 2011-07-27 15:54:51 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-07-27 15:54:51 -0700 |
commit | 4f35092af55d569975e6931ee6e8c0343f749d1e (patch) | |
tree | c15077eab86d9e3da6cee3c63fc07b69d8898479 | |
parent | 3d5a703db83265f7914eed8580de986106abfad2 (diff) | |
parent | 66269ea6f68f2f25888ce1080c94ac782742fafc (diff) | |
download | frameworks_base-4f35092af55d569975e6931ee6e8c0343f749d1e.zip frameworks_base-4f35092af55d569975e6931ee6e8c0343f749d1e.tar.gz frameworks_base-4f35092af55d569975e6931ee6e8c0343f749d1e.tar.bz2 |
Merge "Move extract native libraries to JNI code"
-rw-r--r-- | core/java/com/android/internal/content/NativeLibraryHelper.java | 285 | ||||
-rw-r--r-- | core/jni/Android.mk | 3 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/com_android_internal_content_NativeLibraryHelper.cpp | 375 | ||||
-rw-r--r-- | include/utils/ZipFileRO.h | 15 | ||||
-rw-r--r-- | libs/utils/tests/Android.mk | 3 | ||||
-rw-r--r-- | libs/utils/tests/ZipFileRO_test.cpp | 64 | ||||
-rw-r--r-- | packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java | 29 | ||||
-rw-r--r-- | services/java/com/android/server/pm/PackageManagerService.java | 32 |
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 { |