Merge "Process FuseAppLoop messages in Handler.Callback" into oc-dev
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index 8edd637..088e726 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.os.ProxyFileDescriptorCallback;
 import android.os.Handler;
+import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.OsConstants;
@@ -28,10 +29,11 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.Map;
 import java.util.concurrent.ThreadFactory;
 
-public class FuseAppLoop {
+public class FuseAppLoop implements Handler.Callback {
     private static final String TAG = "FuseAppLoop";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final int ROOT_INODE = 1;
@@ -43,13 +45,11 @@
         }
     };
     private static final int FUSE_OK = 0;
+    private static final int ARGS_POOL_SIZE = 50;
 
     private final Object mLock = new Object();
     private final int mMountPointId;
     private final Thread mThread;
-    private final Handler mDefaultHandler;
-
-    private static final int CMD_FSYNC = 1;
 
     @GuardedBy("mLock")
     private final SparseArray<CallbackEntry> mCallbackMap = new SparseArray<>();
@@ -57,6 +57,9 @@
     @GuardedBy("mLock")
     private final BytesMap mBytesMap = new BytesMap();
 
+    @GuardedBy("mLock")
+    private final LinkedList<Args> mArgsPool = new LinkedList<>();
+
     /**
      * Sequential number can be used as file name and inode in AppFuse.
      * 0 is regarded as an error, 1 is mount point. So we start the number from 2.
@@ -83,7 +86,6 @@
             }
         });
         mThread.start();
-        mDefaultHandler = null;
     }
 
     public int registerCallback(@NonNull ProxyFileDescriptorCallback callback,
@@ -110,7 +112,8 @@
                     break;
                 }
             }
-            mCallbackMap.put(id, new CallbackEntry(callback, handler));
+            mCallbackMap.put(id, new CallbackEntry(
+                    callback, new Handler(handler.getLooper(), this)));
             return id;
         }
     }
@@ -137,78 +140,113 @@
     // Defined in FuseBuffer.h
     private static final int FUSE_MAX_WRITE = 256 * 1024;
 
+    @Override
+    public boolean handleMessage(Message msg) {
+        final Args args = (Args) msg.obj;
+        final CallbackEntry entry = args.entry;
+        final long inode = args.inode;
+        final long unique = args.unique;
+        final int size = args.size;
+        final long offset = args.offset;
+        final byte[] data = args.data;
+
+        try {
+            switch (msg.what) {
+                case FUSE_LOOKUP: {
+                    final long fileSize = entry.callback.onGetSize();
+                    synchronized (mLock) {
+                        if (mInstance != 0) {
+                            native_replyLookup(mInstance, unique, inode, fileSize);
+                        }
+                        recycleLocked(args);
+                    }
+                    break;
+                }
+                case FUSE_GETATTR: {
+                    final long fileSize = entry.callback.onGetSize();
+                    synchronized (mLock) {
+                        if (mInstance != 0) {
+                            native_replyGetAttr(mInstance, unique, inode, fileSize);
+                        }
+                        recycleLocked(args);
+                    }
+                    break;
+                }
+                case FUSE_READ:
+                    final int readSize = entry.callback.onRead(
+                            offset, size, data);
+                    synchronized (mLock) {
+                        if (mInstance != 0) {
+                            native_replyRead(mInstance, unique, readSize, data);
+                        }
+                        recycleLocked(args);
+                    }
+                    break;
+                case FUSE_WRITE:
+                    final int writeSize = entry.callback.onWrite(offset, size, data);
+                    synchronized (mLock) {
+                        if (mInstance != 0) {
+                            native_replyWrite(mInstance, unique, writeSize);
+                        }
+                        recycleLocked(args);
+                    }
+                    break;
+                case FUSE_FSYNC:
+                    entry.callback.onFsync();
+                    synchronized (mLock) {
+                        if (mInstance != 0) {
+                            native_replySimple(mInstance, unique, FUSE_OK);
+                        }
+                        recycleLocked(args);
+                    }
+                    break;
+                case FUSE_RELEASE:
+                    entry.callback.onRelease();
+                    synchronized (mLock) {
+                        if (mInstance != 0) {
+                            native_replySimple(mInstance, unique, FUSE_OK);
+                        }
+                        mBytesMap.stopUsing(entry.getThreadId());
+                        recycleLocked(args);
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown FUSE command: " + msg.what);
+            }
+        } catch (Exception error) {
+            synchronized (mLock) {
+                Log.e(TAG, "", error);
+                replySimpleLocked(unique, getError(error));
+                recycleLocked(args);
+            }
+        }
+
+        return true;
+    }
+
     // Called by JNI.
     @SuppressWarnings("unused")
     private void onCommand(int command, long unique, long inode, long offset, int size,
             byte[] data) {
-        synchronized(mLock) {
+        synchronized (mLock) {
             try {
-                final CallbackEntry entry = getCallbackEntryOrThrowLocked(inode);
-                entry.postRunnable(() -> {
-                    try {
-                        switch (command) {
-                            case FUSE_LOOKUP: {
-                                final long fileSize = entry.callback.onGetSize();
-                                synchronized (mLock) {
-                                    if (mInstance != 0) {
-                                        native_replyLookup(mInstance, unique, inode, fileSize);
-                                    }
-                                }
-                                break;
-                            }
-                            case FUSE_GETATTR: {
-                                final long fileSize = entry.callback.onGetSize();
-                                synchronized (mLock) {
-                                    if (mInstance != 0) {
-                                        native_replyGetAttr(mInstance, unique, inode, fileSize);
-                                    }
-                                }
-                                break;
-                            }
-                            case FUSE_READ:
-                                final int readSize = entry.callback.onRead(offset, size, data);
-                                synchronized (mLock) {
-                                    if (mInstance != 0) {
-                                        native_replyRead(mInstance, unique, readSize, data);
-                                    }
-                                }
-                                break;
-                            case FUSE_WRITE:
-                                final int writeSize = entry.callback.onWrite(offset, size, data);
-                                synchronized (mLock) {
-                                    if (mInstance != 0) {
-                                        native_replyWrite(mInstance, unique, writeSize);
-                                    }
-                                }
-                                break;
-                            case FUSE_FSYNC:
-                                entry.callback.onFsync();
-                                synchronized (mLock) {
-                                    if (mInstance != 0) {
-                                        native_replySimple(mInstance, unique, FUSE_OK);
-                                    }
-                                }
-                                break;
-                            case FUSE_RELEASE:
-                                entry.callback.onRelease();
-                                synchronized (mLock) {
-                                    if (mInstance != 0) {
-                                        native_replySimple(mInstance, unique, FUSE_OK);
-                                    }
-                                    mBytesMap.stopUsing(entry.getThreadId());
-                                }
-                                break;
-                            default:
-                                throw new IllegalArgumentException(
-                                        "Unknown FUSE command: " + command);
-                        }
-                    } catch (Exception error) {
-                        Log.e(TAG, "", error);
-                        replySimple(unique, getError(error));
-                    }
-                });
-            } catch (ErrnoException error) {
-                Log.e(TAG, "", error);
+                final Args args;
+                if (mArgsPool.size() == 0) {
+                    args = new Args();
+                } else {
+                    args = mArgsPool.pop();
+                }
+                args.unique = unique;
+                args.inode = inode;
+                args.offset = offset;
+                args.size = size;
+                args.data = data;
+                args.entry = getCallbackEntryOrThrowLocked(inode);
+                if (!args.entry.handler.sendMessage(
+                        Message.obtain(args.entry.handler, command, 0, 0, args))) {
+                    throw new ErrnoException("onCommand", OsConstants.EBADF);
+                }
+            } catch (Exception error) {
                 replySimpleLocked(unique, getError(error));
             }
         }
@@ -253,9 +291,9 @@
         return entry;
     }
 
-    private void replySimple(long unique, int result) {
-        synchronized (mLock) {
-            replySimpleLocked(unique, result);
+    private void recycleLocked(Args args) {
+        if (mArgsPool.size() < ARGS_POOL_SIZE) {
+            mArgsPool.add(args);
         }
     }
 
@@ -296,13 +334,6 @@
         long getThreadId() {
             return handler.getLooper().getThread().getId();
         }
-
-        void postRunnable(Runnable runnable) throws ErrnoException {
-            final boolean result = handler.post(runnable);
-            if (!result) {
-                throw new ErrnoException("postRunnable", OsConstants.EBADF);
-            }
-        }
     }
 
     /**
@@ -342,4 +373,13 @@
             mEntries.clear();
         }
     }
+
+    private static class Args {
+        long unique;
+        long inode;
+        long offset;
+        int size;
+        byte[] data;
+        CallbackEntry entry;
+    }
 }