File upload.

Implement java side of file upload.  Requires changes to external/
webkit to not break; requires changes to packages/apps/Browser
before it actually is used.

Fix http://b/issue?id=675743
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 9456ae1..d2861cb 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -19,17 +19,21 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.AssetManager;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.ParseException;
+import android.net.Uri;
 import android.net.WebAddress;
 import android.net.http.SslCertificate;
 import android.os.Handler;
 import android.os.Message;
+import android.provider.OpenableColumns;
 import android.util.Log;
 import android.util.TypedValue;
 
 import junit.framework.Assert;
 
+import java.io.InputStream;
 import java.net.URLEncoder;
 import java.util.HashMap;
 import java.util.Map;
@@ -463,6 +467,60 @@
     }
 
     /**
+     * Called by JNI.  Given a URI, find the associated file and return its size
+     * @param uri A String representing the URI of the desired file.
+     * @return int The size of the given file.
+     */
+    private int getFileSize(String uri) {
+        int size = 0;
+        Cursor cursor = mContext.getContentResolver().query(Uri.parse(uri),
+                new String[] { OpenableColumns.SIZE },
+                null,
+                null,
+                null);
+        if (cursor != null) {
+            try {
+                if (cursor.moveToNext()) {
+                    size = cursor.getInt(0);
+                }
+            } finally {
+                cursor.close();
+            }
+        }
+        return size;
+    }
+
+    /**
+     * Called by JNI.  Given a URI, a buffer, and an offset into the buffer,
+     * copy the resource into buffer.
+     * @param uri A String representing the URI of the desired file.
+     * @param buffer The byte array to copy the data into.
+     * @param offset The offet into buffer to place the data.
+     * @return int The size of the given file, or zero if it fails.
+     */
+    private int getFile(String uri, byte[] buffer, int offset) {
+        int size = 0;
+        try {
+            InputStream stream = mContext.getContentResolver()
+                            .openInputStream(Uri.parse(uri));
+            size = stream.available();
+            if (buffer != null && buffer.length - offset >= size) {
+                stream.read(buffer, offset, size);
+            } else {
+                size = 0;
+            }
+            stream.close();
+        } catch (java.io.FileNotFoundException e) {
+            Log.e(LOGTAG, "FileNotFoundException:" + e);
+            size = 0;
+        } catch (java.io.IOException e2) {
+            Log.e(LOGTAG, "IOException: " + e2);
+            size = 0;
+        }
+        return size;
+    }
+
+    /**
      * Start loading a resource.
      * @param loaderHandle The native ResourceLoader that is the target of the
      *                     data.
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 8d55247..bde18600 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -107,6 +107,7 @@
     private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT = 131;
     private static final int RECEIVED_TOUCH_ICON_URL             = 132;
     private static final int GET_VISITED_HISTORY                 = 133;
+    private static final int OPEN_FILE_CHOOSER                   = 134;
 
     // Message triggered by the client to resume execution
     private static final int NOTIFY                              = 200;
@@ -662,6 +663,12 @@
                     mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj);
                 }
                 break;
+
+            case OPEN_FILE_CHOOSER:
+                if (mWebChromeClient != null) {
+                    mWebChromeClient.openFileChooser((UploadFile) msg.obj);
+                }
+                break;
         }
     }
 
@@ -1348,4 +1355,40 @@
         msg.obj = callback;
         sendMessage(msg);
     }
+
+    private class UploadFile implements ValueCallback<Uri> {
+        private Uri mValue;
+        public void onReceiveValue(Uri value) {
+            mValue = value;
+            synchronized (CallbackProxy.this) {
+                CallbackProxy.this.notify();
+            }
+        }
+        public Uri getResult() {
+            return mValue;
+        }
+    }
+
+    /**
+     * Called by WebViewCore to open a file chooser.
+     */
+    /* package */ Uri openFileChooser() {
+        if (mWebChromeClient == null) {
+            return null;
+        }
+        Message myMessage = obtainMessage(OPEN_FILE_CHOOSER);
+        UploadFile uploadFile = new UploadFile();
+        myMessage.obj = uploadFile;
+        synchronized (this) {
+            sendMessage(myMessage);
+            try {
+                wait();
+            } catch (InterruptedException e) {
+                Log.e(LOGTAG,
+                        "Caught exception while waiting for openFileChooser");
+                Log.e(LOGTAG, Log.getStackTraceString(e));
+            }
+        }
+        return uploadFile.getResult();
+    }
 }
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 7f5b862..ae4f7c2 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -17,6 +17,7 @@
 package android.webkit;
 
 import android.graphics.Bitmap;
+import android.net.Uri;
 import android.os.Message;
 import android.view.View;
 
@@ -302,4 +303,13 @@
     public void getVisitedHistory(ValueCallback<String[]> callback) {
     }
 
+    /**
+     * Tell the client to open a file chooser.
+     * @param uploadFile A ValueCallback to set the URI of the file to upload.
+     *      onReceiveValue must be called to wake up the thread.
+     * @hide
+     */
+    public void openFileChooser(ValueCallback<Uri> uploadFile) {
+        uploadFile.onReceiveValue(null);
+    }
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index dbc42ca..3fe6961 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.graphics.Canvas;
 import android.graphics.DrawFilter;
 import android.graphics.Paint;
@@ -26,11 +27,13 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.provider.Browser;
+import android.provider.OpenableColumns;
 import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.KeyEvent;
@@ -273,6 +276,39 @@
         mCallbackProxy.onJsAlert(url, message);
     }
 
+
+    /**
+     * Called by JNI.  Open a file chooser to upload a file.
+     * @return String version of the URI plus the name of the file.
+     * FIXME: Just return the URI here, and in FileSystem::pathGetFileName, call
+     * into Java to get the filename.
+     */
+    private String openFileChooser() {
+        Uri uri = mCallbackProxy.openFileChooser();
+        if (uri == null) return "";
+        // Find out the name, and append it to the URI.
+        // Webkit will treat the name as the filename, and
+        // the URI as the path.  The URI will be used
+        // in BrowserFrame to get the actual data.
+        Cursor cursor = mContext.getContentResolver().query(
+                uri,
+                new String[] { OpenableColumns.DISPLAY_NAME },
+                null,
+                null,
+                null);
+        String name = "";
+        if (cursor != null) {
+            try {
+                if (cursor.moveToNext()) {
+                    name = cursor.getString(0);
+                }
+            } finally {
+                cursor.close();
+            }
+        }
+        return uri.toString() + "/" + name;
+    }
+
     /**
      * Notify the browser that the origin has exceeded it's database quota.
      * @param url The URL that caused the overflow.