blob: 6382ce0ca20b83aa78d1a702cbd960ec471ea93e [file] [log] [blame]
/*
* 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;
}
}