/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* 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 analyser;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.swing.UIManager;
import mereflect.CollaborateClassContext;
import mereflect.JarClassContext;
import mereflect.MEClass;
import mereflect.MEClassContext;
import mereflect.MEClassResource;
import analyser.gui.MainFrame;
import analyser.gui.Settings;
import andreflect.ApkClassContext;
import andreflect.gui.splash.SplashWindow;
public class Analyser
{
public static final String PROP_NAME = "apkanalyser.properties";
protected File m_midletsPath;
protected File m_classPath;
protected Properties m_props;
public static final String[] MIDLET_SUFFIX = { ".jar", ".zip" };
public static final String[] APK_SUFFIX = { ".apk" };
public static final String[] JAR_SUFFIX = { ".jar" };
protected List<File> m_midlets = new ArrayList<File>();
protected List<File> m_apks = new ArrayList<File>();
protected CollaborateClassContext m_cpcontext = new CollaborateClassContext();
public void analyse() throws Exception
{
getMidlets(m_midlets);
getApks(m_apks);
if (m_midlets.isEmpty() && m_apks.isEmpty())
{
throw new IllegalArgumentException("No Apk or midlets found");
}
setClasspath(m_cpcontext);
Set<MEClassContext> depends;
for (int i = 0; i < m_midlets.size(); i++)
{
File midlet = m_midlets.get(i);
JarClassContext midletContext = new JarClassContext(midlet, true);
depends = new TreeSet<MEClassContext>();
String midletName = midlet.getName();
midletName = midletName.substring(0, midletName.length() - MIDLET_SUFFIX[0].length());
MEClassResource[] cRes = midletContext.getClassResources();
for (int j = 0; j < cRes.length; j++)
{
MEClass midletClass = midletContext.getMEClass(cRes[j].getClassName());
String[] depClassNames = midletClass.getDependencies();
for (int k = 0; k < depClassNames.length; k++)
{
String depClassName = depClassNames[k].replace('/', '.');
try
{
MEClass depClass = m_cpcontext.getMEClass(depClassName);
depends.add(depClass.getResource().getContext());
} catch (ClassNotFoundException cnfe)
{
}
}
}
for (Iterator<MEClassContext> j = depends.iterator(); j.hasNext();)
{
MEClassContext ctx = (j.next());
String output = getContextName(ctx);
System.out.println("\t" + output);
}
}
for (int i = 0; i < m_apks.size(); i++)
{
File apk = m_apks.get(i);
ApkClassContext apkContext = new ApkClassContext(apk, true);
depends = new TreeSet<MEClassContext>();
String apkName = apk.getName();
apkName = apkName.substring(0, apkName.length() - APK_SUFFIX[0].length());
MEClassResource[] cRes = apkContext.getClassResources();
for (int j = 0; j < cRes.length; j++)
{
MEClass apkClass = apkContext.getMEClass(cRes[j].getClassName());
String[] depClassNames = apkClass.getDependencies();
for (int k = 0; k < depClassNames.length; k++)
{
String depClassName = depClassNames[k].replace('/', '.');
try
{
MEClass depClass = m_cpcontext.getMEClass(depClassName);
depends.add(depClass.getResource().getContext());
} catch (ClassNotFoundException cnfe)
{
}
}
}
for (Iterator<MEClassContext> j = depends.iterator(); j.hasNext();)
{
MEClassContext ctx = (j.next());
String output = getContextName(ctx);
System.out.println("\t" + output);
}
}
}
public static String getContextName(MEClassContext ctx)
{
String name = ctx.getContextName();
try
{
if (ctx.getContextDescription().equals(JarClassContext.DESCRIPTION))
{
int slashIndex = name.lastIndexOf(File.separatorChar);
name = slashIndex >= 0 ? name.substring(slashIndex + 1) : name;
JarFile jf = ((JarClassContext) ctx).getJar();
if (jf != null)
{
Manifest mf = jf.getManifest();
String manifest = null;
if (mf != null)
{
String mName = mf.getMainAttributes().getValue("MIDlet-Name");
String mVersion = mf.getMainAttributes().getValue("MIDlet-Version");
String mVend = mf.getMainAttributes().getValue("MIDlet-Vendor");
if (mVersion != null)
{
mName += " v" + mVersion;
}
if (mVend != null)
{
mName += " (" + mVend + ")";
}
if (mName != null)
{
manifest = mName;
}
if (manifest != null)
{
name = manifest;
}
else
{
String formOutput = mf.getMainAttributes().getValue("API");
String formVersion = mf.getMainAttributes().getValue("API-Specification-Version");
if (formOutput != null)
{
name = formOutput;
if (formVersion != null) {
name += " " + formVersion;
}
}
}
}
}
} else if (ctx.getContextDescription().equals(ApkClassContext.DESCRIPTION)) {
name = ((ApkClassContext) ctx).getContextDescriptionName();
}
} catch (Exception e) {
e.printStackTrace();
}
return name;
}
public void setClasspath(CollaborateClassContext cpCtx)
{
String cp;
if (Settings.getUseAndroidSDK()
&& Settings.getAndroidSDK() != null) {
ArrayList<File> jarFiles = new ArrayList<File>();
getFiles(jarFiles, Settings.getAndroidSDK(), JAR_SUFFIX);
StringBuffer jarPaths = new StringBuffer();
for (int i = 0; i < jarFiles.size(); i++)
{
File aJar = jarFiles.get(i);
jarPaths.append(aJar.getPath());
jarPaths.append(";");
}
cp = jarPaths.toString();
} else {
cp = Settings.getClasspath();
}
cpCtx.setClasspath(cp);
}
private void getFiles(List<File> result, String path, String[] suffix) {
if (path != null
&& suffix != null
&& suffix.length > 0) {
StringTokenizer st = new StringTokenizer(path, ";");
while (st.hasMoreTokens()) {
String p = st.nextToken();
File f = new File(p);
if (f.exists()) {
if (f.isDirectory()) {
addFiles(f, result, suffix);
} else {
addFile(f, result, suffix);
}
}
}
}
}
private void addFiles(File path, List<File> result, String[] suffix) {
File[] sub = path.listFiles();
for (int i = 0; sub != null && i < sub.length; i++)
{
if (sub[i].isDirectory())
{
addFiles(sub[i], result, suffix);
}
else if (sub[i].isFile())
{
addFile(sub[i], result, suffix);
}
}
}
private void addFile(File path, List<File> result, String[] suffix) {
if (path.exists())
{
String mName = path.getName().toLowerCase();
boolean found = false;
for (int i = 0; !found && i < suffix.length; i++)
{
found = (mName.endsWith(suffix[i]));
}
if (found) {
result.add(path);
}
}
}
public void getMidlets(List<File> midlets) {
getFiles(midlets, Settings.getMidletsPath(), MIDLET_SUFFIX);
}
public void getApks(List<File> apks) {
getFiles(apks, Settings.getMidletsPath(), APK_SUFFIX);
}
// Startup
public Analyser()
{
this(new Properties());
}
public Analyser(Properties p)
{
m_props = p;
}
public static void ctxmain(String[] args)
{
try
{
Class.forName("org.objectweb.asm.ClassVisitor");
} catch (ClassNotFoundException e1)
{
e1.printStackTrace();
}
Analyser a = new Analyser();
a.loadProperties();
// Start gui
SplashWindow.splash(SplashWindow.class.getResource("/splash.jpg"));
Locale.setDefault(Locale.ENGLISH);
sun.awt.AppContext.getAppContext().put("JComponent.defaultLocale", Locale.ENGLISH);
Settings.setSettings(a.m_props);
try {
Settings.load();
} catch (IOException e2) {
}
// init gui
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int inset = 100;
int x = inset;
int y = inset;
int width = Math.max(1000, screenSize.width - inset * 2);
int height = Math.max(800, screenSize.height - inset * 2);
if (Settings.getMainFrameX() > 0) {
x = Settings.getMainFrameX();
}
if (Settings.getMainFrameY() > 0) {
y = Settings.getMainFrameY();
}
if (Settings.getMainFrameWidth() > 0) {
width = Settings.getMainFrameWidth();
}
if (Settings.getMainFrameHeight() > 0) {
height = Settings.getMainFrameHeight();
}
final MainFrame f = MainFrame.getInstance();
f.initialize(width, height);
f.setBounds(x, y, width, height);
f.setVisible(true);
f.addComponentListener(new ComponentListener(){
@Override
public void componentShown(ComponentEvent e) {
SplashWindow.canDispose();
f.removeComponentListener(this);
}
@Override
public void componentResized(ComponentEvent e) {
// nothing to do
}
@Override
public void componentMoved(ComponentEvent e) {
// nothing to do
}
@Override
public void componentHidden(ComponentEvent e) {
// nothing to do
}
});
}
protected boolean loadProperties()
{
File propFile = new File(PROP_NAME);
if (!propFile.exists())
{
return false;
}
else
{
try
{
m_props.load(new FileInputStream(propFile));
} catch (Exception e)
{
e.printStackTrace();
return false;
}
}
return true;
}
}