Merge change I42a0f7a5 into eclair

* changes:
  PackageManager: Support secondary ABI for native code at installation time.
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 39129d4..5ed2d35 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -102,6 +102,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
@@ -2781,72 +2782,156 @@
         return pkg;
     }
 
-    private int cachePackageSharedLibsLI(PackageParser.Package pkg,
-            File dataPath, File scanFile) {
-        File sharedLibraryDir = new File(dataPath.getPath() + "/lib");
-        final String sharedLibraryABI = Build.CPU_ABI;
-        final String apkLibraryDirectory = "lib/" + sharedLibraryABI + "/";
-        final String apkSharedLibraryPrefix = apkLibraryDirectory + "lib";
-        final String sharedLibrarySuffix = ".so";
-        boolean hasNativeCode = false;
-        boolean installedNativeCode = false;
-        try {
-            ZipFile zipFile = new ZipFile(scanFile);
-            Enumeration<ZipEntry> entries =
-                (Enumeration<ZipEntry>) zipFile.entries();
+    // The following constants are returned by cachePackageSharedLibsForAbiLI
+    // 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;
 
-            while (entries.hasMoreElements()) {
-                ZipEntry entry = entries.nextElement();
-                if (entry.isDirectory()) {
-                    if (!hasNativeCode && entry.getName().startsWith("lib")) {
-                        hasNativeCode = true;
-                    }
-                    continue;
+    // Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk
+    // and automatically copy them to /data/data/<appname>/lib if present.
+    //
+    // 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 int cachePackageSharedLibsForAbiLI( PackageParser.Package  pkg,
+        File dataPath, File scanFile, String cpuAbi)
+    throws IOException, ZipException {
+        File sharedLibraryDir = new File(dataPath.getPath() + "/lib");
+        final String apkLib = "lib/";
+        final int apkLibLen = apkLib.length();
+        final int cpuAbiLen = cpuAbi.length();
+        final String libPrefix = "lib";
+        final int libPrefixLen = libPrefix.length();
+        final String libSuffix = ".so";
+        final int libSuffixLen = libSuffix.length();
+        boolean hasNativeLibraries = false;
+        boolean installedNativeLibraries = false;
+
+        // the minimum length of a valid native shared library of the form
+        // lib/<something>/lib<name>.so.
+        final int minEntryLen  = apkLibLen + 2 + libPrefixLen + 1 + libSuffixLen;
+
+        ZipFile zipFile = new ZipFile(scanFile);
+        Enumeration<ZipEntry> entries =
+            (Enumeration<ZipEntry>) 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 libSuffix, i.e. ".so"
+            // - must start with apkLib, i.e. "lib/"
+            if (entryName.length() < minEntryLen ||
+                !entryName.endsWith(libSuffix) ||
+                !entryName.startsWith(apkLib) ) {
+                continue;
+            }
+
+            // file name must start with libPrefix, i.e. "lib"
+            int lastSlash = entryName.lastIndexOf('/');
+
+            if (lastSlash < 0 || 
+                !entryName.regionMatches(lastSlash+1, libPrefix, 0, libPrefixLen) ) {
+                continue;
+            }
+
+            hasNativeLibraries = true;
+
+            // check the cpuAbi now, between lib/ and /lib<name>.so
+            //
+            if (lastSlash != apkLibLen + cpuAbiLen ||
+                !entryName.regionMatches(apkLibLen, 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;
+
+            String sharedLibraryFilePath = sharedLibraryDir.getPath() +
+                File.separator + libFileName;
+            File sharedLibraryFile = new File(sharedLibraryFilePath);
+            if (! sharedLibraryFile.exists() ||
+                sharedLibraryFile.length() != entry.getSize() ||
+                sharedLibraryFile.lastModified() != entry.getTime()) {
+                if (Config.LOGD) {
+                    Log.d(TAG, "Caching shared lib " + entry.getName());
                 }
-                String entryName = entry.getName();
-                if (entryName.startsWith("lib/")) {
-                    hasNativeCode = true;
+                if (mInstaller == null) {
+                    sharedLibraryDir.mkdir();
                 }
-                if (! (entryName.startsWith(apkSharedLibraryPrefix)
-                        && entryName.endsWith(sharedLibrarySuffix))) {
-                    continue;
+                cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir,
+                        sharedLibraryFile);
+            }
+        }
+        if (!hasNativeLibraries)
+            return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
+
+        if (!installedNativeLibraries)
+            return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH;
+
+        return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
+    }
+
+    // extract shared libraries stored in the APK as lib/<cpuAbi>/lib<name>.so
+    // and copy them to /data/data/<appname>/lib.
+    //
+    // 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.
+    //
+    private int cachePackageSharedLibsLI(PackageParser.Package  pkg,
+        File dataPath, File scanFile) {
+        final String cpuAbi = Build.CPU_ABI;
+        try {
+            int result = cachePackageSharedLibsForAbiLI(pkg, dataPath, scanFile, cpuAbi);
+
+            // 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) {
+                String  cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2",null);
+                if (cpuAbi2 != null) {
+                    result = cachePackageSharedLibsForAbiLI(pkg, dataPath, scanFile, cpuAbi2);
                 }
-                String libFileName = entryName.substring(
-                        apkLibraryDirectory.length());
-                if (libFileName.contains("/")
-                        || (!FileUtils.isFilenameSafe(new File(libFileName)))) {
-                    continue;
-                }
-                
-                installedNativeCode = true;
-                
-                String sharedLibraryFilePath = sharedLibraryDir.getPath() +
-                    File.separator + libFileName;
-                File sharedLibraryFile = new File(sharedLibraryFilePath);
-                if (! sharedLibraryFile.exists() ||
-                    sharedLibraryFile.length() != entry.getSize() ||
-                    sharedLibraryFile.lastModified() != entry.getTime()) {
-                    if (Config.LOGD) {
-                        Log.d(TAG, "Caching shared lib " + entry.getName());
-                    }
-                    if (mInstaller == null) {
-                        sharedLibraryDir.mkdir();
-                    }
-                    cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir,
-                            sharedLibraryFile);
+
+                if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
+                    Log.w(TAG,"Native ABI mismatch from package file");
+                    return PackageManager.INSTALL_FAILED_INVALID_APK;
                 }
             }
+        } catch (ZipException e) {
+            Log.w(TAG, "Failed to extract data from package file", e);
+            return PackageManager.INSTALL_FAILED_INVALID_APK;
         } catch (IOException e) {
             Log.w(TAG, "Failed to cache package shared libs", e);
             return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
         }
-        
-        if (hasNativeCode && !installedNativeCode) {
-            Log.w(TAG, "Install failed: .apk has native code but none for arch "
-                    + Build.CPU_ABI);
-            return PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE;
-        }
-        
         return PackageManager.INSTALL_SUCCEEDED;
     }