/* * Copyright (C) 2012 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. */ package com.motorolamobility.preflighting.core.internal.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.eclipse.core.runtime.Path; import com.motorolamobility.preflighting.core.exception.PreflightingToolException; import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS; import com.motorolamobility.preflighting.core.logging.PreflightingLogger; import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration; /** * This class holds methods which deal with APK package. */ public final class ApkUtils { public static final String APP_VALIDATOR_TEMP_DIR = "MotodevAppValidator"; private static final String JAVA_TEMP_DIR_PROPERTY = "java.io.tmpdir"; private static final String TEMP_DIR_PATH = System.getProperty(JAVA_TEMP_DIR_PROPERTY); // Temp folder used for APK extracting public static final File tmpAppValidatorFolder = new File(TEMP_DIR_PATH, APP_VALIDATOR_TEMP_DIR); private static final String RSA = ".rsa"; private static final String DSA = ".dsa"; public static final String APK_EXTENSION = ".apk"; public static final String ZIP_EXTENSION = ".zip"; private static final int BUFFER_SIZE = 1024; /** * Give an APK file, a tree directories and files are extracted, * representing partially the project which generated the APK. * <p> * The files are created in a temporary directory. * * @param apkFile * APK file which the project tree will be extracted from. * @param sdkPath * SDK path where the tools for extracting and interpreting the * APK information will be used. * * @return A file object holding a tree of directories and files which * represent partially the project which generated the APK. * * @throws PreflightingToolException * Exception thrown when there are problems creating the files * and directories structure. */ public static File extractProjectFromAPK(File apkFile, String sdkPath) throws PreflightingToolException { String apkName = apkFile.getName(); // Create a temp directory to contain all extracted packages, if needed if (!tmpAppValidatorFolder.exists()) { try { tmpAppValidatorFolder.mkdir(); } catch (SecurityException se) { PreflightingLogger.error(ApkUtils.class, "It was not possible to extract the android package.", se); //$NON-NLS-1$ throw new PreflightingToolException( PreflightingCoreNLS.ApkUtils_ImpossibleExtractAndroidPackageMessage, se); } } File tmpProjectFile; try { tmpProjectFile = File.createTempFile(apkName, null, tmpAppValidatorFolder); tmpProjectFile.delete(); tmpProjectFile.mkdir(); } catch (IOException ioException) { PreflightingLogger.error(ApkUtils.class, "It was not possible to extract the android package.", ioException); //$NON-NLS-1$ throw new PreflightingToolException( PreflightingCoreNLS.ApkUtils_ImpossibleExtractAndroidPackageMessage, ioException); } String extractionMode = ValidationManagerConfiguration.getInstance().getProperty( ValidationManagerConfiguration.ConfigProperties.APK_EXTRACTION_MODE .getName()); if (extractionMode.equals(ValidationManagerConfiguration.ExtractionModes.APKTOOL_MODE .getMode())) { ApktoolUtils.extractFilesFromApk(apkFile, tmpProjectFile); } else { AaptUtils.extractFilesFromAPK(apkFile, sdkPath, tmpProjectFile); } return tmpProjectFile; } /** * Returns a handler to the temp directory used for extracting APKs. * * @return A handler to the temp directory. */ public static File getAppValidatorTempApkFolder() { return tmpAppValidatorFolder; } /** * Iterates over APK (jar entries) to populate * * @param projectFile * @return * @throws IOException * @throws CertificateException */ public static List<Certificate> populateCertificate(File projectFile) throws IOException, CertificateException { List<Certificate> certList = new ArrayList<Certificate>(); JarFile jar = new JarFile(projectFile); Enumeration<JarEntry> jarEntries = jar.entries(); while (jarEntries.hasMoreElements()) { JarEntry entry = jarEntries.nextElement(); if (entry.getName().toLowerCase().contains(DSA) || entry.getName().toLowerCase().contains(RSA)) { certList.addAll(extractCertificate(jar, entry)); } } return certList; } /** * Extracts certificate from APK * * @param jar * @param entry * rsa or dsa jar item * @return * @throws IOException * I/O problem to read jar * @throws CertificateException * certificate has problems */ private static List<Certificate> extractCertificate(JarFile jar, JarEntry entry) throws IOException, CertificateException { List<Certificate> certList = new ArrayList<Certificate>(); InputStream inStream = null; try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); inStream = jar.getInputStream(entry); Collection<? extends Certificate> c = cf.generateCertificates(inStream); Iterator<? extends Certificate> i = c.iterator(); while (i.hasNext()) { Certificate cert = i.next(); certList.add(cert); } } finally { if (inStream != null) { inStream.close(); } } return certList; } /** * Unzip zipFile and returns the directory created with its contents * @param zipFile * @return * @throws PreflightingToolException */ public static File unzip(File zipFile) throws PreflightingToolException { File tempExtractionDir = null; ZipInputStream apkInputStream = null; FileOutputStream apkOutputStream = null; try { //crate MOTODEV temp folder if (!tmpAppValidatorFolder.exists()) { tmpAppValidatorFolder.mkdir(); } //create extraction folder tempExtractionDir = File.createTempFile(zipFile.getName(), null, tmpAppValidatorFolder); tempExtractionDir.delete(); tempExtractionDir.mkdir(); //open zipFile stream apkInputStream = new ZipInputStream(new FileInputStream(zipFile.getAbsolutePath())); ZipEntry apkZipEntry = apkInputStream.getNextEntry(); byte[] buf = new byte[BUFFER_SIZE]; CRC32 crc = new CRC32(); //while there are apks inside zip file while (apkZipEntry != null) { crc.reset(); if (apkZipEntry.getName().endsWith(APK_EXTENSION)) { //file to be created (apk) try { apkOutputStream = new FileOutputStream(tempExtractionDir.getAbsolutePath() + Path.SEPARATOR + apkZipEntry.getName()); int length = 0; //creates apk and updates CRC during the process while ((length = apkInputStream.read(buf, 0, BUFFER_SIZE)) > -1) { apkOutputStream.write(buf, 0, length); crc.update(buf, 0, length); } //test if extraction went fine if (crc.getValue() != apkZipEntry.getCrc()) { throw new PreflightingToolException(PreflightingCoreNLS.bind( PreflightingCoreNLS.ApkUtils_ZipExtractionFile, apkZipEntry.getName())); } } finally { if (apkOutputStream != null) { try { apkOutputStream.close(); } catch (IOException e) { // do nothing } } } } apkZipEntry = apkInputStream.getNextEntry(); } } catch (IOException ioe) { //error during extraction, abort validation throw new PreflightingToolException(PreflightingCoreNLS.ApkUtils_ZipExtraction, ioe); } finally { try { if (apkInputStream != null) { apkInputStream.close(); } } catch (IOException e) { // do nothing } } return tempExtractionDir; } }