blob: 032224b57b26835628f37bf6f90d94b5340c5fb7 [file] [log] [blame]
/*
* Copyright (C) 2007 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 dalvik.system;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Cloned out of PathClassLoader for TouchDex. This could be made
* substantially smaller, since we don't need most of this.
*/
class TouchDexLoader extends ClassLoader {
private String path;
/*
* Parallel arrays for jar/apk files.
*
* (could stuff these into an object and have a single array;
* improves clarity but adds overhead)
*/
private final String[] mPaths;
private final File[] mFiles;
private final ZipFile[] mZips;
private final DexFile[] mDexs;
/**
* Native library path.
*/
private final String[] mLibPaths;
/**
* Create a ClassLoader that finds files in the specified path.
*/
public TouchDexLoader(String path, ClassLoader parent) {
super(parent);
if (path == null)
throw new NullPointerException();
this.path = path;
mPaths = path.split(":");
//System.out.println("TouchDexLoader: " + mPaths);
mFiles = new File[mPaths.length];
mZips = new ZipFile[mPaths.length];
mDexs = new DexFile[mPaths.length];
boolean wantDex =
System.getProperty("android.vm.dexfile", "").equals("true");
/* open all Zip and DEX files up front */
for (int i = 0; i < mPaths.length; i++) {
//System.out.println("My path is: " + mPaths[i]);
File pathFile = new File(mPaths[i]);
mFiles[i] = pathFile;
if (pathFile.isFile()) {
if (false) { //--------------------
try {
mZips[i] = new ZipFile(pathFile);
}
catch (IOException ioex) {
// expecting IOException and ZipException
//System.out.println("Failed opening '" + archive + "': " + ioex);
//ioex.printStackTrace();
}
} //--------------------
if (wantDex) {
/* we need both DEX and Zip, because dex has no resources */
try {
mDexs[i] = new DexFile(pathFile);
}
catch (IOException ioex) {
System.err.println("Couldn't open " + mPaths[i]
+ " as DEX");
}
}
} else {
System.err.println("File not found: " + mPaths[i]);
}
}
/*
* Prep for native library loading.
*/
String pathList = System.getProperty("java.library.path", ".");
String pathSep = System.getProperty("path.separator", ":");
String fileSep = System.getProperty("file.separator", "/");
mLibPaths = pathList.split(pathSep);
// Add a '/' to the end so we don't have to do the property lookup
// and concatenation later.
for (int i = 0; i < mLibPaths.length; i++) {
if (!mLibPaths[i].endsWith(fileSep))
mLibPaths[i] += fileSep;
if (false)
System.out.println("Native lib path: " + mLibPaths[i]);
}
}
/**
* Find the class with the specified name. None of our ancestors were
* able to find it, so it's up to us now.
*
* "name" is a "binary name", e.g. "java.lang.String" or
* "java.net.URLClassLoader$3$1".
*
* This method will either return a valid Class object or throw an
* exception. Does not return null.
*/
protected Class<?> findClass(String name) throws ClassNotFoundException
{
byte[] data = null;
int i;
//System.out.println("TouchDexLoader " + this + ": findClass '" + name + "'");
for (i = 0; i < mPaths.length; i++) {
//System.out.println("My path is: " + mPaths[i]);
if (mDexs[i] != null) {
String slashName = name.replace('.', '/');
Class clazz = mDexs[i].loadClass(slashName, this);
if (clazz != null)
return clazz;
} else if (mZips[i] != null) {
String fileName = name.replace('.', '/') + ".class";
data = loadFromArchive(mZips[i], fileName);
} else {
File pathFile = mFiles[i];
if (pathFile.isDirectory()) {
String fileName =
mPaths[i] + "/" + name.replace('.', '/') + ".class";
data = loadFromDirectory(fileName);
} else {
//System.out.println("TouchDexLoader: can't find '"
// + mPaths[i] + "'");
}
}
if (data != null) {
//System.out.println(" found class " + name);
int dotIndex = name.lastIndexOf('.');
if (dotIndex != -1) {
String packageName = name.substring(0, dotIndex);
synchronized (this) {
Package packageObj = getPackage(packageName);
if (packageObj == null) {
definePackage(packageName, null, null,
null, null, null, null, null);
}
}
}
return defineClass(name, data, 0, data.length);
}
}
throw new ClassNotFoundException(name + " in loader " + this);
}
/*
* Find a resource by name. This could be in a directory or in an
* archive.
*/
protected URL findResource(String name) {
byte[] data = null;
int i;
//System.out.println("TouchDexLoader: findResource '" + name + "'");
for (i = 0; i < mPaths.length; i++) {
File pathFile = mFiles[i];
ZipFile zip = mZips[i];
if (zip != null) {
if (isInArchive(zip, name)) {
//System.out.println(" found " + name + " in " + pathFile);
// Create URL correctly - was XXX, new code should be ok.
try {
return new URL("jar:file://" + pathFile + "!/" + name);
}
catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
} else if (pathFile.isDirectory()) {
File dataFile = new File(mPaths[i] + "/" + name);
if (dataFile.exists()) {
//System.out.println(" found resource " + name);
// Create URL correctly - was XXX, new code should be ok.
try {
return new URL("file:" + name);
}
catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
} else if (pathFile.isFile()) {
} else {
System.err.println("TouchDexLoader: can't find '"
+ mPaths[i] + "'");
}
}
return null;
}
/*
* Load the contents of a file from a file in a directory.
*
* Returns null if the class wasn't found.
*/
private byte[] loadFromDirectory(String path) {
RandomAccessFile raf;
byte[] fileData;
//System.out.println("Trying to load from " + path);
try {
raf = new RandomAccessFile(path, "r");
}
catch (FileNotFoundException fnfe) {
//System.out.println(" Not found: " + path);
return null;
}
try {
fileData = new byte[(int) raf.length()];
raf.read(fileData);
raf.close();
}
catch (IOException ioe) {
System.err.println("Error reading from " + path);
// swallow it, return null instead
fileData = null;
}
return fileData;
}
/*
* Load a class from a file in an archive. We currently assume that
* the file is a Zip archive.
*
* Returns null if the class wasn't found.
*/
private byte[] loadFromArchive(ZipFile zip, String name) {
ZipEntry entry;
entry = zip.getEntry(name);
if (entry == null)
return null;
ByteArrayOutputStream byteStream;
InputStream stream;
int count;
/*
* Copy the data out of the stream. Because we got the ZipEntry
* from a ZipFile, the uncompressed size is known, and we can set
* the initial size of the ByteArrayOutputStream appropriately.
*/
try {
stream = zip.getInputStream(entry);
byteStream = new ByteArrayOutputStream((int) entry.getSize());
byte[] buf = new byte[4096];
while ((count = stream.read(buf)) > 0)
byteStream.write(buf, 0, count);
stream.close();
}
catch (IOException ioex) {
//System.out.println("Failed extracting '" + archive + "': " +ioex);
return null;
}
//System.out.println(" loaded from Zip");
return byteStream.toByteArray();
}
/*
* Figure out if "name" is a member of "archive".
*/
private boolean isInArchive(ZipFile zip, String name) {
return zip.getEntry(name) != null;
}
/**
* Find a native library.
*
* Return the full pathname of the first appropriate-looking file
* we find.
*/
protected String findLibrary(String libname) {
String fileName = System.mapLibraryName(libname);
for (int i = 0; i < mLibPaths.length; i++) {
String pathName = mLibPaths[i] + fileName;
File test = new File(pathName);
if (test.exists())
return pathName;
}
return null;
}
}