blob: 3bfaec2db1af3aa2fbce38deda0ae59785644ceb [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.motorola.studio.android.obfuscate;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.osgi.framework.Bundle;
import com.motorola.studio.android.AndroidPlugin;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorola.studio.android.common.utilities.AndroidUtils;
/**
* Manager to set Proguard files inside project
* Manipulates the file on <project_root>/default.properties
* Copy proguard.cfg
*/
public class ObfuscatorManager
{
private static final String PROGUARD_PATH = "/files/proguard.cfg";
private static final String PROGUARD_SDK_PATH = "tools/lib/proguard.cfg";
private static final String PROGUARD_FILENAME = "proguard.cfg";
private static final String PROGUARD_CONFIG_STATEMENT = "proguard.config=proguard.cfg";
private static final String DEFAULT_PROPERTIES_FILENAME = "default.properties";
private static final String PROJECT_PROPERTIES_FILENAME = "project.properties";
private static final String NL = System.getProperty("line.separator");
/**
* Prepares the project with the Proguard settings
* to obfuscate when the project is exported in release mode
* @param project
* @param monitor
* @return
*/
public static IStatus obfuscate(IProject project, IProgressMonitor monitor)
{
IStatus status = Status.OK_STATUS;
/*
* Project properties file is the new filename for ADT
*/
File projectPropertiesFile =
project.getFile(PROJECT_PROPERTIES_FILENAME).getLocation().toFile();
File defaultPropertiesFile =
project.getFile(DEFAULT_PROPERTIES_FILENAME).getLocation().toFile();
if (projectPropertiesFile.canWrite())
{
defaultPropertiesFile = projectPropertiesFile;
}
File proguardFile = project.getFile(PROGUARD_FILENAME).getLocation().toFile();
try
{
addProguardLine(defaultPropertiesFile);
if (!fileExists(proguardFile))
{
copyProguardFile(project, monitor);
}
try
{
project.refreshLocal(IResource.DEPTH_ONE, null);
}
catch (CoreException e)
{
// Do nothing, user just have to press F5
}
}
catch (Exception e)
{
StudioLogger.error(ObfuscatorManager.class,
"Error while setting Proguard to obfuscate", e);
status =
new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
"Could not set Proguard to obfuscate", e);
}
StudioLogger.collectUsageData(StudioLogger.WHAT_OBFUSCATE, StudioLogger.KIND_OBFUSCATE,
StudioLogger.DESCRIPTION_DEFAULT, AndroidPlugin.PLUGIN_ID, AndroidPlugin
.getDefault().getBundle().getVersion().toString());
return status;
}
/**
* Removes Proguard settings
* (Project will not be obfuscated when the is exported in release mode)
* @param project
* @return
*/
public static IStatus unobfuscate(IProject project)
{
IStatus status = Status.OK_STATUS;
try
{
File projectPropertiesFile =
project.getFile(PROJECT_PROPERTIES_FILENAME).getLocation().toFile();
File defaultPropertiesFile =
project.getFile(DEFAULT_PROPERTIES_FILENAME).getLocation().toFile();
if (isProguardSet(defaultPropertiesFile))
{
removeProguardLine(defaultPropertiesFile);
}
if (isProguardSet(projectPropertiesFile))
{
removeProguardLine(projectPropertiesFile);
}
try
{
project.refreshLocal(IResource.DEPTH_ONE, null);
}
catch (CoreException e)
{
// Do nothing, user just have to press F5
}
}
catch (Exception e)
{
StudioLogger
.error(ObfuscatorManager.class, "Error while removing Proguard settings", e);
status =
new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
"Could not remove Proguard settings", e);
}
StudioLogger.collectUsageData(StudioLogger.WHAT_OBFUSCATE, StudioLogger.KIND_DESOBFUSCATE,
StudioLogger.DESCRIPTION_DEFAULT, AndroidPlugin.PLUGIN_ID, AndroidPlugin
.getDefault().getBundle().getVersion().toString());
return status;
}
/*
* @param propertiesFile file to write
* @param newContent content to write (replaces entire file)
* @throws IOException
*/
private static void write(File propertiesFile, String newContent) throws IOException
{
Writer out = new OutputStreamWriter(new FileOutputStream(propertiesFile));
try
{
out.write(newContent);
}
finally
{
if (out != null)
{
out.close();
}
}
}
/*
* @param ignoreProguardStatement true it does not return the line with proguard.config=proguard.cfg
* @return
* @throws IOException
*/
private static String read(File propertiesFile, boolean ignoreProguardStatement)
throws IOException
{
StringBuilder text = new StringBuilder();
Scanner scanner = null;
FileInputStream stream = null;
try
{
stream = new FileInputStream(propertiesFile);
scanner = new Scanner(stream);
while (scanner.hasNextLine())
{
String line = scanner.nextLine();
if (!ignoreProguardStatement || !line.contains(PROGUARD_CONFIG_STATEMENT))
{
text.append(line + NL);
}
}
}
finally
{
try
{
if (scanner != null)
{
scanner.close();
}
if (stream != null)
{
stream.close();
}
}
catch (IOException e)
{
StudioLogger
.info("Could not close stream while reading obfuscator properties file. "
+ e.getMessage());
}
}
return text.toString();
}
private static boolean fileExists(File proguardFile)
{
return (proguardFile != null) && proguardFile.exists();
}
/**
* Checks if default.properties have Proguard statement
* and if the proguard.cfg exists in the project
* @param project
* @return
*/
public static boolean isProguardSet(IProject project)
{
File projectPropertiesFile =
project.getFile(PROJECT_PROPERTIES_FILENAME).getLocation().toFile();
File defaultPropertiesFile =
project.getFile(DEFAULT_PROPERTIES_FILENAME).getLocation().toFile();
File proguardFile = project.getFile(PROGUARD_FILENAME).getLocation().toFile();
return (isProguardSet(projectPropertiesFile) || isProguardSet(defaultPropertiesFile))
&& fileExists(proguardFile);
}
/**
* Checks if default.properties have the Proguard statement
* @param propertiesFile
* @return
*/
private static boolean isProguardSet(File propertiesFile)
{
String defaultPropertiesContent = null;
try
{
defaultPropertiesContent = read(propertiesFile, false);
}
catch (IOException e)
{
StudioLogger.error(ObfuscatorManager.class, e.getMessage(), e);
}
return ((defaultPropertiesContent != null) && defaultPropertiesContent
.contains(PROGUARD_CONFIG_STATEMENT));
}
/**
* Add the following line to file
* proguard.config=proguard.cfg
* if it does not exist yet
* @param project
* @throws IOException
*/
private static void addProguardLine(File propertiesFile) throws IOException
{
String currentContent = null;
currentContent = read(propertiesFile, false);
if (!currentContent.toString().contains(PROGUARD_CONFIG_STATEMENT))
{
String newContent =
currentContent.endsWith(NL) ? currentContent + PROGUARD_CONFIG_STATEMENT
: currentContent + NL + PROGUARD_CONFIG_STATEMENT;
write(propertiesFile, newContent);
}
}
/**
* Remove the following line to file
* proguard.config=proguard.cfg
* if it exists
* @param project
* @throws IOException
*/
private static void removeProguardLine(File propertiesFile) throws IOException
{
String contentWithoutProguardStatement = null;
contentWithoutProguardStatement = read(propertiesFile, true);
write(propertiesFile, contentWithoutProguardStatement);
}
private static void copyProguardFile(IProject project, IProgressMonitor monitor)
throws IOException, CoreException
{
SubMonitor subMonitor = SubMonitor.convert(monitor);
URL proguardURL = getProguardFileURL();
if (proguardURL != null)
{
InputStream is = null;
try
{
is = proguardURL.openStream();
IFile destFile = project.getFile(PROGUARD_FILENAME);
destFile.create(is, IResource.NONE, subMonitor);
}
finally
{
if (is != null)
{
is.close();
}
}
}
}
/**
* Get the most suitable proguard file, either from SDK or our plug-in
* @return the URL of the best proguard file found
*/
private static URL getProguardFileURL()
{
String sdkPath = AndroidUtils.getSDKPathByPreference();
File sdkPathFile = sdkPath.isEmpty() ? new File(sdkPath) : null;
File proguardFromSDK = null;
if (sdkPathFile != null)
{
proguardFromSDK = new File(sdkPathFile, PROGUARD_SDK_PATH);
}
URL fileURL = null;
// copy newest proguard file from SDK
if ((proguardFromSDK != null) && proguardFromSDK.canRead())
{
try
{
fileURL = proguardFromSDK.toURI().toURL();
}
catch (MalformedURLException e)
{
StudioLogger.warn(ObfuscatorManager.class,
"Exception converting proguard template file to URL", e);
}
}
//copy file bundled with android plugin
else
{
Bundle bundle = AndroidPlugin.getDefault().getBundle();
fileURL = bundle.getEntry(PROGUARD_PATH);
}
return fileURL;
}
}