Android handler: Fix name collision.

The android handler was using the android framework to create temporary
files. This creates predictible names and ends up creating 2 libraries
with the same name. This is then an issue with ldopen that will not open
those independently.

This CL changes the implementation to create names that will not be
predictible.

R=blundell@chromium.org

Review URL: https://codereview.chromium.org/1133283002
diff --git a/shell/android/android_handler.cc b/shell/android/android_handler.cc
index 9d3181b..555efa8 100644
--- a/shell/android/android_handler.cc
+++ b/shell/android/android_handler.cc
@@ -4,13 +4,17 @@
 
 #include "shell/android/android_handler.h"
 
+#include <fcntl.h>
+
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/scoped_native_library.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "jni/AndroidHandler_jni.h"
 #include "mojo/common/data_pipe_utils.h"
@@ -154,6 +158,28 @@
       });
 }
 
+jstring CreateTemporaryFile(JNIEnv* env,
+                            jclass jcaller,
+                            jstring j_directory,
+                            jstring j_basename,
+                            jstring j_extension) {
+  std::string basename(ConvertJavaStringToUTF8(env, j_basename));
+  std::string extension(ConvertJavaStringToUTF8(env, j_extension));
+  base::FilePath directory(ConvertJavaStringToUTF8(env, j_directory));
+
+  for (;;) {
+    std::string random = base::RandBytesAsString(16);
+    std::string filename =
+        basename + base::HexEncode(random.data(), random.size()) + extension;
+    base::FilePath temporary_file = directory.Append(filename);
+    int fd = open(temporary_file.value().c_str(), O_CREAT | O_EXCL, 0600);
+    if (fd != -1) {
+      close(fd);
+      return ConvertUTF8ToJavaString(env, temporary_file.value()).Release();
+    }
+  }
+}
+
 bool RegisterAndroidHandlerJni(JNIEnv* env) {
   return RegisterNativesImpl(env);
 }
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java b/shell/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java
index 9719f19..a10266f 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java
@@ -69,11 +69,15 @@
         File cacheDir = new File(cachePath);
         File compiledDexDir = new File(cacheDir, "dex");
         File assetDir = new File(cacheDir, "asset");
-        assetDir.mkdirs();
-        File preparedSentinel = new File(cacheDir, "prepared");
 
-        // If the sentinel doesn't exist, extract the assets from the apk.
-        if (!preparedSentinel.exists()) {
+        // If the timestamp file doesn't exist, or the assets are obsolete, extract the assets from
+        // the apk.
+        String timestampToCreate = FileHelper.checkAssetTimestamp(context, cacheDir);
+        if (timestampToCreate != null) {
+            for (File file : cacheDir.listFiles()) {
+                FileHelper.deleteRecursively(file);
+            }
+            assetDir.mkdirs();
             compiledDexDir.mkdirs();
             try {
                 TraceEvent.begin("ExtractBootstrapJavaLibrary");
@@ -85,9 +89,10 @@
                 TraceEvent.begin("MoveBootstrapNativeLibrary");
                 // Rename the bootstrap library to prevent dlopen to think it is alread opened.
                 new File(assetDir, BOOTSTRAP_NATIVE_LIBRARY)
-                        .renameTo(File.createTempFile("bootstrap", ".so", assetDir));
+                        .renameTo(new File(nativeCreateTemporaryFile(
+                                assetDir.getAbsolutePath(), "bootstrap", ".so")));
                 TraceEvent.end("MoveBootstrapNativeLibrary");
-                new java.io.FileOutputStream(preparedSentinel).close();
+                new File(cacheDir, timestampToCreate).createNewFile();
             } catch (Exception e) {
                 Log.e(TAG, "Extraction of bootstrap files from assets failed.", e);
                 return false;
@@ -124,4 +129,7 @@
         return true;
     }
 
+    // Create a new temporary file. The android version has predictable names.
+    private static native String nativeCreateTemporaryFile(
+            String directory, String basename, String extension);
 }
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/FileHelper.java b/shell/android/apk/src/org/chromium/mojo/shell/FileHelper.java
index c455251..b33dd64 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/FileHelper.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/FileHelper.java
@@ -41,7 +41,7 @@
      * resources match the current APK. Otherwise returns a String that represents the filename of a
      * timestamp to create.
      */
-    private static String checkAssetTimestamp(Context context, File outputDir) {
+    public static String checkAssetTimestamp(Context context, File outputDir) {
         PackageManager pm = context.getPackageManager();
         PackageInfo pi = null;
 
@@ -152,7 +152,7 @@
     /**
      * Deletes a file or directory. Directory will be deleted even if not empty.
      */
-    static void deleteRecursively(File file) {
+    public static void deleteRecursively(File file) {
         if (file.isDirectory()) {
             for (File child : file.listFiles()) {
                 deleteRecursively(child);