Commit f8e12998 authored by sky's avatar sky Committed by Commit bot

Revert of Changes caching logic of mojo java apps (patchset #5 id:80001 of...

Revert of Changes caching logic of mojo java apps (patchset #5 id:80001 of https://codereview.chromium.org/1149813002/)

Reason for revert:
Broke windows build.

Original issue's description:
> Changes caching logic of mojo java apps
>
> Previously we would extract all necessary files every time we ran the
> app. This is obviously unnecessary for any bundled apps. Now we
> extract only as necessary.
>
> R=ben@chromium.org, jcivelli@chromium.org
> BUG=none
> TEST=none
>
> Committed: https://crrev.com/128f7a0181634a89a35f681baab1d086d100e377
> Cr-Commit-Position: refs/heads/master@{#330824}

TBR=ben@chromium.org,jcivelli@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=none

Review URL: https://codereview.chromium.org/1147203002

Cr-Commit-Position: refs/heads/master@{#330832}
parent 11433b24
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
#include "mojo/public/c/system/main.h" #include "mojo/public/c/system/main.h"
#include "mojo/runner/android/run_android_application_function.h" #include "mojo/runner/android/run_android_application_function.h"
#include "mojo/runner/native_application_support.h" #include "mojo/runner/native_application_support.h"
#include "mojo/util/filename_util.h"
#include "url/gurl.h"
using base::android::AttachCurrentThread; using base::android::AttachCurrentThread;
using base::android::ScopedJavaLocalRef; using base::android::ScopedJavaLocalRef;
...@@ -36,17 +34,15 @@ namespace { ...@@ -36,17 +34,15 @@ namespace {
void RunAndroidApplication(JNIEnv* env, void RunAndroidApplication(JNIEnv* env,
jobject j_context, jobject j_context,
const base::FilePath& app_path, const base::FilePath& app_path,
jint j_handle, jint j_handle) {
bool is_cached_app) {
InterfaceRequest<Application> application_request = InterfaceRequest<Application> application_request =
MakeRequest<Application>(MakeScopedHandle(MessagePipeHandle(j_handle))); MakeRequest<Application>(MakeScopedHandle(MessagePipeHandle(j_handle)));
// Load the library, so that we can set the application context there if // Load the library, so that we can set the application context there if
// needed. // needed.
// TODO(vtl): We'd use a ScopedNativeLibrary, but it doesn't have .get()! // TODO(vtl): We'd use a ScopedNativeLibrary, but it doesn't have .get()!
base::NativeLibrary app_library = LoadNativeApplication( base::NativeLibrary app_library =
app_path, is_cached_app ? shell::NativeApplicationCleanup::DONT_DELETE LoadNativeApplication(app_path, shell::NativeApplicationCleanup::DELETE);
: shell::NativeApplicationCleanup::DELETE);
if (!app_library) if (!app_library)
return; return;
...@@ -73,57 +69,6 @@ void RunAndroidApplication(JNIEnv* env, ...@@ -73,57 +69,6 @@ void RunAndroidApplication(JNIEnv* env,
base::UnloadNativeLibrary(app_library); base::UnloadNativeLibrary(app_library);
} }
// Returns true if |url| denotes a cached app. If true |app_dir| is set to the
// path of the directory for the app and |path_to_mojo| the path of the app's
// .mojo file.
bool IsCachedApp(JNIEnv* env,
const GURL& url,
base::FilePath* app_dir,
base::FilePath* path_to_mojo) {
ScopedJavaLocalRef<jstring> j_local_apps_dir =
Java_AndroidHandler_getLocalAppsDir(env, GetApplicationContext());
const base::FilePath local_apps_fp(
ConvertJavaStringToUTF8(env, j_local_apps_dir.obj()));
const std::string local_apps(util::FilePathToFileURL(local_apps_fp).spec());
const std::string response_url(GURL(url).spec());
if (response_url.size() <= local_apps.size() ||
local_apps.compare(0u, local_apps.size(), response_url, 0u,
local_apps.size()) != 0) {
return false;
}
const std::string mojo_suffix(".mojo");
// app_rel_path is either something like html_viewer/html_viewer.mojo, or
// html_viewer.mojo, depending upon whether the app has a package.
const std::string app_rel_path(response_url.substr(local_apps.size() + 1));
const size_t slash_index = app_rel_path.find('/');
if (slash_index != std::string::npos) {
const std::string tail =
app_rel_path.substr(slash_index + 1, std::string::npos);
const std::string head = app_rel_path.substr(0, slash_index);
if (head.find('/') != std::string::npos ||
tail.size() <= mojo_suffix.size() ||
tail.compare(tail.size() - mojo_suffix.size(), tail.size(),
mojo_suffix) != 0) {
return false;
}
*app_dir = local_apps_fp.Append(head);
*path_to_mojo = app_dir->Append(tail);
return true;
}
if (app_rel_path.find('/') != std::string::npos ||
app_rel_path.size() <= mojo_suffix.size() ||
app_rel_path.compare(app_rel_path.size() - mojo_suffix.size(),
mojo_suffix.size(), mojo_suffix) != 0) {
return false;
}
*app_dir = local_apps_fp.Append(
app_rel_path.substr(0, app_rel_path.size() - mojo_suffix.size()));
*path_to_mojo = local_apps_fp.Append(app_rel_path);
return true;
}
} // namespace } // namespace
AndroidHandler::AndroidHandler() : content_handler_factory_(this) { AndroidHandler::AndroidHandler() : content_handler_factory_(this) {
...@@ -136,30 +81,13 @@ void AndroidHandler::RunApplication( ...@@ -136,30 +81,13 @@ void AndroidHandler::RunApplication(
InterfaceRequest<Application> application_request, InterfaceRequest<Application> application_request,
URLResponsePtr response) { URLResponsePtr response) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
RunAndroidApplicationFn run_android_application_fn = &RunAndroidApplication;
if (!response->url.is_null()) {
base::FilePath internal_app_path;
base::FilePath path_to_mojo;
if (IsCachedApp(env, GURL(response->url), &internal_app_path,
&path_to_mojo)) {
ScopedJavaLocalRef<jstring> j_internal_app_path(
ConvertUTF8ToJavaString(env, internal_app_path.value()));
ScopedJavaLocalRef<jstring> j_path_to_mojo(
ConvertUTF8ToJavaString(env, path_to_mojo.value()));
Java_AndroidHandler_bootstrapCachedApp(
env, GetApplicationContext(), j_path_to_mojo.obj(),
j_internal_app_path.obj(),
application_request.PassMessagePipe().release().value(),
reinterpret_cast<jlong>(run_android_application_fn));
return;
}
}
ScopedJavaLocalRef<jstring> j_archive_path = ScopedJavaLocalRef<jstring> j_archive_path =
Java_AndroidHandler_getNewTempArchivePath(env, GetApplicationContext()); Java_AndroidHandler_getNewTempArchivePath(env, GetApplicationContext());
base::FilePath archive_path( base::FilePath archive_path(
ConvertJavaStringToUTF8(env, j_archive_path.obj())); ConvertJavaStringToUTF8(env, j_archive_path.obj()));
common::BlockingCopyToFile(response->body.Pass(), archive_path); common::BlockingCopyToFile(response->body.Pass(), archive_path);
RunAndroidApplicationFn run_android_application_fn = &RunAndroidApplication;
Java_AndroidHandler_bootstrap( Java_AndroidHandler_bootstrap(
env, GetApplicationContext(), j_archive_path.obj(), env, GetApplicationContext(), j_archive_path.obj(),
application_request.PassMessagePipe().release().value(), application_request.PassMessagePipe().release().value(),
......
...@@ -5,15 +5,14 @@ ...@@ -5,15 +5,14 @@
package org.chromium.mojo.shell; package org.chromium.mojo.shell;
import android.content.Context; import android.content.Context;
import android.util.Log;
import dalvik.system.DexClassLoader; import dalvik.system.DexClassLoader;
import org.chromium.base.CalledByNative; import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace; import org.chromium.base.JNINamespace;
import org.chromium.base.Log;
import java.io.File; import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
...@@ -46,10 +45,6 @@ public class AndroidHandler { ...@@ -46,10 +45,6 @@ public class AndroidHandler {
private static final String APP_DIRECTORY = "applications"; private static final String APP_DIRECTORY = "applications";
private static final String ASSET_DIRECTORY = "assets"; private static final String ASSET_DIRECTORY = "assets";
private static final String INTERNAL_DIRECTORY = "internal";
private enum AppType { CACHED, UNCACHED }
/** /**
* Deletes directories holding the temporary files. This should be called early on shell startup * Deletes directories holding the temporary files. This should be called early on shell startup
* to clean up after the previous run. * to clean up after the previous run.
...@@ -82,158 +77,45 @@ public class AndroidHandler { ...@@ -82,158 +77,45 @@ public class AndroidHandler {
@CalledByNative @CalledByNative
private static boolean bootstrap(Context context, String archivePath, int handle, private static boolean bootstrap(Context context, String archivePath, int handle,
long runApplicationPtr) { long runApplicationPtr) {
File bootstrapJavaLibrary; File bootstrap_java_library;
File bootstrapNativeLibrary; File bootstrap_native_library;
try {
bootstrapJavaLibrary = FileHelper.extractFromAssets(context, BOOTSTRAP_JAVA_LIBRARY,
getAssetDir(context), FileHelper.FileType.TEMPORARY);
bootstrapNativeLibrary = FileHelper.extractFromAssets(context, BOOTSTRAP_NATIVE_LIBRARY,
getAssetDir(context), FileHelper.FileType.TEMPORARY);
} catch (Exception e) {
Log.e(TAG, "Extraction of bootstrap files from assets failed.", e);
return false;
}
File applicationJavaLibrary;
File applicationNativeLibrary;
try {
File archive = new File(archivePath);
applicationJavaLibrary =
FileHelper.extractFromArchive(archive, JAVA_LIBRARY_SUFFIX, getAppDir(context),
FileHelper.FileType.TEMPORARY, FileHelper.ArchiveType.NORMAL);
applicationNativeLibrary = FileHelper.extractFromArchive(archive, NATIVE_LIBRARY_SUFFIX,
getAppDir(context), FileHelper.FileType.TEMPORARY,
FileHelper.ArchiveType.NORMAL);
} catch (Exception e) {
Log.e(TAG, "Extraction of application files from the archive failed.", e);
return false;
}
return runApp(context, getDexOutputDir(context), applicationJavaLibrary,
applicationNativeLibrary, bootstrapJavaLibrary, bootstrapNativeLibrary, handle,
runApplicationPtr, AppType.UNCACHED);
}
private static File findFileInDirectoryMatchingSuffix(
File dir, final String suffix, final String ignore) {
File[] matchingFiles = dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return !name.equals(ignore) && !name.equals(BOOTSTRAP_JAVA_LIBRARY)
&& !name.equals(BOOTSTRAP_NATIVE_LIBRARY) && name.endsWith(suffix);
}
});
return matchingFiles != null && matchingFiles.length == 1 ? matchingFiles[0] : null;
}
/**
* Extracts and runs a cached application.
*
* @param context the application context
* @param archivePath the path of the archive containing the application to be run
* @param appPathString path where the cached app's resources and other files are
* @param handle handle to the shell to be passed to the native application. On the Java side
* this is opaque payload.
* @param runApplicationPtr pointer to the function that will set the native thunks and call
* into the application MojoMain. On the Java side this is opaque
* payload.
*/
@CalledByNative
private static boolean bootstrapCachedApp(Context context, String archivePath,
String appPathString, int handle, long runApplicationPtr) {
final String appName = new File(appPathString).getName();
final File internalDir = new File(new File(appPathString), INTERNAL_DIRECTORY);
if (!internalDir.exists() && !internalDir.mkdirs()) {
Log.e(TAG, "Unable to create output dir " + internalDir.getAbsolutePath());
return false;
}
final File timestamp = FileHelper.prepareDirectoryForAssets(context, internalDir);
// We make the bootstrap library have a unique name on disk as otherwise we end up sharing
// bootstrap.so,
// which doesn't work.
// TODO(sky): figure out why android is caching the names.
final String bootstrapNativeLibraryName = appName + "-" + BOOTSTRAP_NATIVE_LIBRARY;
File bootstrapJavaLibrary = new File(internalDir, BOOTSTRAP_JAVA_LIBRARY);
File bootstrapNativeLibrary = new File(internalDir, bootstrapNativeLibraryName);
try { try {
// Use the files on disk if we have them, if not extract from the archive. bootstrap_java_library = FileHelper.extractFromAssets(context, BOOTSTRAP_JAVA_LIBRARY,
if (!bootstrapJavaLibrary.exists()) { getAssetDir(context), true);
bootstrapJavaLibrary = FileHelper.extractFromAssets(context, BOOTSTRAP_JAVA_LIBRARY, bootstrap_native_library = FileHelper.extractFromAssets(context,
internalDir, FileHelper.FileType.PERMANENT); BOOTSTRAP_NATIVE_LIBRARY, getAssetDir(context), true);
}
if (!bootstrapNativeLibrary.exists()) {
final File extractedBootstrapNativeLibrary = FileHelper.extractFromAssets(context,
BOOTSTRAP_NATIVE_LIBRARY, internalDir, FileHelper.FileType.PERMANENT);
if (!extractedBootstrapNativeLibrary.renameTo(bootstrapNativeLibrary)) {
Log.e(TAG, "Unable to rename bootstrap library "
+ bootstrapNativeLibrary.getAbsolutePath());
return false;
}
}
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Extraction of bootstrap files from assets failed.", e); Log.e(TAG, "Extraction of bootstrap files from assets failed.", e);
return false; return false;
} }
// Because we allow the .so and .dex.jar to be anything we have to search in the internal File application_java_library;
// directory for matching files. File application_native_library;
// If we find one, we assume it's the one we want.
File applicationJavaLibrary =
findFileInDirectoryMatchingSuffix(internalDir, JAVA_LIBRARY_SUFFIX, "");
File applicationNativeLibrary = findFileInDirectoryMatchingSuffix(
internalDir, NATIVE_LIBRARY_SUFFIX, bootstrapNativeLibraryName);
try { try {
File archive = new File(archivePath); File archive = new File(archivePath);
if (applicationJavaLibrary == null) { application_java_library = FileHelper.extractFromArchive(archive, JAVA_LIBRARY_SUFFIX,
applicationJavaLibrary = FileHelper.extractFromArchive(archive, JAVA_LIBRARY_SUFFIX, getAppDir(context));
internalDir, FileHelper.FileType.PERMANENT, application_native_library = FileHelper.extractFromArchive(archive,
FileHelper.ArchiveType.CONTENT_HANDLER); NATIVE_LIBRARY_SUFFIX, getAppDir(context));
}
if (applicationNativeLibrary == null) {
applicationNativeLibrary = FileHelper.extractFromArchive(archive,
NATIVE_LIBRARY_SUFFIX, internalDir, FileHelper.FileType.PERMANENT,
FileHelper.ArchiveType.CONTENT_HANDLER);
}
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Extraction of application files from the archive failed.", e); Log.e(TAG, "Extraction of application files from the archive failed.", e);
return false; return false;
} }
FileHelper.createTimestampIfNecessary(timestamp); String dexPath = bootstrap_java_library.getAbsolutePath() + File.pathSeparator
+ application_java_library.getAbsolutePath();
return runApp(context, new File(internalDir, DEX_OUTPUT_DIRECTORY), applicationJavaLibrary, DexClassLoader bootstrapLoader = new DexClassLoader(dexPath,
applicationNativeLibrary, bootstrapJavaLibrary, bootstrapNativeLibrary, handle, getDexOutputDir(context).getAbsolutePath(), null,
runApplicationPtr, AppType.CACHED); ClassLoader.getSystemClassLoader());
}
/**
* Creates the class loader containing the bootstrap classes and runs it.
*
* @return true if successfully ran, false if encounteres some sort of error.
*/
private static boolean runApp(Context context, File dexOutputDir, File applicationJavaLibrary,
File applicationNativeLibrary, File bootstrapJavaLibrary, File bootstrapNativeLibrary,
int handle, long runApplicationPtr, AppType appType) {
final String dexPath = bootstrapJavaLibrary.getAbsolutePath() + File.pathSeparator
+ applicationJavaLibrary.getAbsolutePath();
if (!dexOutputDir.exists() && !dexOutputDir.mkdirs()) {
Log.e(TAG, "Unable to create dex output dir " + dexOutputDir.getAbsolutePath());
return false;
}
// TODO(sky): third arg is a path, but appears to have no effect, figure out if this relates
// to weird caching
// logic mentioned above.
DexClassLoader bootstrapLoader = new DexClassLoader(
dexPath, dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
try { try {
Class<?> loadedClass = bootstrapLoader.loadClass(BOOTSTRAP_CLASS); Class<?> loadedClass = bootstrapLoader.loadClass(BOOTSTRAP_CLASS);
Class<? extends Runnable> bootstrapClass = loadedClass.asSubclass(Runnable.class); Class<? extends Runnable> bootstrapClass = loadedClass.asSubclass(Runnable.class);
Constructor<? extends Runnable> constructor = Constructor<? extends Runnable> constructor = bootstrapClass.getConstructor(
bootstrapClass.getConstructor(Context.class, File.class, File.class, Context.class, File.class, File.class, Integer.class, Long.class);
Integer.class, Long.class, Boolean.class); Runnable bootstrapRunnable = constructor.newInstance(context, bootstrap_native_library,
Runnable bootstrapRunnable = constructor.newInstance(context, bootstrapNativeLibrary, application_native_library, Integer.valueOf(handle),
applicationNativeLibrary, Integer.valueOf(handle), Long.valueOf(runApplicationPtr));
Long.valueOf(runApplicationPtr), appType == AppType.CACHED);
bootstrapRunnable.run(); bootstrapRunnable.run();
} catch (Throwable t) { } catch (Throwable t) {
Log.e(TAG, "Running Bootstrap failed.", t); Log.e(TAG, "Running Bootstrap failed.", t);
...@@ -242,11 +124,6 @@ public class AndroidHandler { ...@@ -242,11 +124,6 @@ public class AndroidHandler {
return true; return true;
} }
@CalledByNative
static String getLocalAppsDir(Context context) {
return ShellMain.getLocalAppsDir(context).getAbsolutePath();
}
private static File getDexOutputDir(Context context) { private static File getDexOutputDir(Context context) {
return context.getDir(DEX_OUTPUT_DIRECTORY, Context.MODE_PRIVATE); return context.getDir(DEX_OUTPUT_DIRECTORY, Context.MODE_PRIVATE);
} }
......
...@@ -22,16 +22,14 @@ public class Bootstrap implements Runnable { ...@@ -22,16 +22,14 @@ public class Bootstrap implements Runnable {
private final File mApplicationNativeLibrary; private final File mApplicationNativeLibrary;
private final int mHandle; private final int mHandle;
private final long mRunApplicationPtr; private final long mRunApplicationPtr;
private final boolean mIsCachedApp;
public Bootstrap(Context context, File bootstrapNativeLibrary, File applicationNativeLibrary, public Bootstrap(Context context, File bootstrapNativeLibrary, File applicationNativeLibrary,
Integer handle, Long runApplicationPtr, Boolean isCachedApp) { Integer handle, Long runApplicationPtr) {
mContext = context; mContext = context;
mBootstrapNativeLibrary = bootstrapNativeLibrary; mBootstrapNativeLibrary = bootstrapNativeLibrary;
mApplicationNativeLibrary = applicationNativeLibrary; mApplicationNativeLibrary = applicationNativeLibrary;
mHandle = handle; mHandle = handle;
mRunApplicationPtr = runApplicationPtr; mRunApplicationPtr = runApplicationPtr;
mIsCachedApp = isCachedApp;
} }
@Override @Override
...@@ -39,9 +37,9 @@ public class Bootstrap implements Runnable { ...@@ -39,9 +37,9 @@ public class Bootstrap implements Runnable {
System.load(mBootstrapNativeLibrary.getAbsolutePath()); System.load(mBootstrapNativeLibrary.getAbsolutePath());
System.load(mApplicationNativeLibrary.getAbsolutePath()); System.load(mApplicationNativeLibrary.getAbsolutePath());
nativeBootstrap(mContext, mApplicationNativeLibrary.getAbsolutePath(), mHandle, nativeBootstrap(mContext, mApplicationNativeLibrary.getAbsolutePath(), mHandle,
mRunApplicationPtr, mIsCachedApp); mRunApplicationPtr);
} }
native void nativeBootstrap(Context context, String libraryPath, int handle, native void nativeBootstrap(Context context, String libraryPath, int handle,
long runApplicationPtr, boolean isCachedApp); long runApplicationPtr);
} }
...@@ -7,8 +7,7 @@ package org.chromium.mojo.shell; ...@@ -7,8 +7,7 @@ package org.chromium.mojo.shell;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.util.Log;
import org.chromium.base.Log;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
...@@ -16,6 +15,7 @@ import java.io.File; ...@@ -16,6 +15,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
...@@ -35,88 +35,63 @@ class FileHelper { ...@@ -35,88 +35,63 @@ class FileHelper {
// Prefix used when naming timestamp files. // Prefix used when naming timestamp files.
private static final String TIMESTAMP_PREFIX = "asset_timestamp-"; private static final String TIMESTAMP_PREFIX = "asset_timestamp-";
/**
* Used to indicate the type of destination file that should be created.
*/
public enum FileType {
TEMPORARY,
PERMANENT,
}
public enum ArchiveType {
/**
* The archive was created for a content handler (contains the mojo escape sequence).
*/
CONTENT_HANDLER,
NORMAL,
}
/** /**
* Looks for a timestamp file on disk that indicates the version of the APK that the resource * Looks for a timestamp file on disk that indicates the version of the APK that the resource
* assets were extracted from. Returns null if a timestamp was found and it indicates that the * assets were extracted from. Returns null if a timestamp was found and it indicates that the
* resources match the current APK. Otherwise returns the file to create. * resources match the current APK. Otherwise returns a String that represents the filename of a
* timestamp to create.
*/ */
private static File findAssetTimestamp(Context context, File outputDir) { private static String checkAssetTimestamp(Context context, File outputDir) {
PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager();
PackageInfo pi = null; PackageInfo pi = null;
try { try {
pi = pm.getPackageInfo(context.getPackageName(), 0); pi = pm.getPackageInfo(context.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
return TIMESTAMP_PREFIX;
} }
if (pi == null) { if (pi == null) {
return new File(outputDir, TIMESTAMP_PREFIX); return TIMESTAMP_PREFIX;
} }
final File expectedTimestamp = String expectedTimestamp = TIMESTAMP_PREFIX + pi.versionCode + "-" + pi.lastUpdateTime;
new File(outputDir, TIMESTAMP_PREFIX + pi.versionCode + "-" + pi.lastUpdateTime);
return expectedTimestamp.exists() ? null : expectedTimestamp; String[] timestamps = outputDir.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith(TIMESTAMP_PREFIX);
} }
});
/** if (timestamps.length != 1) {
* Invoke prior to extracting any assets into {@code directory}. If necessary deletes all the // If there's no timestamp, nuke to be safe as we can't tell the age of the files.
* files in the specified directory. The return value must be supplied to {@link // If there's multiple timestamps, something's gone wrong so nuke.
*createTimestampIfNecessary}. return expectedTimestamp;
*
* @param directory directory assets will be extracted to
* @return non-null if a file with the specified name needs to be created after assets have
* been extracted.
*/
public static File prepareDirectoryForAssets(Context context, File directory) {
final File timestamp = findAssetTimestamp(context, directory);
if (timestamp == null) {
return null;
} }
for (File child : directory.listFiles()) {
deleteRecursively(child); if (!expectedTimestamp.equals(timestamps[0])) {
return expectedTimestamp;
} }
return timestamp;
// Timestamp file is already up-to date.
return null;
} }
/** public static File extractFromAssets(Context context, String assetName, File outputDirectory,
* Creates a file used as a timestamp. The supplied file comes from {@link boolean useTempFile) throws IOException, FileNotFoundException {
*prepareDirectoryForAssets}. String timestampToCreate = null;
* if (!useTempFile) {
* @param timestamp path of file to create, or null if a file does not need to be created timestampToCreate = checkAssetTimestamp(context, outputDirectory);
*/ if (timestampToCreate != null) {
public static void createTimestampIfNecessary(File timestamp) { for (File child : outputDirectory.listFiles()) {
if (timestamp == null) { deleteRecursively(child);
return;
} }
try {
timestamp.createNewFile();
} catch (IOException e) {
// In the worst case we don't write a timestamp, so we'll re-extract the asset next
// time.
Log.w(TAG, "Failed to write asset timestamp!");
} }
} }
public static File extractFromAssets(Context context, String assetName, File outputDirectory,
FileType fileType) throws IOException, FileNotFoundException {
File outputFile; File outputFile;
if (fileType == FileType.TEMPORARY) { if (useTempFile) {
// Make the original filename part of the temp file name. // Make the original filename part of the temp file name.
// TODO(ppi): do we need to sanitize the suffix? // TODO(ppi): do we need to sanitize the suffix?
String suffix = "-" + assetName; String suffix = "-" + assetName;
...@@ -136,48 +111,36 @@ class FileHelper { ...@@ -136,48 +111,36 @@ class FileHelper {
} finally { } finally {
inputStream.close(); inputStream.close();
} }
if (timestampToCreate != null) {
try {
new File(outputDirectory, timestampToCreate).createNewFile();
} catch (IOException e) {
// In the worst case we don't write a timestamp, so we'll re-extract the asset next
// time.
Log.w(TAG, "Failed to write asset timestamp!");
}
}
return outputFile; return outputFile;
} }
/** /**
* Extracts the file of the given extension from the archive. Throws FileNotFoundException if no * Extracts the file of the given extension from the archive. Throws FileNotFoundException if no
* matching file is found. * matching file is found.
*
* @return path of extracted file
*/ */
static File extractFromArchive(File archive, String suffixToMatch, File outputDirectory, static File extractFromArchive(File archive, String suffixToMatch,
FileType fileType, ArchiveType archiveType) throws IOException, FileNotFoundException { File outputDirectory) throws IOException, FileNotFoundException {
if (!outputDirectory.exists() && !outputDirectory.mkdirs()) { ZipInputStream zip = new ZipInputStream(new BufferedInputStream(new FileInputStream(
Log.e(TAG, "extractFromArchive unable to create directory " archive)));
+ outputDirectory.getAbsolutePath());
throw new FileNotFoundException();
}
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(archive));
if (archiveType == ArchiveType.CONTENT_HANDLER) {
int currentChar;
do {
currentChar = inputStream.read();
} while (currentChar != -1 && currentChar != '\n');
if (currentChar == -1) {
throw new FileNotFoundException();
}
inputStream = new BufferedInputStream(inputStream);
}
ZipInputStream zip = new ZipInputStream(inputStream);
ZipEntry entry; ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) { while ((entry = zip.getNextEntry()) != null) {
if (entry.getName().endsWith(suffixToMatch)) { if (entry.getName().endsWith(suffixToMatch)) {
// TODO(sky): sanitize name.
final String name = new File(entry.getName()).getName();
File extractedFile;
// Make the original filename part of the temp file name. // Make the original filename part of the temp file name.
if (fileType == FileType.TEMPORARY) { // TODO(ppi): do we need to sanitize the suffix?
final String suffix = "-" + name; String suffix = "-" + new File(entry.getName()).getName();
extractedFile = File.createTempFile(TEMP_FILE_PREFIX, suffix, outputDirectory); File extractedFile = File.createTempFile(TEMP_FILE_PREFIX, suffix,
} else { outputDirectory);
extractedFile = new File(outputDirectory, name);
}
writeStreamToFile(zip, extractedFile); writeStreamToFile(zip, extractedFile);
zip.close(); zip.close();
return extractedFile; return extractedFile;
......
...@@ -27,8 +27,7 @@ import java.util.List; ...@@ -27,8 +27,7 @@ import java.util.List;
public class ShellMain { public class ShellMain {
private static final String TAG = "ShellMain"; private static final String TAG = "ShellMain";
// Directory where applications cached with the shell will be extracted. // Directory where applications bundled with the shell will be extracted.
// TODO(sky): rename this to CACHED_APP_DIRECTORY.
private static final String LOCAL_APP_DIRECTORY = "local_apps"; private static final String LOCAL_APP_DIRECTORY = "local_apps";
// The mojo_shell library is also an executable run in forked processes when running // The mojo_shell library is also an executable run in forked processes when running
// multi-process. // multi-process.
...@@ -73,13 +72,9 @@ public class ShellMain { ...@@ -73,13 +72,9 @@ public class ShellMain {
if (sInitialized) return; if (sInitialized) return;
File localAppsDir = getLocalAppsDir(applicationContext); File localAppsDir = getLocalAppsDir(applicationContext);
try { try {
final File timestamp =
FileHelper.prepareDirectoryForAssets(applicationContext, localAppsDir);
for (String assetPath : getAssetsList(applicationContext)) { for (String assetPath : getAssetsList(applicationContext)) {
FileHelper.extractFromAssets( FileHelper.extractFromAssets(applicationContext, assetPath, localAppsDir, false);
applicationContext, assetPath, localAppsDir, FileHelper.FileType.PERMANENT);
} }
FileHelper.createTimestampIfNecessary(timestamp);
File mojoShell = new File(applicationContext.getApplicationInfo().nativeLibraryDir, File mojoShell = new File(applicationContext.getApplicationInfo().nativeLibraryDir,
MOJO_SHELL_EXECUTABLE); MOJO_SHELL_EXECUTABLE);
...@@ -116,7 +111,7 @@ public class ShellMain { ...@@ -116,7 +111,7 @@ public class ShellMain {
nativeAddApplicationURL(url); nativeAddApplicationURL(url);
} }
static File getLocalAppsDir(Context context) { private static File getLocalAppsDir(Context context) {
return context.getDir(LOCAL_APP_DIRECTORY, Context.MODE_PRIVATE); return context.getDir(LOCAL_APP_DIRECTORY, Context.MODE_PRIVATE);
} }
...@@ -133,7 +128,7 @@ public class ShellMain { ...@@ -133,7 +128,7 @@ public class ShellMain {
* Initializes the native system. This API should be called only once per process. * Initializes the native system. This API should be called only once per process.
**/ **/
private static native void nativeInit(Context context, String mojoShellPath, private static native void nativeInit(Context context, String mojoShellPath,
String[] parameters, String cachedAppsDirectory, String tmpDir); String[] parameters, String bundledAppsDirectory, String tmpDir);
private static native boolean nativeStart(); private static native boolean nativeStart();
......
...@@ -17,13 +17,12 @@ void Bootstrap(JNIEnv* env, ...@@ -17,13 +17,12 @@ void Bootstrap(JNIEnv* env,
jobject j_context, jobject j_context,
jstring j_native_library_path, jstring j_native_library_path,
jint j_handle, jint j_handle,
jlong j_run_application_ptr, jlong j_run_application_ptr) {
jboolean is_cached_app) {
base::FilePath app_path( base::FilePath app_path(
base::android::ConvertJavaStringToUTF8(env, j_native_library_path)); base::android::ConvertJavaStringToUTF8(env, j_native_library_path));
RunAndroidApplicationFn run_android_application_fn = RunAndroidApplicationFn run_android_application_fn =
reinterpret_cast<RunAndroidApplicationFn>(j_run_application_ptr); reinterpret_cast<RunAndroidApplicationFn>(j_run_application_ptr);
run_android_application_fn(env, j_context, app_path, j_handle, is_cached_app); run_android_application_fn(env, j_context, app_path, j_handle);
} }
bool RegisterBootstrapJni(JNIEnv* env) { bool RegisterBootstrapJni(JNIEnv* env) {
......
...@@ -19,8 +19,7 @@ namespace runner { ...@@ -19,8 +19,7 @@ namespace runner {
typedef void (*RunAndroidApplicationFn)(JNIEnv* env, typedef void (*RunAndroidApplicationFn)(JNIEnv* env,
jobject j_context, jobject j_context,
const base::FilePath& app_path, const base::FilePath& app_path,
jint j_handle, jint j_handle);
bool is_cached_app);
} // namespace runner } // namespace runner
} // namespace mojo } // namespace mojo
......
...@@ -31,8 +31,7 @@ public class ShellTestBase { ...@@ -31,8 +31,7 @@ public class ShellTestBase {
AssetManager manager = context.getResources().getAssets(); AssetManager manager = context.getResources().getAssets();
for (String asset : manager.list("")) { for (String asset : manager.list("")) {
if (asset.endsWith(".mojo")) { if (asset.endsWith(".mojo")) {
FileHelper.extractFromAssets( FileHelper.extractFromAssets(context, asset, outputDirectory, false);
context, asset, outputDirectory, FileHelper.FileType.PERMANENT);
} }
} }
......
...@@ -114,14 +114,10 @@ GURL URLResolver::ResolveMojoURL(const GURL& mojo_url) const { ...@@ -114,14 +114,10 @@ GURL URLResolver::ResolveMojoURL(const GURL& mojo_url) const {
if (mojo_base_url_.SchemeIsFile()) { if (mojo_base_url_.SchemeIsFile()) {
const GURL url_with_directory( const GURL url_with_directory(
mojo_base_url_.Resolve(base_url.host() + "/")); mojo_base_url_.Resolve(base_url.host() + "/"));
const base::FilePath dir(util::UrlToFilePath(url_with_directory)); const base::FilePath file_path(util::UrlToFilePath(url_with_directory));
if (base::DirectoryExists(dir)) { if (base::DirectoryExists(file_path))
const base::FilePath mojo_path = dir.Append(base_url.host() + ".mojo");
// Only use the directory if the .mojo exists in the directory.
if (base::PathExists(mojo_path))
return url_with_directory.Resolve(base_url.host() + ".mojo" + query); return url_with_directory.Resolve(base_url.host() + ".mojo" + query);
} }
}
return mojo_base_url_.Resolve(base_url.host() + ".mojo" + query); return mojo_base_url_.Resolve(base_url.host() + ".mojo" + query);
} }
......
...@@ -159,23 +159,13 @@ TEST_F(URLResolverTest, PreferDirectory) { ...@@ -159,23 +159,13 @@ TEST_F(URLResolverTest, PreferDirectory) {
EXPECT_EQ(util::FilePathToFileURL(tmp_dir.path()).spec() + "/foo.mojo", EXPECT_EQ(util::FilePathToFileURL(tmp_dir.path()).spec() + "/foo.mojo",
mapped_url.spec()); mapped_url.spec());
// With an empty directory |mojo:foo| maps to path/foo.mojo. // With a directory |mojo:foo| maps to path/foo/foo.mojo.
const base::FilePath foo_file_path( const base::FilePath foo_file_path(
tmp_dir.path().Append(FILE_PATH_LITERAL("foo"))); tmp_dir.path().Append(FILE_PATH_LITERAL("foo")));
ASSERT_TRUE(base::CreateDirectory(foo_file_path)); ASSERT_TRUE(base::CreateDirectory(foo_file_path));
const GURL mapped_url_with_dir = resolver.ResolveMojoURL(GURL("mojo:foo")); const GURL mapped_url_with_dir = resolver.ResolveMojoURL(GURL("mojo:foo"));
EXPECT_EQ(util::FilePathToFileURL(tmp_dir.path()).spec() + "/foo.mojo",
mapped_url_with_dir.spec());
// When foo.mojo exists in the directory (path/foo/foo.mojo), then it should
// be picked up.
// With an empty directory |mojo:foo| maps to path/foo/foo.mojo.
ASSERT_EQ(1,
base::WriteFile(foo_file_path.Append(FILE_PATH_LITERAL("foo.mojo")),
"a", 1));
const GURL mapped_url_in_dir = resolver.ResolveMojoURL(GURL("mojo:foo"));
EXPECT_EQ(util::FilePathToFileURL(tmp_dir.path()).spec() + "/foo/foo.mojo", EXPECT_EQ(util::FilePathToFileURL(tmp_dir.path()).spec() + "/foo/foo.mojo",
mapped_url_in_dir.spec()); mapped_url_with_dir.spec());
} }
} // namespace } // namespace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment