Use Apache Commons Compress instead of java.util.zip

When the SDK installer unarchives the zip files, it is important to keep
the permissions for executable (Linux/MacOS) or the tools required to
build applications (aapt, aidl, ...) won't work.

Since java.util.zip does not provide support for permissions, we now
use the Apache Commons Compress component that allows us to read the
permission from the archive.
diff --git a/build/sdk.atree b/build/sdk.atree
index a5dc1fe..72de93b 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -127,6 +127,7 @@
 
 # sdkmanager
 bin/android tools/android
+framework/commons-compress-1.0.jar tools/lib/commons-compress-1.0.jar
 framework/sdklib.jar tools/lib/sdklib.jar
 framework/sdkuilib.jar tools/lib/sdkuilib.jar
 framework/sdkmanager.jar tools/lib/sdkmanager.jar
diff --git a/tools/sdkmanager/libs/sdklib/.classpath b/tools/sdkmanager/libs/sdklib/.classpath
index 050eeb2..123ceef 100644
--- a/tools/sdkmanager/libs/sdklib/.classpath
+++ b/tools/sdkmanager/libs/sdklib/.classpath
@@ -1,9 +1,10 @@
-<?xml version="1.0" encoding="UTF-8"?>

-<classpath>

-	<classpathentry kind="src" path="src"/>

-	<classpathentry kind="src" path="tests"/>

-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>

-	<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>

-	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>

-	<classpathentry kind="output" path="bin"/>

-</classpath>

+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="tests"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/commons-compress/commons-compress-1.0.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/sdkmanager/libs/sdklib/src/Android.mk b/tools/sdkmanager/libs/sdklib/src/Android.mk
index 25c7126..c8d6f71 100644
--- a/tools/sdkmanager/libs/sdklib/src/Android.mk
+++ b/tools/sdkmanager/libs/sdklib/src/Android.mk
@@ -20,7 +20,8 @@
 LOCAL_JAVA_RESOURCE_DIRS := .
 
 LOCAL_JAVA_LIBRARIES := \
-        androidprefs
+        androidprefs \
+        commons-compress-1.0
 
 LOCAL_MODULE := sdklib
 
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
index c34e859..3757b74 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
@@ -16,19 +16,21 @@
 

 package com.android.sdklib.internal.repository;

 

+import com.android.sdklib.SdkConstants;

 import com.android.sdklib.SdkManager;

 

+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;

+import org.apache.commons.compress.archivers.zip.ZipFile;

+

 import java.io.File;

-import java.io.FileInputStream;

 import java.io.FileOutputStream;

 import java.io.IOException;

 import java.io.InputStream;

 import java.net.URL;

 import java.security.MessageDigest;

 import java.security.NoSuchAlgorithmException;

+import java.util.Enumeration;

 import java.util.Properties;

-import java.util.zip.ZipEntry;

-import java.util.zip.ZipInputStream;

 

 

 /**

@@ -692,6 +694,7 @@
      * unarchiving. However we return that root folder name to the caller, as it can be used

      * as a template to know what destination directory to use in the Add-on case.

      */

+    @SuppressWarnings("unchecked")

     private boolean unzipFolder(File archiveFile,

             long compressedSize,

             File unzipDestFolder,

@@ -701,11 +704,13 @@
 

         description += " (%1$d%%)";

 

-        FileInputStream fis = null;

-        ZipInputStream  zis = null;

+        ZipFile zipFile = null;

         try {

-            fis = new FileInputStream(archiveFile);

-            zis = new ZipInputStream(fis);

+            zipFile = new ZipFile(archiveFile);

+

+            // figure if we'll need to set the unix permission

+            boolean usingUnixPerm = SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ||

+                    SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX;

 

             // To advance the percent and the progress bar, we don't know the number of

             // items left to unzip. However we know the size of the archive and the size of

@@ -718,8 +723,10 @@
 

             byte[] buf = new byte[65536];

 

-            ZipEntry entry;

-            while ((entry = zis.getNextEntry()) != null) {

+            Enumeration<ZipArchiveEntry> entries =

+                    (Enumeration<ZipArchiveEntry>)zipFile.getEntries();

+            while (entries.hasMoreElements()) {

+                ZipArchiveEntry entry = entries.nextElement();

 

                 String name = entry.getName();

 

@@ -768,7 +775,8 @@
                 try {

                     fos = new FileOutputStream(destFile);

                     int n;

-                    while ((n = zis.read(buf)) != -1) {

+                    InputStream entryContent = zipFile.getInputStream(entry);

+                    while ((n = entryContent.read(buf)) != -1) {

                         if (n > 0) {

                             fos.write(buf, 0, n);

                         }

@@ -779,6 +787,11 @@
                     }

                 }

 

+                // if needed set the permissions.

+                if (usingUnixPerm) {

+                    setPermission(destFile, entry.getUnixMode());

+                }

+

                 // Increment progress bar to match. We update only between files.

                 for(incTotal += entry.getCompressedSize(); incCurr < incTotal; incCurr += incStep) {

                     monitor.incProgress(1);

@@ -801,16 +814,9 @@
             monitor.setResult("Unzip failed: %1$s", e.getMessage());

 

         } finally {

-            if (zis != null) {

+            if (zipFile != null) {

                 try {

-                    zis.close();

-                } catch (IOException e) {

-                    // pass

-                }

-            }

-            if (fis != null) {

-                try {

-                    fis.close();

+                    zipFile.close();

                 } catch (IOException e) {

                     // pass

                 }

@@ -902,5 +908,20 @@
         return false;

     }

 

+    /**

+     * Sets the Unix permission on a file or folder.

+     * @param file The file to set permissions on.

+     * @param unixMode the permissions as received from {@link ZipArchiveEntry#getUnixMode()}.

+     * @throws IOException

+     */

+    private void setPermission(File file, int unixMode) throws IOException {

+        // permissions contains more than user/group/all, and we need the 777 display mode, so we

+        // convert it in octal string and take the last 3 digits.

+        String permission = String.format("%o", unixMode);

+        permission = permission.substring(permission.length() - 3, permission.length());

 

+        Runtime.getRuntime().exec(new String[] {

+           "chmod", permission, file.getAbsolutePath()

+        });

+    }

 }